SWFUpload.as 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519
  1. package {
  2. import flash.display.BlendMode;
  3. import flash.display.DisplayObjectContainer;
  4. import flash.display.Loader;
  5. import flash.display.Stage;
  6. import flash.display.Sprite;
  7. import flash.display.StageAlign;
  8. import flash.display.StageScaleMode;
  9. import flash.net.FileReferenceList;
  10. import flash.net.FileReference;
  11. import flash.net.FileFilter;
  12. import flash.net.URLRequest;
  13. import flash.net.URLRequestMethod;
  14. import flash.net.URLVariables;
  15. import flash.events.*;
  16. import flash.external.ExternalInterface;
  17. import flash.system.Security;
  18. import flash.text.AntiAliasType;
  19. import flash.text.GridFitType;
  20. import flash.text.StaticText;
  21. import flash.text.StyleSheet;
  22. import flash.text.TextDisplayMode;
  23. import flash.text.TextField;
  24. import flash.text.TextFieldType;
  25. import flash.text.TextFieldAutoSize;
  26. import flash.text.TextFormat;
  27. import flash.ui.Mouse;
  28. import flash.utils.Timer;
  29. import FileItem;
  30. import ExternalCall;
  31. public class SWFUpload extends Sprite {
  32. // Cause SWFUpload to start as soon as the movie starts
  33. public static function main():void
  34. {
  35. var SWFUpload:SWFUpload = new SWFUpload();
  36. }
  37. private const build_number:String = "SWFUPLOAD 2.2.0";
  38. // State tracking variables
  39. private var fileBrowserMany:FileReferenceList = new FileReferenceList();
  40. private var fileBrowserOne:FileReference = null; // This isn't set because it can't be reused like the FileReferenceList. It gets setup in the SelectFile method
  41. private var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded.
  42. private var current_file_item:FileItem = null; // the item that is currently being uploaded.
  43. private var file_index:Array = new Array();
  44. private var successful_uploads:Number = 0; // Tracks the uploads that have been completed
  45. private var queue_errors:Number = 0; // Tracks files rejected during queueing
  46. private var upload_errors:Number = 0; // Tracks files that fail upload
  47. private var upload_cancelled:Number = 0; // Tracks number of cancelled files
  48. private var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded.
  49. private var valid_file_extensions:Array = new Array();// Holds the parsed valid extensions.
  50. private var serverDataTimer:Timer = null;
  51. private var assumeSuccessTimer:Timer = null;
  52. private var restoreExtIntTimer:Timer;
  53. private var hasCalledFlashReady:Boolean = false;
  54. // Callbacks
  55. private var flashReady_Callback:String;
  56. private var fileDialogStart_Callback:String;
  57. private var fileQueued_Callback:String;
  58. private var fileQueueError_Callback:String;
  59. private var fileDialogComplete_Callback:String;
  60. private var uploadStart_Callback:String;
  61. private var uploadProgress_Callback:String;
  62. private var uploadError_Callback:String;
  63. private var uploadSuccess_Callback:String;
  64. private var uploadComplete_Callback:String;
  65. private var debug_Callback:String;
  66. private var testExternalInterface_Callback:String;
  67. private var cleanUp_Callback:String;
  68. // Values passed in from the HTML
  69. private var movieName:String;
  70. private var uploadURL:String;
  71. private var filePostName:String;
  72. private var uploadPostObject:Object;
  73. private var fileTypes:String;
  74. private var fileTypesDescription:String;
  75. private var fileSizeLimit:Number;
  76. private var fileUploadLimit:Number = 0;
  77. private var fileQueueLimit:Number = 0;
  78. private var useQueryString:Boolean = false;
  79. private var requeueOnError:Boolean = false;
  80. private var httpSuccess:Array = [];
  81. private var assumeSuccessTimeout:Number = 0;
  82. private var debugEnabled:Boolean;
  83. private var buttonLoader:Loader;
  84. private var buttonTextField:TextField;
  85. private var buttonCursorSprite:Sprite;
  86. private var buttonImageURL:String;
  87. private var buttonWidth:Number;
  88. private var buttonHeight:Number;
  89. private var buttonText:String;
  90. private var buttonTextStyle:String;
  91. private var buttonTextTopPadding:Number;
  92. private var buttonTextLeftPadding:Number;
  93. private var buttonAction:Number;
  94. private var buttonCursor:Number;
  95. private var buttonStateOver:Boolean;
  96. private var buttonStateMouseDown:Boolean;
  97. private var buttonStateDisabled:Boolean;
  98. // Error code "constants"
  99. // Size check constants
  100. private var SIZE_TOO_BIG:Number = 1;
  101. private var SIZE_ZERO_BYTE:Number = -1;
  102. private var SIZE_OK:Number = 0;
  103. // Queue errors
  104. private var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -100;
  105. private var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -110;
  106. private var ERROR_CODE_ZERO_BYTE_FILE:Number = -120;
  107. private var ERROR_CODE_INVALID_FILETYPE:Number = -130;
  108. // Upload Errors
  109. private var ERROR_CODE_HTTP_ERROR:Number = -200;
  110. private var ERROR_CODE_MISSING_UPLOAD_URL:Number = -210;
  111. private var ERROR_CODE_IO_ERROR:Number = -220;
  112. private var ERROR_CODE_SECURITY_ERROR:Number = -230;
  113. private var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -240;
  114. private var ERROR_CODE_UPLOAD_FAILED:Number = -250;
  115. private var ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND:Number = -260;
  116. private var ERROR_CODE_FILE_VALIDATION_FAILED:Number = -270;
  117. private var ERROR_CODE_FILE_CANCELLED:Number = -280;
  118. private var ERROR_CODE_UPLOAD_STOPPED:Number = -290;
  119. // Button Actions
  120. private var BUTTON_ACTION_SELECT_FILE:Number = -100;
  121. private var BUTTON_ACTION_SELECT_FILES:Number = -110;
  122. private var BUTTON_ACTION_START_UPLOAD:Number = -120;
  123. private var BUTTON_CURSOR_ARROW:Number = -1;
  124. private var BUTTON_CURSOR_HAND:Number = -2;
  125. public function SWFUpload() {
  126. // Do the feature detection. Make sure this version of Flash supports the features we need. If not
  127. // abort initialization.
  128. if (!flash.net.FileReferenceList || !flash.net.FileReference || !flash.net.URLRequest || !flash.external.ExternalInterface || !flash.external.ExternalInterface.available || !DataEvent.UPLOAD_COMPLETE_DATA) {
  129. return;
  130. }
  131. Security.allowDomain("*"); // Allow uploading to any domain
  132. // Keep Flash Player busy so it doesn't show the "flash script is running slowly" error
  133. var counter:Number = 0;
  134. root.addEventListener(Event.ENTER_FRAME, function ():void { if (++counter > 100) counter = 0; });
  135. // Setup file FileReferenceList events
  136. this.fileBrowserMany.addEventListener(Event.SELECT, this.Select_Many_Handler);
  137. this.fileBrowserMany.addEventListener(Event.CANCEL, this.DialogCancelled_Handler);
  138. this.stage.align = StageAlign.TOP_LEFT;
  139. this.stage.scaleMode = StageScaleMode.NO_SCALE;
  140. // Setup the button and text label
  141. this.buttonLoader = new Loader();
  142. var doNothing:Function = function ():void { };
  143. this.buttonLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, doNothing );
  144. this.buttonLoader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, doNothing );
  145. this.stage.addChild(this.buttonLoader);
  146. var self:SWFUpload = this;
  147. this.stage.addEventListener(MouseEvent.CLICK, function (event:MouseEvent):void {
  148. self.UpdateButtonState();
  149. self.ButtonClickHandler(event);
  150. });
  151. this.stage.addEventListener(MouseEvent.MOUSE_DOWN, function (event:MouseEvent):void {
  152. self.buttonStateMouseDown = true;
  153. self.UpdateButtonState();
  154. });
  155. this.stage.addEventListener(MouseEvent.MOUSE_UP, function (event:MouseEvent):void {
  156. self.buttonStateMouseDown = false;
  157. self.UpdateButtonState();
  158. });
  159. this.stage.addEventListener(MouseEvent.MOUSE_OVER, function (event:MouseEvent):void {
  160. self.buttonStateMouseDown = event.buttonDown;
  161. self.buttonStateOver = true;
  162. self.UpdateButtonState();
  163. });
  164. this.stage.addEventListener(MouseEvent.MOUSE_OUT, function (event:MouseEvent):void {
  165. self.buttonStateMouseDown = false;
  166. self.buttonStateOver = false;
  167. self.UpdateButtonState();
  168. });
  169. // Handle the mouse leaving the flash movie altogether
  170. this.stage.addEventListener(Event.MOUSE_LEAVE, function (event:Event):void {
  171. self.buttonStateMouseDown = false;
  172. self.buttonStateOver = false;
  173. self.UpdateButtonState();
  174. });
  175. this.buttonTextField = new TextField();
  176. this.buttonTextField.type = TextFieldType.DYNAMIC;
  177. this.buttonTextField.antiAliasType = AntiAliasType.ADVANCED;
  178. this.buttonTextField.autoSize = TextFieldAutoSize.NONE;
  179. this.buttonTextField.cacheAsBitmap = true;
  180. this.buttonTextField.multiline = true;
  181. this.buttonTextField.wordWrap = false;
  182. this.buttonTextField.tabEnabled = false;
  183. this.buttonTextField.background = false;
  184. this.buttonTextField.border = false;
  185. this.buttonTextField.selectable = false;
  186. this.buttonTextField.condenseWhite = true;
  187. this.stage.addChild(this.buttonTextField);
  188. this.buttonCursorSprite = new Sprite();
  189. this.buttonCursorSprite.graphics.beginFill(0xFFFFFF, 0);
  190. this.buttonCursorSprite.graphics.drawRect(0, 0, 1, 1);
  191. this.buttonCursorSprite.graphics.endFill();
  192. this.buttonCursorSprite.buttonMode = true;
  193. this.buttonCursorSprite.x = 0;
  194. this.buttonCursorSprite.y = 0;
  195. this.buttonCursorSprite.addEventListener(MouseEvent.CLICK, doNothing);
  196. this.stage.addChild(this.buttonCursorSprite);
  197. // Get the movie name
  198. this.movieName = root.loaderInfo.parameters.movieName;
  199. // **Configure the callbacks**
  200. // The JavaScript tracks all the instances of SWFUpload on a page. We can access the instance
  201. // associated with this SWF file using the movieName. Each callback is accessible by making
  202. // a call directly to it on our instance. There is no error handling for undefined callback functions.
  203. // A developer would have to deliberately remove the default functions,set the variable to null, or remove
  204. // it from the init function.
  205. this.flashReady_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].flashReady";
  206. this.fileDialogStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogStart";
  207. this.fileQueued_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueued";
  208. this.fileQueueError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueueError";
  209. this.fileDialogComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogComplete";
  210. this.uploadStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadStart";
  211. this.uploadProgress_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadProgress";
  212. this.uploadError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadError";
  213. this.uploadSuccess_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadSuccess";
  214. this.uploadComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadComplete";
  215. this.debug_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].debug";
  216. this.testExternalInterface_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].testExternalInterface";
  217. this.cleanUp_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].cleanUp";
  218. // Get the Flash Vars
  219. this.uploadURL = root.loaderInfo.parameters.uploadURL;
  220. this.filePostName = root.loaderInfo.parameters.filePostName;
  221. this.fileTypes = root.loaderInfo.parameters.fileTypes;
  222. this.fileTypesDescription = root.loaderInfo.parameters.fileTypesDescription + " (" + this.fileTypes + ")";
  223. this.loadPostParams(root.loaderInfo.parameters.params);
  224. if (!this.filePostName) {
  225. this.filePostName = "Filedata";
  226. }
  227. if (!this.fileTypes) {
  228. this.fileTypes = "*.*";
  229. }
  230. if (!this.fileTypesDescription) {
  231. this.fileTypesDescription = "All Files";
  232. }
  233. this.LoadFileExensions(this.fileTypes);
  234. try {
  235. this.debugEnabled = root.loaderInfo.parameters.debugEnabled == "true" ? true : false;
  236. } catch (ex:Object) {
  237. this.debugEnabled = false;
  238. }
  239. try {
  240. this.SetFileSizeLimit(String(root.loaderInfo.parameters.fileSizeLimit));
  241. } catch (ex:Object) {
  242. this.fileSizeLimit = 0;
  243. }
  244. try {
  245. this.fileUploadLimit = Number(root.loaderInfo.parameters.fileUploadLimit);
  246. if (this.fileUploadLimit < 0) this.fileUploadLimit = 0;
  247. } catch (ex:Object) {
  248. this.fileUploadLimit = 0;
  249. }
  250. try {
  251. this.fileQueueLimit = Number(root.loaderInfo.parameters.fileQueueLimit);
  252. if (this.fileQueueLimit < 0) this.fileQueueLimit = 0;
  253. } catch (ex:Object) {
  254. this.fileQueueLimit = 0;
  255. }
  256. // Set the queue limit to match the upload limit when the queue limit is bigger than the upload limit
  257. if (this.fileQueueLimit > this.fileUploadLimit && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
  258. // The the queue limit is unlimited and the upload limit is not then set the queue limit to the upload limit
  259. if (this.fileQueueLimit == 0 && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
  260. try {
  261. this.useQueryString = root.loaderInfo.parameters.useQueryString == "true" ? true : false;
  262. } catch (ex:Object) {
  263. this.useQueryString = false;
  264. }
  265. try {
  266. this.requeueOnError = root.loaderInfo.parameters.requeueOnError == "true" ? true : false;
  267. } catch (ex:Object) {
  268. this.requeueOnError = false;
  269. }
  270. try {
  271. this.SetHTTPSuccess(String(root.loaderInfo.parameters.httpSuccess));
  272. } catch (ex:Object) {
  273. this.SetHTTPSuccess([]);
  274. }
  275. try {
  276. this.SetAssumeSuccessTimeout(Number(root.loaderInfo.parameters.assumeSuccessTimeout));
  277. } catch (ex:Object) {
  278. this.SetAssumeSuccessTimeout(0);
  279. }
  280. try {
  281. this.SetButtonDimensions(Number(root.loaderInfo.parameters.buttonWidth), Number(root.loaderInfo.parameters.buttonHeight));
  282. } catch (ex:Object) {
  283. this.SetButtonDimensions(0, 0);
  284. }
  285. try {
  286. this.SetButtonImageURL(String(root.loaderInfo.parameters.buttonImageURL));
  287. } catch (ex:Object) {
  288. this.SetButtonImageURL("");
  289. }
  290. try {
  291. this.SetButtonText(String(root.loaderInfo.parameters.buttonText));
  292. } catch (ex:Object) {
  293. this.SetButtonText("");
  294. }
  295. try {
  296. this.SetButtonTextPadding(Number(root.loaderInfo.parameters.buttonTextLeftPadding), Number(root.loaderInfo.parameters.buttonTextTopPadding));
  297. } catch (ex:Object) {
  298. this.SetButtonTextPadding(0, 0);
  299. }
  300. try {
  301. this.SetButtonTextStyle(String(root.loaderInfo.parameters.buttonTextStyle));
  302. } catch (ex:Object) {
  303. this.SetButtonTextStyle("");
  304. }
  305. try {
  306. this.SetButtonAction(Number(root.loaderInfo.parameters.buttonAction));
  307. } catch (ex:Object) {
  308. this.SetButtonAction(this.BUTTON_ACTION_SELECT_FILES);
  309. }
  310. try {
  311. this.SetButtonDisabled(root.loaderInfo.parameters.buttonDisabled == "true" ? true : false);
  312. } catch (ex:Object) {
  313. this.SetButtonDisabled(Boolean(false));
  314. }
  315. try {
  316. this.SetButtonCursor(Number(root.loaderInfo.parameters.buttonCursor));
  317. } catch (ex:Object) {
  318. this.SetButtonCursor(this.BUTTON_CURSOR_ARROW);
  319. }
  320. this.SetupExternalInterface();
  321. this.Debug("SWFUpload Init Complete");
  322. this.PrintDebugInfo();
  323. if (ExternalCall.Bool(this.testExternalInterface_Callback)) {
  324. ExternalCall.Simple(this.flashReady_Callback);
  325. this.hasCalledFlashReady = true;
  326. }
  327. // Start periodically checking the external interface
  328. var oSelf:SWFUpload = this;
  329. this.restoreExtIntTimer = new Timer(1000, 0);
  330. this.restoreExtIntTimer.addEventListener(TimerEvent.TIMER, function ():void { oSelf.CheckExternalInterface();} );
  331. this.restoreExtIntTimer.start();
  332. }
  333. // Used to periodically check that the External Interface functions are still working
  334. private function CheckExternalInterface():void {
  335. if (!ExternalCall.Bool(this.testExternalInterface_Callback)) {
  336. this.SetupExternalInterface();
  337. this.Debug("ExternalInterface reinitialized");
  338. if (!this.hasCalledFlashReady) {
  339. ExternalCall.Simple(this.flashReady_Callback);
  340. this.hasCalledFlashReady = true;
  341. }
  342. }
  343. }
  344. // Called by JS to see if it can access the external interface
  345. private function TestExternalInterface():Boolean {
  346. return true;
  347. }
  348. private function SetupExternalInterface():void {
  349. try {
  350. ExternalInterface.addCallback("SelectFile", this.SelectFile);
  351. ExternalInterface.addCallback("SelectFiles", this.SelectFiles);
  352. ExternalInterface.addCallback("StartUpload", this.StartUpload);
  353. ExternalInterface.addCallback("ReturnUploadStart", this.ReturnUploadStart);
  354. ExternalInterface.addCallback("StopUpload", this.StopUpload);
  355. ExternalInterface.addCallback("CancelUpload", this.CancelUpload);
  356. ExternalInterface.addCallback("RequeueUpload", this.RequeueUpload);
  357. ExternalInterface.addCallback("GetStats", this.GetStats);
  358. ExternalInterface.addCallback("SetStats", this.SetStats);
  359. ExternalInterface.addCallback("GetFile", this.GetFile);
  360. ExternalInterface.addCallback("GetFileByIndex", this.GetFileByIndex);
  361. ExternalInterface.addCallback("AddFileParam", this.AddFileParam);
  362. ExternalInterface.addCallback("RemoveFileParam", this.RemoveFileParam);
  363. ExternalInterface.addCallback("SetUploadURL", this.SetUploadURL);
  364. ExternalInterface.addCallback("SetPostParams", this.SetPostParams);
  365. ExternalInterface.addCallback("SetFileTypes", this.SetFileTypes);
  366. ExternalInterface.addCallback("SetFileSizeLimit", this.SetFileSizeLimit);
  367. ExternalInterface.addCallback("SetFileUploadLimit", this.SetFileUploadLimit);
  368. ExternalInterface.addCallback("SetFileQueueLimit", this.SetFileQueueLimit);
  369. ExternalInterface.addCallback("SetFilePostName", this.SetFilePostName);
  370. ExternalInterface.addCallback("SetUseQueryString", this.SetUseQueryString);
  371. ExternalInterface.addCallback("SetRequeueOnError", this.SetRequeueOnError);
  372. ExternalInterface.addCallback("SetHTTPSuccess", this.SetHTTPSuccess);
  373. ExternalInterface.addCallback("SetAssumeSuccessTimeout", this.SetAssumeSuccessTimeout);
  374. ExternalInterface.addCallback("SetDebugEnabled", this.SetDebugEnabled);
  375. ExternalInterface.addCallback("SetButtonImageURL", this.SetButtonImageURL);
  376. ExternalInterface.addCallback("SetButtonDimensions", this.SetButtonDimensions);
  377. ExternalInterface.addCallback("SetButtonText", this.SetButtonText);
  378. ExternalInterface.addCallback("SetButtonTextPadding", this.SetButtonTextPadding);
  379. ExternalInterface.addCallback("SetButtonTextStyle", this.SetButtonTextStyle);
  380. ExternalInterface.addCallback("SetButtonAction", this.SetButtonAction);
  381. ExternalInterface.addCallback("SetButtonDisabled", this.SetButtonDisabled);
  382. ExternalInterface.addCallback("SetButtonCursor", this.SetButtonCursor);
  383. ExternalInterface.addCallback("TestExternalInterface", this.TestExternalInterface);
  384. } catch (ex:Error) {
  385. this.Debug("Callbacks where not set: " + ex.message);
  386. return;
  387. }
  388. ExternalCall.Simple(this.cleanUp_Callback);
  389. }
  390. /* *****************************************
  391. * FileReference Event Handlers
  392. * *************************************** */
  393. private function DialogCancelled_Handler(event:Event):void {
  394. this.Debug("Event: fileDialogComplete: File Dialog window cancelled.");
  395. ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0, 0, this.queued_uploads);
  396. }
  397. private function Open_Handler(event:Event):void {
  398. this.Debug("Event: uploadProgress (OPEN): File ID: " + this.current_file_item.id);
  399. ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), 0, this.current_file_item.file_reference.size);
  400. }
  401. private function FileProgress_Handler(event:ProgressEvent):void {
  402. // On early than Mac OS X 10.3 bytesLoaded is always -1, convert this to zero. Do bytesTotal for good measure.
  403. // http://livedocs.adobe.com/flex/3/langref/flash/net/FileReference.html#event:progress
  404. var bytesLoaded:Number = event.bytesLoaded < 0 ? 0 : event.bytesLoaded;
  405. var bytesTotal:Number = event.bytesTotal < 0 ? 0 : event.bytesTotal;
  406. // Because Flash never fires a complete event if the server doesn't respond after 30 seconds or on Macs if there
  407. // is no content in the response we'll set a timer and assume that the upload is successful after the defined amount of
  408. // time. If the timeout is zero then we won't use the timer.
  409. if (bytesLoaded === bytesTotal && bytesTotal > 0 && this.assumeSuccessTimeout > 0) {
  410. if (this.assumeSuccessTimer !== null) {
  411. this.assumeSuccessTimer.stop();
  412. this.assumeSuccessTimer = null;
  413. }
  414. this.assumeSuccessTimer = new Timer(this.assumeSuccessTimeout * 1000, 1);
  415. this.assumeSuccessTimer.addEventListener(TimerEvent.TIMER_COMPLETE, AssumeSuccessTimer_Handler);
  416. this.assumeSuccessTimer.start();
  417. }
  418. this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + bytesLoaded + ". Total: " + bytesTotal);
  419. ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), bytesLoaded, bytesTotal);
  420. }
  421. private function AssumeSuccessTimer_Handler(event:TimerEvent):void {
  422. this.Debug("Event: AssumeSuccess: " + this.assumeSuccessTimeout + " passed without server response");
  423. this.UploadSuccess(this.current_file_item, "", false);
  424. }
  425. private function Complete_Handler(event:Event):void {
  426. /* Because we can't do COMPLETE or DATA events (we have to do both) we can't
  427. * just call uploadSuccess from the complete handler, we have to wait for
  428. * the Data event which may never come. However, testing shows it always comes
  429. * within a couple milliseconds if it is going to come so the solution is:
  430. *
  431. * Set a timer in the COMPLETE event (which always fires) and if DATA is fired
  432. * it will stop the timer and call uploadComplete
  433. *
  434. * If the timer expires then DATA won't be fired and we call uploadComplete
  435. * */
  436. // Set the timer
  437. if (serverDataTimer != null) {
  438. this.serverDataTimer.stop();
  439. this.serverDataTimer = null;
  440. }
  441. this.serverDataTimer = new Timer(100, 1);
  442. //var self:SWFUpload = this;
  443. this.serverDataTimer.addEventListener(TimerEvent.TIMER, this.ServerDataTimer_Handler);
  444. this.serverDataTimer.start();
  445. }
  446. private function ServerDataTimer_Handler(event:TimerEvent):void {
  447. this.UploadSuccess(this.current_file_item, "");
  448. }
  449. private function ServerData_Handler(event:DataEvent):void {
  450. this.UploadSuccess(this.current_file_item, event.data);
  451. }
  452. private function UploadSuccess(file:FileItem, serverData:String, responseReceived:Boolean = true):void {
  453. if (this.serverDataTimer !== null) {
  454. this.serverDataTimer.stop();
  455. this.serverDataTimer = null;
  456. }
  457. if (this.assumeSuccessTimer !== null) {
  458. this.assumeSuccessTimer.stop();
  459. this.assumeSuccessTimer = null;
  460. }
  461. this.successful_uploads++;
  462. file.file_status = FileItem.FILE_STATUS_SUCCESS;
  463. this.Debug("Event: uploadSuccess: File ID: " + file.id + " Response Received: " + responseReceived.toString() + " Data: " + serverData);
  464. ExternalCall.UploadSuccess(this.uploadSuccess_Callback, file.ToJavaScriptObject(), serverData, responseReceived);
  465. this.UploadComplete(false);
  466. }
  467. private function HTTPError_Handler(event:HTTPStatusEvent):void {
  468. var isSuccessStatus:Boolean = false;
  469. for (var i:Number = 0; i < this.httpSuccess.length; i++) {
  470. if (this.httpSuccess[i] === event.status) {
  471. isSuccessStatus = true;
  472. break;
  473. }
  474. }
  475. if (isSuccessStatus) {
  476. this.Debug("Event: httpError: Translating status code " + event.status + " to uploadSuccess");
  477. var serverDataEvent:DataEvent = new DataEvent(DataEvent.UPLOAD_COMPLETE_DATA, event.bubbles, event.cancelable, "");
  478. this.ServerData_Handler(serverDataEvent);
  479. } else {
  480. this.upload_errors++;
  481. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  482. this.Debug("Event: uploadError: HTTP ERROR : File ID: " + this.current_file_item.id + ". HTTP Status: " + event.status + ".");
  483. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), event.status.toString());
  484. this.UploadComplete(true); // An IO Error is also called so we don't want to complete the upload yet.
  485. }
  486. }
  487. // Note: Flash Player does not support Uploads that require authentication. Attempting this will trigger an
  488. // IO Error or it will prompt for a username and password and may crash the browser (FireFox/Opera)
  489. private function IOError_Handler(event:IOErrorEvent):void {
  490. // Only trigger an IO Error event if we haven't already done an HTTP error
  491. if (this.current_file_item.file_status != FileItem.FILE_STATUS_ERROR) {
  492. this.upload_errors++;
  493. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  494. this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error: " + event.text);
  495. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), event.text);
  496. }
  497. this.UploadComplete(true);
  498. }
  499. private function SecurityError_Handler(event:SecurityErrorEvent):void {
  500. this.upload_errors++;
  501. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  502. this.Debug("Event: uploadError : Security Error : File Number: " + this.current_file_item.id + ". Error text: " + event.text);
  503. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), event.text);
  504. this.UploadComplete(true);
  505. }
  506. private function Select_Many_Handler(event:Event):void {
  507. this.Select_Handler(this.fileBrowserMany.fileList);
  508. }
  509. private function Select_One_Handler(event:Event):void {
  510. var fileArray:Array = new Array(1);
  511. fileArray[0] = this.fileBrowserOne;
  512. this.Select_Handler(fileArray);
  513. }
  514. private function Select_Handler(file_reference_list:Array):void {
  515. this.Debug("Select Handler: Received the files selected from the dialog. Processing the file list...");
  516. var num_files_queued:Number = 0;
  517. // Determine how many queue slots are remaining (check the unlimited (0) settings, successful uploads and queued uploads)
  518. var queue_slots_remaining:Number = 0;
  519. if (this.fileUploadLimit == 0) {
  520. queue_slots_remaining = this.fileQueueLimit == 0 ? file_reference_list.length : (this.fileQueueLimit - this.queued_uploads); // If unlimited queue make the allowed size match however many files were selected.
  521. } else {
  522. var remaining_uploads:Number = this.fileUploadLimit - this.successful_uploads - this.queued_uploads;
  523. if (remaining_uploads < 0) remaining_uploads = 0;
  524. if (this.fileQueueLimit == 0 || this.fileQueueLimit >= remaining_uploads) {
  525. queue_slots_remaining = remaining_uploads;
  526. } else if (this.fileQueueLimit < remaining_uploads) {
  527. queue_slots_remaining = this.fileQueueLimit - this.queued_uploads;
  528. }
  529. }
  530. if (queue_slots_remaining < 0) queue_slots_remaining = 0;
  531. // Check if the number of files selected is greater than the number allowed to queue up.
  532. if (queue_slots_remaining < file_reference_list.length) {
  533. this.Debug("Event: fileQueueError : Selected Files (" + file_reference_list.length + ") exceeds remaining Queue size (" + queue_slots_remaining + ").");
  534. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining.toString());
  535. } else {
  536. // Process each selected file
  537. for (var i:Number = 0; i < file_reference_list.length; i++) {
  538. var file_item:FileItem = new FileItem(file_reference_list[i], this.movieName, this.file_index.length);
  539. this.file_index[file_item.index] = file_item;
  540. // Verify that the file is accessible. Zero byte files and possibly other conditions can cause a file to be inaccessible.
  541. var jsFileObj:Object = file_item.ToJavaScriptObject();
  542. var is_valid_file_reference:Boolean = (jsFileObj.filestatus !== FileItem.FILE_STATUS_ERROR);
  543. if (is_valid_file_reference) {
  544. // Check the size, if it's within the limit add it to the upload list.
  545. var size_result:Number = this.CheckFileSize(file_item);
  546. var is_valid_filetype:Boolean = this.CheckFileType(file_item);
  547. if(size_result == this.SIZE_OK && is_valid_filetype) {
  548. file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  549. this.file_queue.push(file_item);
  550. this.queued_uploads++;
  551. num_files_queued++;
  552. this.Debug("Event: fileQueued : File ID: " + file_item.id);
  553. ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject());
  554. }
  555. else if (!is_valid_filetype) {
  556. file_item.file_reference = null; // Cleanup the object
  557. this.queue_errors++;
  558. this.Debug("Event: fileQueueError : File not of a valid type.");
  559. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_INVALID_FILETYPE, file_item.ToJavaScriptObject(), "File is not an allowed file type.");
  560. }
  561. else if (size_result == this.SIZE_TOO_BIG) {
  562. file_item.file_reference = null; // Cleanup the object
  563. this.queue_errors++;
  564. this.Debug("Event: fileQueueError : File exceeds size limit.");
  565. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit.");
  566. }
  567. else if (size_result == this.SIZE_ZERO_BYTE) {
  568. file_item.file_reference = null; // Cleanup the object
  569. this.queue_errors++;
  570. this.Debug("Event: fileQueueError : File is zero bytes.");
  571. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded.");
  572. }
  573. } else {
  574. file_item.file_reference = null; // Cleanup the object
  575. this.queue_errors++;
  576. this.Debug("Event: fileQueueError : File is zero bytes or FileReference is invalid.");
  577. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes or cannot be accessed and cannot be uploaded.");
  578. }
  579. }
  580. }
  581. this.Debug("Event: fileDialogComplete : Finished processing selected files. Files selected: " + file_reference_list.length + ". Files Queued: " + num_files_queued);
  582. ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length, num_files_queued, this.queued_uploads);
  583. }
  584. /* ****************************************************************
  585. Externally exposed functions
  586. ****************************************************************** */
  587. // Opens a file browser dialog that allows one file to be selected.
  588. private function SelectFile():void {
  589. this.fileBrowserOne = new FileReference();
  590. this.fileBrowserOne.addEventListener(Event.SELECT, this.Select_One_Handler);
  591. this.fileBrowserOne.addEventListener(Event.CANCEL, this.DialogCancelled_Handler);
  592. // Default file type settings
  593. var allowed_file_types:String = "*.*";
  594. var allowed_file_types_description:String = "All Files";
  595. // Get the instance settings
  596. if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
  597. if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
  598. this.Debug("Event: fileDialogStart : Browsing files. Single Select. Allowed file types: " + allowed_file_types);
  599. ExternalCall.Simple(this.fileDialogStart_Callback);
  600. try {
  601. this.fileBrowserOne.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]);
  602. } catch (ex:Error) {
  603. this.Debug("Exception: " + ex.toString());
  604. }
  605. }
  606. // Opens a file browser dialog that allows multiple files to be selected.
  607. private function SelectFiles():void {
  608. var allowed_file_types:String = "*.*";
  609. var allowed_file_types_description:String = "All Files";
  610. if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
  611. if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
  612. this.Debug("Event: fileDialogStart : Browsing files. Multi Select. Allowed file types: " + allowed_file_types);
  613. ExternalCall.Simple(this.fileDialogStart_Callback);
  614. try {
  615. this.fileBrowserMany.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]);
  616. } catch (ex:Error) {
  617. this.Debug("Exception: " + ex.toString());
  618. }
  619. }
  620. // Cancel the current upload and stops. Doesn't advance the upload pointer. The current file is requeued at the beginning.
  621. private function StopUpload():void {
  622. if (this.current_file_item != null) {
  623. // Cancel the upload and re-queue the FileItem
  624. this.current_file_item.file_reference.cancel();
  625. // Remove the event handlers
  626. this.removeFileReferenceEventListeners(this.current_file_item);
  627. this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  628. this.file_queue.unshift(this.current_file_item);
  629. var js_object:Object = this.current_file_item.ToJavaScriptObject();
  630. this.current_file_item = null;
  631. this.Debug("Event: uploadError: upload stopped. File ID: " + js_object.ID);
  632. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped");
  633. this.Debug("Event: uploadComplete. File ID: " + js_object.ID);
  634. ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
  635. this.Debug("StopUpload(): upload stopped.");
  636. } else {
  637. this.Debug("StopUpload(): No file is currently uploading. Nothing to do.");
  638. }
  639. }
  640. /* Cancels the upload specified by file_id
  641. * If the file is currently uploading it is cancelled and the uploadComplete
  642. * event gets called.
  643. * If the file is not currently uploading then only the uploadCancelled event is fired.
  644. * */
  645. private function CancelUpload(file_id:String, triggerErrorEvent:Boolean = true):void {
  646. var file_item:FileItem = null;
  647. // Check the current file item
  648. if (this.current_file_item != null && (this.current_file_item.id == file_id || !file_id)) {
  649. this.current_file_item.file_reference.cancel();
  650. this.current_file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
  651. this.upload_cancelled++;
  652. if (triggerErrorEvent) {
  653. this.Debug("Event: uploadError: File ID: " + this.current_file_item.id + ". Cancelled current upload");
  654. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled.");
  655. } else {
  656. this.Debug("Event: cancelUpload: File ID: " + this.current_file_item.id + ". Cancelled current upload. Suppressed uploadError event.");
  657. }
  658. this.UploadComplete(false);
  659. } else if (file_id) {
  660. // Find the file in the queue
  661. var file_index:Number = this.FindIndexInFileQueue(file_id);
  662. if (file_index >= 0) {
  663. // Remove the file from the queue
  664. file_item = FileItem(this.file_queue[file_index]);
  665. file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
  666. this.file_queue[file_index] = null;
  667. this.queued_uploads--;
  668. this.upload_cancelled++;
  669. // Cancel the file (just for good measure) and make the callback
  670. file_item.file_reference.cancel();
  671. this.removeFileReferenceEventListeners(file_item);
  672. file_item.file_reference = null;
  673. if (triggerErrorEvent) {
  674. this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload");
  675. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
  676. } else {
  677. this.Debug("Event: cancelUpload: File ID: " + file_item.id + ". Cancelled current upload. Suppressed uploadError event.");
  678. }
  679. // Get rid of the file object
  680. file_item = null;
  681. }
  682. } else {
  683. // Get the first file and cancel it
  684. while (this.file_queue.length > 0 && file_item == null) {
  685. // Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
  686. file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
  687. if (typeof(file_item) == "undefined") {
  688. file_item = null;
  689. continue;
  690. }
  691. }
  692. if (file_item != null) {
  693. file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
  694. this.queued_uploads--;
  695. this.upload_cancelled++;
  696. // Cancel the file (just for good measure) and make the callback
  697. file_item.file_reference.cancel();
  698. this.removeFileReferenceEventListeners(file_item);
  699. file_item.file_reference = null;
  700. if (triggerErrorEvent) {
  701. this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload");
  702. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
  703. } else {
  704. this.Debug("Event: cancelUpload: File ID: " + file_item.id + ". Cancelled current upload. Suppressed uploadError event.");
  705. }
  706. // Get rid of the file object
  707. file_item = null;
  708. }
  709. }
  710. }
  711. /* Requeues the indicated file. Returns true if successful or if the file is
  712. * already in the queue. Otherwise returns false.
  713. * */
  714. private function RequeueUpload(fileIdentifier:*):Boolean {
  715. var file:FileItem = null;
  716. if (typeof(fileIdentifier) === "number") {
  717. var fileIndex:Number = Number(fileIdentifier);
  718. if (fileIndex >= 0 && fileIndex < this.file_index.length) {
  719. file = this.file_index[fileIndex];
  720. }
  721. } else if (typeof(fileIdentifier) === "string") {
  722. file = FindFileInFileIndex(String(fileIdentifier));
  723. } else {
  724. return false;
  725. }
  726. if (file !== null) {
  727. if (file.file_status === FileItem.FILE_STATUS_IN_PROGRESS || file.file_status === FileItem.FILE_STATUS_NEW) {
  728. return false;
  729. } else if (file.file_status !== FileItem.FILE_STATUS_QUEUED) {
  730. file.file_status = FileItem.FILE_STATUS_QUEUED;
  731. this.file_queue.unshift(file);
  732. this.queued_uploads++;
  733. }
  734. return true;
  735. } else {
  736. return false;
  737. }
  738. }
  739. private function GetStats():Object {
  740. return {
  741. in_progress : this.current_file_item == null ? 0 : 1,
  742. files_queued : this.queued_uploads,
  743. successful_uploads : this.successful_uploads,
  744. upload_errors : this.upload_errors,
  745. upload_cancelled : this.upload_cancelled,
  746. queue_errors : this.queue_errors
  747. };
  748. }
  749. private function SetStats(stats:Object):void {
  750. this.successful_uploads = typeof(stats["successful_uploads"]) === "number" ? stats["successful_uploads"] : this.successful_uploads;
  751. this.upload_errors = typeof(stats["upload_errors"]) === "number" ? stats["upload_errors"] : this.upload_errors;
  752. this.upload_cancelled = typeof(stats["upload_cancelled"]) === "number" ? stats["upload_cancelled"] : this.upload_cancelled;
  753. this.queue_errors = typeof(stats["queue_errors"]) === "number" ? stats["queue_errors"] : this.queue_errors;
  754. }
  755. private function GetFile(file_id:String):Object {
  756. var file_index:Number = this.FindIndexInFileQueue(file_id);
  757. if (file_index >= 0) {
  758. var file:FileItem = this.file_queue[file_index];
  759. } else {
  760. if (this.current_file_item != null) {
  761. file = this.current_file_item;
  762. } else {
  763. for (var i:Number = 0; i < this.file_queue.length; i++) {
  764. file = this.file_queue[i];
  765. if (file != null) break;
  766. }
  767. }
  768. }
  769. if (file == null) {
  770. return null;
  771. } else {
  772. return file.ToJavaScriptObject();
  773. }
  774. }
  775. private function GetFileByIndex(index:Number):Object {
  776. if (index < 0 || index > this.file_index.length - 1) {
  777. return null;
  778. } else {
  779. return this.file_index[index].ToJavaScriptObject();
  780. }
  781. }
  782. private function AddFileParam(file_id:String, name:String, value:String):Boolean {
  783. var item:FileItem = this.FindFileInFileIndex(file_id);
  784. if (item != null) {
  785. item.AddParam(name, value);
  786. return true;
  787. }
  788. else {
  789. return false;
  790. }
  791. }
  792. private function RemoveFileParam(file_id:String, name:String):Boolean {
  793. var item:FileItem = this.FindFileInFileIndex(file_id);
  794. if (item != null) {
  795. item.RemoveParam(name);
  796. return true;
  797. }
  798. else {
  799. return false;
  800. }
  801. }
  802. private function SetUploadURL(url:String):void {
  803. if (typeof(url) !== "undefined" && url !== "") {
  804. this.uploadURL = url;
  805. }
  806. }
  807. private function SetPostParams(post_object:Object):void {
  808. if (typeof(post_object) !== "undefined" && post_object !== null) {
  809. this.uploadPostObject = post_object;
  810. }
  811. }
  812. private function SetFileTypes(types:String, description:String):void {
  813. this.fileTypes = types;
  814. this.fileTypesDescription = description;
  815. this.LoadFileExensions(this.fileTypes);
  816. }
  817. // Sets the file size limit. Accepts size values with units: 100 b, 1KB, 23Mb, 4 Gb
  818. // Parsing is not robust. "100 200 MB KB B GB" parses as "100 MB"
  819. private function SetFileSizeLimit(size:String):void {
  820. var value:Number = 0;
  821. var unit:String = "kb";
  822. // Trim the string
  823. var trimPattern:RegExp = /^\s*|\s*$/;
  824. size = size.toLowerCase();
  825. size = size.replace(trimPattern, "");
  826. // Get the value part
  827. var values:Array = size.match(/^\d+/);
  828. if (values !== null && values.length > 0) {
  829. value = parseInt(values[0]);
  830. }
  831. if (isNaN(value) || value < 0) value = 0;
  832. // Get the units part
  833. var units:Array = size.match(/(b|kb|mb|gb)/);
  834. if (units != null && units.length > 0) {
  835. unit = units[0];
  836. }
  837. // Set the multiplier for converting the unit to bytes
  838. var multiplier:Number = 1024;
  839. if (unit === "b")
  840. multiplier = 1;
  841. else if (unit === "mb")
  842. multiplier = 1048576;
  843. else if (unit === "gb")
  844. multiplier = 1073741824;
  845. this.fileSizeLimit = value * multiplier;
  846. }
  847. private function SetFileUploadLimit(file_upload_limit:Number):void {
  848. if (file_upload_limit < 0) file_upload_limit = 0;
  849. this.fileUploadLimit = file_upload_limit;
  850. }
  851. private function SetFileQueueLimit(file_queue_limit:Number):void {
  852. if (file_queue_limit < 0) file_queue_limit = 0;
  853. this.fileQueueLimit = file_queue_limit;
  854. }
  855. private function SetFilePostName(file_post_name:String):void {
  856. if (file_post_name != "") {
  857. this.filePostName = file_post_name;
  858. }
  859. }
  860. private function SetUseQueryString(use_query_string:Boolean):void {
  861. this.useQueryString = use_query_string;
  862. }
  863. private function SetRequeueOnError(requeue_on_error:Boolean):void {
  864. this.requeueOnError = requeue_on_error;
  865. }
  866. private function SetHTTPSuccess(http_status_codes:*):void {
  867. this.httpSuccess = [];
  868. if (typeof http_status_codes === "string") {
  869. var status_code_strings:Array = http_status_codes.replace(" ", "").split(",");
  870. for each (var http_status_string:String in status_code_strings)
  871. {
  872. try {
  873. this.httpSuccess.push(Number(http_status_string));
  874. } catch (ex:Object) {
  875. // Ignore errors
  876. this.Debug("Could not add HTTP Success code: " + http_status_string);
  877. }
  878. }
  879. }
  880. else if (typeof http_status_codes === "object" && typeof http_status_codes.length === "number") {
  881. for each (var http_status:* in http_status_codes)
  882. {
  883. try {
  884. this.Debug("adding: " + http_status);
  885. this.httpSuccess.push(Number(http_status));
  886. } catch (ex:Object) {
  887. this.Debug("Could not add HTTP Success code: " + http_status);
  888. }
  889. }
  890. }
  891. }
  892. private function SetAssumeSuccessTimeout(timeout_seconds:Number):void {
  893. this.assumeSuccessTimeout = timeout_seconds < 0 ? 0 : timeout_seconds;
  894. }
  895. private function SetDebugEnabled(debug_enabled:Boolean):void {
  896. this.debugEnabled = debug_enabled;
  897. }
  898. /* *************************************************************
  899. Button Handling Functions
  900. *************************************************************** */
  901. private function SetButtonImageURL(button_image_url:String):void {
  902. this.buttonImageURL = button_image_url;
  903. try {
  904. if (this.buttonImageURL !== null && this.buttonImageURL !== "") {
  905. this.buttonLoader.load(new URLRequest(this.buttonImageURL));
  906. }
  907. } catch (ex:Object) {
  908. }
  909. }
  910. private function ButtonClickHandler(e:MouseEvent):void {
  911. if (!this.buttonStateDisabled) {
  912. if (this.buttonAction === this.BUTTON_ACTION_SELECT_FILE) {
  913. this.SelectFile();
  914. }
  915. else if (this.buttonAction === this.BUTTON_ACTION_START_UPLOAD) {
  916. this.StartUpload();
  917. }
  918. else {
  919. this.SelectFiles();
  920. }
  921. }
  922. }
  923. private function UpdateButtonState():void {
  924. var xOffset:Number = 0;
  925. var yOffset:Number = 0;
  926. this.buttonLoader.x = xOffset;
  927. this.buttonLoader.y = yOffset;
  928. if (this.buttonStateDisabled) {
  929. this.buttonLoader.y = this.buttonHeight * -3 + yOffset;
  930. }
  931. else if (this.buttonStateMouseDown) {
  932. this.buttonLoader.y = this.buttonHeight * -2 + yOffset;
  933. }
  934. else if (this.buttonStateOver) {
  935. this.buttonLoader.y = this.buttonHeight * -1 + yOffset;
  936. }
  937. else {
  938. this.buttonLoader.y = -yOffset;
  939. }
  940. };
  941. private function SetButtonDimensions(width:Number = -1, height:Number = -1):void {
  942. if (width >= 0) {
  943. this.buttonWidth = width;
  944. }
  945. if (height >= 0) {
  946. this.buttonHeight = height;
  947. }
  948. this.buttonTextField.width = this.buttonWidth;
  949. this.buttonTextField.height = this.buttonHeight;
  950. this.buttonCursorSprite.width = this.buttonWidth;
  951. this.buttonCursorSprite.height = this.buttonHeight;
  952. this.UpdateButtonState();
  953. }
  954. private function SetButtonText(button_text:String):void {
  955. this.buttonText = button_text;
  956. this.SetButtonTextStyle(this.buttonTextStyle);
  957. }
  958. private function SetButtonTextStyle(button_text_style:String):void {
  959. this.buttonTextStyle = button_text_style;
  960. var style:StyleSheet = new StyleSheet();
  961. style.parseCSS(this.buttonTextStyle);
  962. this.buttonTextField.styleSheet = style;
  963. this.buttonTextField.htmlText = this.buttonText;
  964. }
  965. private function SetButtonTextPadding(left:Number, top:Number):void {
  966. this.buttonTextField.x = this.buttonTextLeftPadding = left;
  967. this.buttonTextField.y = this.buttonTextTopPadding = top;
  968. }
  969. private function SetButtonDisabled(disabled:Boolean):void {
  970. this.buttonStateDisabled = disabled;
  971. this.UpdateButtonState();
  972. }
  973. private function SetButtonAction(button_action:Number):void {
  974. this.buttonAction = button_action;
  975. }
  976. private function SetButtonCursor(button_cursor:Number):void {
  977. this.buttonCursor = button_cursor;
  978. this.buttonCursorSprite.useHandCursor = (button_cursor === this.BUTTON_CURSOR_HAND);
  979. }
  980. /* *************************************************************
  981. File processing and handling functions
  982. *************************************************************** */
  983. private function StartUpload(file_id:String = ""):void {
  984. // Only upload a file uploads are being processed.
  985. if (this.current_file_item != null) {
  986. this.Debug("StartUpload(): Upload already in progress. Not starting another upload.");
  987. return;
  988. }
  989. this.Debug("StartUpload: " + (file_id ? "File ID: " + file_id : "First file in queue"));
  990. // Check the upload limit
  991. if (this.successful_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) {
  992. this.Debug("Event: uploadError : Upload limit reached. No more files can be uploaded.");
  993. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, null, "The upload limit has been reached.");
  994. this.current_file_item = null;
  995. return;
  996. }
  997. // Get the next file to upload
  998. if (!file_id) {
  999. while (this.file_queue.length > 0 && this.current_file_item == null) {
  1000. this.current_file_item = FileItem(this.file_queue.shift());
  1001. if (typeof(this.current_file_item) == "undefined") {
  1002. this.current_file_item = null;
  1003. }
  1004. }
  1005. } else {
  1006. var file_index:Number = this.FindIndexInFileQueue(file_id);
  1007. if (file_index >= 0) {
  1008. // Set the file as the current upload and remove it from the queue
  1009. this.current_file_item = FileItem(this.file_queue[file_index]);
  1010. this.file_queue[file_index] = null;
  1011. } else {
  1012. this.Debug("Event: uploadError : File ID not found in queue: " + file_id);
  1013. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND, null, "File ID not found in the queue.");
  1014. }
  1015. }
  1016. if (this.current_file_item != null) {
  1017. // Trigger the uploadStart event which will call ReturnUploadStart to begin the actual upload
  1018. this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id);
  1019. this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS;
  1020. ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject());
  1021. }
  1022. // Otherwise we've would have looped through all the FileItems. This means the queue is empty)
  1023. else {
  1024. this.Debug("StartUpload(): No files found in the queue.");
  1025. }
  1026. }
  1027. // This starts the upload when the user returns TRUE from the uploadStart event. Rather than just have the value returned from
  1028. // the function we do a return function call so we can use the setTimeout work-around for Flash/JS circular calls.
  1029. private function ReturnUploadStart(start_upload:Boolean):void {
  1030. if (this.current_file_item == null) {
  1031. this.Debug("ReturnUploadStart called but no file was prepped for uploading. The file may have been cancelled or stopped.");
  1032. return;
  1033. }
  1034. var js_object:Object;
  1035. if (start_upload) {
  1036. try {
  1037. // Set the event handlers
  1038. this.current_file_item.file_reference.addEventListener(Event.OPEN, this.Open_Handler);
  1039. this.current_file_item.file_reference.addEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler);
  1040. this.current_file_item.file_reference.addEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler);
  1041. this.current_file_item.file_reference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler);
  1042. this.current_file_item.file_reference.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler);
  1043. this.current_file_item.file_reference.addEventListener(Event.COMPLETE, this.Complete_Handler);
  1044. this.current_file_item.file_reference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler);
  1045. // Get the request (post values, etc)
  1046. var request:URLRequest = this.BuildRequest();
  1047. if (this.uploadURL.length == 0) {
  1048. this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty.");
  1049. // Remove the event handlers
  1050. this.removeFileReferenceEventListeners(this.current_file_item);
  1051. this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  1052. this.file_queue.unshift(this.current_file_item);
  1053. js_object = this.current_file_item.ToJavaScriptObject();
  1054. this.current_file_item = null;
  1055. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, js_object, "Upload URL string is empty.");
  1056. } else {
  1057. this.Debug("ReturnUploadStart(): File accepted by startUpload event and readied for upload. Starting upload to " + request.url + " for File ID: " + this.current_file_item.id);
  1058. this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS;
  1059. this.current_file_item.file_reference.upload(request, this.filePostName, false);
  1060. }
  1061. } catch (ex:Error) {
  1062. this.Debug("ReturnUploadStart: Exception occurred: " + message);
  1063. this.upload_errors++;
  1064. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  1065. var message:String = ex.errorID + "\n" + ex.name + "\n" + ex.message + "\n" + ex.getStackTrace();
  1066. this.Debug("Event: uploadError(): Upload Failed. Exception occurred: " + message);
  1067. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message);
  1068. this.UploadComplete(true);
  1069. }
  1070. } else {
  1071. // Remove the event handlers
  1072. this.removeFileReferenceEventListeners(this.current_file_item);
  1073. // Re-queue the FileItem
  1074. this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  1075. js_object = this.current_file_item.ToJavaScriptObject();
  1076. this.file_queue.unshift(this.current_file_item);
  1077. this.current_file_item = null;
  1078. this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading the file.");
  1079. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file.");
  1080. this.Debug("Event: uploadComplete : Call to uploadStart returned false. Not uploading the file.");
  1081. ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
  1082. }
  1083. }
  1084. // Completes the file upload by deleting it's reference, advancing the pointer.
  1085. // Once this event fires a new upload can be started.
  1086. private function UploadComplete(eligible_for_requeue:Boolean):void {
  1087. var jsFileObj:Object = this.current_file_item.ToJavaScriptObject();
  1088. this.removeFileReferenceEventListeners(this.current_file_item);
  1089. if (!eligible_for_requeue || this.requeueOnError == false) {
  1090. this.current_file_item.file_reference = null;
  1091. this.queued_uploads--;
  1092. } else if (this.requeueOnError == true) {
  1093. this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  1094. this.file_queue.unshift(this.current_file_item);
  1095. }
  1096. this.current_file_item = null;
  1097. this.Debug("Event: uploadComplete : Upload cycle complete.");
  1098. ExternalCall.UploadComplete(this.uploadComplete_Callback, jsFileObj);
  1099. }
  1100. /* *************************************************************
  1101. Utility Functions
  1102. *************************************************************** */
  1103. // Check the size of the file against the allowed file size. If it is less the return TRUE. If it is too large return FALSE
  1104. private function CheckFileSize(file_item:FileItem):Number {
  1105. if (file_item.file_reference.size == 0) {
  1106. return this.SIZE_ZERO_BYTE;
  1107. } else if (this.fileSizeLimit != 0 && file_item.file_reference.size > this.fileSizeLimit) {
  1108. return this.SIZE_TOO_BIG;
  1109. } else {
  1110. return this.SIZE_OK;
  1111. }
  1112. }
  1113. private function CheckFileType(file_item:FileItem):Boolean {
  1114. // If no extensions are defined then a *.* was passed and the check is unnecessary
  1115. if (this.valid_file_extensions.length == 0) {
  1116. return true;
  1117. }
  1118. var fileRef:FileReference = file_item.file_reference;
  1119. var last_dot_index:Number = fileRef.name.lastIndexOf(".");
  1120. var extension:String = "";
  1121. if (last_dot_index >= 0) {
  1122. extension = fileRef.name.substr(last_dot_index + 1).toLowerCase();
  1123. }
  1124. var is_valid_filetype:Boolean = false;
  1125. for (var i:Number=0; i < this.valid_file_extensions.length; i++) {
  1126. if (String(this.valid_file_extensions[i]) == extension) {
  1127. is_valid_filetype = true;
  1128. break;
  1129. }
  1130. }
  1131. return is_valid_filetype;
  1132. }
  1133. private function BuildRequest():URLRequest {
  1134. // Create the request object
  1135. var request:URLRequest = new URLRequest();
  1136. request.method = URLRequestMethod.POST;
  1137. var file_post:Object = this.current_file_item.GetPostObject();
  1138. if (this.useQueryString) {
  1139. var pairs:Array = new Array();
  1140. for (key in this.uploadPostObject) {
  1141. this.Debug("Global URL Item: " + key + "=" + this.uploadPostObject[key]);
  1142. if (this.uploadPostObject.hasOwnProperty(key)) {
  1143. pairs.push(escape(key) + "=" + escape(this.uploadPostObject[key]));
  1144. }
  1145. }
  1146. for (key in file_post) {
  1147. this.Debug("File Post Item: " + key + "=" + file_post[key]);
  1148. if (file_post.hasOwnProperty(key)) {
  1149. pairs.push(escape(key) + "=" + escape(file_post[key]));
  1150. }
  1151. }
  1152. request.url = this.uploadURL + (this.uploadURL.indexOf("?") > -1 ? "&" : "?") + pairs.join("&");
  1153. } else {
  1154. var key:String;
  1155. var post:URLVariables = new URLVariables();
  1156. for (key in this.uploadPostObject) {
  1157. this.Debug("Global Post Item: " + key + "=" + this.uploadPostObject[key]);
  1158. if (this.uploadPostObject.hasOwnProperty(key)) {
  1159. post[key] = this.uploadPostObject[key];
  1160. }
  1161. }
  1162. for (key in file_post) {
  1163. this.Debug("File Post Item: " + key + "=" + file_post[key]);
  1164. if (file_post.hasOwnProperty(key)) {
  1165. post[key] = file_post[key];
  1166. }
  1167. }
  1168. request.url = this.uploadURL;
  1169. request.data = post;
  1170. }
  1171. return request;
  1172. }
  1173. private function Debug(msg:String):void {
  1174. try {
  1175. if (this.debugEnabled) {
  1176. var lines:Array = msg.split("\n");
  1177. for (var i:Number=0; i < lines.length; i++) {
  1178. lines[i] = "SWF DEBUG: " + lines[i];
  1179. }
  1180. ExternalCall.Debug(this.debug_Callback, lines.join("\n"));
  1181. }
  1182. } catch (ex:Error) {
  1183. // pretend nothing happened
  1184. trace(ex);
  1185. }
  1186. }
  1187. private function PrintDebugInfo():void {
  1188. var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n";
  1189. debug_info += "Build Number: " + this.build_number + "\n";
  1190. debug_info += "movieName: " + this.movieName + "\n";
  1191. debug_info += "Upload URL: " + this.uploadURL + "\n";
  1192. debug_info += "File Types String: " + this.fileTypes + "\n";
  1193. debug_info += "Parsed File Types: " + this.valid_file_extensions.toString() + "\n";
  1194. debug_info += "HTTP Success: " + this.httpSuccess.join(", ") + "\n";
  1195. debug_info += "File Types Description: " + this.fileTypesDescription + "\n";
  1196. debug_info += "File Size Limit: " + this.fileSizeLimit + " bytes\n";
  1197. debug_info += "File Upload Limit: " + this.fileUploadLimit + "\n";
  1198. debug_info += "File Queue Limit: " + this.fileQueueLimit + "\n";
  1199. debug_info += "Post Params:\n";
  1200. for (var key:String in this.uploadPostObject) {
  1201. if (this.uploadPostObject.hasOwnProperty(key)) {
  1202. debug_info += " " + key + "=" + this.uploadPostObject[key] + "\n";
  1203. }
  1204. }
  1205. debug_info += "----- END SWF DEBUG OUTPUT ----\n";
  1206. this.Debug(debug_info);
  1207. }
  1208. private function FindIndexInFileQueue(file_id:String):Number {
  1209. for (var i:Number = 0; i < this.file_queue.length; i++) {
  1210. var item:FileItem = this.file_queue[i];
  1211. if (item != null && item.id == file_id) return i;
  1212. }
  1213. return -1;
  1214. }
  1215. private function FindFileInFileIndex(file_id:String):FileItem {
  1216. for (var i:Number = 0; i < this.file_index.length; i++) {
  1217. var item:FileItem = this.file_index[i];
  1218. if (item != null && item.id == file_id) return item;
  1219. }
  1220. return null;
  1221. }
  1222. // Parse the file extensions in to an array so we can validate them agains
  1223. // the files selected later.
  1224. private function LoadFileExensions(filetypes:String):void {
  1225. var extensions:Array = filetypes.split(";");
  1226. this.valid_file_extensions = new Array();
  1227. for (var i:Number=0; i < extensions.length; i++) {
  1228. var extension:String = String(extensions[i]);
  1229. var dot_index:Number = extension.lastIndexOf(".");
  1230. if (dot_index >= 0) {
  1231. extension = extension.substr(dot_index + 1).toLowerCase();
  1232. } else {
  1233. extension = extension.toLowerCase();
  1234. }
  1235. // If one of the extensions is * then we allow all files
  1236. if (extension == "*") {
  1237. this.valid_file_extensions = new Array();
  1238. break;
  1239. }
  1240. this.valid_file_extensions.push(extension);
  1241. }
  1242. }
  1243. private function loadPostParams(param_string:String):void {
  1244. var post_object:Object = {};
  1245. if (param_string != null) {
  1246. var name_value_pairs:Array = param_string.split("&amp;");
  1247. for (var i:Number = 0; i < name_value_pairs.length; i++) {
  1248. var name_value:String = String(name_value_pairs[i]);
  1249. var index_of_equals:Number = name_value.indexOf("=");
  1250. if (index_of_equals > 0) {
  1251. post_object[decodeURIComponent(name_value.substring(0, index_of_equals))] = decodeURIComponent(name_value.substr(index_of_equals + 1));
  1252. }
  1253. }
  1254. }
  1255. this.uploadPostObject = post_object;
  1256. }
  1257. private function removeFileReferenceEventListeners(file_item:FileItem):void {
  1258. if (file_item != null && file_item.file_reference != null) {
  1259. file_item.file_reference.removeEventListener(Event.OPEN, this.Open_Handler);
  1260. file_item.file_reference.removeEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler);
  1261. file_item.file_reference.removeEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler);
  1262. file_item.file_reference.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler);
  1263. file_item.file_reference.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler);
  1264. file_item.file_reference.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler);
  1265. }
  1266. }
  1267. }
  1268. }