dojox/form/FileUploader.js

  • Provides:

    • dojox.form.FileUploader
  • Requires:

    • dojox.embed.Flash in common
    • dojo.io.iframe in common in project dojo
    • dojox.html.styles in common
    • dijit._Widget in common in project dijit
    • dijit._Templated in common in project dijit
    • dojox.embed.flashVars in common
    • dijit._Contained in common in project dijit
  • dojox.form.FileUploader

    • type
      Function
    • chains:
      • dijit._Widget: (prototype)
      • dijit._Widget: (call)
      • dijit._Templated: (call)
      • dijit._Contained: (call)
    • mixins:
      • dijit._Templated.prototype: (prototype)
      • dijit._Contained.prototype: (prototype)
    • example
      fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
      or
      fileMask = [
      ["Jpeg File", 	"*.jpg;*.jpeg"],
      ["GIF File", 	"*.gif"],
      ["PNG File", 	"*.png"],
      ["All Images", 	"*.jpg;*.jpeg;*.gif;*.png"],
      ]
      NOTE: MacType is not supported, as it does not work very well.
      fileMask will work on a Mac, but differently than
      Windows.
    • source: [view]
        this._subs = [];
    • summary
  • dojox.form.FileUploader.swfPath

    • summary
  • dojox.form.FileUploader.templateString

    • summary
  • dojox.form.FileUploader.uploadUrl

    • type
      String
    • summary
      The url targeted for upload. An absolute URL is preferred. Relative URLs are
      changed to absolute.
  • dojox.form.FileUploader.isDebug

    • type
      Boolean
    • summary
      If true, outputs traces from the SWF to console. What exactly gets passed
      is very relative, and depends upon what traces have been left in the DEFT SWF.
  • dojox.form.FileUploader.devMode

    • type
      Boolean.
    • summary
      Re-implemented. devMode increases the logging, adding style tracing from the SWF.
      
      
      id: String
      The object id, just like any other widget in Dojo. However, this id
      is also used as a reference for the SWF
      id: "",
  • dojox.form.FileUploader.baseClass

    • type
      String
    • summary
      The name of the class that will style the button in a "normal" state.
      If baseClass is not defined, 'class' will be used.
      NOTE: By default the uploader will be styled like a dijit buttons and
      adhere to the the themes. Tundra, Soria, and Nihilo are supported.
      You can cascade the existing style by using 'class' or 'style'. If you
      overwrite baseClass, you should overwrite the remaing state classes
      that follow) as well.
  • dojox.form.FileUploader.hoverClass

    • type
      String
    • summary
      The name of the class that will style the button in a "hover" state. A specific
      class should be made to do this. Do not rely on a target like button:hover{...}
  • dojox.form.FileUploader.activeClass

    • type
      String
    • summary
      The name of the class that will style the button in a "press" state. A specific
      class should be made to do this. Do not rely on a target like button:active{...}
  • dojox.form.FileUploader.disabledClass

    • type
      String
    • summary
      The name of the class that will style the button when its disabled.
  • dojox.form.FileUploader.force

    • type
      String
    • summary
      Use "flash" to always use Flash (and hopefully force the user to download the plugin
      if they don't have it). Use "html" to always use the HTML uploader. An empty string
      (default) will check for the right version of Flash and use HTML if not available.
  • dojox.form.FileUploader.uploaderType

    • tags: readonly
    • type
      String
    • summary
      Internal. What type of uploader is being used: "flash" or "html"
  • dojox.form.FileUploader.flashObject

    • tags: readonly
    • type
      dojox.embed.Flash
    • summary
      The object that creates the SWF embed object. Mostly Internal.
  • dojox.form.FileUploader.flashMovie

    • tags: readonly
    • type
      Function
    • summary
      The SWF. Mostly Internal.
  • dojox.form.FileUploader.insideNode

    • tags: readonly
    • type
      HTMLNode
    • summary
      The div that holds the SWF and form/fileInput
  • dojox.form.FileUploader.deferredUploading

    • type
      Number
    • summary
      (1 - X)
      (Flash only) throttles the upload to a certain amount of files at a time.
      By default, Flash uploads file one at a time to the server, but in parallel.
      Firefox will try to queue all files at once, leading to problems. Set this
      to the amount to upload in parallel at a time.
      Generally, 1 should work fine, but you can experiment with queuing more than
      one at a time.
      This is of course ignored if selectMultipleFiles equals false.
  • dojox.form.FileUploader.fileListId

    • type
      String
    • summary
      The id of a dom node to be used as a container for the pending file list.
  • dojox.form.FileUploader.uploadOnChange

    • type
      Boolean
    • summary
      If true, uploads imediately after a file has been selected. If false,
      waits for upload() to be called.
  • dojox.form.FileUploader.selectMultipleFiles

    • type
      Boolean
    • summary
      If true and flash mode, multiple files may be selected from the dialog.
      If html mode, files are not uploaded until upload() is called. The references
      to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
  • dojox.form.FileUploader.htmlFieldName

    • type
      String
    • summary
      The name of the field of the fileInput that the server is expecting
  • dojox.form.FileUploader.flashFieldName

    • type
      String
    • summary
      The name of the field of the flash uploaded files that the server is expecting
  • dojox.form.FileUploader.fileMask

    • type
      Array
    • summary
      Array[Description, FileTypes], Array[...]...]
      (an array, or an array of arrays)
      Restrict file selection to certain file types
      Empty array defaults to "All Files"
  • dojox.form.FileUploader.minFlashVersion

    • type
      Number
    • summary
      The minimum of version of Flash player to target. 0 would always install Flash, 100
      would never install it. The Flash Player has supported multiple uploads since
      version 8, so it could go as low as that safely.
  • dojox.form.FileUploader.tabIndex

    • type
      Number|String
    • summary
      The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
      protection to prevent you from tabbing to the uploader. Stupid.
  • dojox.form.FileUploader.showProgress

    • type
      Boolean
    • summary
      If true, the button changes to a progress bar during upload.
  • dojox.form.FileUploader.progressMessage

    • type
      String
    • summary
      The message shown while the button is changed to a progress bar
  • dojox.form.FileUploader.progressBackgroundUrl

    • type
      String|Uri
    • summary
      The background image to use for the button-progress
  • dojox.form.FileUploader.progressBackgroundColor

    • type
      String|Number
    • summary
      The background color to use for the button-progress
  • dojox.form.FileUploader.progressWidgetId

    • type
      String
    • summary
      The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
      automatically.
  • dojox.form.FileUploader.skipServerCheck

    • type
      Boolean
    • summary
      If true, will not verify that the server was sent the correct format.
      This can be safely set to true. The purpose of the server side check
      is mainly to show the dev if they've implemented the different returns
      correctly.
  • dojox.form.FileUploader.serverTimeout

    • type
      Number
    • summary
      (milliseconds)
      The amount of time given to the uploaded file
      to wait for a server response. After this amount
      of time, the onComplete is fired but with a 'server timeout'
      error in the returned item.
  • dojox.form.FileUploader.log

    • type
      Function
    • source: [view]
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
    • summary
      Due to the excessive logging necessary to make this code happen,
      It's easier to turn it on and off here in one place.
      Also helpful if there are multiple uploaders on one page.
    • chains:
      • Array.prototype.slice: (call)
  • dojox.form.FileUploader.postMixInProperties

    • type
      Function
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();
    • summary
  • dojox.form.FileUploader.startup

    • type
      Function
    • source: [view]
    • summary
  • dojox.form.FileUploader.postCreate

    • type
      Function
    • source: [view]
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
    • summary
  • dojox.form.FileUploader.getHiddenWidget

    • type
      Function
    • source: [view]
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
    • summary
      Internal.
      If a parent widget has an onShow event, it is assumed
      that it is hidden and the parsing of the uploader is
      delayed until onShow fires. Note that the widget must
      fire onShow even if it is defaulted to showing/selected.
      this seems to work for Tabs (the primary fix).
  • dojox.form.FileUploader.getHiddenNode

    • type
      Function
    • parameters:
      • node: (typeof DomNode)
    • source: [view]
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
    • summary
      Internal.
      If a parent node is styled as display:none,
      returns that node. This node will be temporarilly
      changed to display:block. Note if the node is in
      a widget that has an onShow event, this is
      overridden.
  • dojox.form.FileUploader.getButtonStyle

    • type
      Function
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
    • summary
  • dojox.form.FileUploader.setButtonStyle

    • type
      Function
    • source: [view]
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
    • summary
      Internal.
      Set up internal dom nodes for button construction.
  • dojox.form.FileUploader.onChange

    • type
      Function
    • parameters:
      • dataArray: (typeof )
    • source: [view]
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
    • summary
      stub to connect
      Fires when files are selected
      Event is an array of last files selected
  • dojox.form.FileUploader.onProgress

    • type
      Function
    • parameters:
      • dataArray: (typeof )
    • source: [view]
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
    • summary
      Stub to connect
      Fires as progress returns from SWF
      Event is an array of all files uploading
      Can be connected to for HTML uploader,
      but will not return anything.
  • dojox.form.FileUploader.onComplete

    • type
      Function
    • parameters:
      • dataArray: (typeof )
    • source: [view]
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
    • summary
      stub to connect
      Fires when all files have uploaded
      Event is an array of all files
  • dojox.form.FileUploader.onCancel

    • type
      Function
    • source: [view]
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
    • summary
      Stub to connect
      Fires when dialog box has been closed
      without a file selection
  • dojox.form.FileUploader.onError

    • type
      Function
    • parameters:
      • evtObject: (typeof Object or String)
    • source: [view]
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
    • summary
      Fires on errors
      
      FIXME: Unsure of a standard form for receiving errors
  • dojox.form.FileUploader.onReady

    • type
      Function
    • parameters:
      • uploader: (typeof dojox.form.FileUploader)
    • source: [view]
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
    • summary
      Stub - Fired when dojox.embed.Flash has created the
      Flash object, but it has not necessarilly finished
      downloading, and is ready to be communicated with.
  • dojox.form.FileUploader.onLoad

    • type
      Function
    • parameters:
      • uploader: (typeof dojox.form.FileUploader)
    • source: [view]
        // summary:
        //  Stub - SWF has been downloaded 100%.
    • summary
      Stub - SWF has been downloaded 100%.
  • dojox.form.FileUploader.submit

    • type
      Function
    • parameters:
      • form: (typeof form node )
    • source: [view]
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
    • summary
      If FileUploader is in a form, and other data should be sent
      along with the files, use this instead of form submit.
    • returns
      Boolean
  • dojox.form.FileUploader.upload

    • type
      Function
    • parameters:
      • data: (typeof Object )
        Object
        postData to be sent to server
    • source: [view]
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
    • summary
      When called, begins file upload
  • dojox.form.FileUploader.removeFile

    • type
      Function
    • parameters:
      • name: (typeof String)
        The name of the file to be removed. Typically the file name,
        such as: picture01.png
      • noListEdit: (typeof Boolean)
        Internal. If true don't remove files from list.
    • source: [view]
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
    • summary
      Removes a file from the pending file list.
      Removes pending data from the Flash movie
      and fileInputes from the HTML uploader.
      If a file container node is bound, the file
      will also be removed.
  • dojox.form.FileUploader.destroy

    • type
      Function
    • source: [view]
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
    • summary
      Destroys uploader button
  • dojox.form.FileUploader._displayProgress

    • type
      Function
    • parameters:
      • display: (typeof Boolean or Number)
    • source: [view]
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
    • summary
      Shows and updates the built-in progress bar.
  • dojox.form.FileUploader._animateProgress

    • type
      Function
    • source: [view]
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
    • summary
      Internal. Animated the built-in progress bar
  • dojox.form.FileUploader._error

    • type
      Function
    • parameters:
      • evt: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader._addToFileList

    • type
      Function
    • source: [view]
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
    • summary
      Internal only. If there is a file list, adds a file to it.
      If you need to use a function such as this, connect to
      onChange and update outside of this widget.
  • dojox.form.FileUploader._change

    • type
      Function
    • parameters:
      • dataArray: (typeof )
    • source: [view]
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
    • summary
      Internal. Updates uploader selection
  • dojox.form.FileUploader._complete

    • type
      Function
    • parameters:
      • dataArray: (typeof )
    • source: [view]
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }
    • summary
      Internal. Handles tasks after files have finished uploading
  • dojox.form.FileUploader._progress

    • type
      Function
    • parameters:
      • dataObject: (typeof )
    • source: [view]
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);
    • summary
      Internal. Calculate progress
  • dojox.form.FileUploader._getDisabledAttr

    • type
      Function
    • source: [view]
        return this._disabled;
    • summary
      Internal. To get disabled use: widget.get(&quot;disabled&quot;);
  • dojox.form.FileUploader._setDisabledAttr

    • type
      Function
    • parameters:
      • disabled: (typeof )
    • source: [view]
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
    • summary
      Internal. To set disabled use: widget.set(&quot;disabled&quot;, true | false);
  • dojox.form.FileUploader._onFlashBlur

    • type
      Function
    • source: [view]
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
    • summary
      Internal. Detects when Flash movies reliquishes focus.
      We have to find all the tabIndexes in the doc and figure
      out whom to give focus to next.
  • dojox.form.FileUploader._disconnect

    • type
      Function
    • source: [view]
        dojo.forEach(this._cons, dojo.disconnect, dojo);
    • summary
      Internal. Disconnects fileInput in favor of new one.
  • dojox.form.FileUploader.uploadHTML

    • type
      Function
    • source: [view]
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
    • summary
      Internal. You could use this, but you should use upload() or submit();
      which can also handle the post data.
      
      NOTE on deferredUploading:
      This is not enabled for HTML. Workaround would be to force
      singleFile uploads.
      TODO:
      Investigate removing fileInputs and resending form
      multiple times adding each fileInput
  • dojox.form.FileUploader.createHtmlUploader

    • type
      Function
    • source: [view]
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
    • summary
      Internal. Fires of methods to build HTML Uploader.
  • dojox.form.FileUploader._connectInput

    • type
      Function
    • source: [view]
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
    • summary
      Internal. HTML Uploader connections. These get disconnected
      after upload or if multi upload.
  • dojox.form.FileUploader._checkHtmlCancel

    • type
      Function
    • parameters:
      • mouseType: (typeof )
    • source: [view]
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
    • summary
      Internal. Check if the dialog was opened and canceled without file selection.
  • dojox.form.FileUploader._styleContent

    • type
      Function
    • source: [view]
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }
    • summary
      Internal.Apply style to node
  • dojox.form.FileUploader._resetHTML

    • type
      Function
    • source: [view]
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
    • summary
      Internal. After upload, this is called to clear the form and build a new
      fileInput.
  • dojox.form.FileUploader._buildForm

    • type
      Function
    • source: [view]
        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('
      ');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
    • summary
      Build the form that holds the fileInput
  • dojox.form.FileUploader._buildFileInput

    • type
      Function
    • source: [view]
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
    • summary
      Build the fileInput field
  • dojox.form.FileUploader._renumberInputs

    • type
      Function
    • source: [view]
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
    • summary
  • dojox.form.FileUploader._setFormStyle

    • type
      Function
    • source: [view]
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
    • summary
      Apply a dynamic style to the form and input
  • dojox.form.FileUploader._setHtmlPostData

    • type
      Function
    • source: [view]
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
    • summary
      Internal.Apply postData to hidden fields in form
  • dojox.form.FileUploader.uploadFlash

    • type
      Function
    • source: [view]
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
    • summary
      Internal. You should use upload() or submit();
  • dojox.form.FileUploader.createFlashUploader

    • type
      Function
    • source: [view]
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();
    • summary
      Internal. Creates Flash Uploader
  • dojox.form.FileUploader._connectFlash

    • type
      Function
    • source: [view]
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
    • summary
      Subscribing to published topics coming from the
      Flash uploader.
    • description
      Sacrificing some readbilty for compactness. this.id
      will be on the beginning of the topic, so more than
      one uploader can be on a page and can have unique calls.
  • dojox.form.FileUploader._doSub

    • type
      Function
    • parameters:
      • subStr: (typeof )
      • funcStr: (typeof )
    • source: [view]
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
    • summary
      Internal. Shortcut for subscribes to Flash movie
  • dojox.form.FileUploader.urlencode

    • type
      Function
    • parameters:
      • url: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
       },


       _addToFileList: function(){
        // summary:
        //  Internal only. If there is a file list, adds a file to it.
        //  If you need to use a function such as this, connect to
        //  onChange and update outside of this widget.
        //
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
       },


       _change: function(dataArray){
        // summary:
        //  Internal. Updates uploader selection
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
       },


       _complete: function(dataArray){
        // summary:
        //  Internal. Handles tasks after files have finished uploading
        //
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }


       },


       _progress: function(dataObject){
        // summary:
        //  Internal. Calculate progress
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);


       },
       _getDisabledAttr: function(){
        // summary:
        //  Internal. To get disabled use: widget.get("disabled");
        return this._disabled;
       },


       _setDisabledAttr: function(disabled){
        // summary:
        //  Internal. To set disabled use: widget.set("disabled", true | false);
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
       },


       _onFlashBlur: function(){
        // summary:
        //  Internal. Detects when Flash movies reliquishes focus.
        //  We have to find all the tabIndexes in the doc and figure
        //  out whom to give focus to next.
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
       },
       _disconnect: function(){
        // summary:
        //  Internal. Disconnects fileInput in favor of new one.
        dojo.forEach(this._cons, dojo.disconnect, dojo);
       },


       /*************************
        *   HTML   *
        *************************/
       uploadHTML: function(){
        // summary:
        //  Internal. You could use this, but you should use upload() or submit();
        //  which can also handle the post data.
        //
        // NOTE on deferredUploading:
        //   This is not enabled for HTML. Workaround would be to force
        //  singleFile uploads.
        // TODO:
        //  Investigate removing fileInputs and resending form
        //  multiple times adding each fileInput
        //
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
       },


       createHtmlUploader: function(){
        // summary:
        //  Internal. Fires of methods to build HTML Uploader.
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
       },


       _connectInput: function(){
        // summary:
        //  Internal. HTML Uploader connections. These get disconnected
        //  after upload or if multi upload.
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _checkHtmlCancel: function(mouseType){
        // summary:
        //  Internal. Check if the dialog was opened and canceled without file selection.
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
       },


       _styleContent: function(){
        // summary:
        //  Internal.Apply style to node
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }


       },
       _resetHTML: function(){
        // summary:
        //  Internal. After upload, this is called to clear the form and build a new
        //  fileInput.
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
       },
       _buildForm: function(){
        // summary:
        //  Build the form that holds the fileInput
        //


        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
       },


       _buildFileInput: function(){
        // summary:
        //  Build the fileInput field
        //
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
       },


       _renumberInputs: function(){
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
       },


       _setFormStyle: function(){
        // summary:
        //  Apply a dynamic style to the form and input
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
       },


       _setHtmlPostData: function(){
        // summary:
        //  Internal.Apply postData to hidden fields in form
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
       },


       /*************************
        *   FLASH   *
        *************************/
       uploadFlash: function(){
        // summary:
        //  Internal. You should use upload() or submit();
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
       },


       createFlashUploader: function(){
        // summary:
        //  Internal. Creates Flash Uploader
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();


       },


       _connectFlash: function(){
        //  summary:
        //  Subscribing to published topics coming from the
        //  Flash uploader.
        //  description:
        //  Sacrificing some readbilty for compactness. this.id
        //  will be on the beginning of the topic, so more than
        //  one uploader can be on a page and can have unique calls.
        //
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _doSub: function(subStr, funcStr){
        // summary:
        //  Internal. Shortcut for subscribes to Flash movie
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
       },


       /*************************************
        *  DOM INSPECTION METHODS   *
        *************************************/


       urlencode: function(url){
        // Using symbols in place of URL chars that will break in Flash serialization.
        if(!url || url == "none"){
         return false;
        }
        return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader.isButton

    • type
      Function
    • parameters:
      • node: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
       },


       _addToFileList: function(){
        // summary:
        //  Internal only. If there is a file list, adds a file to it.
        //  If you need to use a function such as this, connect to
        //  onChange and update outside of this widget.
        //
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
       },


       _change: function(dataArray){
        // summary:
        //  Internal. Updates uploader selection
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
       },


       _complete: function(dataArray){
        // summary:
        //  Internal. Handles tasks after files have finished uploading
        //
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }


       },


       _progress: function(dataObject){
        // summary:
        //  Internal. Calculate progress
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);


       },
       _getDisabledAttr: function(){
        // summary:
        //  Internal. To get disabled use: widget.get("disabled");
        return this._disabled;
       },


       _setDisabledAttr: function(disabled){
        // summary:
        //  Internal. To set disabled use: widget.set("disabled", true | false);
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
       },


       _onFlashBlur: function(){
        // summary:
        //  Internal. Detects when Flash movies reliquishes focus.
        //  We have to find all the tabIndexes in the doc and figure
        //  out whom to give focus to next.
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
       },
       _disconnect: function(){
        // summary:
        //  Internal. Disconnects fileInput in favor of new one.
        dojo.forEach(this._cons, dojo.disconnect, dojo);
       },


       /*************************
        *   HTML   *
        *************************/
       uploadHTML: function(){
        // summary:
        //  Internal. You could use this, but you should use upload() or submit();
        //  which can also handle the post data.
        //
        // NOTE on deferredUploading:
        //   This is not enabled for HTML. Workaround would be to force
        //  singleFile uploads.
        // TODO:
        //  Investigate removing fileInputs and resending form
        //  multiple times adding each fileInput
        //
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
       },


       createHtmlUploader: function(){
        // summary:
        //  Internal. Fires of methods to build HTML Uploader.
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
       },


       _connectInput: function(){
        // summary:
        //  Internal. HTML Uploader connections. These get disconnected
        //  after upload or if multi upload.
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _checkHtmlCancel: function(mouseType){
        // summary:
        //  Internal. Check if the dialog was opened and canceled without file selection.
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
       },


       _styleContent: function(){
        // summary:
        //  Internal.Apply style to node
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }


       },
       _resetHTML: function(){
        // summary:
        //  Internal. After upload, this is called to clear the form and build a new
        //  fileInput.
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
       },
       _buildForm: function(){
        // summary:
        //  Build the form that holds the fileInput
        //


        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
       },


       _buildFileInput: function(){
        // summary:
        //  Build the fileInput field
        //
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
       },


       _renumberInputs: function(){
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
       },


       _setFormStyle: function(){
        // summary:
        //  Apply a dynamic style to the form and input
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
       },


       _setHtmlPostData: function(){
        // summary:
        //  Internal.Apply postData to hidden fields in form
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
       },


       /*************************
        *   FLASH   *
        *************************/
       uploadFlash: function(){
        // summary:
        //  Internal. You should use upload() or submit();
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
       },


       createFlashUploader: function(){
        // summary:
        //  Internal. Creates Flash Uploader
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();


       },


       _connectFlash: function(){
        //  summary:
        //  Subscribing to published topics coming from the
        //  Flash uploader.
        //  description:
        //  Sacrificing some readbilty for compactness. this.id
        //  will be on the beginning of the topic, so more than
        //  one uploader can be on a page and can have unique calls.
        //
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _doSub: function(subStr, funcStr){
        // summary:
        //  Internal. Shortcut for subscribes to Flash movie
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
       },


       /*************************************
        *  DOM INSPECTION METHODS   *
        *************************************/


       urlencode: function(url){
        // Using symbols in place of URL chars that will break in Flash serialization.
        if(!url || url == "none"){
         return false;
        }
        return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
       },


       isButton: function(node){
        // testing if button for styling purposes
        var tn = node.tagName.toLowerCase();
        return tn == "button" || tn == "input";
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader.getTextStyle

    • type
      Function
    • parameters:
      • node: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
       },


       _addToFileList: function(){
        // summary:
        //  Internal only. If there is a file list, adds a file to it.
        //  If you need to use a function such as this, connect to
        //  onChange and update outside of this widget.
        //
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
       },


       _change: function(dataArray){
        // summary:
        //  Internal. Updates uploader selection
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
       },


       _complete: function(dataArray){
        // summary:
        //  Internal. Handles tasks after files have finished uploading
        //
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }


       },


       _progress: function(dataObject){
        // summary:
        //  Internal. Calculate progress
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);


       },
       _getDisabledAttr: function(){
        // summary:
        //  Internal. To get disabled use: widget.get("disabled");
        return this._disabled;
       },


       _setDisabledAttr: function(disabled){
        // summary:
        //  Internal. To set disabled use: widget.set("disabled", true | false);
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
       },


       _onFlashBlur: function(){
        // summary:
        //  Internal. Detects when Flash movies reliquishes focus.
        //  We have to find all the tabIndexes in the doc and figure
        //  out whom to give focus to next.
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
       },
       _disconnect: function(){
        // summary:
        //  Internal. Disconnects fileInput in favor of new one.
        dojo.forEach(this._cons, dojo.disconnect, dojo);
       },


       /*************************
        *   HTML   *
        *************************/
       uploadHTML: function(){
        // summary:
        //  Internal. You could use this, but you should use upload() or submit();
        //  which can also handle the post data.
        //
        // NOTE on deferredUploading:
        //   This is not enabled for HTML. Workaround would be to force
        //  singleFile uploads.
        // TODO:
        //  Investigate removing fileInputs and resending form
        //  multiple times adding each fileInput
        //
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
       },


       createHtmlUploader: function(){
        // summary:
        //  Internal. Fires of methods to build HTML Uploader.
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
       },


       _connectInput: function(){
        // summary:
        //  Internal. HTML Uploader connections. These get disconnected
        //  after upload or if multi upload.
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _checkHtmlCancel: function(mouseType){
        // summary:
        //  Internal. Check if the dialog was opened and canceled without file selection.
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
       },


       _styleContent: function(){
        // summary:
        //  Internal.Apply style to node
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }


       },
       _resetHTML: function(){
        // summary:
        //  Internal. After upload, this is called to clear the form and build a new
        //  fileInput.
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
       },
       _buildForm: function(){
        // summary:
        //  Build the form that holds the fileInput
        //


        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
       },


       _buildFileInput: function(){
        // summary:
        //  Build the fileInput field
        //
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
       },


       _renumberInputs: function(){
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
       },


       _setFormStyle: function(){
        // summary:
        //  Apply a dynamic style to the form and input
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
       },


       _setHtmlPostData: function(){
        // summary:
        //  Internal.Apply postData to hidden fields in form
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
       },


       /*************************
        *   FLASH   *
        *************************/
       uploadFlash: function(){
        // summary:
        //  Internal. You should use upload() or submit();
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
       },


       createFlashUploader: function(){
        // summary:
        //  Internal. Creates Flash Uploader
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();


       },


       _connectFlash: function(){
        //  summary:
        //  Subscribing to published topics coming from the
        //  Flash uploader.
        //  description:
        //  Sacrificing some readbilty for compactness. this.id
        //  will be on the beginning of the topic, so more than
        //  one uploader can be on a page and can have unique calls.
        //
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _doSub: function(subStr, funcStr){
        // summary:
        //  Internal. Shortcut for subscribes to Flash movie
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
       },


       /*************************************
        *  DOM INSPECTION METHODS   *
        *************************************/


       urlencode: function(url){
        // Using symbols in place of URL chars that will break in Flash serialization.
        if(!url || url == "none"){
         return false;
        }
        return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
       },


       isButton: function(node){
        // testing if button for styling purposes
        var tn = node.tagName.toLowerCase();
        return tn == "button" || tn == "input";
       },


       getTextStyle: function(node){
        // getting font info
        var o = {};
        o.ff = dojo.style(node, "fontFamily");
        if(o.ff){
         o.ff = o.ff.replace(", ", ","); // remove spaces. IE in Flash no likee
         o.ff = o.ff.replace(/\"|\'/g, "");
         o.ff = o.ff == "sans-serif" ? "Arial" : o.ff; // Flash doesn't know what sans-serif is
         o.fw = dojo.style(node, "fontWeight");
         o.fi = dojo.style(node, "fontStyle");
         o.fs = parseInt(dojo.style(node, "fontSize"), 10);
         if(dojo.style(node, "fontSize").indexOf("%") > -1){
          // IE doesn't convert % to px. For god sakes.
          var n = node;
          while(n.tagName){
           if(dojo.style(n, "fontSize").indexOf("%") == -1){
            o.fs = parseInt(dojo.style(n, "fontSize"), 10);
            break;
           }
           if(n.tagName.toLowerCase()=="body"){
            // if everyting is %, the the font size is 16px * the %
            o.fs = 16 * .01 * parseInt(dojo.style(n, "fontSize"), 10);
           }
           n = n.parentNode;
          }
         }
         o.fc = new dojo.Color(dojo.style(node, "color")).toHex();
         o.fc = parseInt(o.fc.substring(1,Infinity),16);
        }
        o.lh = dojo.style(node, "lineHeight");
        o.ta = dojo.style(node, "textAlign");
        o.ta = o.ta == "start" || !o.ta ? "left" : o.ta;
        o.va = this.isButton(node) ? "middle" : o.lh == o.h ? "middle" : dojo.style(node, "verticalAlign");
        return o;
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader.getText

    • type
      Function
    • parameters:
      • node: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
       },


       _addToFileList: function(){
        // summary:
        //  Internal only. If there is a file list, adds a file to it.
        //  If you need to use a function such as this, connect to
        //  onChange and update outside of this widget.
        //
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
       },


       _change: function(dataArray){
        // summary:
        //  Internal. Updates uploader selection
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
       },


       _complete: function(dataArray){
        // summary:
        //  Internal. Handles tasks after files have finished uploading
        //
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }


       },


       _progress: function(dataObject){
        // summary:
        //  Internal. Calculate progress
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);


       },
       _getDisabledAttr: function(){
        // summary:
        //  Internal. To get disabled use: widget.get("disabled");
        return this._disabled;
       },


       _setDisabledAttr: function(disabled){
        // summary:
        //  Internal. To set disabled use: widget.set("disabled", true | false);
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
       },


       _onFlashBlur: function(){
        // summary:
        //  Internal. Detects when Flash movies reliquishes focus.
        //  We have to find all the tabIndexes in the doc and figure
        //  out whom to give focus to next.
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
       },
       _disconnect: function(){
        // summary:
        //  Internal. Disconnects fileInput in favor of new one.
        dojo.forEach(this._cons, dojo.disconnect, dojo);
       },


       /*************************
        *   HTML   *
        *************************/
       uploadHTML: function(){
        // summary:
        //  Internal. You could use this, but you should use upload() or submit();
        //  which can also handle the post data.
        //
        // NOTE on deferredUploading:
        //   This is not enabled for HTML. Workaround would be to force
        //  singleFile uploads.
        // TODO:
        //  Investigate removing fileInputs and resending form
        //  multiple times adding each fileInput
        //
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
       },


       createHtmlUploader: function(){
        // summary:
        //  Internal. Fires of methods to build HTML Uploader.
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
       },


       _connectInput: function(){
        // summary:
        //  Internal. HTML Uploader connections. These get disconnected
        //  after upload or if multi upload.
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _checkHtmlCancel: function(mouseType){
        // summary:
        //  Internal. Check if the dialog was opened and canceled without file selection.
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
       },


       _styleContent: function(){
        // summary:
        //  Internal.Apply style to node
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }


       },
       _resetHTML: function(){
        // summary:
        //  Internal. After upload, this is called to clear the form and build a new
        //  fileInput.
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
       },
       _buildForm: function(){
        // summary:
        //  Build the form that holds the fileInput
        //


        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
       },


       _buildFileInput: function(){
        // summary:
        //  Build the fileInput field
        //
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
       },


       _renumberInputs: function(){
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
       },


       _setFormStyle: function(){
        // summary:
        //  Apply a dynamic style to the form and input
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
       },


       _setHtmlPostData: function(){
        // summary:
        //  Internal.Apply postData to hidden fields in form
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
       },


       /*************************
        *   FLASH   *
        *************************/
       uploadFlash: function(){
        // summary:
        //  Internal. You should use upload() or submit();
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
       },


       createFlashUploader: function(){
        // summary:
        //  Internal. Creates Flash Uploader
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();


       },


       _connectFlash: function(){
        //  summary:
        //  Subscribing to published topics coming from the
        //  Flash uploader.
        //  description:
        //  Sacrificing some readbilty for compactness. this.id
        //  will be on the beginning of the topic, so more than
        //  one uploader can be on a page and can have unique calls.
        //
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _doSub: function(subStr, funcStr){
        // summary:
        //  Internal. Shortcut for subscribes to Flash movie
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
       },


       /*************************************
        *  DOM INSPECTION METHODS   *
        *************************************/


       urlencode: function(url){
        // Using symbols in place of URL chars that will break in Flash serialization.
        if(!url || url == "none"){
         return false;
        }
        return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
       },


       isButton: function(node){
        // testing if button for styling purposes
        var tn = node.tagName.toLowerCase();
        return tn == "button" || tn == "input";
       },


       getTextStyle: function(node){
        // getting font info
        var o = {};
        o.ff = dojo.style(node, "fontFamily");
        if(o.ff){
         o.ff = o.ff.replace(", ", ","); // remove spaces. IE in Flash no likee
         o.ff = o.ff.replace(/\"|\'/g, "");
         o.ff = o.ff == "sans-serif" ? "Arial" : o.ff; // Flash doesn't know what sans-serif is
         o.fw = dojo.style(node, "fontWeight");
         o.fi = dojo.style(node, "fontStyle");
         o.fs = parseInt(dojo.style(node, "fontSize"), 10);
         if(dojo.style(node, "fontSize").indexOf("%") > -1){
          // IE doesn't convert % to px. For god sakes.
          var n = node;
          while(n.tagName){
           if(dojo.style(n, "fontSize").indexOf("%") == -1){
            o.fs = parseInt(dojo.style(n, "fontSize"), 10);
            break;
           }
           if(n.tagName.toLowerCase()=="body"){
            // if everyting is %, the the font size is 16px * the %
            o.fs = 16 * .01 * parseInt(dojo.style(n, "fontSize"), 10);
           }
           n = n.parentNode;
          }
         }
         o.fc = new dojo.Color(dojo.style(node, "color")).toHex();
         o.fc = parseInt(o.fc.substring(1,Infinity),16);
        }
        o.lh = dojo.style(node, "lineHeight");
        o.ta = dojo.style(node, "textAlign");
        o.ta = o.ta == "start" || !o.ta ? "left" : o.ta;
        o.va = this.isButton(node) ? "middle" : o.lh == o.h ? "middle" : dojo.style(node, "verticalAlign");
        return o;
       },


       getText: function(node){
        // Get the text of the button. It's possible to use HTML in the Flash Button,
        // but the results are not spectacular.
        var cn = dojo.trim(node.innerHTML);
        if(cn.indexOf("<") >- 1){
         cn = escape(cn);
        }
        return cn;
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader.getStyle

    • type
      Function
    • parameters:
      • node: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
       },


       _addToFileList: function(){
        // summary:
        //  Internal only. If there is a file list, adds a file to it.
        //  If you need to use a function such as this, connect to
        //  onChange and update outside of this widget.
        //
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
       },


       _change: function(dataArray){
        // summary:
        //  Internal. Updates uploader selection
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
       },


       _complete: function(dataArray){
        // summary:
        //  Internal. Handles tasks after files have finished uploading
        //
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }


       },


       _progress: function(dataObject){
        // summary:
        //  Internal. Calculate progress
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);


       },
       _getDisabledAttr: function(){
        // summary:
        //  Internal. To get disabled use: widget.get("disabled");
        return this._disabled;
       },


       _setDisabledAttr: function(disabled){
        // summary:
        //  Internal. To set disabled use: widget.set("disabled", true | false);
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
       },


       _onFlashBlur: function(){
        // summary:
        //  Internal. Detects when Flash movies reliquishes focus.
        //  We have to find all the tabIndexes in the doc and figure
        //  out whom to give focus to next.
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
       },
       _disconnect: function(){
        // summary:
        //  Internal. Disconnects fileInput in favor of new one.
        dojo.forEach(this._cons, dojo.disconnect, dojo);
       },


       /*************************
        *   HTML   *
        *************************/
       uploadHTML: function(){
        // summary:
        //  Internal. You could use this, but you should use upload() or submit();
        //  which can also handle the post data.
        //
        // NOTE on deferredUploading:
        //   This is not enabled for HTML. Workaround would be to force
        //  singleFile uploads.
        // TODO:
        //  Investigate removing fileInputs and resending form
        //  multiple times adding each fileInput
        //
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
       },


       createHtmlUploader: function(){
        // summary:
        //  Internal. Fires of methods to build HTML Uploader.
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
       },


       _connectInput: function(){
        // summary:
        //  Internal. HTML Uploader connections. These get disconnected
        //  after upload or if multi upload.
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _checkHtmlCancel: function(mouseType){
        // summary:
        //  Internal. Check if the dialog was opened and canceled without file selection.
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
       },


       _styleContent: function(){
        // summary:
        //  Internal.Apply style to node
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }


       },
       _resetHTML: function(){
        // summary:
        //  Internal. After upload, this is called to clear the form and build a new
        //  fileInput.
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
       },
       _buildForm: function(){
        // summary:
        //  Build the form that holds the fileInput
        //


        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
       },


       _buildFileInput: function(){
        // summary:
        //  Build the fileInput field
        //
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
       },


       _renumberInputs: function(){
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
       },


       _setFormStyle: function(){
        // summary:
        //  Apply a dynamic style to the form and input
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
       },


       _setHtmlPostData: function(){
        // summary:
        //  Internal.Apply postData to hidden fields in form
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
       },


       /*************************
        *   FLASH   *
        *************************/
       uploadFlash: function(){
        // summary:
        //  Internal. You should use upload() or submit();
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
       },


       createFlashUploader: function(){
        // summary:
        //  Internal. Creates Flash Uploader
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();


       },


       _connectFlash: function(){
        //  summary:
        //  Subscribing to published topics coming from the
        //  Flash uploader.
        //  description:
        //  Sacrificing some readbilty for compactness. this.id
        //  will be on the beginning of the topic, so more than
        //  one uploader can be on a page and can have unique calls.
        //
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _doSub: function(subStr, funcStr){
        // summary:
        //  Internal. Shortcut for subscribes to Flash movie
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
       },


       /*************************************
        *  DOM INSPECTION METHODS   *
        *************************************/


       urlencode: function(url){
        // Using symbols in place of URL chars that will break in Flash serialization.
        if(!url || url == "none"){
         return false;
        }
        return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
       },


       isButton: function(node){
        // testing if button for styling purposes
        var tn = node.tagName.toLowerCase();
        return tn == "button" || tn == "input";
       },


       getTextStyle: function(node){
        // getting font info
        var o = {};
        o.ff = dojo.style(node, "fontFamily");
        if(o.ff){
         o.ff = o.ff.replace(", ", ","); // remove spaces. IE in Flash no likee
         o.ff = o.ff.replace(/\"|\'/g, "");
         o.ff = o.ff == "sans-serif" ? "Arial" : o.ff; // Flash doesn't know what sans-serif is
         o.fw = dojo.style(node, "fontWeight");
         o.fi = dojo.style(node, "fontStyle");
         o.fs = parseInt(dojo.style(node, "fontSize"), 10);
         if(dojo.style(node, "fontSize").indexOf("%") > -1){
          // IE doesn't convert % to px. For god sakes.
          var n = node;
          while(n.tagName){
           if(dojo.style(n, "fontSize").indexOf("%") == -1){
            o.fs = parseInt(dojo.style(n, "fontSize"), 10);
            break;
           }
           if(n.tagName.toLowerCase()=="body"){
            // if everyting is %, the the font size is 16px * the %
            o.fs = 16 * .01 * parseInt(dojo.style(n, "fontSize"), 10);
           }
           n = n.parentNode;
          }
         }
         o.fc = new dojo.Color(dojo.style(node, "color")).toHex();
         o.fc = parseInt(o.fc.substring(1,Infinity),16);
        }
        o.lh = dojo.style(node, "lineHeight");
        o.ta = dojo.style(node, "textAlign");
        o.ta = o.ta == "start" || !o.ta ? "left" : o.ta;
        o.va = this.isButton(node) ? "middle" : o.lh == o.h ? "middle" : dojo.style(node, "verticalAlign");
        return o;
       },


       getText: function(node){
        // Get the text of the button. It's possible to use HTML in the Flash Button,
        // but the results are not spectacular.
        var cn = dojo.trim(node.innerHTML);
        if(cn.indexOf("<") >- 1){
         cn = escape(cn);
        }
        return cn;
       },


       getStyle: function(node){
        // getting the style of a node. Using very abbreviated characters which the
        // Flash movie understands.
        var o = {};
        var dim = dojo.contentBox(node);
        var pad = dojo._getPadExtents(node);
        o.p = [pad.t, pad.w-pad.l, pad.h-pad.t, pad.l];
        o.w = dim.w + pad.w;
        o.h = dim.h + pad.h;
        o.d = dojo.style(node, "display");
        var clr = new dojo.Color(dojo.style(node, "backgroundColor"));
        // if no color, Safari sets #000000 and alpha=0 since we don't support alpha,
        // it makes black - make it white
        o.bc = clr.a == 0 ? "#ffffff" : clr.toHex();
        o.bc = parseInt(o.bc.substring(1,Infinity),16);
        var url = this.urlencode(dojo.style(node, "backgroundImage"));
        if(url){
         o.bi = {
          url:url,
          rp:dojo.style(node, "backgroundRepeat"),
          pos: escape(dojo.style(node, "backgroundPosition"))
         };
         if(!o.bi.pos){
          // IE does Xpx and Ypx, not "X% Y%"
          var rx = dojo.style(node, "backgroundPositionX");
          var ry = dojo.style(node, "backgroundPositionY");
          rx = (rx == "left") ? "0%" : (rx == "right") ? "100%" : rx;
          ry = (ry == "top") ? "0%" : (ry == "bottom") ? "100%" : ry;
          o.bi.pos = escape(rx+" "+ry);
         }
        }
        return dojo.mixin(o, this.getTextStyle(node));
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader.getTempNodeStyle

    • type
      Function
    • parameters:
      • node: (typeof )
      • _class: (typeof )
      • isDijitButton: (typeof )
    • source: [view]
      dojo.provide("dojox.form.FileUploader");
      dojo.require("dojox.embed.Flash");
      dojo.require("dojo.io.iframe");
      dojo.require("dojox.html.styles");
      dojo.require("dijit._Widget");
      dojo.require("dijit._Templated");
      dojo.require("dojox.embed.flashVars");
      dojo.require("dijit._Contained");


      console.warn("DEPRECATED: dojox.form.FileUploader is no longer supported and will be removed in 2.0. Suggested that you use dojox.form.Uploader instead.");


       // Usage Notes:
       //  To center text vertically, use vertical-align:middle;
       //   which emulates a boxModel button. Using line-height to center text
       //   can cause height problems in IE6




      dojo.declare("dojox.form.FileUploader", [dijit._Widget, dijit._Templated, dijit._Contained], {
       // version:
       //  1.5 (deprecated)
       // summary:
       //   Handles File Uploading to a server (PHP script included for testing)
       //
       //  FileUploader is now a WIDGET. You do not have to pass a button
       //  in. Passing a button is still supported until version 1.5 to maintain
       //  backwards compatibility, but it is not reccomended. Just create your
       //  uploader like any other widget.
       //
       // description:
       //  If the correct version of Flash Player is available (> 9.0) , a SWF
       //  is used. If Flash Player is not installed or is outdated, a typical
       //  html fileInput is used. This process can be overridden with
       //  force:"flash" or force:"html".
       //
       //  FileUploader works with Flash 10.
       //
       //  The button styles are now recreated in Flash, so there is no longer
       //  using an invisible Flash movie with wmode=transparent. This way the Flash button
       //  is actually placed inline with the DOM, not floating above it and constantly
       //  resetting its position. The "Windows Firefox clickable bug" should be fixed (and
       //  hopefully some Linux problems).
       //
       //  The HTML button is created in a new way and it is now inline as is the
       //  FLash button. Styling is much easier and more versatile.
       //
       // Dependencies:
       //  FileUploader no longer uses FileInput.css. It now uses FileUploader.css
       //  See requires for JavaScript dependencies.
       //
       // NEW FEATURES -
       //  There are a ton of features and fixes in this version.
       //   Disabled: Can be toggled with widget.attr("disable", true|false)
       //   Submit: A convenience method has been added for if the uploader is in a form.
       //     Instead of submitting the form, call uploader.submit(theForm), and the
       //     Uploader will handle all of the form values and post the data.
       //   Selected List: If passing the ID of a container, the Uploaders will populate it
       //     with the selected files.
       //   Deleting Files: You can now delete pending files.
       //   Progress Built in: showProgress:true will change the button to a progress
       //     bar on upload.
       //   Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
       //     widget. If the Progress widget is initially hidden, it will change to
       //     visible and then restored after upload.
       //   A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
       //     to browser limtations)
       //   Deferred Uploading: (Flash only) throttles the upload to one file at a time
       //
       //
       // CDN USERS -
       //  FileUpload now works with the CDN but with limitations. The SWF must
       //  be from the same domain as the HTML page. 'swfPath' has been exposed
       //  so that you may link to that file (could of course be the same SWF in
       //  dojox resource folder). The SWF will *NOT* work from the
       //  CDN server. This would require a special XML file that would allow
       //  access to your server, and the logistics to that is impossible.
       //
       // LIMITATIONS
       //  - This is not designed to be a part of a form, it contains its own. (See submit())
       //  - Currently does not in a Dialog box or a Tab where it is not initially visible,
       //  - The default style inherits font sizes - but a parent container should have a font size
       //   set somewhere of the results could be inconsistent.
       //
       // OPERA USERS -
       //  It works better than the 1.3 version. fileInputs apperantly can't have opacity
       //  set to zero. The Flash uploader works but files are auto-uploaded. Must be a
       //  flashVar problem.
       //
       // Safari Bug note:
       // The bug is in the way Safari handles the connection:
       //  https://bugs.webkit.org/show_bug.cgi?id=5760
       //  I added this to the virtual host in the Apache conf file, and now it
       //  works like a charm:
       //  BrowserMatch Safari nokeepalive
       //
       swfPath: dojo.config.uploaderPath || dojo.moduleUrl("dojox.form", "resources/fileuploader.swf"),




       templateString:'
      ',


       // uploadUrl: String
       //  The url targeted for upload. An absolute URL is preferred. Relative URLs are
       //  changed to absolute.
       uploadUrl: "",
       //
       // isDebug: Boolean
       //  If true, outputs traces from the SWF to console. What exactly gets passed
       //  is very relative, and depends upon what traces have been left in the DEFT SWF.
       isDebug:false,
       //
       // devMode: Boolean.
       //  Re-implemented. devMode increases the logging, adding style tracing from the SWF.
       devMode:false,
       //
       // id: String
       //  The object id, just like any other widget in Dojo. However, this id
       //  is also used as a reference for the SWF
       // id: "",
       //
       // baseClass: String
       //  The name of the class that will style the button in a "normal" state.
       //  If baseClass is not defined, 'class' will be used.
       //  NOTE: By default the uploader will be styled like a dijit buttons and
       //  adhere to the the themes. Tundra, Soria, and Nihilo are supported.
       //  You can cascade the existing style by using 'class' or 'style'. If you
       //  overwrite baseClass, you should overwrite the remaing state classes
       //  that follow) as well.
       baseClass:"dojoxUploaderNorm",
       //
       // hoverClass: String
       //  The name of the class that will style the button in a "hover" state. A specific
       //  class should be made to do this. Do not rely on a target like button:hover{...}
       hoverClass:"dojoxUploaderHover",
       //
       // activeClass: String
       //  The name of the class that will style the button in a "press" state. A specific
       //  class should be made to do this. Do not rely on a target like button:active{...}
       activeClass:"dojoxUploaderActive",
       //
       // disabledClass: String
       //  The name of the class that will style the button when its disabled.
       disabledClass:"dojoxUploaderDisabled",
       //
       // force: String
       //  Use "flash" to always use Flash (and hopefully force the user to download the plugin
       //  if they don't have it). Use "html" to always use the HTML uploader. An empty string
       //  (default) will check for the right version of Flash and use HTML if not available.
       force:"",
       //
       // uploaderType: [readonly] String
       //  Internal. What type of uploader is being used: "flash" or "html"
       uploaderType:"",
       //
       // flashObject: [readonly] dojox.embed.Flash
       //  The object that creates the SWF embed object. Mostly Internal.
       flashObject: null,
       //
       // flashMovie: [readonly] Function
       //  The SWF. Mostly Internal.
       flashMovie: null,
       //
       // insideNode: [readonly] HTMLNode
       //  The div that holds the SWF and form/fileInput
       insideNode: null,
       //
       // deferredUploading: Number (1 - X)
       //  (Flash only) throttles the upload to a certain amount of files at a time.
       //  By default, Flash uploads file one at a time to the server, but in parallel.
       //  Firefox will try to queue all files at once, leading to problems. Set this
       //  to the amount to upload in parallel at a time.
       //  Generally, 1 should work fine, but you can experiment with queuing more than
       //  one at a time.
       //  This is of course ignored if selectMultipleFiles equals false.
       deferredUploading:1,
       //
       // fileListId: String
       //  The id of a dom node to be used as a container for the pending file list.
       fileListId:"",
       //
       // uploadOnChange: Boolean
       //  If true, uploads imediately after a file has been selected. If false,
       //  waits for upload() to be called.
       uploadOnChange: false,
       //
       // selectMultipleFiles: Boolean
       //  If true and flash mode, multiple files may be selected from the dialog.
       //  If html mode, files are not uploaded until upload() is called. The references
       //  to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
       selectMultipleFiles: true,
       //
       // htmlFieldName: String
       //  The name of the field of the fileInput that the server is expecting
       htmlFieldName:"uploadedfile",
       //
       // flashFieldName: String
       //  The name of the field of the flash uploaded files that the server is expecting
       flashFieldName:"flashUploadFiles",
       //
       // fileMask: Array[ Array[Description, FileTypes], Array[...]...]
       //   (an array, or an array of arrays)
       //  Restrict file selection to certain file types
       //   Empty array defaults to "All Files"
       // example:
       // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
       // or
       // fileMask = [
       //  ["Jpeg File",  "*.jpg;*.jpeg"],
       //  ["GIF File",  "*.gif"],
       //  ["PNG File",  "*.png"],
       //  ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
       // ]
       // NOTE: MacType is not supported, as it does not work very well.
       //   fileMask will work on a Mac, but differently than
       //   Windows.
       fileMask: null,
       //
       // minFlashVersion: Number
       //  The minimum of version of Flash player to target. 0 would always install Flash, 100
       //  would never install it. The Flash Player has supported multiple uploads since
       //  version 8, so it could go as low as that safely.
       minFlashVersion:9,
       //
       // tabIndex: Number|String
       //  The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
       //  protection to prevent you from tabbing to the uploader. Stupid.
       tabIndex:-1,
       //
       // showProgress: Boolean
       //  If true, the button changes to a progress bar during upload.
       showProgress:false,
       //
       // progressMessage: String
       //  The message shown while the button is changed to a progress bar
       progressMessage:"Loading",
       //
       // progressBackgroundUrl: String|Uri
       //  The background image to use for the button-progress
       progressBackgroundUrl:dojo.moduleUrl("dijit", "themes/tundra/images/buttonActive.png"),
       //
       // progressBackgroundColor: String|Number
       //  The background color to use for the button-progress
       progressBackgroundColor:"#ededed",
       //
       // progressWidgetId:String
       //  The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
       //  automatically.
       progressWidgetId:"",
       //
       // skipServerCheck: Boolean
       //   If true, will not verify that the server was sent the correct format.
       //  This can be safely set to true. The purpose of the server side check
       //  is mainly to show the dev if they've implemented the different returns
       //  correctly.
       skipServerCheck:false,
       //
       // serverTimeout:Number (milliseconds)
       //  The amount of time given to the uploaded file
       //  to wait for a server response. After this amount
       //  of time, the onComplete is fired but with a 'server timeout'
       //  error in the returned item.
       serverTimeout: 5000,




       log: function(){
        // summary:
        //  Due to the excessive logging necessary to make this code happen,
        //  It's easier to turn it on and off here in one place.
        //  Also helpful if there are multiple uploaders on one page.
        if(this.isDebug){
         console["log"](Array.prototype.slice.call(arguments).join(" "));
        }
       },


       constructor: function(){
        this._subs = [];
       },


       postMixInProperties: function(){
        // internal stuff:
        this.fileList = [];
        this._cons = [];
        this.fileMask = this.fileMask || [];
        this.fileInputs = [];
        this.fileCount = 0;
        this.flashReady = false;
        this._disabled = false;
        this.force = this.force.toLowerCase(); // Pete FTW.
        this.uploaderType = ((dojox.embed.Flash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
        this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;


        this._refNode = this.srcNodeRef;


        this.getButtonStyle();




       },


       startup: function(){


       },


       postCreate: function(){
        this.inherited(arguments);


        // internal stuff:
        this.setButtonStyle();
        var createMethod;
        if(this.uploaderType == "flash"){
         createMethod = "createFlashUploader";
        }else{
         this.uploaderType = "html";
         createMethod = "createHtmlUploader";


        }


        this[createMethod]();


        if(this.fileListId){
         this.connect(dojo.byId(this.fileListId), "click", function(evt){
          var p = evt.target.parentNode.parentNode.parentNode; // in a table
          if(p.id && p.id.indexOf("file_")>-1){
           this.removeFile(p.id.split("file_")[1]);
          }
         });
        }


        // cleaning up solves memory leak issues in the HTML version
        dojo.addOnUnload(this, this.destroy);
       },


       getHiddenWidget: function(){
        // summary:
        //  Internal.
        //  If a parent widget has an onShow event, it is assumed
        //  that it is hidden and the parsing of the uploader is
        //  delayed until onShow fires. Note that the widget must
        //  fire onShow even if it is defaulted to showing/selected.
        //  this seems to work for Tabs (the primary fix).
        //
        var node = this.domNode.parentNode;
        while(node){
         var id = node.getAttribute && node.getAttribute("widgetId");
         if(id && dijit.byId(id).onShow){
          return dijit.byId(id);
         }
         node = node.parentNode;
        }
        return null;
       },


       getHiddenNode: function(/*DomNode*/ node){
        // summary:
        //  Internal.
        //  If a parent node is styled as display:none,
        //  returns that node. This node will be temporarilly
        //  changed to display:block. Note if the node is in
        //  a widget that has an onShow event, this is
        //  overridden.
        //
        if(!node){ return null; }
        var hidden = null;
        var p = node.parentNode;
        while(p && p.tagName.toLowerCase() != "body"){
         var d = dojo.style(p, "display");
         if(d == "none"){
          hidden = p;
          break;
         }
         p = p.parentNode;
        }
        return hidden;
       },


       getButtonStyle: function(){
        // summary:
        //  Internal.
        //  Get necessary style information from srcRefNode and
        //  assigned styles
        //




        // TODO:
        //  To call this from postCreate....
        //  could do the style stuff initially, but if hidden they will be bad sizes
        //  could then redo the sizes
        //  alt is to create a genuine button and copy THAT instead of how doing now


        var refNode = this.srcNodeRef;
        this._hiddenNode = this.getHiddenNode(refNode);
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "block");
        }


        if(!refNode && this.button && this.button.domNode){
         // backwards compat for a Dijit button
         var isDijitButton = true;
         var cls = this.button.domNode.className + " dijitButtonNode";
         var txt = this.getText(dojo.query(".dijitButtonText", this.button.domNode)[0]);
         var domTxt = '';
         refNode = dojo.place(domTxt, this.button.domNode, "after");  /// Pete doesn't like this?
         this.srcNodeRef = refNode;
         this.button.destroy();


         this.baseClass = "dijitButton";
         this.hoverClass = "dijitButtonHover";
         this.pressClass = "dijitButtonActive";
         this.disabledClass = "dijitButtonDisabled";


        }else if(!this.srcNodeRef && this.button){
         refNode = this.button;
        }


        if(dojo.attr(refNode, "class")){
         this.baseClass += " " + dojo.attr(refNode, "class");
        }
        dojo.attr(refNode, "class", this.baseClass);




        this.norm = this.getStyle(refNode);
        this.width = this.norm.w;
        this.height = this.norm.h;


        if(this.uploaderType == "flash"){


         this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
         this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
         this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);


         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm,
          ov:this.over,
          dn:this.down,
          ds:this.dsbl
         };
        }else{
         this.fhtml = {
          cn:this.getText(refNode),
          nr:this.norm
         }
         if(this.norm.va == "middle"){
          this.norm.lh = this.norm.h;
         }
        }


        if(this.devMode){
         this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
         this.log("fhtml:", this.fhtml)
         this.log("norm:", this.norm)
         this.log("over:", this.over)
         this.log("down:", this.down)


        }
       },


       setButtonStyle: function(){
        // summary:
        //  Internal.
        //  Set up internal dom nodes for button construction.
        //
        dojo.style(this.domNode, {
         width:this.fhtml.nr.w+"px",
         height:(this.fhtml.nr.h)+"px",
         padding:"0px",
         lineHeight: "normal",
         position:"relative"
        });
        if(this.uploaderType == "html" && this.norm.va == "middle"){
         dojo.style(this.domNode, "lineHeight", this.norm.lh + "px");
        }
        if(this.showProgress){
         this.progTextNode.innerHTML = this.progressMessage;
         dojo.style(this.progTextNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          lineHeight:(this.fhtml.nr.h+0)+"px",
          position:"absolute"
         });
         dojo.style(this.progNode, {
          width:this.fhtml.nr.w+"px",
          height:(this.fhtml.nr.h+0)+"px",
          padding:"0px",
          margin:"0px",
          left:"0px",
          position:"absolute",
          display:"none",
          backgroundImage:"url("+this.progressBackgroundUrl+")",
          backgroundPosition:"bottom",
          backgroundRepeat:"repeat-x",
          backgroundColor:this.progressBackgroundColor
         });
        }else{
         dojo.destroy(this.progNode);
        }
        dojo.style(this.insideNode,{
         position:"absolute",
         top:"0px",
         left:"0px",
         display:""
        });
        dojo.addClass(this.domNode, this.srcNodeRef.className);
        if(this.fhtml.nr.d.indexOf("inline")>-1){
         dojo.addClass(this.domNode, "dijitInline");
        }


        try{
         this.insideNode.innerHTML = this.fhtml.cn;
        }catch(e){
         // You have got to be kidding me. IE does us he favor of checking that
         // we aren't inserting the improper type of content with innerHTML into
         // an inline element. Alert us with an "Unknown Runtime Error". You can't
         // MAKE this stuff up.
         //
         if(this.uploaderType == "flash"){
         this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
          dojo.body().appendChild(this.insideNode);
          this.insideNode.innerHTML = this.fhtml.cn;
          var c = dojo.connect(this, "onReady", this, function(){ dojo.disconnect(c);
           this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
           this.domNode.appendChild(this.insideNode);
          });
         }else{
          this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
         }
        }
        if(this._hiddenNode){
         dojo.style(this._hiddenNode, "display", "none");
        }
       },




       /*************************
        *  Public Events  *
        *************************/


       // The following events are inherited from _Widget and still may be connected:
       // onClick
       // onMouseUp
       // onMouseDown
       // onMouseOver
       // onMouseOut


       onChange: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when files are selected
        //   Event is an array of last files selected
       },


       onProgress: function(dataArray){
        // summary:
        //   Stub to connect
        //   Fires as progress returns from SWF
        //   Event is an array of all files uploading
        //  Can be connected to for HTML uploader,
        //  but will not return anything.
       },


       onComplete: function(dataArray){
        // summary:
        //   stub to connect
        //   Fires when all files have uploaded
        //   Event is an array of all files
       },


       onCancel: function(){
        // summary:
        //   Stub to connect
        //   Fires when dialog box has been closed
        //  without a file selection
       },


       onError: function(/* Object or String */evtObject){
        // summary:
        //  Fires on errors
        //
        //FIXME: Unsure of a standard form for receiving errors
       },


       onReady: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - Fired when dojox.embed.Flash has created the
        //  Flash object, but it has not necessarilly finished
        //  downloading, and is ready to be communicated with.
       },


       onLoad: function(/* dojox.form.FileUploader */ uploader){
        // summary:
        //  Stub - SWF has been downloaded 100%.
       },


       /*************************
        *  Public Methods  *
        *************************/
       submit: function(/* form node ? */form){
        // summary:
        //  If FileUploader is in a form, and other data should be sent
        //  along with the files, use this instead of form submit.
        //
        var data = form ? dojo.formToObject(form) : null;
        this.upload(data);
        return false; // Boolean
       },
       upload: function(/*Object ? */data){
        // summary:
        //   When called, begins file upload
        // data: Object
        //  postData to be sent to server
        //
        if(!this.fileList.length){
         return false;
        }
        if(!this.uploadUrl){
         console.warn("uploadUrl not provided. Aborting.");
         return false;
        }
        if(!this.showProgress){
         this.set("disabled", true);
        }


        if(this.progressWidgetId){


         var node = dijit.byId(this.progressWidgetId).domNode;
         if(dojo.style(node, "display") == "none"){
          this.restoreProgDisplay = "none";
          dojo.style(node, "display", "block");
         }
         if(dojo.style(node, "visibility") == "hidden"){
          this.restoreProgDisplay = "hidden";
          dojo.style(node, "visibility", "visible");
         }
        }


        if(data && !data.target){
         this.postData = data;
        }
        this.log("upload type:", this.uploaderType, " - postData:", this.postData);


        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         f.bytesLoaded = 0;
         f.bytesTotal = f.size || 100000;
         f.percent = 0;
        }
        if(this.uploaderType == "flash"){
         this.uploadFlash();
        }else{
         this.uploadHTML();
        }
        // prevent form submit
        return false;
       },
       removeFile: function(/*String*/name, /*Boolean*/noListEdit){
        // summary:
        //  Removes a file from the pending file list.
        //  Removes pending data from the Flash movie
        //  and fileInputes from the HTML uploader.
        //  If a file container node is bound, the file
        //  will also be removed.
        // name:String
        //  The name of the file to be removed. Typically the file name,
        //  such as: picture01.png
        // noListEdit:Boolean
        //  Internal. If true don't remove files from list.
        //
        var i;
        for(i = 0; i < this.fileList.length; i++){
         if(this.fileList[i].name == name){
          if(!noListEdit){ // if onComplete, don't do this
           this.fileList.splice(i,1);
          }
          break;
         }
        }
        if(this.uploaderType == "flash"){
         this.flashMovie.removeFile(name);
        }else if(!noListEdit){
         dojo.destroy(this.fileInputs[i]);
         this.fileInputs.splice(i,1);
         this._renumberInputs();
        }
        if(this.fileListId){
         dojo.destroy("file_"+name);
        }
       },


       destroy: function(){
        // summary:
        //  Destroys uploader button
        if(this.uploaderType == "flash" && !this.flashMovie){
         this._cons.push(dojo.connect(this, "onLoad", this, "destroy"));
         return;
        }
        dojo.forEach(this._subs, dojo.unsubscribe, dojo);
        dojo.forEach(this._cons, dojo.disconnect, dojo);
        if(this.scrollConnect){
         dojo.disconnect(this.scrollConnect);
        }
        if(this.uploaderType == "flash"){
         this.flashObject.destroy();
         delete this.flashObject;
        }else{
         dojo.destroy(this._fileInput);
         dojo.destroy(this._formNode);
        }
        this.inherited(arguments);
       },


       /*************************
        *  Private Events  *
        *************************/
       _displayProgress: function(/*Boolean or Number */display){
        // summary:
        //  Shows and updates the built-in progress bar.
        //
        if(display === true){
         if(this.uploaderType == "flash"){
          dojo.style(this.insideNode,"top", "-2500px");
         }else{
          dojo.style(this.insideNode,"display", "none");
         }
         dojo.style(this.progNode,"display","");
        }else if(display === false){
         dojo.style(this.insideNode,{
          display: "",
          left: "0px"
         });
         dojo.style(this.progNode,"display","none");
        }else{
         var w = display * this.fhtml.nr.w;
         dojo.style(this.progNode, "width", w + "px");
        }
       },
       _animateProgress: function(){
        // summary:
        //  Internal. Animated the built-in progress bar
        this._displayProgress(true);
        var _uploadDone = false;
        var c = dojo.connect(this, "_complete", function(){
         dojo.disconnect(c);
         _uploadDone = true;
        });
        var w = 0;
        var interval = setInterval(dojo.hitch(this, function(){
         w+=5;
         if(w>this.fhtml.nr.w){
          w = 0;
          _uploadDone = true;
         }
         this._displayProgress(w/this.fhtml.nr.w);


         if(_uploadDone){
          clearInterval(interval);
          setTimeout(dojo.hitch(this, function(){
           this._displayProgress(false);
          }), 500);
         }


        }),50);
       },


       _error: function(evt){
        //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
        //var msg = evtObject.msg ? evtObject.msg : evtObject;
        if(typeof(evt)=="string"){
         evt = new Error(evt);
        }
        this.onError(evt);
       },


       _addToFileList: function(){
        // summary:
        //  Internal only. If there is a file list, adds a file to it.
        //  If you need to use a function such as this, connect to
        //  onChange and update outside of this widget.
        //
        if(this.fileListId){
         var str = '';
         dojo.forEach(this.fileList, function(d){
          // have to use tables because of IE. Grumble.
          str += '
      '+d.name+''+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'
      '
         }, this);
         dojo.byId(this.fileListId).innerHTML = str;
        }
       },


       _change: function(dataArray){
        // summary:
        //  Internal. Updates uploader selection
        if(dojo.isIE){
         //IE6 uses the entire path in the name, which isn't terrible, but much different
         // than everything else
         dojo.forEach(dataArray, function(f){
          f.name = f.name.split("\\")[f.name.split("\\").length-1];
         });
        }
        if(this.selectMultipleFiles){
         this.fileList = this.fileList.concat(dataArray);
        }else{
         if(this.fileList[0]){
          this.removeFile(this.fileList[0].name, true);
         }
         this.fileList = dataArray;
        }
        this._addToFileList();
        this.onChange(dataArray);
        if(this.uploadOnChange){
         if(this.uploaderType == "html"){
          this._buildFileInput();
         }
         this.upload();
        }else if(this.uploaderType == "html" && this.selectMultipleFiles){
         this._buildFileInput();
         this._connectInput();
        }
       },


       _complete: function(dataArray){
        // summary:
        //  Internal. Handles tasks after files have finished uploading
        //
        dataArray = dojo.isArray(dataArray) ? dataArray : [dataArray];


        // Yes. Yes I do have to do three loops here. ugh.
        //
        // Check if one of the files had an error
        dojo.forEach(dataArray, function(f){
         if(f.ERROR){ this._error(f.ERROR); }
        }, this);


        // Have to be set them all too 100%, because
        // onProgress does not always fire
        dojo.forEach(this.fileList, function(f){
         f.bytesLoaded = 1;
         f.bytesTotal = 1;
         f.percent = 100;
         this._progress(f);
        }, this);
        // we're done. remove files.
        dojo.forEach(this.fileList, function(f){
         this.removeFile(f.name, true);
        }, this);


        this.onComplete(dataArray);


        this.fileList = [];
        this._resetHTML();
        this.set("disabled", false);




        if(this.restoreProgDisplay){
         // using timeout so prog shows on screen for at least a short time
         setTimeout(dojo.hitch(this, function(){
          dojo.style(dijit.byId(this.progressWidgetId).domNode,
           this.restoreProgDisplay == "none" ? "display" : "visibility",
           this.restoreProgDisplay
          );
         }), 500);
        }


       },


       _progress: function(dataObject){
        // summary:
        //  Internal. Calculate progress
        var total = 0;
        var loaded = 0;
        for (var i = 0; i < this.fileList.length; i++){
         var f = this.fileList[i];
         if(f.name == dataObject.name){
          f.bytesLoaded = dataObject.bytesLoaded;
          f.bytesTotal = dataObject.bytesTotal;
          f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
          this.log(f.name, "percent:", f.percent)
         }
         loaded += Math.ceil(.001 * f.bytesLoaded);
         total += Math.ceil(.001 * f.bytesTotal);
        }
        var percent = Math.ceil(loaded / total * 100);
        if(this.progressWidgetId){
         dijit.byId(this.progressWidgetId).update({progress:percent+"%"});
        }
        if(this.showProgress){
         this._displayProgress(percent * .01);
        }
        this.onProgress(this.fileList);


       },
       _getDisabledAttr: function(){
        // summary:
        //  Internal. To get disabled use: widget.get("disabled");
        return this._disabled;
       },


       _setDisabledAttr: function(disabled){
        // summary:
        //  Internal. To set disabled use: widget.set("disabled", true | false);
        if(this._disabled == disabled){ return; }


        if(this.uploaderType == "flash"){
         if(!this.flashReady){
          var _fc = dojo.connect(this, "onLoad", this, function(){
           dojo.disconnect(_fc);
           this._setDisabledAttr(disabled);
          });
          return;
         }
         this._disabled = disabled;
         this.flashMovie.doDisable(disabled);
        }else{
         this._disabled = disabled;
         dojo.style(this._fileInput, "display", this._disabled ? "none" : "");
        }
        dojo.toggleClass(this.domNode, this.disabledClass, disabled);
       },


       _onFlashBlur: function(){
        // summary:
        //  Internal. Detects when Flash movies reliquishes focus.
        //  We have to find all the tabIndexes in the doc and figure
        //  out whom to give focus to next.
        this.flashMovie.blur();
        if(!this.nextFocusObject && this.tabIndex){
         var nodes = dojo.query("[tabIndex]");
         for(var i = 0; i    if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
           this.nextFocusObject = nodes[i];
           break;
          }
         }
        }
        this.nextFocusObject.focus();
       },
       _disconnect: function(){
        // summary:
        //  Internal. Disconnects fileInput in favor of new one.
        dojo.forEach(this._cons, dojo.disconnect, dojo);
       },


       /*************************
        *   HTML   *
        *************************/
       uploadHTML: function(){
        // summary:
        //  Internal. You could use this, but you should use upload() or submit();
        //  which can also handle the post data.
        //
        // NOTE on deferredUploading:
        //   This is not enabled for HTML. Workaround would be to force
        //  singleFile uploads.
        // TODO:
        //  Investigate removing fileInputs and resending form
        //  multiple times adding each fileInput
        //
        if(this.selectMultipleFiles){
         dojo.destroy(this._fileInput);
        }
        this._setHtmlPostData();
        if(this.showProgress){
         this._animateProgress();
        }
        var dfd = dojo.io.iframe.send({
         url: this.uploadUrl.toString(),
         form: this._formNode,
         handleAs: "json",
         error: dojo.hitch(this, function(err){
          this._error("HTML Upload Error:" + err.message);
         }),
         load: dojo.hitch(this, function(data, ioArgs, widgetRef){
          this._complete(data);
         })
        });
       },


       createHtmlUploader: function(){
        // summary:
        //  Internal. Fires of methods to build HTML Uploader.
        this._buildForm();
        this._setFormStyle();
        this._buildFileInput();
        this._connectInput();
        this._styleContent();
        dojo.style(this.insideNode, "visibility", "visible");
        this.onReady();
       },


       _connectInput: function(){
        // summary:
        //  Internal. HTML Uploader connections. These get disconnected
        //  after upload or if multi upload.
        this._disconnect();
        this._cons.push(dojo.connect(this._fileInput, "mouseover", this, function(evt){
         dojo.addClass(this.domNode, this.hoverClass);
         this.onMouseOver(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseout", this, function(evt){
         setTimeout(dojo.hitch(this, function(){
          dojo.removeClass(this.domNode, this.activeClass);
          dojo.removeClass(this.domNode, this.hoverClass);
          this.onMouseOut(evt);
          this._checkHtmlCancel("off");
         }), 0);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mousedown", this, function(evt){
         dojo.addClass(this.domNode, this.activeClass);
         dojo.removeClass(this.domNode, this.hoverClass);
         this.onMouseDown(evt);
        }));
        this._cons.push(dojo.connect(this._fileInput, "mouseup", this, function(evt){
         dojo.removeClass(this.domNode, this.activeClass);
         this.onMouseUp(evt);
         this.onClick(evt);
         this._checkHtmlCancel("up");
        }));
        this._cons.push(dojo.connect(this._fileInput, "change", this, function(){
         this._checkHtmlCancel("change");
         this._change([{
          name: this._fileInput.value,
          type: "",
          size: 0
         }]);
        }));
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _checkHtmlCancel: function(mouseType){
        // summary:
        //  Internal. Check if the dialog was opened and canceled without file selection.
        if(mouseType == "change"){
         this.dialogIsOpen = false;
        }
        if(mouseType == "up"){
         this.dialogIsOpen = true;
        }
        if(mouseType == "off"){
         if(this.dialogIsOpen){
          this.onCancel();
         }
         this.dialogIsOpen = false;
        }
       },


       _styleContent: function(){
        // summary:
        //  Internal.Apply style to node
        var o = this.fhtml.nr;


        dojo.style(this.insideNode, {
         width:o.w+"px",
         height:o.va == "middle"?o.h+"px":"auto",
         textAlign:o.ta,
         paddingTop:o.p[0]+"px",
         paddingRight:o.p[1]+"px",
         paddingBottom:o.p[2]+"px",
         paddingLeft:o.p[3]+"px"
        });


        try{
         dojo.style(this.insideNode, "lineHeight", "inherit");
        }catch(e){
         // There are certain cases where IE refuses to set lineHeight.
         // For the life of me I cannot figure out the combination of
         // styles that IE doesn't like. Steaming... Pile...
        }


       },
       _resetHTML: function(){
        // summary:
        //  Internal. After upload, this is called to clear the form and build a new
        //  fileInput.
        if(this.uploaderType == "html" && this._formNode){
         this.fileInputs = [];
         dojo.query("*", this._formNode).forEach(function(n){
          dojo.destroy(n);
         });
         this.fileCount = 0;
         this._buildFileInput();
         this._connectInput();
        }
       },
       _buildForm: function(){
        // summary:
        //  Build the form that holds the fileInput
        //


        if(this._formNode){ return; }


        if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){
         this._formNode = document.createElement('');
         this._formNode.encoding = "multipart/form-data";
         this._formNode.id = dijit.getUniqueId("FileUploaderForm"); // needed for dynamic style
         this.domNode.appendChild(this._formNode);
        }else{
         this._formNode = dojo.create('form', {
          enctype:"multipart/form-data",
          method:"post",
          id:dijit.getUniqueId("FileUploaderForm")
         }, this.domNode);
        }
       },


       _buildFileInput: function(){
        // summary:
        //  Build the fileInput field
        //
        if(this._fileInput){
         this._disconnect();
         // FIXME:
         // Just hiding it which works, but we lose
         // reference to it and can't remove it from
         // the upload list.
         this._fileInput.id = this._fileInput.id + this.fileCount;
         dojo.style(this._fileInput, "display", "none");
        }
        this._fileInput = document.createElement('input');
        this.fileInputs.push(this._fileInput);
        // server will need to know this variable:
        var nm = this.htmlFieldName;
        var _id = this.id;
        if(this.selectMultipleFiles){
         nm += this.fileCount;
         _id += this.fileCount;
         this.fileCount++;
        }


        dojo.attr(this._fileInput, {
         id:this.id,
         name:nm,
         type:"file"
        });


        dojo.addClass(this._fileInput, "dijitFileInputReal");
        this._formNode.appendChild(this._fileInput);
        var real = dojo.marginBox(this._fileInput);
        dojo.style(this._fileInput, {
         position:"relative",
         left:(this.fhtml.nr.w - real.w) + "px",
         opacity:0
        });
       },


       _renumberInputs: function(){
        if(!this.selectMultipleFiles){ return; }
        var nm;
        this.fileCount = 0;
        dojo.forEach(this.fileInputs, function(inp){
         nm = this.htmlFieldName + this.fileCount;
         this.fileCount++;
         dojo.attr(inp, "name", nm);
        }, this);
       },


       _setFormStyle: function(){
        // summary:
        //  Apply a dynamic style to the form and input
        var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
        // Now create a style associated with the form ID
        dojox.html.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
        dojo.style(this.domNode, {
         overflow:"hidden",
         position:"relative"
        });
        dojo.style(this.insideNode, "position", "absolute");
       },


       _setHtmlPostData: function(){
        // summary:
        //  Internal.Apply postData to hidden fields in form
        if(this.postData){
         for (var nm in this.postData){
          dojo.create("input", {
           type: "hidden",
           name: nm,
           value: this.postData[nm]
          }, this._formNode);
         }
        }
       },


       /*************************
        *   FLASH   *
        *************************/
       uploadFlash: function(){
        // summary:
        //  Internal. You should use upload() or submit();
        try{
         if(this.showProgress){
          this._displayProgress(true);
          var c = dojo.connect(this, "_complete", this, function(){
           dojo.disconnect(c);
           this._displayProgress(false);
          });
         }


         var o = {};
         for(var nm in this.postData){
          o[nm] = this.postData[nm];
         }
         this.flashMovie.doUpload(o);


        }catch(err){
         this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
        }
       },


       createFlashUploader: function(){
        // summary:
        //  Internal. Creates Flash Uploader
        this.uploadUrl = this.uploadUrl.toString();
        if(this.uploadUrl){
         if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
          // Appears to be a relative path. Attempt to
          // convert it to absolute, so it will better
          //target the SWF.
          //
          var loc = window.location.href.split("/");
          loc.pop();
          loc = loc.join("/")+"/";
          this.uploadUrl = loc+this.uploadUrl;
          this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
         }else{
          this.log("SWF URL unmodified:", this.uploadUrl)
         }
        }else{
         console.warn("Warning: no uploadUrl provided.");
        }


        var w = this.fhtml.nr.w;
        var h = this.fhtml.nr.h;


        var args = {
         expressInstall:true,
         path: this.swfPath.uri || this.swfPath,
         width: w,
         height: h,
         allowScriptAccess:"always",
         allowNetworking:"all",
         vars: {
          uploadDataFieldName: this.flashFieldName,
          uploadUrl: this.uploadUrl,
          uploadOnSelect: this.uploadOnChange,
          deferredUploading:this.deferredUploading || 0,
          selectMultipleFiles: this.selectMultipleFiles,
          id: this.id,
          isDebug: this.isDebug,
          devMode:this.devMode,
          flashButton:dojox.embed.flashVars.serialize("fh", this.fhtml),
          fileMask:dojox.embed.flashVars.serialize("fm", this.fileMask),
          noReturnCheck: this.skipServerCheck,
          serverTimeout:this.serverTimeout
         },
         params: {
          scale:"noscale",
          wmode:"opaque",
          allowScriptAccess:"always",
          allowNetworking:"all"
         }


        };


        this.flashObject = new dojox.embed.Flash(args, this.insideNode);
        this.flashObject.onError = dojo.hitch(function(msg){
         this._error("Flash Error: " + msg);
        });
        this.flashObject.onReady = dojo.hitch(this, function(){
         dojo.style(this.insideNode, "visibility", "visible");
         this.log("FileUploader flash object ready");
         this.onReady(this);
        });
        this.flashObject.onLoad = dojo.hitch(this, function(mov){
         this.flashMovie = mov;
         this.flashReady = true;


         this.onLoad(this);
        });
        this._connectFlash();


       },


       _connectFlash: function(){
        //  summary:
        //  Subscribing to published topics coming from the
        //  Flash uploader.
        //  description:
        //  Sacrificing some readbilty for compactness. this.id
        //  will be on the beginning of the topic, so more than
        //  one uploader can be on a page and can have unique calls.
        //
        this._doSub("/filesSelected", "_change");
        this._doSub("/filesUploaded", "_complete");
        this._doSub("/filesProgress", "_progress");
        this._doSub("/filesError", "_error");
        this._doSub("/filesCanceled", "onCancel");
        this._doSub("/stageBlur", "_onFlashBlur");
        this._doSub("/up", "onMouseUp");
        this._doSub("/down", "onMouseDown");
        this._doSub("/over", "onMouseOver");
        this._doSub("/out", "onMouseOut");


        this.connect(this.domNode, "focus", function(){
         // TODO: some kind of indicator that the Flash button
         // is in focus
         this.flashMovie.focus();
         this.flashMovie.doFocus();
        });
        if(this.tabIndex>=0){
         dojo.attr(this.domNode, "tabIndex", this.tabIndex);
        }
       },


       _doSub: function(subStr, funcStr){
        // summary:
        //  Internal. Shortcut for subscribes to Flash movie
        this._subs.push(dojo.subscribe(this.id + subStr, this, funcStr));
       },


       /*************************************
        *  DOM INSPECTION METHODS   *
        *************************************/


       urlencode: function(url){
        // Using symbols in place of URL chars that will break in Flash serialization.
        if(!url || url == "none"){
         return false;
        }
        return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
       },


       isButton: function(node){
        // testing if button for styling purposes
        var tn = node.tagName.toLowerCase();
        return tn == "button" || tn == "input";
       },


       getTextStyle: function(node){
        // getting font info
        var o = {};
        o.ff = dojo.style(node, "fontFamily");
        if(o.ff){
         o.ff = o.ff.replace(", ", ","); // remove spaces. IE in Flash no likee
         o.ff = o.ff.replace(/\"|\'/g, "");
         o.ff = o.ff == "sans-serif" ? "Arial" : o.ff; // Flash doesn't know what sans-serif is
         o.fw = dojo.style(node, "fontWeight");
         o.fi = dojo.style(node, "fontStyle");
         o.fs = parseInt(dojo.style(node, "fontSize"), 10);
         if(dojo.style(node, "fontSize").indexOf("%") > -1){
          // IE doesn't convert % to px. For god sakes.
          var n = node;
          while(n.tagName){
           if(dojo.style(n, "fontSize").indexOf("%") == -1){
            o.fs = parseInt(dojo.style(n, "fontSize"), 10);
            break;
           }
           if(n.tagName.toLowerCase()=="body"){
            // if everyting is %, the the font size is 16px * the %
            o.fs = 16 * .01 * parseInt(dojo.style(n, "fontSize"), 10);
           }
           n = n.parentNode;
          }
         }
         o.fc = new dojo.Color(dojo.style(node, "color")).toHex();
         o.fc = parseInt(o.fc.substring(1,Infinity),16);
        }
        o.lh = dojo.style(node, "lineHeight");
        o.ta = dojo.style(node, "textAlign");
        o.ta = o.ta == "start" || !o.ta ? "left" : o.ta;
        o.va = this.isButton(node) ? "middle" : o.lh == o.h ? "middle" : dojo.style(node, "verticalAlign");
        return o;
       },


       getText: function(node){
        // Get the text of the button. It's possible to use HTML in the Flash Button,
        // but the results are not spectacular.
        var cn = dojo.trim(node.innerHTML);
        if(cn.indexOf("<") >- 1){
         cn = escape(cn);
        }
        return cn;
       },


       getStyle: function(node){
        // getting the style of a node. Using very abbreviated characters which the
        // Flash movie understands.
        var o = {};
        var dim = dojo.contentBox(node);
        var pad = dojo._getPadExtents(node);
        o.p = [pad.t, pad.w-pad.l, pad.h-pad.t, pad.l];
        o.w = dim.w + pad.w;
        o.h = dim.h + pad.h;
        o.d = dojo.style(node, "display");
        var clr = new dojo.Color(dojo.style(node, "backgroundColor"));
        // if no color, Safari sets #000000 and alpha=0 since we don't support alpha,
        // it makes black - make it white
        o.bc = clr.a == 0 ? "#ffffff" : clr.toHex();
        o.bc = parseInt(o.bc.substring(1,Infinity),16);
        var url = this.urlencode(dojo.style(node, "backgroundImage"));
        if(url){
         o.bi = {
          url:url,
          rp:dojo.style(node, "backgroundRepeat"),
          pos: escape(dojo.style(node, "backgroundPosition"))
         };
         if(!o.bi.pos){
          // IE does Xpx and Ypx, not "X% Y%"
          var rx = dojo.style(node, "backgroundPositionX");
          var ry = dojo.style(node, "backgroundPositionY");
          rx = (rx == "left") ? "0%" : (rx == "right") ? "100%" : rx;
          ry = (ry == "top") ? "0%" : (ry == "bottom") ? "100%" : ry;
          o.bi.pos = escape(rx+" "+ry);
         }
        }
        return dojo.mixin(o, this.getTextStyle(node));
       },


       getTempNodeStyle: function(node, _class, isDijitButton){
        // This sets up a temp node to get the style of the hover, active, and disabled states
        var temp, style;
        if(isDijitButton){
         // backwards compat until dojo 1.5
         temp = dojo.place("<"+node.tagName+">"+node.innerHTML+"", node.parentNode); //+" "+_class+"
         var first = temp.firstChild;
         dojo.addClass(first, node.className);
         dojo.addClass(temp, _class);
         style = this.getStyle(first);
        }else{
         temp = dojo.place("<"+node.tagName+">"+node.innerHTML+"", node.parentNode);
         dojo.addClass(temp, node.className);
         dojo.addClass(temp, _class);
         temp.id = node.id;
         style = this.getStyle(temp);
        }
        // dev note: comment out this line to see what the
        // button states look like to the FileUploader
        dojo.destroy(temp);
        return style;
    • returns
      Boolean
    • summary
  • dojox.form.FileUploader.fileList

    • summary
  • dojox.form.FileUploader._cons

    • summary
  • dojox.form.FileUploader.fileInputs

    • summary
  • dojox.form.FileUploader.fileCount

    • summary
  • dojox.form.FileUploader.flashReady

    • summary
  • dojox.form.FileUploader._disabled

    • summary
  • dojox.form.FileUploader._refNode

    • summary
  • dojox.form.FileUploader._hiddenNode

    • summary
  • dojox.form.FileUploader.srcNodeRef

    • summary
  • dojox.form.FileUploader.pressClass

    • summary
  • dojox.form.FileUploader.norm

    • summary
  • dojox.form.FileUploader.width

    • summary
  • dojox.form.FileUploader.height

    • summary
  • dojox.form.FileUploader.over

    • summary
  • dojox.form.FileUploader.down

    • summary
  • dojox.form.FileUploader.dsbl

    • summary
  • dojox.form.FileUploader.fhtml

    • summary
  • dojox.form.FileUploader.norm.va

    • summary
  • dojox.form.FileUploader.norm.lh

    • summary
  • dojox.form.FileUploader.progTextNode.innerHTML

    • summary
  • dojox.form.FileUploader.insideNode.innerHTML

    • summary
  • dojox.form.FileUploader.restoreProgDisplay

    • summary
  • dojox.form.FileUploader.postData

    • summary
  • dojox.form.FileUploader.nextFocusObject

    • summary
  • dojox.form.FileUploader.dialogIsOpen

    • summary
  • dojox.form.FileUploader._formNode

    • summary
  • dojox.form.FileUploader._formNode.encoding

    • summary
  • dojox.form.FileUploader._formNode.id

    • summary
  • dojox.form.FileUploader._fileInput.id

    • summary
  • dojox.form.FileUploader._fileInput

    • summary
  • dojox.form.FileUploader.flashObject.onError

    • summary
  • dojox.form.FileUploader.flashObject.onReady

    • summary
  • dojox.form.FileUploader.flashObject.onLoad

    • summary
  • dojox.form.FileUploader._subs

    • summary
  • o

    • mixins:
      • this.getTextStyle(node): (normal)
    • summary
  • dojox.form

    • type
      Object
    • summary
  • dojox

    • type
      Object
    • summary