Uploader.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. require({cache:{
  2. 'url:dojox/form/resources/Uploader.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdojoAttachEvent=\"ondijitclick:_onClick\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdojoAttachPoint=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" dojoAttachPoint=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdojoAttachPoint=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><!--no need to have this for Uploader \n\t<input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdojoAttachPoint=\"valueNode\"\n/--></span>\n"}});
  3. define("dojox/form/Uploader", [
  4. "dojo/_base/kernel",
  5. "dojo/_base/declare",
  6. "dojo/_base/lang",
  7. "dojo/_base/array",
  8. "dojo/_base/connect",
  9. "dojo/_base/window",
  10. "dojo/dom-style",
  11. "dojo/dom-geometry",
  12. "dojo/dom-attr",
  13. "dojo/dom-construct",
  14. "dojo/dom-form",
  15. "dijit",
  16. "dijit/form/Button",
  17. "dojox/form/uploader/Base",
  18. "dojo/i18n!./nls/Uploader",
  19. "dojo/text!./resources/Uploader.html"
  20. ],function(kernel, declare, lang, array, connect, win, domStyle, domGeometry, domAttr, domConstruct, domForm, dijit, Button, uploader, res, template){
  21. kernel.experimental("dojox.form.Uploader");
  22. //
  23. // TODO:
  24. // i18n
  25. // label via innerHTML
  26. // Doc and or test what can be extended.
  27. // Doc custom file events
  28. // Use new FileReader() for thumbnails
  29. // flashFieldName should default to Flash
  30. // get('value'); and set warning
  31. // Make it so URL can change (current set to Flash on build)
  32. //
  33. /*=====
  34. uploader = dojox.form.uploader.Base;
  35. WidgetsInTemplateMixin = dijit._WidgetsInTemplateMixin;
  36. =====*/
  37. declare("dojox.form.Uploader", [uploader, Button], {
  38. //
  39. // Version: 1.6
  40. //
  41. // summary:
  42. // A widget that creates a stylable file-input button, with optional multi-file selection,
  43. // using only HTML elements. Non-HTML5 browsers have fallback options of Flash or an iframe.
  44. //
  45. // description:
  46. // A bare-bones, stylable file-input button, with optional multi-file selection. The list
  47. // of files is not displayed, that is for you to handle by connecting to the onChange
  48. // event, or use the dojox.form.uploader.FileList.
  49. //
  50. // Uploader without plugins does not have any ability to upload - it is for use in forms
  51. // where you handle the upload either by a standard POST or with Ajax using an iFrame. This
  52. // class is for convenience of multiple files only. No progress events are available.
  53. //
  54. // If the browser supports a file-input with the "multiple" attribute, that will be used.
  55. // If the browser does not support "multiple" (ergo, IE) multiple inputs are used,
  56. // one for each selection.
  57. //
  58. //
  59. // uploadOnSelect: Boolean
  60. // If true, uploads imediately after a file has been selected. If false,
  61. // waits for upload() to be called.
  62. uploadOnSelect:false,
  63. // tabIndex: Number|String
  64. // The tab order in the DOM.
  65. tabIndex:0,
  66. // multiple: Boolean
  67. // If true and flash mode, multiple files may be selected from the dialog.
  68. multiple:false,
  69. //
  70. // label: String
  71. // The text used in the button that when clicked, opens a system Browse Dialog.
  72. label:res.label,
  73. //
  74. // url: String
  75. // The url targeted for upload. An absolute URL is preferred. Relative URLs are
  76. // changed to absolute.
  77. url:"",
  78. //
  79. // name: String
  80. // The name attribute needs to end with square brackets: [] as this is the standard way
  81. // of handling an attribute "array". This requires a slightly different technique on the
  82. // server.
  83. name:"uploadedfile",
  84. //
  85. // flashFieldName: String
  86. // If set, this will be the name of the field of the flash uploaded files that the server
  87. // is expecting. If not set, "Flash" is appended to the "name" property.
  88. flashFieldName:"",
  89. //
  90. // uploadType: String [readonly]
  91. // The type of uploader being used. As an alternative to determining the upload type on the
  92. // server based on the fieldName, this property could be sent to the server to help
  93. // determine what type of parsing should be used.
  94. uploadType:"form",
  95. //
  96. // showInput: String [const]
  97. // Position to show an input which shows selected filename(s). Possible
  98. // values are "before", "after", which specifies where the input should
  99. // be placed with reference to the containerNode which contains the
  100. // label). By default, this is empty string (no such input will be
  101. // shown). Specify showInput="before" to mimic the look&feel of a
  102. // native file input element.
  103. showInput: "",
  104. _nameIndex:0,
  105. templateString: template,
  106. baseClass: 'dijitUploader '+Button.prototype.baseClass,
  107. postMixInProperties: function(){
  108. this._inputs = [];
  109. this._cons = [];
  110. this.inherited(arguments);
  111. },
  112. buildRendering: function(){
  113. console.warn("buildRendering", this.id)
  114. this.inherited(arguments);
  115. domStyle.set(this.domNode, {
  116. overflow:"hidden",
  117. position:"relative"
  118. });
  119. this._buildDisplay();
  120. //change the button node not occupy tabIndex: the real file input
  121. //will have tabIndex set
  122. domAttr.set(this.titleNode, 'tabIndex', -1);
  123. },
  124. _buildDisplay: function(){
  125. if(this.showInput){
  126. this.displayInput = dojo.create('input', {
  127. 'class':'dijitUploadDisplayInput',
  128. 'tabIndex':-1, 'autocomplete':'off'},
  129. this.containerNode, this.showInput);
  130. //schedule the attachpoint to be cleaned up on destroy
  131. this._attachPoints.push('displayInput');
  132. this.connect(this,'onChange', function(files){
  133. var i=0,l=files.length, f, r=[];
  134. while((f=files[i++])){
  135. if(f && f.name){
  136. r.push(f.name);
  137. }
  138. }
  139. this.displayInput.value = r.join(', ');
  140. });
  141. this.connect(this,'reset', function(){
  142. this.displayInput.value = '';
  143. });
  144. }
  145. },
  146. startup: function(){
  147. if(this._buildInitialized){
  148. return;
  149. }
  150. this._buildInitialized = true;
  151. this._getButtonStyle(this.domNode);
  152. this._setButtonStyle();
  153. this.inherited(arguments);
  154. },
  155. /*************************
  156. * Public Events *
  157. *************************/
  158. onChange: function(/* Array */fileArray){
  159. // summary:
  160. // stub to connect
  161. // Fires when files are selected
  162. // Event is an array of last files selected
  163. },
  164. onBegin: function(/* Array */dataArray){
  165. // summary:
  166. // Fires when upload begins
  167. },
  168. onProgress: function(/* Object */customEvent){
  169. // summary:
  170. // Stub to connect
  171. // Fires on upload progress. Event is a normalized object of common properties
  172. // from HTML5 uploaders and the Flash uploader. Will not fire for IFrame.
  173. // customEvent:
  174. // bytesLoaded: Number
  175. // Amount of bytes uploaded so far of entire payload (all files)
  176. // bytesTotal: Number
  177. // Amount of bytes of entire payload (all files)
  178. // type: String
  179. // Type of event (progress or load)
  180. // timeStamp: Number
  181. // Timestamp of when event occurred
  182. },
  183. onComplete: function(/* Object */customEvent){
  184. // summary:
  185. // stub to connect
  186. // Fires when all files have uploaded
  187. // Event is an array of all files
  188. this.reset();
  189. },
  190. onCancel: function(){
  191. // summary:
  192. // Stub to connect
  193. // Fires when dialog box has been closed
  194. // without a file selection
  195. },
  196. onAbort: function(){
  197. // summary:
  198. // Stub to connect
  199. // Fires when upload in progress was canceled
  200. },
  201. onError: function(/* Object or String */evtObject){
  202. // summary:
  203. // Fires on errors
  204. //
  205. //FIXME: Unsure of a standard form of error events
  206. },
  207. /*************************
  208. * Public Methods *
  209. *************************/
  210. upload: function(/*Object ? */formData){
  211. // summary:
  212. // When called, begins file upload. Only supported with plugins.
  213. },
  214. submit: function(/* form Node ? */form){
  215. // summary:
  216. // If Uploader is in a form, and other data should be sent along with the files, use
  217. // this instead of form submit.
  218. form = !!form ? form.tagName ? form : this.getForm() : this.getForm();
  219. var data = domForm.toObject(form);
  220. this.upload(data);
  221. },
  222. reset: function(){
  223. // summary
  224. // Resets entire input, clearing all files.
  225. // NOTE:
  226. // Removing individual files is not yet supported, because the HTML5 uploaders can't
  227. // be edited.
  228. // TODO:
  229. // Add this ability by effectively, not uploading them
  230. //
  231. delete this._files;
  232. this._disconnectButton();
  233. array.forEach(this._inputs, domConstruct.destroy, dojo);
  234. this._inputs = [];
  235. this._nameIndex = 0;
  236. this._createInput();
  237. },
  238. getFileList: function(){
  239. // summary:
  240. // Returns a list of selected files.
  241. //
  242. var fileArray = [];
  243. if(this.supports("multiple")){
  244. array.forEach(this._files, function(f, i){
  245. fileArray.push({
  246. index:i,
  247. name:f.name,
  248. size:f.size,
  249. type:f.type
  250. });
  251. }, this);
  252. }else{
  253. array.forEach(this._inputs, function(n, i){
  254. if(n.value){
  255. fileArray.push({
  256. index:i,
  257. name:n.value.substring(n.value.lastIndexOf("\\")+1),
  258. size:0,
  259. type:n.value.substring(n.value.lastIndexOf(".")+1)
  260. });
  261. }
  262. }, this);
  263. }
  264. return fileArray; // Array
  265. },
  266. /*********************************************
  267. * Private Property. Get off my lawn. *
  268. *********************************************/
  269. _getValueAttr: function(){
  270. // summary:
  271. // Internal. To get disabled use: uploader.get("disabled");
  272. return this.getFileList();
  273. },
  274. _setValueAttr: function(disabled){
  275. console.error("Uploader value is read only");
  276. },
  277. _setDisabledAttr: function(disabled){
  278. // summary:
  279. // Internal. To set disabled use: uploader.set("disabled", true);
  280. if(this._disabled == disabled){ return; }
  281. this.inherited(arguments);
  282. domStyle.set(this.inputNode, "display", disabled ? "none" : "");
  283. },
  284. _getButtonStyle: function(node){
  285. this.btnSize = {w:domStyle.get(node,'width'), h:domStyle.get(node,'height')};
  286. },
  287. _setButtonStyle: function(){
  288. this.inputNodeFontSize = Math.max(2, Math.max(Math.ceil(this.btnSize.w / 60), Math.ceil(this.btnSize.h / 15)));
  289. this._createInput();
  290. },
  291. _createInput: function(){
  292. if(this._inputs.length){
  293. domStyle.set(this.inputNode, {
  294. top:"500px"
  295. });
  296. this._disconnectButton();
  297. this._nameIndex++;
  298. }
  299. var name;
  300. if(this.supports("multiple")){
  301. // FF3.5+, WebKit
  302. name = this.name+"s[]";
  303. }else{
  304. // <=IE8
  305. name = this.name + (this.multiple ? this._nameIndex : "");
  306. }
  307. // reset focusNode to the inputNode, so when the button is clicked,
  308. // the focus is properly moved to the input element
  309. this.focusNode = this.inputNode = domConstruct.create("input", {type:"file", name:name}, this.domNode, "first");
  310. if(this.supports("multiple") && this.multiple){
  311. domAttr.set(this.inputNode, "multiple", true);
  312. }
  313. this._inputs.push(this.inputNode);
  314. domStyle.set(this.inputNode, {
  315. position:"absolute",
  316. fontSize:this.inputNodeFontSize+"em",
  317. top:"-3px",
  318. right:"-3px",
  319. opacity:0
  320. });
  321. this._connectButton();
  322. },
  323. _connectButton: function(){
  324. this._cons.push(connect.connect(this.inputNode, "change", this, function(evt){
  325. this._files = this.inputNode.files;
  326. this.onChange(this.getFileList(evt));
  327. if(!this.supports("multiple") && this.multiple) this._createInput();
  328. }));
  329. if(this.tabIndex > -1){
  330. this.inputNode.tabIndex = this.tabIndex;
  331. this._cons.push(connect.connect(this.inputNode, "focus", this, function(){
  332. this.titleNode.style.outline= "1px dashed #ccc";
  333. }));
  334. this._cons.push(connect.connect(this.inputNode, "blur", this, function(){
  335. this.titleNode.style.outline = "";
  336. }));
  337. }
  338. },
  339. _disconnectButton: function(){
  340. array.forEach(this._cons, connect.disconnect);
  341. this._cons.splice(0,this._cons.length);
  342. }
  343. });
  344. dojox.form.UploaderOrg = dojox.form.Uploader;
  345. var extensions = [dojox.form.UploaderOrg];
  346. dojox.form.addUploaderPlugin = function(plug){
  347. // summary:
  348. // Handle Uploader plugins. When the dojox.form.addUploaderPlugin() function is called,
  349. // the dojox.form.Uploader is recreated using the new plugin (mixin).
  350. //
  351. extensions.push(plug);
  352. declare("dojox.form.Uploader", extensions, {});
  353. }
  354. return dojox.form.Uploader;
  355. });