FileUploader.js 43 KB

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