PageRenderTime 246ms CodeModel.GetById 36ms RepoModel.GetById 1ms app.codeStats 0ms

/public/swfupload/Flash9/SWFUpload.as

https://github.com/averyj/radiant-upload-manager
ActionScript | 1029 lines | 754 code | 178 blank | 97 comment | 194 complexity | 1924fb87c9e3e5194ebc512202c562a7 MD5 | raw file
  1. package {
  2. /*
  3. * Todo:
  4. * I should look in to using array.splice to remove cancelled files from the array.
  5. * Add GetFile(file_id) function that returns the FileItem js object for any file (defaults to current or first in queue).
  6. * */
  7. import flash.display.Stage;
  8. import flash.display.Sprite;
  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 FileItem;
  19. import ExternalCall;
  20. public class SWFUpload extends Sprite {
  21. // Cause SWFUpload to start as soon as the movie starts
  22. public static function main():void
  23. {
  24. var SWFUpload:SWFUpload = new SWFUpload();
  25. }
  26. private const build_number:String = "SWFUPLOAD 2.1.0 FP9 2008-05-12";
  27. // State tracking variables
  28. private var fileBrowserMany:FileReferenceList = new FileReferenceList();
  29. 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
  30. private var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded.
  31. private var current_file_item:FileItem = null; // the item that is currently being uploaded.
  32. private var file_index:Array = new Array();
  33. private var successful_uploads:Number = 0; // Tracks the uploads that have been completed
  34. private var queue_errors:Number = 0; // Tracks files rejected during queueing
  35. private var upload_errors:Number = 0; // Tracks files that fail upload
  36. private var upload_cancelled:Number = 0; // Tracks number of cancelled files
  37. private var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded.
  38. private var valid_file_extensions:Array = new Array();// Holds the parsed valid extensions.
  39. // Callbacks
  40. private var flashReady_Callback:String;
  41. private var fileDialogStart_Callback:String;
  42. private var fileQueued_Callback:String;
  43. private var fileQueueError_Callback:String;
  44. private var fileDialogComplete_Callback:String;
  45. private var uploadStart_Callback:String;
  46. private var uploadProgress_Callback:String;
  47. private var uploadError_Callback:String;
  48. private var uploadSuccess_Callback:String;
  49. private var uploadComplete_Callback:String;
  50. private var debug_Callback:String;
  51. // Values passed in from the HTML
  52. private var movieName:String;
  53. private var uploadURL:String;
  54. private var filePostName:String;
  55. private var uploadPostObject:Object;
  56. private var fileTypes:String;
  57. private var fileTypesDescription:String;
  58. private var fileSizeLimit:Number;
  59. private var fileUploadLimit:Number = 0;
  60. private var fileQueueLimit:Number = 0;
  61. private var useQueryString:Boolean = false;
  62. private var requeueOnError:Boolean = false;
  63. private var debugEnabled:Boolean;
  64. // Error code "constants"
  65. // Size check constants
  66. private var SIZE_TOO_BIG:Number = 1;
  67. private var SIZE_ZERO_BYTE:Number = -1;
  68. private var SIZE_OK:Number = 0;
  69. // Queue errors
  70. private var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -100;
  71. private var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -110;
  72. private var ERROR_CODE_ZERO_BYTE_FILE:Number = -120;
  73. private var ERROR_CODE_INVALID_FILETYPE:Number = -130;
  74. // Upload Errors
  75. private var ERROR_CODE_HTTP_ERROR:Number = -200;
  76. private var ERROR_CODE_MISSING_UPLOAD_URL:Number = -210;
  77. private var ERROR_CODE_IO_ERROR:Number = -220;
  78. private var ERROR_CODE_SECURITY_ERROR:Number = -230;
  79. private var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -240;
  80. private var ERROR_CODE_UPLOAD_FAILED:Number = -250;
  81. private var ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND:Number = -260;
  82. private var ERROR_CODE_FILE_VALIDATION_FAILED:Number = -270;
  83. private var ERROR_CODE_FILE_CANCELLED:Number = -280;
  84. private var ERROR_CODE_UPLOAD_STOPPED:Number = -290;
  85. public function SWFUpload() {
  86. // Do the feature detection. Make sure this version of Flash supports the features we need. If not
  87. // abort initialization.
  88. if (!flash.net.FileReferenceList || !flash.net.FileReference || !flash.net.URLRequest || !flash.external.ExternalInterface || !flash.external.ExternalInterface.available || !DataEvent.UPLOAD_COMPLETE_DATA) {
  89. return;
  90. }
  91. Security.allowDomain("*"); // Allow uploading to any domain
  92. // Keep Flash Player busy so it doesn't show the "flash script is running slowly" error
  93. var counter:Number = 0;
  94. root.addEventListener(Event.ENTER_FRAME, function ():void { if (++counter > 100) counter = 100; });
  95. // Setup file FileReferenceList events
  96. this.fileBrowserMany.addEventListener(Event.SELECT, this.Select_Many_Handler);
  97. this.fileBrowserMany.addEventListener(Event.CANCEL, this.DialogCancelled_Handler);
  98. // Get the move name
  99. this.movieName = root.loaderInfo.parameters.movieName;
  100. // **Configure the callbacks**
  101. // The JavaScript tracks all the instances of SWFUpload on a page. We can access the instance
  102. // associated with this SWF file using the movieName. Each callback is accessible by making
  103. // a call directly to it on our instance. There is no error handling for undefined callback functions.
  104. // A developer would have to deliberately remove the default functions,set the variable to null, or remove
  105. // it from the init function.
  106. this.flashReady_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].flashReady";
  107. this.fileDialogStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogStart";
  108. this.fileQueued_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueued";
  109. this.fileQueueError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileQueueError";
  110. this.fileDialogComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].fileDialogComplete";
  111. this.uploadStart_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadStart";
  112. this.uploadProgress_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadProgress";
  113. this.uploadError_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadError";
  114. this.uploadSuccess_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadSuccess";
  115. this.uploadComplete_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].uploadComplete";
  116. this.debug_Callback = "SWFUpload.instances[\"" + this.movieName + "\"].debug";
  117. // Get the Flash Vars
  118. this.uploadURL = root.loaderInfo.parameters.uploadURL;
  119. this.filePostName = root.loaderInfo.parameters.filePostName;
  120. this.fileTypes = root.loaderInfo.parameters.fileTypes;
  121. this.fileTypesDescription = root.loaderInfo.parameters.fileTypesDescription + " (" + this.fileTypes + ")";
  122. this.loadPostParams(root.loaderInfo.parameters.params);
  123. if (!this.filePostName) {
  124. this.filePostName = "Filedata";
  125. }
  126. if (!this.fileTypes) {
  127. this.fileTypes = "*.*";
  128. }
  129. if (!this.fileTypesDescription) {
  130. this.fileTypesDescription = "All Files";
  131. }
  132. this.LoadFileExensions(this.fileTypes);
  133. try {
  134. this.debugEnabled = root.loaderInfo.parameters.debugEnabled == "true" ? true : false;
  135. } catch (ex:Object) {
  136. this.debugEnabled = false;
  137. }
  138. try {
  139. this.SetFileSizeLimit(String(root.loaderInfo.parameters.fileSizeLimit));
  140. } catch (ex:Object) {
  141. this.fileSizeLimit = 0;
  142. }
  143. try {
  144. this.fileUploadLimit = Number(root.loaderInfo.parameters.fileUploadLimit);
  145. if (this.fileUploadLimit < 0) this.fileUploadLimit = 0;
  146. } catch (ex:Object) {
  147. this.fileUploadLimit = 0;
  148. }
  149. try {
  150. this.fileQueueLimit = Number(root.loaderInfo.parameters.fileQueueLimit);
  151. if (this.fileQueueLimit < 0) this.fileQueueLimit = 0;
  152. } catch (ex:Object) {
  153. this.fileQueueLimit = 0;
  154. }
  155. // Set the queue limit to match the upload limit when the queue limit is bigger than the upload limit
  156. if (this.fileQueueLimit > this.fileUploadLimit && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
  157. // The the queue limit is unlimited and the upload limit is not then set the queue limit to the upload limit
  158. if (this.fileQueueLimit == 0 && this.fileUploadLimit != 0) this.fileQueueLimit = this.fileUploadLimit;
  159. try {
  160. this.useQueryString = root.loaderInfo.parameters.useQueryString == "true" ? true : false;
  161. } catch (ex:Object) {
  162. this.useQueryString = false;
  163. }
  164. try {
  165. this.requeueOnError = root.loaderInfo.parameters.requeueOnError == "true" ? true : false;
  166. } catch (ex:Object) {
  167. this.requeueOnError = false;
  168. }
  169. try {
  170. ExternalInterface.addCallback("SelectFile", this.SelectFile);
  171. ExternalInterface.addCallback("SelectFiles", this.SelectFiles);
  172. ExternalInterface.addCallback("StartUpload", this.StartUpload);
  173. ExternalInterface.addCallback("ReturnUploadStart", this.ReturnUploadStart);
  174. ExternalInterface.addCallback("StopUpload", this.StopUpload);
  175. ExternalInterface.addCallback("CancelUpload", this.CancelUpload);
  176. ExternalInterface.addCallback("GetStats", this.GetStats);
  177. ExternalInterface.addCallback("SetStats", this.SetStats);
  178. ExternalInterface.addCallback("GetFile", this.GetFile);
  179. ExternalInterface.addCallback("GetFileByIndex", this.GetFileByIndex);
  180. ExternalInterface.addCallback("AddFileParam", this.AddFileParam);
  181. ExternalInterface.addCallback("RemoveFileParam", this.RemoveFileParam);
  182. ExternalInterface.addCallback("SetUploadURL", this.SetUploadURL);
  183. ExternalInterface.addCallback("SetPostParams", this.SetPostParams);
  184. ExternalInterface.addCallback("SetFileTypes", this.SetFileTypes);
  185. ExternalInterface.addCallback("SetFileSizeLimit", this.SetFileSizeLimit);
  186. ExternalInterface.addCallback("SetFileUploadLimit", this.SetFileUploadLimit);
  187. ExternalInterface.addCallback("SetFileQueueLimit", this.SetFileQueueLimit);
  188. ExternalInterface.addCallback("SetFilePostName", this.SetFilePostName);
  189. ExternalInterface.addCallback("SetUseQueryString", this.SetUseQueryString);
  190. ExternalInterface.addCallback("SetRequeueOnError", this.SetRequeueOnError);
  191. ExternalInterface.addCallback("SetDebugEnabled", this.SetDebugEnabled);
  192. } catch (ex:Error) {
  193. this.Debug("Callbacks where not set.");
  194. return;
  195. }
  196. this.Debug("SWFUpload Init Complete");
  197. this.PrintDebugInfo();
  198. ExternalCall.Simple(this.flashReady_Callback);
  199. }
  200. /* *****************************************
  201. * FileReference Event Handlers
  202. * *************************************** */
  203. private function DialogCancelled_Handler(event:Event):void {
  204. this.Debug("Event: fileDialogComplete: File Dialog window cancelled.");
  205. ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, 0, 0);
  206. }
  207. private function Open_Handler(event:Event):void {
  208. this.Debug("Event: uploadProgress (OPEN): File ID: " + this.current_file_item.id);
  209. ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), 0, this.current_file_item.file_reference.size);
  210. }
  211. private function FileProgress_Handler(event:ProgressEvent):void {
  212. // On early than Mac OS X 10.3 bytesLoaded is always -1, convert this to zero. Do bytesTotal for good measure.
  213. // http://livedocs.adobe.com/flex/3/langref/flash/net/FileReference.html#event:progress
  214. var bytesLoaded:Number = event.bytesLoaded < 0 ? 0 : event.bytesLoaded;
  215. var bytesTotal:Number = event.bytesTotal < 0 ? 0 : event.bytesTotal;
  216. this.Debug("Event: uploadProgress: File ID: " + this.current_file_item.id + ". Bytes: " + bytesLoaded + ". Total: " + bytesTotal);
  217. ExternalCall.UploadProgress(this.uploadProgress_Callback, this.current_file_item.ToJavaScriptObject(), bytesLoaded, bytesTotal);
  218. }
  219. private function ServerData_Handler(event:DataEvent):void {
  220. this.successful_uploads++;
  221. this.current_file_item.file_status = FileItem.FILE_STATUS_SUCCESS;
  222. this.Debug("Event: uploadSuccess: File ID: " + this.current_file_item.id + " Data: " + event.data);
  223. ExternalCall.UploadSuccess(this.uploadSuccess_Callback, this.current_file_item.ToJavaScriptObject(), event.data);
  224. this.UploadComplete(false);
  225. }
  226. private function HTTPError_Handler(event:HTTPStatusEvent):void {
  227. this.upload_errors++;
  228. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  229. this.Debug("Event: uploadError: HTTP ERROR : File ID: " + this.current_file_item.id + ". HTTP Status: " + event.status + ".");
  230. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), event.status.toString());
  231. this.UploadComplete(true); // An IO Error is also called so we don't want to complete the upload yet.
  232. }
  233. // Note: Flash Player does not support Uploads that require authentication. Attempting this will trigger an
  234. // IO Error or it will prompt for a username and password and may crash the browser (FireFox/Opera)
  235. private function IOError_Handler(event:IOErrorEvent):void {
  236. // Only trigger an IO Error event if we haven't already done an HTTP error
  237. if (this.current_file_item.file_status != FileItem.FILE_STATUS_ERROR) {
  238. this.upload_errors++;
  239. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  240. this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". IO Error: " + event.text);
  241. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), event.text);
  242. }
  243. this.UploadComplete(true);
  244. }
  245. private function SecurityError_Handler(event:SecurityErrorEvent):void {
  246. this.upload_errors++;
  247. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  248. this.Debug("Event: uploadError : Security Error : File Number: " + this.current_file_item.id + ". Error text: " + event.text);
  249. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), event.text);
  250. this.UploadComplete(true);
  251. }
  252. private function Select_Many_Handler(event:Event):void {
  253. this.Select_Handler(this.fileBrowserMany.fileList);
  254. }
  255. private function Select_One_Handler(event:Event):void {
  256. var fileArray:Array = new Array(1);
  257. fileArray[0] = this.fileBrowserOne;
  258. this.Select_Handler(fileArray);
  259. }
  260. private function Select_Handler(file_reference_list:Array):void {
  261. this.Debug("Select Handler: Received the files selected from the dialog. Processing the file list...");
  262. var num_files_queued:Number = 0;
  263. // Determine how many queue slots are remaining (check the unlimited (0) settings, successful uploads and queued uploads)
  264. var queue_slots_remaining:Number = 0;
  265. if (this.fileUploadLimit == 0) {
  266. 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.
  267. } else {
  268. var remaining_uploads:Number = this.fileUploadLimit - this.successful_uploads - this.queued_uploads;
  269. if (remaining_uploads < 0) remaining_uploads = 0;
  270. if (this.fileQueueLimit == 0 || this.fileQueueLimit >= remaining_uploads) {
  271. queue_slots_remaining = remaining_uploads;
  272. } else if (this.fileQueueLimit < remaining_uploads) {
  273. queue_slots_remaining = this.fileQueueLimit;
  274. }
  275. }
  276. // Check if the number of files selected is greater than the number allowed to queue up.
  277. if (queue_slots_remaining < file_reference_list.length) {
  278. this.Debug("Event: fileQueueError : Selected Files (" + file_reference_list.length + ") exceeds remaining Queue size (" + queue_slots_remaining + ").");
  279. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining.toString());
  280. } else {
  281. // Process each selected file
  282. for (var i:Number = 0; i < file_reference_list.length; i++) {
  283. var file_item:FileItem = new FileItem(file_reference_list[i], this.movieName, this.file_index.length);
  284. this.file_index[file_item.index] = file_item;
  285. // Verify that the file is accessible. Zero byte files and possibly other conditions can cause a file to be inaccessible.
  286. var jsFileObj:Object = file_item.ToJavaScriptObject();
  287. var is_valid_file_reference:Boolean = (jsFileObj.filestatus !== FileItem.FILE_STATUS_ERROR);
  288. if (is_valid_file_reference) {
  289. // Check the size, if it's within the limit add it to the upload list.
  290. var size_result:Number = this.CheckFileSize(file_item);
  291. var is_valid_filetype:Boolean = this.CheckFileType(file_item);
  292. if(size_result == this.SIZE_OK && is_valid_filetype) {
  293. file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  294. this.file_queue.push(file_item);
  295. this.queued_uploads++;
  296. num_files_queued++;
  297. this.Debug("Event: fileQueued : File ID: " + file_item.id);
  298. ExternalCall.FileQueued(this.fileQueued_Callback, file_item.ToJavaScriptObject());
  299. }
  300. else if (!is_valid_filetype) {
  301. file_item.file_reference = null; // Cleanup the object
  302. this.queue_errors++;
  303. this.Debug("Event: fileQueueError : File not of a valid type.");
  304. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_INVALID_FILETYPE, file_item.ToJavaScriptObject(), "File is not an allowed file type.");
  305. }
  306. else if (size_result == this.SIZE_TOO_BIG) {
  307. file_item.file_reference = null; // Cleanup the object
  308. this.queue_errors++;
  309. this.Debug("Event: fileQueueError : File exceeds size limit.");
  310. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit.");
  311. }
  312. else if (size_result == this.SIZE_ZERO_BYTE) {
  313. file_item.file_reference = null; // Cleanup the object
  314. this.queue_errors++;
  315. this.Debug("Event: fileQueueError : File is zero bytes.");
  316. ExternalCall.FileQueueError(this.fileQueueError_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded.");
  317. }
  318. } else {
  319. file_item.file_reference = null; // Cleanup the object
  320. this.queue_errors++;
  321. this.Debug("Event: fileQueueError : File is zero bytes or FileReference is invalid.");
  322. 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.");
  323. }
  324. }
  325. }
  326. this.Debug("Event: fileDialogComplete : Finished processing selected files. Files selected: " + file_reference_list.length + ". Files Queued: " + num_files_queued);
  327. ExternalCall.FileDialogComplete(this.fileDialogComplete_Callback, file_reference_list.length, num_files_queued);
  328. }
  329. /* ****************************************************************
  330. Externally exposed functions
  331. ****************************************************************** */
  332. // Opens a file browser dialog that allows one file to be selected.
  333. private function SelectFile():void {
  334. this.fileBrowserOne = new FileReference();
  335. this.fileBrowserOne.addEventListener(Event.SELECT, this.Select_One_Handler);
  336. this.fileBrowserOne.addEventListener(Event.CANCEL, this.DialogCancelled_Handler);
  337. // Default file type settings
  338. var allowed_file_types:String = "*.*";
  339. var allowed_file_types_description:String = "All Files";
  340. // Get the instance settings
  341. if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
  342. if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
  343. this.Debug("Event: fileDialogStart : Browsing files. Single Select. Allowed file types: " + allowed_file_types);
  344. ExternalCall.Simple(this.fileDialogStart_Callback);
  345. try {
  346. this.fileBrowserOne.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]);
  347. } catch (ex:Error) {
  348. this.Debug("Exception: " + ex.toString());
  349. }
  350. }
  351. // Opens a file browser dialog that allows multiple files to be selected.
  352. private function SelectFiles():void {
  353. var allowed_file_types:String = "*.*";
  354. var allowed_file_types_description:String = "All Files";
  355. if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
  356. if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
  357. this.Debug("Event: fileDialogStart : Browsing files. Multi Select. Allowed file types: " + allowed_file_types);
  358. ExternalCall.Simple(this.fileDialogStart_Callback);
  359. try {
  360. this.fileBrowserMany.browse([new FileFilter(allowed_file_types_description, allowed_file_types)]);
  361. } catch (ex:Error) {
  362. this.Debug("Exception: " + ex.toString());
  363. }
  364. }
  365. // Cancel the current upload and stops. Doesn't advance the upload pointer. The current file is requeued at the beginning.
  366. private function StopUpload():void {
  367. if (this.current_file_item != null) {
  368. // Cancel the upload and re-queue the FileItem
  369. this.current_file_item.file_reference.cancel();
  370. this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  371. // Remove the event handlers
  372. this.removeFileReferenceEventListeners(this.current_file_item);
  373. this.file_queue.unshift(this.current_file_item);
  374. var js_object:Object = this.current_file_item.ToJavaScriptObject();
  375. this.current_file_item = null;
  376. this.Debug("Event: uploadError: upload stopped. File ID: " + js_object.ID);
  377. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_STOPPED, js_object, "Upload Stopped");
  378. this.Debug("Event: uploadComplete. File ID: " + js_object.ID);
  379. ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
  380. this.Debug("StopUpload(): upload stopped.");
  381. } else {
  382. this.Debug("StopUpload(): No file is currently uploading. Nothing to do.");
  383. }
  384. }
  385. /* Cancels the upload specified by file_id
  386. * If the file is currently uploading it is cancelled and the uploadComplete
  387. * event gets called.
  388. * If the file is not currently uploading then only the uploadCancelled event is fired.
  389. * */
  390. private function CancelUpload(file_id:String):void {
  391. var file_item:FileItem = null;
  392. // Check the current file item
  393. if (this.current_file_item != null && (this.current_file_item.id == file_id || !file_id)) {
  394. this.current_file_item.file_reference.cancel();
  395. this.current_file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
  396. this.upload_cancelled++;
  397. this.Debug("Event: uploadError: File ID: " + this.current_file_item.id + ". Cancelled current upload");
  398. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, this.current_file_item.ToJavaScriptObject(), "File Upload Cancelled.");
  399. this.UploadComplete(false);
  400. } else if (file_id) {
  401. // Find the file in the queue
  402. var file_index:Number = this.FindIndexInFileQueue(file_id);
  403. if (file_index >= 0) {
  404. // Remove the file from the queue
  405. file_item = FileItem(this.file_queue[file_index]);
  406. file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
  407. this.file_queue[file_index] = null;
  408. this.queued_uploads--;
  409. this.upload_cancelled++;
  410. // Cancel the file (just for good measure) and make the callback
  411. file_item.file_reference.cancel();
  412. this.removeFileReferenceEventListeners(file_item);
  413. file_item.file_reference = null;
  414. this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload");
  415. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
  416. // Get rid of the file object
  417. file_item = null;
  418. }
  419. } else {
  420. // Get the first file and cancel it
  421. while (this.file_queue.length > 0 && file_item == null) {
  422. // Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
  423. file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
  424. if (typeof(file_item) == "undefined") {
  425. file_item = null;
  426. continue;
  427. }
  428. }
  429. if (file_item != null) {
  430. file_item.file_status = FileItem.FILE_STATUS_CANCELLED;
  431. this.queued_uploads--;
  432. this.upload_cancelled++;
  433. // Cancel the file (just for good measure) and make the callback
  434. file_item.file_reference.cancel();
  435. this.removeFileReferenceEventListeners(file_item);
  436. file_item.file_reference = null;
  437. this.Debug("Event: uploadError : " + file_item.id + ". Cancelled queued upload");
  438. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_CANCELLED, file_item.ToJavaScriptObject(), "File Cancelled");
  439. // Get rid of the file object
  440. file_item = null;
  441. }
  442. }
  443. }
  444. private function GetStats():Object {
  445. return {
  446. in_progress : this.current_file_item == null ? 0 : 1,
  447. files_queued : this.queued_uploads,
  448. successful_uploads : this.successful_uploads,
  449. upload_errors : this.upload_errors,
  450. upload_cancelled : this.upload_cancelled,
  451. queue_errors : this.queue_errors
  452. };
  453. }
  454. private function SetStats(stats:Object):void {
  455. this.successful_uploads = typeof(stats["successful_uploads"]) === "number" ? stats["successful_uploads"] : this.successful_uploads;
  456. this.upload_errors = typeof(stats["upload_errors"]) === "number" ? stats["upload_errors"] : this.upload_errors;
  457. this.upload_cancelled = typeof(stats["upload_cancelled"]) === "number" ? stats["upload_cancelled"] : this.upload_cancelled;
  458. this.queue_errors = typeof(stats["queue_errors"]) === "number" ? stats["queue_errors"] : this.queue_errors;
  459. }
  460. private function GetFile(file_id:String):Object {
  461. var file_index:Number = this.FindIndexInFileQueue(file_id);
  462. if (file_index >= 0) {
  463. var file:FileItem = this.file_queue[file_index];
  464. } else {
  465. if (this.current_file_item != null) {
  466. file = this.current_file_item;
  467. } else {
  468. for (var i:Number = 0; i < this.file_queue.length; i++) {
  469. file = this.file_queue[i];
  470. if (file != null) break;
  471. }
  472. }
  473. }
  474. if (file == null) {
  475. return null;
  476. } else {
  477. return file.ToJavaScriptObject();
  478. }
  479. }
  480. private function GetFileByIndex(index:Number):Object {
  481. if (index < 0 || index > this.file_index.length - 1) {
  482. return null;
  483. } else {
  484. return this.file_index[index].ToJavaScriptObject();
  485. }
  486. }
  487. private function AddFileParam(file_id:String, name:String, value:String):Boolean {
  488. var item:FileItem = this.FindFileInFileIndex(file_id);
  489. if (item != null) {
  490. item.AddParam(name, value);
  491. return true;
  492. }
  493. else {
  494. return false;
  495. }
  496. }
  497. private function RemoveFileParam(file_id:String, name:String):Boolean {
  498. var item:FileItem = this.FindFileInFileIndex(file_id);
  499. if (item != null) {
  500. item.RemoveParam(name);
  501. return true;
  502. }
  503. else {
  504. return false;
  505. }
  506. }
  507. private function SetUploadURL(url:String):void {
  508. if (typeof(url) !== "undefined" && url !== "") {
  509. this.uploadURL = url;
  510. }
  511. }
  512. private function SetPostParams(post_object:Object):void {
  513. if (typeof(post_object) !== "undefined" && post_object !== null) {
  514. this.uploadPostObject = post_object;
  515. }
  516. }
  517. private function SetFileTypes(types:String, description:String):void {
  518. this.fileTypes = types;
  519. this.fileTypesDescription = description;
  520. this.LoadFileExensions(this.fileTypes);
  521. }
  522. // Sets the file size limit. Accepts size values with units: 100 b, 1KB, 23Mb, 4 Gb
  523. // Parsing is not robust. "100 200 MB KB B GB" parses as "100 MB"
  524. private function SetFileSizeLimit(size:String):void {
  525. var value:Number = 0;
  526. var unit:String = "kb";
  527. // Trim the string
  528. var trimPattern:RegExp = /^\s*|\s*$/;
  529. size = size.toLowerCase();
  530. size = size.replace(trimPattern, "");
  531. // Get the value part
  532. var values:Array = size.match(/^\d+/);
  533. if (values !== null && values.length > 0) {
  534. value = parseInt(values[0]);
  535. }
  536. if (isNaN(value) || value < 0) value = 0;
  537. // Get the units part
  538. var units:Array = size.match(/(b|kb|mb|gb)/);
  539. if (units != null && units.length > 0) {
  540. unit = units[0];
  541. }
  542. // Set the multiplier for converting the unit to bytes
  543. var multiplier:Number = 1024;
  544. if (unit === "b")
  545. multiplier = 1;
  546. else if (unit === "mb")
  547. multiplier = 1048576;
  548. else if (unit === "gb")
  549. multiplier = 1073741824;
  550. this.fileSizeLimit = value * multiplier;
  551. }
  552. private function SetFileUploadLimit(file_upload_limit:Number):void {
  553. if (file_upload_limit < 0) file_upload_limit = 0;
  554. this.fileUploadLimit = file_upload_limit;
  555. }
  556. private function SetFileQueueLimit(file_queue_limit:Number):void {
  557. if (file_queue_limit < 0) file_queue_limit = 0;
  558. this.fileQueueLimit = file_queue_limit;
  559. }
  560. private function SetFilePostName(file_post_name:String):void {
  561. if (file_post_name != "") {
  562. this.filePostName = file_post_name;
  563. }
  564. }
  565. private function SetUseQueryString(use_query_string:Boolean):void {
  566. this.useQueryString = use_query_string;
  567. }
  568. private function SetRequeueOnError(requeue_on_error:Boolean):void {
  569. this.requeueOnError = requeue_on_error;
  570. }
  571. private function SetDebugEnabled(debug_enabled:Boolean):void {
  572. this.debugEnabled = debug_enabled;
  573. }
  574. /* *************************************************************
  575. File processing and handling functions
  576. *************************************************************** */
  577. private function StartUpload(file_id:String = ""):void {
  578. // Only upload a file uploads are being processed.
  579. if (this.current_file_item != null) {
  580. this.Debug("StartUpload(): Upload already in progress. Not starting another upload.");
  581. return;
  582. }
  583. this.Debug("StartUpload: " + (file_id ? "File ID: " + file_id : "First file in queue"));
  584. // Check the upload limit
  585. if (this.successful_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) {
  586. this.Debug("Event: uploadError : Upload limit reached. No more files can be uploaded.");
  587. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, null, "The upload limit has been reached.");
  588. this.current_file_item = null;
  589. return;
  590. }
  591. // Get the next file to upload
  592. if (!file_id) {
  593. while (this.file_queue.length > 0 && this.current_file_item == null) {
  594. this.current_file_item = FileItem(this.file_queue.shift());
  595. if (typeof(this.current_file_item) == "undefined") {
  596. this.current_file_item = null;
  597. }
  598. }
  599. } else {
  600. var file_index:Number = this.FindIndexInFileQueue(file_id);
  601. if (file_index >= 0) {
  602. // Set the file as the current upload and remove it from the queue
  603. this.current_file_item = FileItem(this.file_queue[file_index]);
  604. this.file_queue[file_index] = null;
  605. } else {
  606. this.Debug("Event: uploadError : File ID not found in queue: " + file_id);
  607. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_SPECIFIED_FILE_ID_NOT_FOUND, null, "File ID not found in the queue.");
  608. }
  609. }
  610. if (this.current_file_item != null) {
  611. // Trigger the uploadStart event which will call ReturnUploadStart to begin the actual upload
  612. this.Debug("Event: uploadStart : File ID: " + this.current_file_item.id);
  613. ExternalCall.UploadStart(this.uploadStart_Callback, this.current_file_item.ToJavaScriptObject());
  614. }
  615. // Otherwise we've would have looped through all the FileItems. This means the queue is empty)
  616. else {
  617. this.Debug("StartUpload(): No files found in the queue.");
  618. }
  619. }
  620. // This starts the upload when the user returns TRUE from the uploadStart event. Rather than just have the value returned from
  621. // the function we do a return function call so we can use the setTimeout work-around for Flash/JS circular calls.
  622. private function ReturnUploadStart(start_upload:Boolean):void {
  623. if (this.current_file_item == null) {
  624. this.Debug("ReturnUploadStart called but no file was prepped for uploading. The file may have been cancelled or stopped.");
  625. return;
  626. }
  627. if (start_upload) {
  628. try {
  629. // Set the event handlers
  630. this.current_file_item.file_reference.addEventListener(Event.OPEN, this.Open_Handler);
  631. this.current_file_item.file_reference.addEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler);
  632. this.current_file_item.file_reference.addEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler);
  633. this.current_file_item.file_reference.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler);
  634. this.current_file_item.file_reference.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler);
  635. this.current_file_item.file_reference.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler);
  636. // Get the request (post values, etc)
  637. var request:URLRequest = this.BuildRequest();
  638. if (this.uploadURL.length == 0) {
  639. this.Debug("Event: uploadError : IO Error : File ID: " + this.current_file_item.id + ". Upload URL string is empty.");
  640. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_MISSING_UPLOAD_URL, this.current_file_item.ToJavaScriptObject(), "Upload URL string is empty.");
  641. } else {
  642. 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);
  643. this.current_file_item.file_status = FileItem.FILE_STATUS_IN_PROGRESS;
  644. this.current_file_item.file_reference.upload(request, this.filePostName, false);
  645. }
  646. } catch (ex:Error) {
  647. this.Debug("ReturnUploadStart: Exception occurred: " + message);
  648. this.upload_errors++;
  649. this.current_file_item.file_status = FileItem.FILE_STATUS_ERROR;
  650. var message:String = ex.errorID + "\n" + ex.name + "\n" + ex.message + "\n" + ex.getStackTrace();
  651. this.Debug("Event: uploadError(): Upload Failed. Exception occurred: " + message);
  652. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), message);
  653. this.UploadComplete(true);
  654. }
  655. } else {
  656. // Remove the event handlers
  657. this.removeFileReferenceEventListeners(this.current_file_item);
  658. // Re-queue the FileItem
  659. this.current_file_item.file_status = FileItem.FILE_STATUS_QUEUED;
  660. var js_object:Object = this.current_file_item.ToJavaScriptObject();
  661. this.file_queue.unshift(this.current_file_item);
  662. this.current_file_item = null;
  663. this.Debug("Event: uploadError : Call to uploadStart returned false. Not uploading the file.");
  664. ExternalCall.UploadError(this.uploadError_Callback, this.ERROR_CODE_FILE_VALIDATION_FAILED, js_object, "Call to uploadStart return false. Not uploading file.");
  665. this.Debug("Event: uploadComplete : Call to uploadStart returned false. Not uploading the file.");
  666. ExternalCall.UploadComplete(this.uploadComplete_Callback, js_object);
  667. }
  668. }
  669. // Completes the file upload by deleting it's reference, advancing the pointer.
  670. // Once this event files a new upload can be started.
  671. private function UploadComplete(eligible_for_requeue:Boolean):void {
  672. var jsFileObj:Object = this.current_file_item.ToJavaScriptObject();
  673. this.removeFileReferenceEventListeners(this.current_file_item);
  674. if (!eligible_for_requeue || this.requeueOnError == false) {
  675. this.current_file_item.file_reference = null;
  676. this.queued_uploads--;
  677. } else if (this.requeueOnError == true) {
  678. this.file_queue.unshift(this.current_file_item);
  679. }
  680. this.current_file_item = null;
  681. this.Debug("Event: uploadComplete : Upload cycle complete.");
  682. ExternalCall.UploadComplete(this.uploadComplete_Callback, jsFileObj);
  683. }
  684. /* *************************************************************
  685. Utility Functions
  686. *************************************************************** */
  687. // 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
  688. private function CheckFileSize(file_item:FileItem):Number {
  689. if (file_item.file_reference.size == 0) {
  690. return this.SIZE_ZERO_BYTE;
  691. } else if (this.fileSizeLimit != 0 && file_item.file_reference.size > this.fileSizeLimit) {
  692. return this.SIZE_TOO_BIG;
  693. } else {
  694. return this.SIZE_OK;
  695. }
  696. }
  697. private function CheckFileType(file_item:FileItem):Boolean {
  698. // If no extensions are defined then a *.* was passed and the check is unnecessary
  699. if (this.valid_file_extensions.length == 0) {
  700. return true;
  701. }
  702. var fileRef:FileReference = file_item.file_reference;
  703. var last_dot_index:Number = fileRef.name.lastIndexOf(".");
  704. var extension:String = "";
  705. if (last_dot_index >= 0) {
  706. extension = fileRef.name.substr(last_dot_index + 1).toLowerCase();
  707. }
  708. var is_valid_filetype:Boolean = false;
  709. for (var i:Number=0; i < this.valid_file_extensions.length; i++) {
  710. if (String(this.valid_file_extensions[i]) == extension) {
  711. is_valid_filetype = true;
  712. break;
  713. }
  714. }
  715. return is_valid_filetype;
  716. }
  717. private function BuildRequest():URLRequest {
  718. // Create the request object
  719. var request:URLRequest = new URLRequest();
  720. request.method = URLRequestMethod.POST;
  721. var file_post:Object = this.current_file_item.GetPostObject();
  722. if (this.useQueryString) {
  723. var pairs:Array = new Array();
  724. for (key in this.uploadPostObject) {
  725. this.Debug("Global URL Item: " + key + "=" + this.uploadPostObject[key]);
  726. if (this.uploadPostObject.hasOwnProperty(key)) {
  727. pairs.push(escape(key) + "=" + escape(this.uploadPostObject[key]));
  728. }
  729. }
  730. for (key in file_post) {
  731. this.Debug("File Post Item: " + key + "=" + file_post[key]);
  732. if (file_post.hasOwnProperty(key)) {
  733. pairs.push(escape(key) + "=" + escape(file_post[key]));
  734. }
  735. }
  736. request.url = this.uploadURL + (this.uploadURL.indexOf("?") > -1 ? "&" : "?") + pairs.join("&");
  737. } else {
  738. var key:String;
  739. var post:URLVariables = new URLVariables();
  740. for (key in this.uploadPostObject) {
  741. this.Debug("Global Post Item: " + key + "=" + this.uploadPostObject[key]);
  742. if (this.uploadPostObject.hasOwnProperty(key)) {
  743. post[key] = this.uploadPostObject[key];
  744. }
  745. }
  746. for (key in file_post) {
  747. this.Debug("File Post Item: " + key + "=" + file_post[key]);
  748. if (file_post.hasOwnProperty(key)) {
  749. post[key] = file_post[key];
  750. }
  751. }
  752. request.url = this.uploadURL;
  753. request.data = post;
  754. }
  755. return request;
  756. }
  757. private function Debug(msg:String):void {
  758. try {
  759. if (this.debugEnabled) {
  760. var lines:Array = msg.split("\n");
  761. for (var i:Number=0; i < lines.length; i++) {
  762. lines[i] = "SWF DEBUG: " + lines[i];
  763. }
  764. ExternalCall.Debug(this.debug_Callback, lines.join("\n"));
  765. }
  766. } catch (ex:Error) {
  767. // pretend nothing happened
  768. trace(ex);
  769. }
  770. }
  771. private function PrintDebugInfo():void {
  772. var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n";
  773. debug_info += "Build Number: " + this.build_number + "\n";
  774. debug_info += "movieName: " + this.movieName + "\n";
  775. debug_info += "Upload URL: " + this.uploadURL + "\n";
  776. debug_info += "File Types String: " + this.fileTypes + "\n";
  777. debug_info += "Parsed File Types: " + this.valid_file_extensions.toString() + "\n";
  778. debug_info += "File Types Description: " + this.fileTypesDescription + "\n";
  779. debug_info += "File Size Limit: " + this.fileSizeLimit + " bytes\n";
  780. debug_info += "File Upload Limit: " + this.fileUploadLimit + "\n";
  781. debug_info += "File Queue Limit: " + this.fileQueueLimit + "\n";
  782. debug_info += "Post Params:\n";
  783. for (var key:String in this.uploadPostObject) {
  784. if (this.uploadPostObject.hasOwnProperty(key)) {
  785. debug_info += " " + key + "=" + this.uploadPostObject[key] + "\n";
  786. }
  787. }
  788. debug_info += "----- END SWF DEBUG OUTPUT ----\n";
  789. this.Debug(debug_info);
  790. }
  791. private function FindIndexInFileQueue(file_id:String):Number {
  792. for (var i:Number = 0; i<this.file_queue.length; i++) {
  793. var item:FileItem = this.file_queue[i];
  794. if (item != null && item.id == file_id) return i;
  795. }
  796. return -1;
  797. }
  798. private function FindFileInFileIndex(file_id:String):FileItem {
  799. for (var i:Number = 0; i < this.file_index.length; i++) {
  800. var item:FileItem = this.file_index[i];
  801. if (item != null && item.id == file_id) return item;
  802. }
  803. return null;
  804. }
  805. // Parse the file extensions in to an array so we can validate them agains
  806. // the files selected later.
  807. private function LoadFileExensions(filetypes:String):void {
  808. var extensions:Array = filetypes.split(";");
  809. this.valid_file_extensions = new Array();
  810. for (var i:Number=0; i < extensions.length; i++) {
  811. var extension:String = String(extensions[i]);
  812. var dot_index:Number = extension.lastIndexOf(".");
  813. if (dot_index >= 0) {
  814. extension = extension.substr(dot_index + 1).toLowerCase();
  815. } else {
  816. extension = extension.toLowerCase();
  817. }
  818. // If one of the extensions is * then we allow all files
  819. if (extension == "*") {
  820. this.valid_file_extensions = new Array();
  821. break;
  822. }
  823. this.valid_file_extensions.push(extension);
  824. }
  825. }
  826. private function loadPostParams(param_string:String):void {
  827. var post_object:Object = {};
  828. if (param_string != null) {
  829. var name_value_pairs:Array = param_string.split("&amp;");
  830. for (var i:Number = 0; i < name_value_pairs.length; i++) {
  831. var name_value:String = String(name_value_pairs[i]);
  832. var index_of_equals:Number = name_value.indexOf("=");
  833. if (index_of_equals > 0) {
  834. post_object[decodeURIComponent(name_value.substring(0, index_of_equals))] = decodeURIComponent(name_value.substr(index_of_equals + 1));
  835. }
  836. }
  837. }
  838. this.uploadPostObject = post_object;
  839. }
  840. private function removeFileReferenceEventListeners(file_item:FileItem):void {
  841. if (file_item != null && file_item.file_reference != null) {
  842. file_item.file_reference.removeEventListener(Event.OPEN, this.Open_Handler);
  843. file_item.file_reference.removeEventListener(ProgressEvent.PROGRESS, this.FileProgress_Handler);
  844. file_item.file_reference.removeEventListener(IOErrorEvent.IO_ERROR, this.IOError_Handler);
  845. file_item.file_reference.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.SecurityError_Handler);
  846. file_item.file_reference.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.HTTPError_Handler);
  847. file_item.file_reference.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, this.ServerData_Handler);
  848. }
  849. }
  850. }
  851. }