Uploader.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.form.Uploader"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.form.Uploader"] = true;
  8. dojo.provide("dojox.form.Uploader");
  9. dojo.experimental("dojox.form.Uploader");
  10. dojo.require("dojox.form.uploader.Base");
  11. dojo.require("dijit.form.Button");
  12. //
  13. // TODO:
  14. // i18n
  15. // label via innerHTML
  16. // Doc and or test what can be extended.
  17. // Doc custom file events
  18. // Use new FileReader() for thumbnails
  19. // flashFieldName should default to Flash
  20. // get('value'); and set warning
  21. //
  22. dojo.declare("dojox.form.Uploader", [dojox.form.uploader.Base], {
  23. //
  24. // Version: 1.6
  25. //
  26. // summary:
  27. // A widget that creates a stylable file-input button, with optional multi-file selection,
  28. // using only HTML elements. Non-HTML5 browsers have fallback options of Flash or an iframe.
  29. //
  30. // description:
  31. // A bare-bones, stylable file-input button, with optional multi-file selection. The list
  32. // of files is not displayed, that is for you to handle by connecting to the onChange
  33. // event, or use the dojox.form.uploader.FileList.
  34. //
  35. // Uploader without plugins does not have any ability to upload - it is for use in forms
  36. // where you handle the upload either by a standard POST or with Ajax using an iFrame. This
  37. // class is for convenience of multiple files only. No progress events are available.
  38. //
  39. // If the browser supports a file-input with the "multiple" attribute, that will be used.
  40. // If the browser does not support "multiple" (ergo, IE) multiple inputs are used,
  41. // one for each selection.
  42. //
  43. //
  44. // uploadOnSelect: Boolean
  45. // If true, uploads imediately after a file has been selected. If false,
  46. // waits for upload() to be called.
  47. uploadOnSelect:false,
  48. // tabIndex: Number|String
  49. // The tab order in the DOM.
  50. tabIndex:0,
  51. // multiple: Boolean
  52. // If true and flash mode, multiple files may be selected from the dialog.
  53. multiple:false,
  54. //
  55. // label: String
  56. // The text used in the button that when clicked, opens a system Browse Dialog.
  57. label:"Upload...",
  58. //
  59. // url: String
  60. // The url targeted for upload. An absolute URL is preferred. Relative URLs are
  61. // changed to absolute.
  62. url:"",
  63. //
  64. // name: String
  65. // The name attribute needs to end with square brackets: [] as this is the standard way
  66. // of handling an attribute "array". This requires a slightly different technique on the
  67. // server.
  68. name:"uploadedfile",
  69. //
  70. // flashFieldName: String
  71. // If set, this will be the name of the field of the flash uploaded files that the server
  72. // is expecting. If not set, "Flash" is appended to the "name" property.
  73. flashFieldName:"",
  74. //
  75. // uploadType: String [readonly]
  76. // The type of uploader being used. As an alternative to determining the upload type on the
  77. // server based on the fieldName, this property could be sent to the server to help
  78. // determine what type of parsing should be used.
  79. uploadType:"form",
  80. //
  81. _nameIndex:0,
  82. widgetsInTemplate:true,
  83. templateString:'<div class="dojoxFileInput"><div dojoType="dijit.form.Button" dojoAttachPoint="button">${label}</div></div>',
  84. postMixInProperties: function(){
  85. this._inputs = [];
  86. this._getButtonStyle(this.srcNodeRef);
  87. this.inherited(arguments);
  88. },
  89. postCreate: function(){
  90. var restore = false;
  91. var parent = this.domNode.parentNode;
  92. var position = this._getNodePosition(this.domNode);
  93. if(!this.btnSize.w || !this.btnSize.h) {
  94. dojo.body().appendChild(this.domNode);
  95. this._getButtonStyle(this.domNode);
  96. restore = true;
  97. }
  98. this._setButtonStyle();
  99. if(restore){
  100. dojo.place(this.domNode, position.node, position.pos)
  101. }
  102. this.inherited(arguments);
  103. },
  104. /*************************
  105. * Public Events *
  106. *************************/
  107. onChange: function(/* Array */fileArray){
  108. // summary:
  109. // stub to connect
  110. // Fires when files are selected
  111. // Event is an array of last files selected
  112. },
  113. onBegin: function(/* Array */dataArray){
  114. // summary:
  115. // Fires when upload begins
  116. },
  117. onProgress: function(/* Object */customEvent){
  118. // summary:
  119. // Stub to connect
  120. // Fires on upload progress. Event is a normalized object of common properties
  121. // from HTML5 uploaders and the Flash uploader. Will not fire for IFrame.
  122. // customEvent:
  123. // bytesLoaded: Number
  124. // Amount of bytes uploaded so far of entire payload (all files)
  125. // bytesTotal: Number
  126. // Amount of bytes of entire payload (all files)
  127. // type: String
  128. // Type of event (progress or load)
  129. // timeStamp: Number
  130. // Timestamp of when event occurred
  131. },
  132. onComplete: function(/* Object */customEvent){
  133. // summary:
  134. // stub to connect
  135. // Fires when all files have uploaded
  136. // Event is an array of all files
  137. this.reset();
  138. },
  139. onCancel: function(){
  140. // summary:
  141. // Stub to connect
  142. // Fires when dialog box has been closed
  143. // without a file selection
  144. },
  145. onAbort: function(){
  146. // summary:
  147. // Stub to connect
  148. // Fires when upload in progress was canceled
  149. },
  150. onError: function(/* Object or String */evtObject){
  151. // summary:
  152. // Fires on errors
  153. //
  154. //FIXME: Unsure of a standard form of error events
  155. },
  156. /*************************
  157. * Public Methods *
  158. *************************/
  159. upload: function(/*Object ? */formData){
  160. // summary:
  161. // When called, begins file upload. Only supported with plugins.
  162. },
  163. submit: function(/* form Node ? */form){
  164. // summary:
  165. // If Uploader is in a form, and other data should be sent along with the files, use
  166. // this instead of form submit. Only supported with plugins.
  167. },
  168. reset: function(){
  169. // summary
  170. // Resets entire input, clearing all files.
  171. // NOTE:
  172. // Removing individual files is not yet supported, because the HTML5 uploaders can't
  173. // be edited.
  174. // TODO:
  175. // Add this ability by effectively, not uploading them
  176. //
  177. this._disconnectButton();
  178. dojo.forEach(this._inputs, dojo.destroy, dojo);
  179. this._inputs = [];
  180. this._nameIndex = 0;
  181. this._createInput();
  182. },
  183. getFileList: function(){
  184. // summary:
  185. // Returns a list of selected files.
  186. //
  187. var fileArray = [];
  188. if(this.supports("multiple")){
  189. dojo.forEach(this.inputNode.files, function(f, i){
  190. fileArray.push({
  191. index:i,
  192. name:f.name,
  193. size:f.size,
  194. type:f.type
  195. });
  196. }, this);
  197. }else{
  198. dojo.forEach(this._inputs, function(n, i){
  199. fileArray.push({
  200. index:i,
  201. name:n.value.substring(n.value.lastIndexOf("\\")+1),
  202. size:0,
  203. type:n.value.substring(n.value.lastIndexOf(".")+1)
  204. });
  205. }, this)
  206. }
  207. return fileArray; // Array
  208. },
  209. /*********************************************
  210. * Private Property. Get off my lawn. *
  211. *********************************************/
  212. _getValueAttr: function(){
  213. // summary:
  214. // Internal. To get disabled use: uploader.get("disabled");
  215. return this.getFileList();
  216. },
  217. _setValueAttr: function(disabled){
  218. console.error("Uploader value is read only");
  219. },
  220. _getDisabledAttr: function(){
  221. // summary:
  222. // Internal. To get disabled use: uploader.get("disabled");
  223. return this._disabled;
  224. },
  225. _setDisabledAttr: function(disabled){
  226. // summary:
  227. // Internal. To set disabled use: uploader.set("disabled", true);
  228. if(this._disabled == disabled){ return; }
  229. this.button.set('disabled', disabled);
  230. dojo.style(this.inputNode, "display", disabled ? "none" : "block");
  231. },
  232. _getNodePosition: function(node){
  233. if(node.previousSibling){
  234. return {
  235. node:node.previousSibling,
  236. pos:"after"
  237. }
  238. }
  239. return {
  240. node:node.nextSibling,
  241. pos:"before"
  242. }
  243. },
  244. _getButtonStyle: function(node){
  245. if(!node){
  246. // we don't want this to happen. But if it does, try and display *something*.
  247. this.btnSize = {
  248. w:200,
  249. h:25
  250. };
  251. }else{
  252. this.btnSize = dojo.marginBox(node);
  253. }
  254. },
  255. _setButtonStyle: function(){
  256. var hasParent = true;
  257. if(!this.domNode.parentNode || !this.domNode.parentNode.tagName){
  258. document.body.appendChild(this.domNode);
  259. hasParent = false;
  260. }
  261. dojo.style(this.domNode, {
  262. width:this.btnSize.w+"px",
  263. height:(this.btnSize.h+4)+"px",
  264. overflow:"hidden",
  265. position:"relative"
  266. });
  267. this.inputNodeFontSize = Math.max(2, Math.max(Math.ceil(this.btnSize.w / 60), Math.ceil(this.btnSize.h / 15)));
  268. this._createInput();
  269. dojo.style(this.button.domNode, {
  270. margin:"0px",
  271. display:"block",
  272. verticalAlign:"top" // IE fix
  273. });
  274. dojo.style(this.button.domNode.firstChild, {
  275. margin:"0px",
  276. display:"block"
  277. //height:this.btnSize.h+"px"
  278. });
  279. if(!hasParent){
  280. document.body.removeChild(this.domNode);
  281. }
  282. },
  283. _createInput: function(){
  284. if(this._inputs.length){
  285. dojo.style(this.inputNode, {
  286. top:"500px"
  287. });
  288. this._disconnectButton();
  289. this._nameIndex++;
  290. }
  291. var name;
  292. if(this.supports("multiple")){
  293. // FF3.5+, WebKit
  294. name = this.name+"s[]";
  295. }else{
  296. // <=IE8
  297. name = this.name + (this.multiple ? this._nameIndex : "");
  298. }
  299. this.inputNode = dojo.create("input", {type:"file", name:name, className:"dojoxInputNode"}, this.domNode, "first");
  300. if(this.supports("multiple") && this.multiple){
  301. dojo.attr(this.inputNode, "multiple", true);
  302. }
  303. this._inputs.push(this.inputNode);
  304. dojo.style(this.inputNode, {
  305. fontSize:this.inputNodeFontSize+"em"
  306. });
  307. var size = dojo.marginBox(this.inputNode);
  308. dojo.style(this.inputNode, {
  309. position:"absolute",
  310. top:"-2px",
  311. left:"-"+(size.w-this.btnSize.w-2)+"px",
  312. opacity:0
  313. });
  314. this._connectButton();
  315. },
  316. _connectButton: function(){
  317. this._cons = [];
  318. var cs = dojo.hitch(this, function(nm){
  319. this._cons.push(dojo.connect(this.inputNode, nm, this, function(evt){
  320. this.button._cssMouseEvent({type:nm})
  321. }));
  322. });
  323. cs("mouseover");
  324. cs("mouseout");
  325. cs("mousedown");
  326. this._cons.push(dojo.connect(this.inputNode, "change", this, function(evt){
  327. this.onChange(this.getFileList(evt));
  328. if(!this.supports("multiple") && this.multiple) this._createInput();
  329. }));
  330. this.button.set('tabIndex', -1);
  331. if(this.tabIndex > -1){
  332. this.inputNode.tabIndex = this.tabIndex;
  333. var restoreBorderStyle = dojo.style(this.button.domNode.firstChild, "border");
  334. this._cons.push(dojo.connect(this.inputNode, "focus", this, function(){
  335. dojo.style(this.button.domNode.firstChild, "border", "1px dashed #ccc");
  336. }));
  337. this._cons.push(dojo.connect(this.inputNode, "blur", this, function(){
  338. dojo.style(this.button.domNode.firstChild, "border", restoreBorderStyle);
  339. }));
  340. }
  341. },
  342. _disconnectButton: function(){
  343. dojo.forEach(this._cons, dojo.disconnect, dojo);
  344. }
  345. });
  346. (function(){
  347. dojox.form.UploaderOrg = dojox.form.Uploader;
  348. var extensions = [dojox.form.UploaderOrg];
  349. dojox.form.addUploaderPlugin = function(plug){
  350. // summary:
  351. // Handle Uploader plugins. When the dojox.form.addUploaderPlugin() function is called,
  352. // the dojox.form.Uploader is recreated using the new plugin (mixin).
  353. //
  354. extensions.push(plug);
  355. dojo.declare("dojox.form.Uploader", extensions, {});
  356. }
  357. })();
  358. }