You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
12 KiB
406 lines
12 KiB
define([ |
|
"dojo/_base/kernel", |
|
"dojo/_base/declare", |
|
"dojo/_base/lang", |
|
"dojo/_base/array", |
|
"dojo/_base/connect", |
|
"dojo/_base/window", |
|
"dojo/dom-style", |
|
"dojo/dom-class", |
|
"dojo/dom-geometry", |
|
"dojo/dom-attr", |
|
"dojo/dom-construct", |
|
"dojo/dom-form", |
|
"dijit", |
|
"dijit/form/Button", |
|
"./uploader/_Base", |
|
"./uploader/_HTML5", |
|
"./uploader/_IFrame", |
|
"./uploader/_Flash", |
|
"dojo/i18n!./nls/Uploader", |
|
"dojo/text!./resources/Uploader.html" |
|
],function(kernel, declare, lang, array, connect, win, domStyle, domClass, domGeometry, domAttr, domConstruct, |
|
domForm, dijit, Button, Base, HTML5, IFrame, Flash, res, template){ |
|
|
|
// TODO: |
|
// i18n |
|
// label via innerHTML |
|
// Doc and or test what can be extended. |
|
// Doc custom file events |
|
// Use new FileReader() for thumbnails |
|
// flashFieldName should default to Flash |
|
// get('value'); and set warning |
|
// Make it so URL can change (current set to Flash on build) |
|
// |
|
|
|
return declare("dojox.form.Uploader", [Base, Button, HTML5, IFrame, Flash], { |
|
// summary: |
|
// A widget that creates a stylable file-input button, with optional multi-file selection, |
|
// using only HTML elements. Non-HTML5 browsers have fallback options of Flash or an iframe. |
|
// |
|
// description: |
|
// A bare-bones, stylable file-input button, with optional multi-file selection. The list |
|
// of files is not displayed, that is for you to handle by connecting to the onChange |
|
// event, or use the dojox.form.uploader.FileList. |
|
// |
|
// Uploader without plugins does not have any ability to upload - it is for use in forms |
|
// where you handle the upload either by a standard POST or with Ajax using an iFrame. This |
|
// class is for convenience of multiple files only. No progress events are available. |
|
// |
|
// If the browser supports a file-input with the "multiple" attribute, that will be used. |
|
// If the browser does not support "multiple" (ergo, IE) multiple inputs are used, |
|
// one for each selection. |
|
// |
|
// Version: 1.6 |
|
|
|
|
|
// uploadOnSelect: Boolean |
|
// If true, uploads immediately after a file has been selected. If false, |
|
// waits for upload() to be called. |
|
uploadOnSelect:false, |
|
|
|
// tabIndex: Number|String |
|
// The tab order in the DOM. |
|
tabIndex:0, |
|
|
|
// multiple: Boolean |
|
// If true and flash mode, multiple files may be selected from the dialog. |
|
multiple:false, |
|
|
|
// label: String |
|
// The text used in the button that when clicked, opens a system Browse Dialog. |
|
label:res.label, |
|
|
|
// url: String |
|
// The url targeted for upload. An absolute URL is preferred. Relative URLs are |
|
// changed to absolute. |
|
url:"", |
|
|
|
// name: String |
|
// The name attribute needs to end with square brackets: [] as this is the standard way |
|
// of handling an attribute "array". This requires a slightly different technique on the |
|
// server. |
|
name:"uploadedfile", |
|
|
|
// flashFieldName: String |
|
// If set, this will be the name of the field of the flash uploaded files that the server |
|
// is expecting. If not set, "Flash" is appended to the "name" property. |
|
flashFieldName:"", |
|
|
|
// force: String |
|
// options: form, html5, iframe, flash |
|
// Empty string defaults to html5 if available, and iframe if not. |
|
// Use "flash" to always use Flash (and hopefully force the user to download the plugin |
|
// if they don't have it). |
|
// Use "iframe" to always use an iframe, and never flash nor html5. Sometimes preferred |
|
// for consistent results. |
|
// Use "form" to not use ajax and post to a page. |
|
force:"", |
|
|
|
// uploadType: String [readonly] |
|
// The type of uploader being used. As an alternative to determining the upload type on the |
|
// server based on the fieldName, this property could be sent to the server to help |
|
// determine what type of parsing should be used. |
|
// This is set based on the force property and what features are available in the browser. |
|
uploadType:"", |
|
|
|
// showInput: String [const] |
|
// Position to show an input which shows selected filename(s). Possible |
|
// values are "before", "after", which specifies where the input should |
|
// be placed with reference to the containerNode which contains the |
|
// label). By default, this is empty string (no such input will be |
|
// shown). Specify showInput="before" to mimic the look&feel of a |
|
// native file input element. |
|
showInput: "", |
|
|
|
// focusedClass: String |
|
// The class applied to the button when it is focused (via TAB key) |
|
focusedClass:"dijitButtonHover", |
|
|
|
_nameIndex:0, |
|
|
|
templateString: template, |
|
|
|
baseClass: 'dijitUploader '+Button.prototype.baseClass, |
|
|
|
postMixInProperties: function(){ |
|
this._inputs = []; |
|
this._cons = []; |
|
this.force = this.force.toLowerCase(); |
|
if(this.supports("multiple")){ |
|
this.uploadType = this.force === 'form' ? 'form' : 'html5'; |
|
}else{ |
|
this.uploadType = this.force === 'flash' ? 'flash' : 'iframe'; |
|
} |
|
|
|
this.inherited(arguments); |
|
}, |
|
buildRendering: function(){ |
|
this.inherited(arguments); |
|
domStyle.set(this.domNode, { |
|
overflow:"hidden", |
|
position:"relative" |
|
}); |
|
this._buildDisplay(); |
|
//change the button node not occupy tabIndex: the real file input |
|
//will have tabIndex set |
|
domAttr.set(this.titleNode, 'tabIndex', -1); |
|
}, |
|
_buildDisplay: function(){ |
|
if(this.showInput){ |
|
this.displayInput = domConstruct.create('input', { |
|
'class':'dijitUploadDisplayInput', |
|
'tabIndex':-1, 'autocomplete':'off', |
|
'role':'presentation'}, |
|
this.containerNode, this.showInput); |
|
//schedule the attachpoint to be cleaned up on destroy |
|
this._attachPoints.push('displayInput'); |
|
this.connect(this,'onChange', function(files){ |
|
var i=0,l=files.length, f, r=[]; |
|
while((f=files[i++])){ |
|
if(f && f.name){ |
|
r.push(f.name); |
|
} |
|
} |
|
this.displayInput.value = r.join(', '); |
|
}); |
|
this.connect(this,'reset', function(){ |
|
this.displayInput.value = ''; |
|
}); |
|
} |
|
}, |
|
|
|
startup: function(){ |
|
if(this._buildInitialized){ |
|
return; |
|
} |
|
this._buildInitialized = true; |
|
this._getButtonStyle(this.domNode); |
|
this._setButtonStyle(); |
|
this.inherited(arguments); |
|
}, |
|
|
|
/************************* |
|
* Public Events * |
|
*************************/ |
|
|
|
onChange: function(/*Array*/ fileArray){ |
|
// summary: |
|
// stub to connect |
|
// Fires when files are selected |
|
// Event is an array of last files selected |
|
}, |
|
|
|
onBegin: function(/*Array*/ dataArray){ |
|
// summary: |
|
// Fires when upload begins |
|
}, |
|
|
|
onProgress: function(/*Object*/ customEvent){ |
|
// summary: |
|
// Stub to connect |
|
// Fires on upload progress. Event is a normalized object of common properties |
|
// from HTML5 uploaders and the Flash uploader. Will not fire for IFrame. |
|
// customEvent: |
|
// - bytesLoaded: Number: |
|
// Amount of bytes uploaded so far of entire payload (all files) |
|
// - bytesTotal: Number: |
|
// Amount of bytes of entire payload (all files) |
|
// - type: String: |
|
// Type of event (progress or load) |
|
// - timeStamp: Number: |
|
// Timestamp of when event occurred |
|
}, |
|
|
|
onComplete: function(/*Object*/ customEvent){ |
|
// summary: |
|
// stub to connect |
|
// Fires when all files have uploaded |
|
// Event is an array of all files |
|
this.reset(); |
|
}, |
|
|
|
onCancel: function(){ |
|
// summary: |
|
// Stub to connect |
|
// Fires when dialog box has been closed |
|
// without a file selection |
|
}, |
|
|
|
onAbort: function(){ |
|
// summary: |
|
// Stub to connect |
|
// Fires when upload in progress was canceled |
|
}, |
|
|
|
onError: function(/*Object or String*/ evtObject){ |
|
// summary: |
|
// Fires on errors |
|
|
|
// FIXME: Unsure of a standard form of error events |
|
}, |
|
|
|
/************************* |
|
* Public Methods * |
|
*************************/ |
|
|
|
upload: function(/*Object?*/ formData){ |
|
// summary: |
|
// When called, begins file upload. Only supported with plugins. |
|
formData = formData || {}; |
|
formData.uploadType = this.uploadType; |
|
this.inherited(arguments); |
|
}, |
|
|
|
submit: function(/*form Node?*/ form){ |
|
// summary: |
|
// If Uploader is in a form, and other data should be sent along with the files, use |
|
// this instead of form submit. |
|
form = !!form ? form.tagName ? form : this.getForm() : this.getForm(); |
|
var data = domForm.toObject(form); |
|
data.uploadType = this.uploadType; |
|
this.upload(data); |
|
}, |
|
|
|
reset: function(){ |
|
// summary: |
|
// Resets entire input, clearing all files. |
|
// NOTE: |
|
// Removing individual files is not yet supported, because the HTML5 uploaders can't |
|
// be edited. |
|
// TODO: |
|
// Add this ability by effectively, not uploading them |
|
// |
|
delete this._files; |
|
this._disconnectButton(); |
|
array.forEach(this._inputs, domConstruct.destroy); |
|
this._inputs = []; |
|
this._nameIndex = 0; |
|
this._createInput(); |
|
}, |
|
|
|
getFileList: function(){ |
|
// summary: |
|
// Returns a list of selected files. |
|
|
|
var fileArray = []; |
|
if(this.supports("multiple")){ |
|
array.forEach(this._files, function(f, i){ |
|
fileArray.push({ |
|
index:i, |
|
name:f.name, |
|
size:f.size, |
|
type:f.type |
|
}); |
|
}, this); |
|
}else{ |
|
array.forEach(this._inputs, function(n, i){ |
|
if(n.value){ |
|
fileArray.push({ |
|
index:i, |
|
name:n.value.substring(n.value.lastIndexOf("\\")+1), |
|
size:0, |
|
type:n.value.substring(n.value.lastIndexOf(".")+1) |
|
}); |
|
} |
|
}, this); |
|
|
|
} |
|
return fileArray; // Array |
|
}, |
|
|
|
/********************************************* |
|
* Private Property. Get off my lawn. * |
|
*********************************************/ |
|
|
|
_getValueAttr: function(){ |
|
// summary: |
|
// Internal. To get disabled use: uploader.get("disabled"); |
|
return this.getFileList(); |
|
}, |
|
|
|
_setValueAttr: function(disabled){ |
|
console.error("Uploader value is read only"); |
|
}, |
|
|
|
_setDisabledAttr: function(disabled){ |
|
// summary: |
|
// Internal. To set disabled use: uploader.set("disabled", true); |
|
if(this.disabled == disabled || !this.inputNode){ return; } |
|
this.inherited(arguments); |
|
domStyle.set(this.inputNode, "display", disabled ? "none" : ""); |
|
}, |
|
|
|
_getButtonStyle: function(node){ |
|
this.btnSize = {w:domStyle.get(node,'width'), h:domStyle.get(node,'height')}; |
|
}, |
|
|
|
_setButtonStyle: function(){ |
|
this.inputNodeFontSize = Math.max(2, Math.max(Math.ceil(this.btnSize.w / 60), Math.ceil(this.btnSize.h / 15))); |
|
this._createInput(); |
|
}, |
|
|
|
_getFileFieldName: function(){ |
|
var name; |
|
if(this.supports("multiple") && this.multiple){ |
|
name = this.name+"s[]"; |
|
}else{ |
|
// <=IE8 |
|
name = this.name + (this.multiple ? this._nameIndex : ""); |
|
} |
|
return name; |
|
}, |
|
|
|
_createInput: function(){ |
|
if(this._inputs.length){ |
|
domStyle.set(this.inputNode, { |
|
top:"500px" |
|
}); |
|
this._disconnectButton(); |
|
this._nameIndex++; |
|
} |
|
var name = this._getFileFieldName(); |
|
// reset focusNode to the inputNode, so when the button is clicked, |
|
// the focus is properly moved to the input element |
|
this.focusNode = this.inputNode = domConstruct.create("input", {type:"file", name:name, "aria-labelledby":this.id+"_label"}, this.domNode, "first"); |
|
if(this.supports("multiple") && this.multiple){ |
|
domAttr.set(this.inputNode, "multiple", true); |
|
} |
|
this._inputs.push(this.inputNode); |
|
|
|
domStyle.set(this.inputNode, { |
|
position:"absolute", |
|
fontSize:this.inputNodeFontSize+"em", |
|
top:"-3px", |
|
right:"-3px", |
|
opacity:0 |
|
}); |
|
this._connectButton(); |
|
}, |
|
|
|
_connectButton: function(){ |
|
this._cons.push(connect.connect(this.inputNode, "change", this, function(evt){ |
|
this._files = this.inputNode.files; |
|
this.onChange(this.getFileList(evt)); |
|
if(!this.supports("multiple") && this.multiple) this._createInput(); |
|
})); |
|
|
|
if(this.tabIndex > -1){ |
|
this.inputNode.tabIndex = this.tabIndex; |
|
|
|
this._cons.push(connect.connect(this.inputNode, "focus", this, function(){ |
|
domClass.add(this.domNode, this.focusedClass); |
|
})); |
|
this._cons.push(connect.connect(this.inputNode, "blur", this, function(){ |
|
domClass.remove(this.domNode, this.focusedClass); |
|
})); |
|
} |
|
}, |
|
|
|
_disconnectButton: function(){ |
|
array.forEach(this._cons, connect.disconnect); |
|
this._cons.splice(0,this._cons.length); |
|
} |
|
}); |
|
|
|
});
|
|
|