1 | define([ |
---|
2 | "dojo/_base/kernel", |
---|
3 | "dojo/_base/declare", |
---|
4 | "dojo/_base/lang", |
---|
5 | "dojo/_base/array", |
---|
6 | "dojo/_base/connect", |
---|
7 | "dojo/_base/window", |
---|
8 | "dojo/_base/sniff", |
---|
9 | "dojo/query", |
---|
10 | "dojo/dom-style", |
---|
11 | "dojo/dom-geometry", |
---|
12 | "dojo/dom-attr", |
---|
13 | "dojo/dom-class", |
---|
14 | "dojo/dom-construct", |
---|
15 | "dojo/dom-form", |
---|
16 | "dojo/_base/config", |
---|
17 | "dijit/_base/manager", |
---|
18 | "dojo/io/iframe", |
---|
19 | "dojo/_base/Color", |
---|
20 | "dojo/_base/unload", |
---|
21 | "dijit/_Widget", |
---|
22 | "dijit/_TemplatedMixin", |
---|
23 | "dijit/_Contained", |
---|
24 | "dojox/embed/Flash", |
---|
25 | "dojox/embed/flashVars", |
---|
26 | "dojox/html/styles" |
---|
27 | ],function(kernel, declare, lang, array, connect, win, has, query, domStyle, domGeometry, domAttr, domClass, domConstruct, domForm, config, manager, ioIframe, Color, unloadUtils, Widget, TemplatedMixin, Contained, embedFlash, embedFlashVars, htmlStyles){ |
---|
28 | |
---|
29 | kernel.deprecated("dojox.form.FileUploader", "Use dojox.form.Uploader", "2.0"); |
---|
30 | |
---|
31 | // Usage Notes: |
---|
32 | // To center text vertically, use vertical-align:middle; |
---|
33 | // which emulates a boxModel button. Using line-height to center text |
---|
34 | // can cause height problems in IE6 |
---|
35 | |
---|
36 | |
---|
37 | /*===== |
---|
38 | Widget = dijit._Widget; |
---|
39 | TemplatedMixin = dijit._TemplatedMixin; |
---|
40 | Contained = dijit._Contained; |
---|
41 | =====*/ |
---|
42 | declare("dojox.form.FileUploader", [Widget, TemplatedMixin, Contained], { |
---|
43 | // version: |
---|
44 | // 1.5 (deprecated) |
---|
45 | // summary: |
---|
46 | // Handles File Uploading to a server (PHP script included for testing) |
---|
47 | // |
---|
48 | // FileUploader is now a WIDGET. You do not have to pass a button |
---|
49 | // in. Passing a button is still supported until version 1.5 to maintain |
---|
50 | // backwards compatibility, but it is not reccomended. Just create your |
---|
51 | // uploader like any other widget. |
---|
52 | // |
---|
53 | // description: |
---|
54 | // If the correct version of Flash Player is available (> 9.0) , a SWF |
---|
55 | // is used. If Flash Player is not installed or is outdated, a typical |
---|
56 | // html fileInput is used. This process can be overridden with |
---|
57 | // force:"flash" or force:"html". |
---|
58 | // |
---|
59 | // FileUploader works with Flash 10. |
---|
60 | // |
---|
61 | // The button styles are now recreated in Flash, so there is no longer |
---|
62 | // using an invisible Flash movie with wmode=transparent. This way the Flash button |
---|
63 | // is actually placed inline with the DOM, not floating above it and constantly |
---|
64 | // resetting its position. The "Windows Firefox clickable bug" should be fixed (and |
---|
65 | // hopefully some Linux problems). |
---|
66 | // |
---|
67 | // The HTML button is created in a new way and it is now inline as is the |
---|
68 | // FLash button. Styling is much easier and more versatile. |
---|
69 | // |
---|
70 | // Dependencies: |
---|
71 | // FileUploader no longer uses FileInput.css. It now uses FileUploader.css |
---|
72 | // See requires for JavaScript dependencies. |
---|
73 | // |
---|
74 | // NEW FEATURES - |
---|
75 | // There are a ton of features and fixes in this version. |
---|
76 | // Disabled: Can be toggled with widget.attr("disable", true|false) |
---|
77 | // Submit: A convenience method has been added for if the uploader is in a form. |
---|
78 | // Instead of submitting the form, call uploader.submit(theForm), and the |
---|
79 | // Uploader will handle all of the form values and post the data. |
---|
80 | // Selected List: If passing the ID of a container, the Uploaders will populate it |
---|
81 | // with the selected files. |
---|
82 | // Deleting Files: You can now delete pending files. |
---|
83 | // Progress Built in: showProgress:true will change the button to a progress |
---|
84 | // bar on upload. |
---|
85 | // Progress Attach: Passing progressWidgetId will tell the Uploader of a progress |
---|
86 | // widget. If the Progress widget is initially hidden, it will change to |
---|
87 | // visible and then restored after upload. |
---|
88 | // A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due |
---|
89 | // to browser limtations) |
---|
90 | // Deferred Uploading: (Flash only) throttles the upload to one file at a time |
---|
91 | // |
---|
92 | // |
---|
93 | // CDN USERS - |
---|
94 | // FileUpload now works with the CDN but with limitations. The SWF must |
---|
95 | // be from the same domain as the HTML page. 'swfPath' has been exposed |
---|
96 | // so that you may link to that file (could of course be the same SWF in |
---|
97 | // dojox resource folder). The SWF will *NOT* work from the |
---|
98 | // CDN server. This would require a special XML file that would allow |
---|
99 | // access to your server, and the logistics to that is impossible. |
---|
100 | // |
---|
101 | // LIMITATIONS |
---|
102 | // - This is not designed to be a part of a form, it contains its own. (See submit()) |
---|
103 | // - Currently does not in a Dialog box or a Tab where it is not initially visible, |
---|
104 | // - The default style inherits font sizes - but a parent container should have a font size |
---|
105 | // set somewhere of the results could be inconsistent. |
---|
106 | // |
---|
107 | // OPERA USERS - |
---|
108 | // It works better than the 1.3 version. fileInputs apperantly can't have opacity |
---|
109 | // set to zero. The Flash uploader works but files are auto-uploaded. Must be a |
---|
110 | // flashVar problem. |
---|
111 | // |
---|
112 | // Safari Bug note: |
---|
113 | // The bug is in the way Safari handles the connection: |
---|
114 | // https://bugs.webkit.org/show_bug.cgi?id=5760 |
---|
115 | // I added this to the virtual host in the Apache conf file, and now it |
---|
116 | // works like a charm: |
---|
117 | // BrowserMatch Safari nokeepalive |
---|
118 | // |
---|
119 | swfPath: config.uploaderPath || require.toUrl("dojox/form/resources/fileuploader.swf"), |
---|
120 | |
---|
121 | |
---|
122 | templateString:'<div><div dojoAttachPoint="progNode"><div dojoAttachPoint="progTextNode"></div></div><div dojoAttachPoint="insideNode" class="uploaderInsideNode"></div></div>', |
---|
123 | |
---|
124 | // uploadUrl: String |
---|
125 | // The url targeted for upload. An absolute URL is preferred. Relative URLs are |
---|
126 | // changed to absolute. |
---|
127 | uploadUrl: "", |
---|
128 | // |
---|
129 | // isDebug: Boolean |
---|
130 | // If true, outputs traces from the SWF to console. What exactly gets passed |
---|
131 | // is very relative, and depends upon what traces have been left in the DEFT SWF. |
---|
132 | isDebug:false, |
---|
133 | // |
---|
134 | // devMode: Boolean. |
---|
135 | // Re-implemented. devMode increases the logging, adding style tracing from the SWF. |
---|
136 | devMode:false, |
---|
137 | // |
---|
138 | // id: String |
---|
139 | // The object id, just like any other widget in Dojo. However, this id |
---|
140 | // is also used as a reference for the SWF |
---|
141 | // id: "", |
---|
142 | // |
---|
143 | // baseClass: String |
---|
144 | // The name of the class that will style the button in a "normal" state. |
---|
145 | // If baseClass is not defined, 'class' will be used. |
---|
146 | // NOTE: By default the uploader will be styled like a dijit buttons and |
---|
147 | // adhere to the the themes. Tundra, Soria, and Nihilo are supported. |
---|
148 | // You can cascade the existing style by using 'class' or 'style'. If you |
---|
149 | // overwrite baseClass, you should overwrite the remaing state classes |
---|
150 | // that follow) as well. |
---|
151 | baseClass:"dojoxUploaderNorm", |
---|
152 | // |
---|
153 | // hoverClass: String |
---|
154 | // The name of the class that will style the button in a "hover" state. A specific |
---|
155 | // class should be made to do this. Do not rely on a target like button:hover{...} |
---|
156 | hoverClass:"dojoxUploaderHover", |
---|
157 | // |
---|
158 | // activeClass: String |
---|
159 | // The name of the class that will style the button in a "press" state. A specific |
---|
160 | // class should be made to do this. Do not rely on a target like button:active{...} |
---|
161 | activeClass:"dojoxUploaderActive", |
---|
162 | // |
---|
163 | // disabledClass: String |
---|
164 | // The name of the class that will style the button when its disabled. |
---|
165 | disabledClass:"dojoxUploaderDisabled", |
---|
166 | // |
---|
167 | // force: String |
---|
168 | // Use "flash" to always use Flash (and hopefully force the user to download the plugin |
---|
169 | // if they don't have it). Use "html" to always use the HTML uploader. An empty string |
---|
170 | // (default) will check for the right version of Flash and use HTML if not available. |
---|
171 | force:"", |
---|
172 | // |
---|
173 | // uploaderType: [readonly] String |
---|
174 | // Internal. What type of uploader is being used: "flash" or "html" |
---|
175 | uploaderType:"", |
---|
176 | // |
---|
177 | // flashObject: [readonly] dojox.embed.Flash |
---|
178 | // The object that creates the SWF embed object. Mostly Internal. |
---|
179 | flashObject: null, |
---|
180 | // |
---|
181 | // flashMovie: [readonly] Function |
---|
182 | // The SWF. Mostly Internal. |
---|
183 | flashMovie: null, |
---|
184 | // |
---|
185 | // insideNode: [readonly] HTMLNode |
---|
186 | // The div that holds the SWF and form/fileInput |
---|
187 | insideNode: null, |
---|
188 | // |
---|
189 | // deferredUploading: Number (1 - X) |
---|
190 | // (Flash only) throttles the upload to a certain amount of files at a time. |
---|
191 | // By default, Flash uploads file one at a time to the server, but in parallel. |
---|
192 | // Firefox will try to queue all files at once, leading to problems. Set this |
---|
193 | // to the amount to upload in parallel at a time. |
---|
194 | // Generally, 1 should work fine, but you can experiment with queuing more than |
---|
195 | // one at a time. |
---|
196 | // This is of course ignored if selectMultipleFiles equals false. |
---|
197 | deferredUploading:1, |
---|
198 | // |
---|
199 | // fileListId: String |
---|
200 | // The id of a dom node to be used as a container for the pending file list. |
---|
201 | fileListId:"", |
---|
202 | // |
---|
203 | // uploadOnChange: Boolean |
---|
204 | // If true, uploads imediately after a file has been selected. If false, |
---|
205 | // waits for upload() to be called. |
---|
206 | uploadOnChange: false, |
---|
207 | // |
---|
208 | // selectMultipleFiles: Boolean |
---|
209 | // If true and flash mode, multiple files may be selected from the dialog. |
---|
210 | // If html mode, files are not uploaded until upload() is called. The references |
---|
211 | // to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc. |
---|
212 | selectMultipleFiles: true, |
---|
213 | // |
---|
214 | // htmlFieldName: String |
---|
215 | // The name of the field of the fileInput that the server is expecting |
---|
216 | htmlFieldName:"uploadedfile", |
---|
217 | // |
---|
218 | // flashFieldName: String |
---|
219 | // The name of the field of the flash uploaded files that the server is expecting |
---|
220 | flashFieldName:"flashUploadFiles", |
---|
221 | // |
---|
222 | // fileMask: Array[ Array[Description, FileTypes], Array[...]...] |
---|
223 | // (an array, or an array of arrays) |
---|
224 | // Restrict file selection to certain file types |
---|
225 | // Empty array defaults to "All Files" |
---|
226 | // example: |
---|
227 | // fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"] |
---|
228 | // or |
---|
229 | // fileMask = [ |
---|
230 | // ["Jpeg File", "*.jpg;*.jpeg"], |
---|
231 | // ["GIF File", "*.gif"], |
---|
232 | // ["PNG File", "*.png"], |
---|
233 | // ["All Images", "*.jpg;*.jpeg;*.gif;*.png"], |
---|
234 | // ] |
---|
235 | // NOTE: MacType is not supported, as it does not work very well. |
---|
236 | // fileMask will work on a Mac, but differently than |
---|
237 | // Windows. |
---|
238 | fileMask: null, |
---|
239 | // |
---|
240 | // minFlashVersion: Number |
---|
241 | // The minimum of version of Flash player to target. 0 would always install Flash, 100 |
---|
242 | // would never install it. The Flash Player has supported multiple uploads since |
---|
243 | // version 8, so it could go as low as that safely. |
---|
244 | minFlashVersion:9, |
---|
245 | // |
---|
246 | // tabIndex: Number|String |
---|
247 | // The tab order in the DOM. Only supported by Flash. HTML Uploaders have security |
---|
248 | // protection to prevent you from tabbing to the uploader. Stupid. |
---|
249 | tabIndex:-1, |
---|
250 | // |
---|
251 | // showProgress: Boolean |
---|
252 | // If true, the button changes to a progress bar during upload. |
---|
253 | showProgress:false, |
---|
254 | // |
---|
255 | // progressMessage: String |
---|
256 | // The message shown while the button is changed to a progress bar |
---|
257 | progressMessage:"Loading", |
---|
258 | // |
---|
259 | // progressBackgroundUrl: String|Uri |
---|
260 | // The background image to use for the button-progress |
---|
261 | progressBackgroundUrl:require.toUrl("dijit/themes/tundra/images/buttonActive.png"), |
---|
262 | // |
---|
263 | // progressBackgroundColor: String|Number |
---|
264 | // The background color to use for the button-progress |
---|
265 | progressBackgroundColor:"#ededed", |
---|
266 | // |
---|
267 | // progressWidgetId:String |
---|
268 | // The widget id of a Dijit Progress bar. The Uploader will bind to it and update it |
---|
269 | // automatically. |
---|
270 | progressWidgetId:"", |
---|
271 | // |
---|
272 | // skipServerCheck: Boolean |
---|
273 | // If true, will not verify that the server was sent the correct format. |
---|
274 | // This can be safely set to true. The purpose of the server side check |
---|
275 | // is mainly to show the dev if they've implemented the different returns |
---|
276 | // correctly. |
---|
277 | skipServerCheck:false, |
---|
278 | // |
---|
279 | // serverTimeout:Number (milliseconds) |
---|
280 | // The amount of time given to the uploaded file |
---|
281 | // to wait for a server response. After this amount |
---|
282 | // of time, the onComplete is fired but with a 'server timeout' |
---|
283 | // error in the returned item. |
---|
284 | serverTimeout: 5000, |
---|
285 | |
---|
286 | |
---|
287 | log: function(){ |
---|
288 | // summary: |
---|
289 | // Due to the excessive logging necessary to make this code happen, |
---|
290 | // It's easier to turn it on and off here in one place. |
---|
291 | // Also helpful if there are multiple uploaders on one page. |
---|
292 | if(this.isDebug){ |
---|
293 | console["log"](Array.prototype.slice.call(arguments).join(" ")); |
---|
294 | } |
---|
295 | }, |
---|
296 | |
---|
297 | constructor: function(){ |
---|
298 | this._subs = []; |
---|
299 | }, |
---|
300 | |
---|
301 | postMixInProperties: function(){ |
---|
302 | // internal stuff: |
---|
303 | this.fileList = []; |
---|
304 | this._cons = []; |
---|
305 | this.fileMask = this.fileMask || []; |
---|
306 | this.fileInputs = []; |
---|
307 | this.fileCount = 0; |
---|
308 | this.flashReady = false; |
---|
309 | this._disabled = false; |
---|
310 | this.force = this.force.toLowerCase(); // Pete FTW. |
---|
311 | this.uploaderType = ((embedFlash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html"; |
---|
312 | this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading; |
---|
313 | |
---|
314 | this._refNode = this.srcNodeRef; |
---|
315 | |
---|
316 | this.getButtonStyle(); |
---|
317 | }, |
---|
318 | |
---|
319 | startup: function(){ |
---|
320 | }, |
---|
321 | |
---|
322 | postCreate: function(){ |
---|
323 | this.inherited(arguments); |
---|
324 | |
---|
325 | // internal stuff: |
---|
326 | this.setButtonStyle(); |
---|
327 | var createMethod; |
---|
328 | if(this.uploaderType == "flash"){ |
---|
329 | createMethod = "createFlashUploader"; |
---|
330 | }else{ |
---|
331 | this.uploaderType = "html"; |
---|
332 | createMethod = "createHtmlUploader"; |
---|
333 | |
---|
334 | } |
---|
335 | |
---|
336 | this[createMethod](); |
---|
337 | |
---|
338 | if(this.fileListId){ |
---|
339 | this.connect(dom.byId(this.fileListId), "click", function(evt){ |
---|
340 | var p = evt.target.parentNode.parentNode.parentNode; // in a table |
---|
341 | if(p.id && p.id.indexOf("file_")>-1){ |
---|
342 | this.removeFile(p.id.split("file_")[1]); |
---|
343 | } |
---|
344 | }); |
---|
345 | } |
---|
346 | |
---|
347 | // cleaning up solves memory leak issues in the HTML version |
---|
348 | unloadUtils.addOnUnload(this, this.destroy); |
---|
349 | }, |
---|
350 | |
---|
351 | getHiddenNode: function(/*DomNode*/ node){ |
---|
352 | // summary: |
---|
353 | // Internal. |
---|
354 | // If a parent node is styled as display:none, |
---|
355 | // returns that node. This node will be temporarilly |
---|
356 | // changed to display:block. Note if the node is in |
---|
357 | // a widget that has an onShow event, this is |
---|
358 | // overridden. |
---|
359 | // |
---|
360 | if(!node){ return null; } |
---|
361 | var hidden = null; |
---|
362 | var p = node.parentNode; |
---|
363 | while(p && p.tagName.toLowerCase() != "body"){ |
---|
364 | var d = domStyle.get(p, "display"); |
---|
365 | if(d == "none"){ |
---|
366 | hidden = p; |
---|
367 | break; |
---|
368 | } |
---|
369 | p = p.parentNode; |
---|
370 | } |
---|
371 | return hidden; |
---|
372 | }, |
---|
373 | |
---|
374 | getButtonStyle: function(){ |
---|
375 | // summary: |
---|
376 | // Internal. |
---|
377 | // Get necessary style information from srcRefNode and |
---|
378 | // assigned styles |
---|
379 | // |
---|
380 | |
---|
381 | |
---|
382 | // TODO: |
---|
383 | // To call this from postCreate.... |
---|
384 | // could do the style stuff initially, but if hidden they will be bad sizes |
---|
385 | // could then redo the sizes |
---|
386 | // alt is to create a genuine button and copy THAT instead of how doing now |
---|
387 | |
---|
388 | var refNode = this.srcNodeRef; |
---|
389 | this._hiddenNode = this.getHiddenNode(refNode); |
---|
390 | if(this._hiddenNode){ |
---|
391 | domStyle.set(this._hiddenNode, "display", "block"); |
---|
392 | } |
---|
393 | |
---|
394 | if(!refNode && this.button && this.button.domNode){ |
---|
395 | // backwards compat for a Dijit button |
---|
396 | var isDijitButton = true; |
---|
397 | var cls = this.button.domNode.className + " dijitButtonNode"; |
---|
398 | var txt = this.getText(query(".dijitButtonText", this.button.domNode)[0]); |
---|
399 | var domTxt = '<button id="'+this.button.id+'" class="'+cls+'">'+txt+'</button>'; |
---|
400 | refNode = domConstruct.place(domTxt, this.button.domNode, "after"); /// Pete doesn't like this? |
---|
401 | this.srcNodeRef = refNode; |
---|
402 | this.button.destroy(); |
---|
403 | |
---|
404 | this.baseClass = "dijitButton"; |
---|
405 | this.hoverClass = "dijitButtonHover"; |
---|
406 | this.pressClass = "dijitButtonActive"; |
---|
407 | this.disabledClass = "dijitButtonDisabled"; |
---|
408 | |
---|
409 | }else if(!this.srcNodeRef && this.button){ |
---|
410 | refNode = this.button; |
---|
411 | } |
---|
412 | |
---|
413 | if(domAttr.get(refNode, "class")){ |
---|
414 | this.baseClass += " " + domAttr.get(refNode, "class"); |
---|
415 | } |
---|
416 | domAttr.set(refNode, "class", this.baseClass); |
---|
417 | |
---|
418 | |
---|
419 | this.norm = this.getStyle(refNode); |
---|
420 | this.width = this.norm.w; |
---|
421 | this.height = this.norm.h; |
---|
422 | |
---|
423 | if(this.uploaderType == "flash"){ |
---|
424 | |
---|
425 | this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton); |
---|
426 | this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton); |
---|
427 | this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton); |
---|
428 | |
---|
429 | this.fhtml = { |
---|
430 | cn:this.getText(refNode), |
---|
431 | nr:this.norm, |
---|
432 | ov:this.over, |
---|
433 | dn:this.down, |
---|
434 | ds:this.dsbl |
---|
435 | }; |
---|
436 | }else{ |
---|
437 | this.fhtml = { |
---|
438 | cn:this.getText(refNode), |
---|
439 | nr:this.norm |
---|
440 | } |
---|
441 | if(this.norm.va == "middle"){ |
---|
442 | this.norm.lh = this.norm.h; |
---|
443 | } |
---|
444 | } |
---|
445 | |
---|
446 | if(this.devMode){ |
---|
447 | this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass); |
---|
448 | this.log("fhtml:", this.fhtml) |
---|
449 | this.log("norm:", this.norm) |
---|
450 | this.log("over:", this.over) |
---|
451 | this.log("down:", this.down) |
---|
452 | |
---|
453 | } |
---|
454 | }, |
---|
455 | |
---|
456 | setButtonStyle: function(){ |
---|
457 | // summary: |
---|
458 | // Internal. |
---|
459 | // Set up internal dom nodes for button construction. |
---|
460 | // |
---|
461 | domStyle.set(this.domNode, { |
---|
462 | width:this.fhtml.nr.w+"px", |
---|
463 | height:(this.fhtml.nr.h)+"px", |
---|
464 | padding:"0px", |
---|
465 | lineHeight: "normal", |
---|
466 | position:"relative" |
---|
467 | }); |
---|
468 | if(this.uploaderType == "html" && this.norm.va == "middle"){ |
---|
469 | domStyle.set(this.domNode, "lineHeight", this.norm.lh + "px"); |
---|
470 | } |
---|
471 | if(this.showProgress){ |
---|
472 | this.progTextNode.innerHTML = this.progressMessage; |
---|
473 | domStyle.set(this.progTextNode, { |
---|
474 | width:this.fhtml.nr.w+"px", |
---|
475 | height:(this.fhtml.nr.h+0)+"px", |
---|
476 | padding:"0px", |
---|
477 | margin:"0px", |
---|
478 | left:"0px", |
---|
479 | lineHeight:(this.fhtml.nr.h+0)+"px", |
---|
480 | position:"absolute" |
---|
481 | }); |
---|
482 | domStyle.set(this.progNode, { |
---|
483 | width:this.fhtml.nr.w+"px", |
---|
484 | height:(this.fhtml.nr.h+0)+"px", |
---|
485 | padding:"0px", |
---|
486 | margin:"0px", |
---|
487 | left:"0px", |
---|
488 | position:"absolute", |
---|
489 | display:"none", |
---|
490 | backgroundImage:"url("+this.progressBackgroundUrl+")", |
---|
491 | backgroundPosition:"bottom", |
---|
492 | backgroundRepeat:"repeat-x", |
---|
493 | backgroundColor:this.progressBackgroundColor |
---|
494 | }); |
---|
495 | }else{ |
---|
496 | domConstruct.destroy(this.progNode); |
---|
497 | } |
---|
498 | domStyle.set(this.insideNode,{ |
---|
499 | position:"absolute", |
---|
500 | top:"0px", |
---|
501 | left:"0px", |
---|
502 | display:"" |
---|
503 | }); |
---|
504 | domClass.add(this.domNode, this.srcNodeRef.className); |
---|
505 | if(this.fhtml.nr.d.indexOf("inline")>-1){ |
---|
506 | domClass.add(this.domNode, "dijitInline"); |
---|
507 | } |
---|
508 | |
---|
509 | try{ |
---|
510 | this.insideNode.innerHTML = this.fhtml.cn; |
---|
511 | }catch(e){ |
---|
512 | // You have got to be kidding me. IE does us he favor of checking that |
---|
513 | // we aren't inserting the improper type of content with innerHTML into |
---|
514 | // an inline element. Alert us with an "Unknown Runtime Error". You can't |
---|
515 | // MAKE this stuff up. |
---|
516 | // |
---|
517 | if(this.uploaderType == "flash"){ |
---|
518 | this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode); |
---|
519 | win.body().appendChild(this.insideNode); |
---|
520 | this.insideNode.innerHTML = this.fhtml.cn; |
---|
521 | var c = connect.connect(this, "onReady", this, function(){ connect.disconnect(c); |
---|
522 | this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode); |
---|
523 | this.domNode.appendChild(this.insideNode); |
---|
524 | }); |
---|
525 | }else{ |
---|
526 | this.insideNode.appendChild(document.createTextNode(this.fhtml.cn)); |
---|
527 | } |
---|
528 | } |
---|
529 | if(this._hiddenNode){ |
---|
530 | domStyle.set(this._hiddenNode, "display", "none"); |
---|
531 | } |
---|
532 | }, |
---|
533 | |
---|
534 | |
---|
535 | /************************* |
---|
536 | * Public Events * |
---|
537 | *************************/ |
---|
538 | |
---|
539 | // The following events are inherited from _Widget and still may be connected: |
---|
540 | // onClick |
---|
541 | // onMouseUp |
---|
542 | // onMouseDown |
---|
543 | // onMouseOver |
---|
544 | // onMouseOut |
---|
545 | |
---|
546 | onChange: function(dataArray){ |
---|
547 | // summary: |
---|
548 | // stub to connect |
---|
549 | // Fires when files are selected |
---|
550 | // Event is an array of last files selected |
---|
551 | }, |
---|
552 | |
---|
553 | onProgress: function(dataArray){ |
---|
554 | // summary: |
---|
555 | // Stub to connect |
---|
556 | // Fires as progress returns from SWF |
---|
557 | // Event is an array of all files uploading |
---|
558 | // Can be connected to for HTML uploader, |
---|
559 | // but will not return anything. |
---|
560 | }, |
---|
561 | |
---|
562 | onComplete: function(dataArray){ |
---|
563 | // summary: |
---|
564 | // stub to connect |
---|
565 | // Fires when all files have uploaded |
---|
566 | // Event is an array of all files |
---|
567 | }, |
---|
568 | |
---|
569 | onCancel: function(){ |
---|
570 | // summary: |
---|
571 | // Stub to connect |
---|
572 | // Fires when dialog box has been closed |
---|
573 | // without a file selection |
---|
574 | }, |
---|
575 | |
---|
576 | onError: function(/* Object or String */evtObject){ |
---|
577 | // summary: |
---|
578 | // Fires on errors |
---|
579 | // |
---|
580 | //FIXME: Unsure of a standard form for receiving errors |
---|
581 | }, |
---|
582 | |
---|
583 | onReady: function(/* dojox.form.FileUploader */ uploader){ |
---|
584 | // summary: |
---|
585 | // Stub - Fired when embedFlash has created the |
---|
586 | // Flash object, but it has not necessarilly finished |
---|
587 | // downloading, and is ready to be communicated with. |
---|
588 | }, |
---|
589 | |
---|
590 | onLoad: function(/* dojox.form.FileUploader */ uploader){ |
---|
591 | // summary: |
---|
592 | // Stub - SWF has been downloaded 100%. |
---|
593 | }, |
---|
594 | |
---|
595 | /************************* |
---|
596 | * Public Methods * |
---|
597 | *************************/ |
---|
598 | submit: function(/* form node ? */form){ |
---|
599 | // summary: |
---|
600 | // If FileUploader is in a form, and other data should be sent |
---|
601 | // along with the files, use this instead of form submit. |
---|
602 | // |
---|
603 | var data = form ? domForm.toObject(form) : null; |
---|
604 | this.upload(data); |
---|
605 | return false; // Boolean |
---|
606 | }, |
---|
607 | upload: function(/*Object ? */data){ |
---|
608 | // summary: |
---|
609 | // When called, begins file upload |
---|
610 | // data: Object |
---|
611 | // postData to be sent to server |
---|
612 | // |
---|
613 | if(!this.fileList.length){ |
---|
614 | return false; |
---|
615 | } |
---|
616 | if(!this.uploadUrl){ |
---|
617 | console.warn("uploadUrl not provided. Aborting."); |
---|
618 | return false; |
---|
619 | } |
---|
620 | if(!this.showProgress){ |
---|
621 | this.set("disabled", true); |
---|
622 | } |
---|
623 | |
---|
624 | if(this.progressWidgetId){ |
---|
625 | |
---|
626 | var node = manager.byId(this.progressWidgetId).domNode; |
---|
627 | if(domStyle.get(node, "display") == "none"){ |
---|
628 | this.restoreProgDisplay = "none"; |
---|
629 | domStyle.set(node, "display", "block"); |
---|
630 | } |
---|
631 | if(domStyle.get(node, "visibility") == "hidden"){ |
---|
632 | this.restoreProgDisplay = "hidden"; |
---|
633 | domStyle.set(node, "visibility", "visible"); |
---|
634 | } |
---|
635 | } |
---|
636 | |
---|
637 | if(data && !data.target){ |
---|
638 | this.postData = data; |
---|
639 | } |
---|
640 | this.log("upload type:", this.uploaderType, " - postData:", this.postData); |
---|
641 | |
---|
642 | for(var i = 0; i < this.fileList.length; i++){ |
---|
643 | var f = this.fileList[i]; |
---|
644 | f.bytesLoaded = 0; |
---|
645 | f.bytesTotal = f.size || 100000; |
---|
646 | f.percent = 0; |
---|
647 | } |
---|
648 | if(this.uploaderType == "flash"){ |
---|
649 | this.uploadFlash(); |
---|
650 | }else{ |
---|
651 | this.uploadHTML(); |
---|
652 | } |
---|
653 | // prevent form submit |
---|
654 | return false; |
---|
655 | }, |
---|
656 | removeFile: function(/*String*/name, /*Boolean*/noListEdit){ |
---|
657 | // summary: |
---|
658 | // Removes a file from the pending file list. |
---|
659 | // Removes pending data from the Flash movie |
---|
660 | // and fileInputes from the HTML uploader. |
---|
661 | // If a file container node is bound, the file |
---|
662 | // will also be removed. |
---|
663 | // name:String |
---|
664 | // The name of the file to be removed. Typically the file name, |
---|
665 | // such as: picture01.png |
---|
666 | // noListEdit:Boolean |
---|
667 | // Internal. If true don't remove files from list. |
---|
668 | // |
---|
669 | var i; |
---|
670 | for(i = 0; i < this.fileList.length; i++){ |
---|
671 | if(this.fileList[i].name == name){ |
---|
672 | if(!noListEdit){ // if onComplete, don't do this |
---|
673 | this.fileList.splice(i,1); |
---|
674 | } |
---|
675 | break; |
---|
676 | } |
---|
677 | } |
---|
678 | if(this.uploaderType == "flash"){ |
---|
679 | this.flashMovie.removeFile(name); |
---|
680 | }else if(!noListEdit){ |
---|
681 | domConstruct.destroy(this.fileInputs[i]); |
---|
682 | this.fileInputs.splice(i,1); |
---|
683 | this._renumberInputs(); |
---|
684 | } |
---|
685 | if(this.fileListId){ |
---|
686 | domConstruct.destroy("file_"+name); |
---|
687 | } |
---|
688 | }, |
---|
689 | |
---|
690 | destroy: function(){ |
---|
691 | // summary: |
---|
692 | // Destroys uploader button |
---|
693 | if(this.uploaderType == "flash" && !this.flashMovie){ |
---|
694 | this._cons.push(connect.connect(this, "onLoad", this, "destroy")); |
---|
695 | return; |
---|
696 | } |
---|
697 | array.forEach(this._subs, connect.unsubscribe, dojo); |
---|
698 | array.forEach(this._cons, connect.disconnect, dojo); |
---|
699 | if(this.scrollConnect){ |
---|
700 | connect.disconnect(this.scrollConnect); |
---|
701 | } |
---|
702 | if(this.uploaderType == "flash"){ |
---|
703 | this.flashObject.destroy(); |
---|
704 | delete this.flashObject; |
---|
705 | }else{ |
---|
706 | domConstruct.destroy(this._fileInput); |
---|
707 | domConstruct.destroy(this._formNode); |
---|
708 | } |
---|
709 | this.inherited(arguments); |
---|
710 | }, |
---|
711 | |
---|
712 | /************************* |
---|
713 | * Private Events * |
---|
714 | *************************/ |
---|
715 | _displayProgress: function(/*Boolean or Number */display){ |
---|
716 | // summary: |
---|
717 | // Shows and updates the built-in progress bar. |
---|
718 | // |
---|
719 | if(display === true){ |
---|
720 | if(this.uploaderType == "flash"){ |
---|
721 | domStyle.set(this.insideNode,"top", "-2500px"); |
---|
722 | }else{ |
---|
723 | domStyle.set(this.insideNode,"display", "none"); |
---|
724 | } |
---|
725 | domStyle.set(this.progNode,"display",""); |
---|
726 | }else if(display === false){ |
---|
727 | domStyle.set(this.insideNode,{ |
---|
728 | display: "", |
---|
729 | top: "0" |
---|
730 | }); |
---|
731 | domStyle.set(this.progNode,"display","none"); |
---|
732 | }else{ |
---|
733 | var w = display * this.fhtml.nr.w; |
---|
734 | domStyle.set(this.progNode, "width", w + "px"); |
---|
735 | } |
---|
736 | }, |
---|
737 | _animateProgress: function(){ |
---|
738 | // summary: |
---|
739 | // Internal. Animated the built-in progress bar |
---|
740 | this._displayProgress(true); |
---|
741 | var _uploadDone = false; |
---|
742 | var c = connect.connect(this, "_complete", function(){ |
---|
743 | connect.disconnect(c); |
---|
744 | _uploadDone = true; |
---|
745 | }); |
---|
746 | var w = 0; |
---|
747 | var interval = setInterval(lang.hitch(this, function(){ |
---|
748 | w+=5; |
---|
749 | if(w>this.fhtml.nr.w){ |
---|
750 | w = 0; |
---|
751 | _uploadDone = true; |
---|
752 | } |
---|
753 | this._displayProgress(w/this.fhtml.nr.w); |
---|
754 | |
---|
755 | if(_uploadDone){ |
---|
756 | clearInterval(interval); |
---|
757 | setTimeout(lang.hitch(this, function(){ |
---|
758 | this._displayProgress(false); |
---|
759 | }), 500); |
---|
760 | } |
---|
761 | |
---|
762 | }),50); |
---|
763 | }, |
---|
764 | |
---|
765 | _error: function(evt){ |
---|
766 | //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR"; |
---|
767 | //var msg = evtObject.msg ? evtObject.msg : evtObject; |
---|
768 | if(typeof(evt)=="string"){ |
---|
769 | evt = new Error(evt); |
---|
770 | } |
---|
771 | this.onError(evt); |
---|
772 | }, |
---|
773 | |
---|
774 | _addToFileList: function(){ |
---|
775 | // summary: |
---|
776 | // Internal only. If there is a file list, adds a file to it. |
---|
777 | // If you need to use a function such as this, connect to |
---|
778 | // onChange and update outside of this widget. |
---|
779 | // |
---|
780 | if(this.fileListId){ |
---|
781 | var str = ''; |
---|
782 | array.forEach(this.fileList, function(d){ |
---|
783 | // have to use tables because of IE. Grumble. |
---|
784 | str += '<table id="file_'+d.name+'" class="fileToUpload"><tr><td class="fileToUploadClose"></td><td class="fileToUploadName">'+d.name+'</td><td class="fileToUploadSize">'+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'</td></tr></table>' |
---|
785 | }, this); |
---|
786 | dom.byId(this.fileListId).innerHTML = str; |
---|
787 | } |
---|
788 | }, |
---|
789 | |
---|
790 | _change: function(dataArray){ |
---|
791 | // summary: |
---|
792 | // Internal. Updates uploader selection |
---|
793 | if(has("ie")){ |
---|
794 | //IE6 uses the entire path in the name, which isn't terrible, but much different |
---|
795 | // than everything else |
---|
796 | array.forEach(dataArray, function(f){ |
---|
797 | f.name = f.name.split("\\")[f.name.split("\\").length-1]; |
---|
798 | }); |
---|
799 | } |
---|
800 | if(this.selectMultipleFiles){ |
---|
801 | this.fileList = this.fileList.concat(dataArray); |
---|
802 | }else{ |
---|
803 | if(this.fileList[0]){ |
---|
804 | this.removeFile(this.fileList[0].name, true); |
---|
805 | } |
---|
806 | this.fileList = dataArray; |
---|
807 | } |
---|
808 | this._addToFileList(); |
---|
809 | this.onChange(dataArray); |
---|
810 | if(this.uploadOnChange){ |
---|
811 | if(this.uploaderType == "html"){ |
---|
812 | this._buildFileInput(); |
---|
813 | } |
---|
814 | this.upload(); |
---|
815 | }else if(this.uploaderType == "html" && this.selectMultipleFiles){ |
---|
816 | this._buildFileInput(); |
---|
817 | this._connectInput(); |
---|
818 | } |
---|
819 | }, |
---|
820 | |
---|
821 | _complete: function(dataArray){ |
---|
822 | // summary: |
---|
823 | // Internal. Handles tasks after files have finished uploading |
---|
824 | // |
---|
825 | dataArray = lang.isArray(dataArray) ? dataArray : [dataArray]; |
---|
826 | |
---|
827 | // Yes. Yes I do have to do three loops here. ugh. |
---|
828 | // |
---|
829 | // Check if one of the files had an error |
---|
830 | array.forEach(dataArray, function(f){ |
---|
831 | if(f.ERROR){ this._error(f.ERROR); } |
---|
832 | }, this); |
---|
833 | |
---|
834 | // Have to be set them all too 100%, because |
---|
835 | // onProgress does not always fire |
---|
836 | array.forEach(this.fileList, function(f){ |
---|
837 | f.bytesLoaded = 1; |
---|
838 | f.bytesTotal = 1; |
---|
839 | f.percent = 100; |
---|
840 | this._progress(f); |
---|
841 | }, this); |
---|
842 | // we're done. remove files. |
---|
843 | array.forEach(this.fileList, function(f){ |
---|
844 | this.removeFile(f.name, true); |
---|
845 | }, this); |
---|
846 | |
---|
847 | this.onComplete(dataArray); |
---|
848 | |
---|
849 | this.fileList = []; |
---|
850 | this._resetHTML(); |
---|
851 | this.set("disabled", false); |
---|
852 | |
---|
853 | |
---|
854 | if(this.restoreProgDisplay){ |
---|
855 | // using timeout so prog shows on screen for at least a short time |
---|
856 | setTimeout(lang.hitch(this, function(){ |
---|
857 | domStyle.set(manager.byId(this.progressWidgetId).domNode, |
---|
858 | this.restoreProgDisplay == "none" ? "display" : "visibility", |
---|
859 | this.restoreProgDisplay |
---|
860 | ); |
---|
861 | }), 500); |
---|
862 | } |
---|
863 | |
---|
864 | }, |
---|
865 | |
---|
866 | _progress: function(dataObject){ |
---|
867 | // summary: |
---|
868 | // Internal. Calculate progress |
---|
869 | var total = 0; |
---|
870 | var loaded = 0; |
---|
871 | for(var i = 0; i < this.fileList.length; i++){ |
---|
872 | var f = this.fileList[i]; |
---|
873 | if(f.name == dataObject.name){ |
---|
874 | f.bytesLoaded = dataObject.bytesLoaded; |
---|
875 | f.bytesTotal = dataObject.bytesTotal; |
---|
876 | f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100); |
---|
877 | this.log(f.name, "percent:", f.percent) |
---|
878 | } |
---|
879 | loaded += Math.ceil(.001 * f.bytesLoaded); |
---|
880 | total += Math.ceil(.001 * f.bytesTotal); |
---|
881 | } |
---|
882 | var percent = Math.ceil(loaded / total * 100); |
---|
883 | if(this.progressWidgetId){ |
---|
884 | manager.byId(this.progressWidgetId).update({progress:percent+"%"}); |
---|
885 | } |
---|
886 | if(this.showProgress){ |
---|
887 | this._displayProgress(percent * .01); |
---|
888 | } |
---|
889 | this.onProgress(this.fileList); |
---|
890 | |
---|
891 | }, |
---|
892 | _getDisabledAttr: function(){ |
---|
893 | // summary: |
---|
894 | // Internal. To get disabled use: widget.get("disabled"); |
---|
895 | return this._disabled; |
---|
896 | }, |
---|
897 | |
---|
898 | _setDisabledAttr: function(disabled){ |
---|
899 | // summary: |
---|
900 | // Internal. To set disabled use: widget.set("disabled", true | false); |
---|
901 | if(this._disabled == disabled){ return; } |
---|
902 | |
---|
903 | if(this.uploaderType == "flash"){ |
---|
904 | if(!this.flashReady){ |
---|
905 | var _fc = connect.connect(this, "onLoad", this, function(){ |
---|
906 | connect.disconnect(_fc); |
---|
907 | this._setDisabledAttr(disabled); |
---|
908 | }); |
---|
909 | return; |
---|
910 | } |
---|
911 | this._disabled = disabled; |
---|
912 | this.flashMovie.doDisable(disabled); |
---|
913 | }else{ |
---|
914 | this._disabled = disabled; |
---|
915 | domStyle.set(this._fileInput, "display", this._disabled ? "none" : ""); |
---|
916 | } |
---|
917 | domClass.toggle(this.domNode, this.disabledClass, disabled); |
---|
918 | }, |
---|
919 | |
---|
920 | _onFlashBlur: function(){ |
---|
921 | // summary: |
---|
922 | // Internal. Detects when Flash movies reliquishes focus. |
---|
923 | // We have to find all the tabIndexes in the doc and figure |
---|
924 | // out whom to give focus to next. |
---|
925 | this.flashMovie.blur(); |
---|
926 | if(!this.nextFocusObject && this.tabIndex){ |
---|
927 | var nodes = query("[tabIndex]"); |
---|
928 | for(var i = 0; i<nodes.length; i++){ |
---|
929 | if(nodes[i].tabIndex >= Number(this.tabIndex)+1){ |
---|
930 | this.nextFocusObject = nodes[i]; |
---|
931 | break; |
---|
932 | } |
---|
933 | } |
---|
934 | } |
---|
935 | this.nextFocusObject.focus(); |
---|
936 | }, |
---|
937 | _disconnect: function(){ |
---|
938 | // summary: |
---|
939 | // Internal. Disconnects fileInput in favor of new one. |
---|
940 | array.forEach(this._cons, connect.disconnect, dojo); |
---|
941 | }, |
---|
942 | |
---|
943 | /************************* |
---|
944 | * HTML * |
---|
945 | *************************/ |
---|
946 | uploadHTML: function(){ |
---|
947 | // summary: |
---|
948 | // Internal. You could use this, but you should use upload() or submit(); |
---|
949 | // which can also handle the post data. |
---|
950 | // |
---|
951 | // NOTE on deferredUploading: |
---|
952 | // This is not enabled for HTML. Workaround would be to force |
---|
953 | // singleFile uploads. |
---|
954 | // TODO: |
---|
955 | // Investigate removing fileInputs and resending form |
---|
956 | // multiple times adding each fileInput |
---|
957 | // |
---|
958 | if(this.selectMultipleFiles){ |
---|
959 | domConstruct.destroy(this._fileInput); |
---|
960 | } |
---|
961 | this._setHtmlPostData(); |
---|
962 | if(this.showProgress){ |
---|
963 | this._animateProgress(); |
---|
964 | } |
---|
965 | var dfd = ioIframe.send({ |
---|
966 | url: this.uploadUrl.toString(), |
---|
967 | form: this._formNode, |
---|
968 | handleAs: "json", |
---|
969 | error: lang.hitch(this, function(err){ |
---|
970 | this._error("HTML Upload Error:" + err.message); |
---|
971 | }), |
---|
972 | load: lang.hitch(this, function(data, ioArgs, widgetRef){ |
---|
973 | this._complete(data); |
---|
974 | }) |
---|
975 | }); |
---|
976 | }, |
---|
977 | |
---|
978 | createHtmlUploader: function(){ |
---|
979 | // summary: |
---|
980 | // Internal. Fires of methods to build HTML Uploader. |
---|
981 | this._buildForm(); |
---|
982 | this._setFormStyle(); |
---|
983 | this._buildFileInput(); |
---|
984 | this._connectInput(); |
---|
985 | this._styleContent(); |
---|
986 | domStyle.set(this.insideNode, "visibility", "visible"); |
---|
987 | this.onReady(); |
---|
988 | }, |
---|
989 | |
---|
990 | _connectInput: function(){ |
---|
991 | // summary: |
---|
992 | // Internal. HTML Uploader connections. These get disconnected |
---|
993 | // after upload or if multi upload. |
---|
994 | this._disconnect(); |
---|
995 | this._cons.push(connect.connect(this._fileInput, "mouseover", this, function(evt){ |
---|
996 | domClass.add(this.domNode, this.hoverClass); |
---|
997 | this.onMouseOver(evt); |
---|
998 | })); |
---|
999 | this._cons.push(connect.connect(this._fileInput, "mouseout", this, function(evt){ |
---|
1000 | setTimeout(lang.hitch(this, function(){ |
---|
1001 | domClass.remove(this.domNode, this.activeClass); |
---|
1002 | domClass.remove(this.domNode, this.hoverClass); |
---|
1003 | this.onMouseOut(evt); |
---|
1004 | this._checkHtmlCancel("off"); |
---|
1005 | }), 0); |
---|
1006 | })); |
---|
1007 | this._cons.push(connect.connect(this._fileInput, "mousedown", this, function(evt){ |
---|
1008 | domClass.add(this.domNode, this.activeClass); |
---|
1009 | domClass.remove(this.domNode, this.hoverClass); |
---|
1010 | this.onMouseDown(evt); |
---|
1011 | })); |
---|
1012 | this._cons.push(connect.connect(this._fileInput, "mouseup", this, function(evt){ |
---|
1013 | domClass.remove(this.domNode, this.activeClass); |
---|
1014 | this.onMouseUp(evt); |
---|
1015 | this.onClick(evt); |
---|
1016 | this._checkHtmlCancel("up"); |
---|
1017 | })); |
---|
1018 | this._cons.push(connect.connect(this._fileInput, "change", this, function(){ |
---|
1019 | this._checkHtmlCancel("change"); |
---|
1020 | this._change([{ |
---|
1021 | name: this._fileInput.value, |
---|
1022 | type: "", |
---|
1023 | size: 0 |
---|
1024 | }]); |
---|
1025 | })); |
---|
1026 | if(this.tabIndex>=0){ |
---|
1027 | domAttr.set(this.domNode, "tabIndex", this.tabIndex); |
---|
1028 | } |
---|
1029 | }, |
---|
1030 | |
---|
1031 | _checkHtmlCancel: function(mouseType){ |
---|
1032 | // summary: |
---|
1033 | // Internal. Check if the dialog was opened and canceled without file selection. |
---|
1034 | if(mouseType == "change"){ |
---|
1035 | this.dialogIsOpen = false; |
---|
1036 | } |
---|
1037 | if(mouseType == "up"){ |
---|
1038 | this.dialogIsOpen = true; |
---|
1039 | } |
---|
1040 | if(mouseType == "off"){ |
---|
1041 | if(this.dialogIsOpen){ |
---|
1042 | this.onCancel(); |
---|
1043 | } |
---|
1044 | this.dialogIsOpen = false; |
---|
1045 | } |
---|
1046 | }, |
---|
1047 | |
---|
1048 | _styleContent: function(){ |
---|
1049 | // summary: |
---|
1050 | // Internal.Apply style to node |
---|
1051 | var o = this.fhtml.nr; |
---|
1052 | |
---|
1053 | domStyle.set(this.insideNode, { |
---|
1054 | width:o.w+"px", |
---|
1055 | height:o.va == "middle"?o.h+"px":"auto", |
---|
1056 | textAlign:o.ta, |
---|
1057 | paddingTop:o.p[0]+"px", |
---|
1058 | paddingRight:o.p[1]+"px", |
---|
1059 | paddingBottom:o.p[2]+"px", |
---|
1060 | paddingLeft:o.p[3]+"px" |
---|
1061 | }); |
---|
1062 | |
---|
1063 | try{ |
---|
1064 | domStyle.set(this.insideNode, "lineHeight", "inherit"); |
---|
1065 | }catch(e){ |
---|
1066 | // There are certain cases where IE refuses to set lineHeight. |
---|
1067 | // For the life of me I cannot figure out the combination of |
---|
1068 | // styles that IE doesn't like. Steaming... Pile... |
---|
1069 | } |
---|
1070 | |
---|
1071 | }, |
---|
1072 | _resetHTML: function(){ |
---|
1073 | // summary: |
---|
1074 | // Internal. After upload, this is called to clear the form and build a new |
---|
1075 | // fileInput. |
---|
1076 | if(this.uploaderType == "html" && this._formNode){ |
---|
1077 | this.fileInputs = []; |
---|
1078 | query("*", this._formNode).forEach(function(n){ |
---|
1079 | domConstruct.destroy(n); |
---|
1080 | }); |
---|
1081 | this.fileCount = 0; |
---|
1082 | this._buildFileInput(); |
---|
1083 | this._connectInput(); |
---|
1084 | } |
---|
1085 | }, |
---|
1086 | _buildForm: function(){ |
---|
1087 | // summary: |
---|
1088 | // Build the form that holds the fileInput |
---|
1089 | // |
---|
1090 | |
---|
1091 | if(this._formNode){ return; } |
---|
1092 | |
---|
1093 | if(has("ie") < 9 || (has("ie") && has("quirks"))){ |
---|
1094 | this._formNode = document.createElement('<form enctype="multipart/form-data" method="post">'); |
---|
1095 | this._formNode.encoding = "multipart/form-data"; |
---|
1096 | this._formNode.id = manager.getUniqueId("FileUploaderForm"); // needed for dynamic style |
---|
1097 | this.domNode.appendChild(this._formNode); |
---|
1098 | }else{ |
---|
1099 | this._formNode = domConstruct.create('form', { |
---|
1100 | enctype:"multipart/form-data", |
---|
1101 | method:"post", |
---|
1102 | id:manager.getUniqueId("FileUploaderForm") |
---|
1103 | }, this.domNode); |
---|
1104 | } |
---|
1105 | }, |
---|
1106 | |
---|
1107 | _buildFileInput: function(){ |
---|
1108 | // summary: |
---|
1109 | // Build the fileInput field |
---|
1110 | // |
---|
1111 | if(this._fileInput){ |
---|
1112 | this._disconnect(); |
---|
1113 | // FIXME: |
---|
1114 | // Just hiding it which works, but we lose |
---|
1115 | // reference to it and can't remove it from |
---|
1116 | // the upload list. |
---|
1117 | this._fileInput.id = this._fileInput.id + this.fileCount; |
---|
1118 | domStyle.set(this._fileInput, "display", "none"); |
---|
1119 | } |
---|
1120 | this._fileInput = document.createElement('input'); |
---|
1121 | this.fileInputs.push(this._fileInput); |
---|
1122 | // server will need to know this variable: |
---|
1123 | var nm = this.htmlFieldName; |
---|
1124 | var _id = this.id; |
---|
1125 | if(this.selectMultipleFiles){ |
---|
1126 | nm += this.fileCount; |
---|
1127 | _id += this.fileCount; |
---|
1128 | this.fileCount++; |
---|
1129 | } |
---|
1130 | |
---|
1131 | domAttr.set(this._fileInput, { |
---|
1132 | id:this.id, |
---|
1133 | name:nm, |
---|
1134 | type:"file" |
---|
1135 | }); |
---|
1136 | |
---|
1137 | domClass.add(this._fileInput, "dijitFileInputReal"); |
---|
1138 | this._formNode.appendChild(this._fileInput); |
---|
1139 | var real = domGeometry.getMarginBox(this._fileInput); |
---|
1140 | domStyle.set(this._fileInput, { |
---|
1141 | position:"relative", |
---|
1142 | left:(this.fhtml.nr.w - real.w) + "px", |
---|
1143 | opacity:0 |
---|
1144 | }); |
---|
1145 | }, |
---|
1146 | |
---|
1147 | _renumberInputs: function(){ |
---|
1148 | if(!this.selectMultipleFiles){ return; } |
---|
1149 | var nm; |
---|
1150 | this.fileCount = 0; |
---|
1151 | array.forEach(this.fileInputs, function(inp){ |
---|
1152 | nm = this.htmlFieldName + this.fileCount; |
---|
1153 | this.fileCount++; |
---|
1154 | domAttr.set(inp, "name", nm); |
---|
1155 | }, this); |
---|
1156 | }, |
---|
1157 | |
---|
1158 | _setFormStyle: function(){ |
---|
1159 | // summary: |
---|
1160 | // Apply a dynamic style to the form and input |
---|
1161 | var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15))); |
---|
1162 | // Now create a style associated with the form ID |
---|
1163 | htmlStyles.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em"); |
---|
1164 | domStyle.set(this.domNode, { |
---|
1165 | overflow:"hidden", |
---|
1166 | position:"relative" |
---|
1167 | }); |
---|
1168 | domStyle.set(this.insideNode, "position", "absolute"); |
---|
1169 | }, |
---|
1170 | |
---|
1171 | _setHtmlPostData: function(){ |
---|
1172 | // summary: |
---|
1173 | // Internal.Apply postData to hidden fields in form |
---|
1174 | if(this.postData){ |
---|
1175 | for(var nm in this.postData){ |
---|
1176 | domConstruct.create("input", { |
---|
1177 | type: "hidden", |
---|
1178 | name: nm, |
---|
1179 | value: this.postData[nm] |
---|
1180 | }, this._formNode); |
---|
1181 | } |
---|
1182 | } |
---|
1183 | }, |
---|
1184 | |
---|
1185 | /************************* |
---|
1186 | * FLASH * |
---|
1187 | *************************/ |
---|
1188 | uploadFlash: function(){ |
---|
1189 | // summary: |
---|
1190 | // Internal. You should use upload() or submit(); |
---|
1191 | try{ |
---|
1192 | if(this.showProgress){ |
---|
1193 | this._displayProgress(true); |
---|
1194 | var c = connect.connect(this, "_complete", this, function(){ |
---|
1195 | connect.disconnect(c); |
---|
1196 | this._displayProgress(false); |
---|
1197 | }); |
---|
1198 | } |
---|
1199 | |
---|
1200 | var o = {}; |
---|
1201 | for(var nm in this.postData){ |
---|
1202 | o[nm] = this.postData[nm]; |
---|
1203 | } |
---|
1204 | this.flashMovie.doUpload(o); |
---|
1205 | |
---|
1206 | }catch(err){ |
---|
1207 | this._error("FileUploader - Sorry, the SWF failed to initialize." + err); |
---|
1208 | } |
---|
1209 | }, |
---|
1210 | |
---|
1211 | createFlashUploader: function(){ |
---|
1212 | // summary: |
---|
1213 | // Internal. Creates Flash Uploader |
---|
1214 | this.uploadUrl = this.uploadUrl.toString(); |
---|
1215 | if(this.uploadUrl){ |
---|
1216 | if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){ |
---|
1217 | // Appears to be a relative path. Attempt to |
---|
1218 | // convert it to absolute, so it will better |
---|
1219 | //target the SWF. |
---|
1220 | // |
---|
1221 | var loc = window.location.href.split("/"); |
---|
1222 | loc.pop(); |
---|
1223 | loc = loc.join("/")+"/"; |
---|
1224 | this.uploadUrl = loc+this.uploadUrl; |
---|
1225 | this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl); |
---|
1226 | }else{ |
---|
1227 | this.log("SWF URL unmodified:", this.uploadUrl) |
---|
1228 | } |
---|
1229 | }else{ |
---|
1230 | console.warn("Warning: no uploadUrl provided."); |
---|
1231 | } |
---|
1232 | |
---|
1233 | var w = this.fhtml.nr.w; |
---|
1234 | var h = this.fhtml.nr.h; |
---|
1235 | |
---|
1236 | var args = { |
---|
1237 | expressInstall:true, |
---|
1238 | path: this.swfPath.uri || this.swfPath, |
---|
1239 | width: w, |
---|
1240 | height: h, |
---|
1241 | allowScriptAccess:"always", |
---|
1242 | allowNetworking:"all", |
---|
1243 | vars: { |
---|
1244 | uploadDataFieldName: this.flashFieldName, |
---|
1245 | uploadUrl: this.uploadUrl, |
---|
1246 | uploadOnSelect: this.uploadOnChange, |
---|
1247 | deferredUploading:this.deferredUploading || 0, |
---|
1248 | selectMultipleFiles: this.selectMultipleFiles, |
---|
1249 | id: this.id, |
---|
1250 | isDebug: this.isDebug, |
---|
1251 | devMode:this.devMode, |
---|
1252 | flashButton:embedFlashVars.serialize("fh", this.fhtml), |
---|
1253 | fileMask:embedFlashVars.serialize("fm", this.fileMask), |
---|
1254 | noReturnCheck: this.skipServerCheck, |
---|
1255 | serverTimeout:this.serverTimeout |
---|
1256 | }, |
---|
1257 | params: { |
---|
1258 | scale:"noscale", |
---|
1259 | wmode:"opaque", |
---|
1260 | allowScriptAccess:"always", |
---|
1261 | allowNetworking:"all" |
---|
1262 | } |
---|
1263 | |
---|
1264 | }; |
---|
1265 | |
---|
1266 | this.flashObject = new embedFlash(args, this.insideNode); |
---|
1267 | this.flashObject.onError = lang.hitch(function(msg){ |
---|
1268 | this._error("Flash Error: " + msg); |
---|
1269 | }); |
---|
1270 | this.flashObject.onReady = lang.hitch(this, function(){ |
---|
1271 | domStyle.set(this.insideNode, "visibility", "visible"); |
---|
1272 | this.log("FileUploader flash object ready"); |
---|
1273 | this.onReady(this); |
---|
1274 | }); |
---|
1275 | this.flashObject.onLoad = lang.hitch(this, function(mov){ |
---|
1276 | this.flashMovie = mov; |
---|
1277 | this.flashReady = true; |
---|
1278 | |
---|
1279 | this.onLoad(this); |
---|
1280 | }); |
---|
1281 | this._connectFlash(); |
---|
1282 | |
---|
1283 | }, |
---|
1284 | |
---|
1285 | _connectFlash: function(){ |
---|
1286 | // summary: |
---|
1287 | // Subscribing to published topics coming from the |
---|
1288 | // Flash uploader. |
---|
1289 | // description: |
---|
1290 | // Sacrificing some readbilty for compactness. this.id |
---|
1291 | // will be on the beginning of the topic, so more than |
---|
1292 | // one uploader can be on a page and can have unique calls. |
---|
1293 | // |
---|
1294 | this._doSub("/filesSelected", "_change"); |
---|
1295 | this._doSub("/filesUploaded", "_complete"); |
---|
1296 | this._doSub("/filesProgress", "_progress"); |
---|
1297 | this._doSub("/filesError", "_error"); |
---|
1298 | this._doSub("/filesCanceled", "onCancel"); |
---|
1299 | this._doSub("/stageBlur", "_onFlashBlur"); |
---|
1300 | this._doSub("/up", "onMouseUp"); |
---|
1301 | this._doSub("/down", "onMouseDown"); |
---|
1302 | this._doSub("/over", "onMouseOver"); |
---|
1303 | this._doSub("/out", "onMouseOut"); |
---|
1304 | |
---|
1305 | this.connect(this.domNode, "focus", function(){ |
---|
1306 | // TODO: some kind of indicator that the Flash button |
---|
1307 | // is in focus |
---|
1308 | this.flashMovie.focus(); |
---|
1309 | this.flashMovie.doFocus(); |
---|
1310 | }); |
---|
1311 | if(this.tabIndex>=0){ |
---|
1312 | domAttr.set(this.domNode, "tabIndex", this.tabIndex); |
---|
1313 | } |
---|
1314 | }, |
---|
1315 | |
---|
1316 | _doSub: function(subStr, funcStr){ |
---|
1317 | // summary: |
---|
1318 | // Internal. Shortcut for subscribes to Flash movie |
---|
1319 | this._subs.push(connect.subscribe(this.id + subStr, this, funcStr)); |
---|
1320 | }, |
---|
1321 | |
---|
1322 | /************************************* |
---|
1323 | * DOM INSPECTION METHODS * |
---|
1324 | *************************************/ |
---|
1325 | |
---|
1326 | urlencode: function(url){ |
---|
1327 | // Using symbols in place of URL chars that will break in Flash serialization. |
---|
1328 | if(!url || url == "none"){ |
---|
1329 | return false; |
---|
1330 | } |
---|
1331 | return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,""); |
---|
1332 | }, |
---|
1333 | |
---|
1334 | isButton: function(node){ |
---|
1335 | // testing if button for styling purposes |
---|
1336 | var tn = node.tagName.toLowerCase(); |
---|
1337 | return tn == "button" || tn == "input"; |
---|
1338 | }, |
---|
1339 | |
---|
1340 | getTextStyle: function(node){ |
---|
1341 | // getting font info |
---|
1342 | var o = {}; |
---|
1343 | o.ff = domStyle.get(node, "fontFamily"); |
---|
1344 | if(o.ff){ |
---|
1345 | o.ff = o.ff.replace(", ", ","); // remove spaces. IE in Flash no likee |
---|
1346 | o.ff = o.ff.replace(/\"|\'/g, ""); |
---|
1347 | o.ff = o.ff == "sans-serif" ? "Arial" : o.ff; // Flash doesn't know what sans-serif is |
---|
1348 | o.fw = domStyle.get(node, "fontWeight"); |
---|
1349 | o.fi = domStyle.get(node, "fontStyle"); |
---|
1350 | o.fs = parseInt(domStyle.get(node, "fontSize"), 10); |
---|
1351 | if(domStyle.get(node, "fontSize").indexOf("%") > -1){ |
---|
1352 | // IE doesn't convert % to px. For god sakes. |
---|
1353 | var n = node; |
---|
1354 | while(n.tagName){ |
---|
1355 | if(domStyle.get(n, "fontSize").indexOf("%") == -1){ |
---|
1356 | o.fs = parseInt(domStyle.get(n, "fontSize"), 10); |
---|
1357 | break; |
---|
1358 | } |
---|
1359 | if(n.tagName.toLowerCase()=="body"){ |
---|
1360 | // if everyting is %, the the font size is 16px * the % |
---|
1361 | o.fs = 16 * .01 * parseInt(domStyle.get(n, "fontSize"), 10); |
---|
1362 | } |
---|
1363 | n = n.parentNode; |
---|
1364 | } |
---|
1365 | } |
---|
1366 | o.fc = new Color(domStyle.get(node, "color")).toHex(); |
---|
1367 | o.fc = parseInt(o.fc.substring(1,Infinity),16); |
---|
1368 | } |
---|
1369 | o.lh = domStyle.get(node, "lineHeight"); |
---|
1370 | o.ta = domStyle.get(node, "textAlign"); |
---|
1371 | o.ta = o.ta == "start" || !o.ta ? "left" : o.ta; |
---|
1372 | o.va = this.isButton(node) ? "middle" : o.lh == o.h ? "middle" : domStyle.get(node, "verticalAlign"); |
---|
1373 | return o; |
---|
1374 | }, |
---|
1375 | |
---|
1376 | getText: function(node){ |
---|
1377 | // Get the text of the button. It's possible to use HTML in the Flash Button, |
---|
1378 | // but the results are not spectacular. |
---|
1379 | var cn = lang.trim(node.innerHTML); |
---|
1380 | if(cn.indexOf("<") >- 1){ |
---|
1381 | cn = escape(cn); |
---|
1382 | } |
---|
1383 | return cn; |
---|
1384 | }, |
---|
1385 | |
---|
1386 | getStyle: function(node){ |
---|
1387 | // getting the style of a node. Using very abbreviated characters which the |
---|
1388 | // Flash movie understands. |
---|
1389 | var o = {}; |
---|
1390 | var dim = domGeometry.getContentBox(node); |
---|
1391 | var pad = domGeometry.getPadExtents(node); |
---|
1392 | o.p = [pad.t, pad.w-pad.l, pad.h-pad.t, pad.l]; |
---|
1393 | o.w = dim.w + pad.w; |
---|
1394 | o.h = dim.h + pad.h; |
---|
1395 | o.d = domStyle.get(node, "display"); |
---|
1396 | var clr = new Color(domStyle.get(node, "backgroundColor")); |
---|
1397 | // if no color, Safari sets #000000 and alpha=0 since we don't support alpha, |
---|
1398 | // it makes black - make it white |
---|
1399 | o.bc = clr.a == 0 ? "#ffffff" : clr.toHex(); |
---|
1400 | o.bc = parseInt(o.bc.substring(1,Infinity),16); |
---|
1401 | var url = this.urlencode(domStyle.get(node, "backgroundImage")); |
---|
1402 | if(url){ |
---|
1403 | o.bi = { |
---|
1404 | url:url, |
---|
1405 | rp:domStyle.get(node, "backgroundRepeat"), |
---|
1406 | pos: escape(domStyle.get(node, "backgroundPosition")) |
---|
1407 | }; |
---|
1408 | if(!o.bi.pos){ |
---|
1409 | // IE does Xpx and Ypx, not "X% Y%" |
---|
1410 | var rx = domStyle.get(node, "backgroundPositionX"); |
---|
1411 | var ry = domStyle.get(node, "backgroundPositionY"); |
---|
1412 | rx = (rx == "left") ? "0%" : (rx == "right") ? "100%" : rx; |
---|
1413 | ry = (ry == "top") ? "0%" : (ry == "bottom") ? "100%" : ry; |
---|
1414 | o.bi.pos = escape(rx+" "+ry); |
---|
1415 | } |
---|
1416 | } |
---|
1417 | return lang.mixin(o, this.getTextStyle(node)); |
---|
1418 | }, |
---|
1419 | |
---|
1420 | getTempNodeStyle: function(node, _class, isDijitButton){ |
---|
1421 | // This sets up a temp node to get the style of the hover, active, and disabled states |
---|
1422 | var temp, style; |
---|
1423 | if(isDijitButton){ |
---|
1424 | // backwards compat until dojo 1.5 |
---|
1425 | temp = domConstruct.place("<"+node.tagName+"><span>"+node.innerHTML+"</span></"+node.tagName+">", node.parentNode); //+" "+_class+" |
---|
1426 | var first = temp.firstChild; |
---|
1427 | domClass.add(first, node.className); |
---|
1428 | domClass.add(temp, _class); |
---|
1429 | style = this.getStyle(first); |
---|
1430 | }else{ |
---|
1431 | temp = domConstruct.place("<"+node.tagName+">"+node.innerHTML+"</"+node.tagName+">", node.parentNode); |
---|
1432 | domClass.add(temp, node.className); |
---|
1433 | domClass.add(temp, _class); |
---|
1434 | temp.id = node.id; |
---|
1435 | style = this.getStyle(temp); |
---|
1436 | } |
---|
1437 | // dev note: comment out this line to see what the |
---|
1438 | // button states look like to the FileUploader |
---|
1439 | domConstruct.destroy(temp); |
---|
1440 | return style; |
---|
1441 | } |
---|
1442 | }); |
---|
1443 | return dojox.form.FileUploader; |
---|
1444 | }); |
---|