text: define([
  	"dojo/_base/declare",
  	"dojo/_base/lang",
  	"dojo/_base/array",
  	"dojo/_base/event"
  ],function(declare, lang, arrayUtil, event){
  
  	return declare("dojox.form.uploader._HTML5", [], {
  		// summary:
  		//		A mixin for dojox/form/Uploader that adds HTML5 multiple-file upload capabilities and
  		//		progress events.
  		//
  		// description:
  		//		Note that this does not add these capabilities to browsers that don't support them.
  		//		For IE8 or older browsers, _IFrame or _Flash mixins will be used.
  		//
  		
  		// debug message:
  		errMsg:"Error uploading files. Try checking permissions",
  	
  		// Overwrites "form" and could possibly be overwritten again by iframe or flash plugin.
  		uploadType:"html5",
  		
  		postMixInProperties: function(){
  			this.inherited(arguments);
  			if(this.uploadType === "html5"){ }
  		},
  	
  		postCreate: function(){
  			this.connectForm();
  			this.inherited(arguments);
  			if(this.uploadOnSelect){
  				this.connect(this, "onChange", function(data){
  					this.upload(data[0]);
  				});
  			}
  		},
  	
  		_drop: function(e){
  			event.stop(e);
  			var dt = e.dataTransfer;
  			this._files = dt.files;
  			this.onChange(this.getFileList());
  		},
  		/*************************
  		 *	   Public Methods	 *
  		 *************************/
  	
  		upload: function(/*Object ? */ formData){
  			// summary:
  			//		See: dojox.form.Uploader.upload
  				
  			this.onBegin(this.getFileList());
  			this.uploadWithFormData(formData);
  		},
  	
  		addDropTarget: function(node, /*Boolean?*/ onlyConnectDrop){
  			// summary:
  			//		Add a dom node which will act as the drop target area so user
  			//		can drop files to this node.
  			// description:
  			//		If onlyConnectDrop is true, dragenter/dragover/dragleave events
  			//		won't be connected to dojo.stopEvent, and they need to be
  			//		canceled by user code to allow DnD files to happen.
  			//		This API is only available in HTML5 plugin (only HTML5 allows
  			//		DnD files).
  			if(!onlyConnectDrop){
  				this.connect(node, 'dragenter', event.stop);
  				this.connect(node, 'dragover', event.stop);
  				this.connect(node, 'dragleave', event.stop);
  			}
  			this.connect(node, 'drop', '_drop');
  		},
  		
  		uploadWithFormData: function(/*Object*/ data){
  			// summary:
  			//		Used with WebKit and Firefox 4+
  			//		Upload files using the much friendlier FormData browser object.
  			// tags:
  			//		private
  	
  			if(!this.getUrl()){
  				console.error("No upload url found.", this); return;
  			}
  			var fd = new FormData(), fieldName=this._getFileFieldName();
  			arrayUtil.forEach(this._files, function(f, i){
  				fd.append(fieldName, f);
  			}, this);
  	
  			if(data){
  				data.uploadType = this.uploadType;
  				for(var nm in data){
  					fd.append(nm, data[nm]);
  				}
  			}
  	
  			var xhr = this.createXhr();
  			xhr.send(fd);
  		},
  	
  		_xhrProgress: function(evt){
  			if(evt.lengthComputable){
  				var o = {
  					bytesLoaded:evt.loaded,
  					bytesTotal:evt.total,
  					type:evt.type,
  					timeStamp:evt.timeStamp
  				};
  				if(evt.type == "load"){
  					// 100%
  					o.percent = "100%";
  					o.decimal = 1;
  				}else{
  					o.decimal = evt.loaded / evt.total;
  					o.percent = Math.ceil((evt.loaded / evt.total)*100)+"%";
  				}
  				this.onProgress(o);
  			}
  		},
  	
  		createXhr: function(){
  			var xhr = new XMLHttpRequest();
  			var timer;
  			xhr.upload.addEventListener("progress", lang.hitch(this, "_xhrProgress"), false);
  			xhr.addEventListener("load", lang.hitch(this, "_xhrProgress"), false);
  			xhr.addEventListener("error", lang.hitch(this, function(evt){
  				this.onError(evt);
  				clearInterval(timer);
  			}), false);
  			xhr.addEventListener("abort", lang.hitch(this, function(evt){
  				this.onAbort(evt);
  				clearInterval(timer);
  			}), false);
  			xhr.onreadystatechange = lang.hitch(this, function(){
  				if(xhr.readyState === 4){
  	//				console.info("COMPLETE")
  					clearInterval(timer);
  					try{
  						this.onComplete(JSON.parse(xhr.responseText.replace(/^\{\}&&/,'')));
  					}catch(e){
  						var msg = "Error parsing server result:";
  						console.error(msg, e);
  						console.error(xhr.responseText);
  						this.onError(msg, e);
  					}
  				}
  			});
  			xhr.open("POST", this.getUrl());
  			xhr.setRequestHeader("Accept","application/json");
  			
  			timer = setInterval(lang.hitch(this, function(){
  				try{
  					if(typeof(xhr.statusText)){} // accessing this error throws an error. Awesomeness.
  				}catch(e){
  					//this.onError("Error uploading file."); // not always an error.
  					clearInterval(timer);
  				}
  			}),250);
  	
  			return xhr;
  		}
  	
  	});
  
  });
  