Main.as 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /**
  2. * Swiff.Uploader
  3. *
  4. * Credits: A lot of good parts are inspired by the awesome www.swfupload.org
  5. *
  6. * @licence MIT Licence
  7. *
  8. * @author Harald Kirschner <http://digitarald.de>
  9. * @author Anders Rasmussen <aras@dr.dk>
  10. * @author Valerio Proietti, <http://mad4milk.net>
  11. * @copyright Authors
  12. */
  13. package
  14. {
  15. import flash.display.Sprite;
  16. import flash.display.StageAlign;
  17. import flash.display.StageScaleMode;
  18. import flash.display.MovieClip;
  19. import flash.display.Loader;
  20. import flash.events.*;
  21. import flash.utils.*;
  22. import flash.system.Security;
  23. import flash.net.FileReference;
  24. import flash.net.FileReferenceList;
  25. import flash.net.FileFilter;
  26. import flash.net.URLRequest;
  27. import flash.external.ExternalInterface;
  28. import flash.text.AntiAliasType;
  29. import flash.text.GridFitType;
  30. import flash.text.StaticText;
  31. import flash.text.StyleSheet;
  32. import flash.text.TextDisplayMode;
  33. import flash.text.TextField;
  34. import flash.text.TextFieldType;
  35. import flash.text.TextFieldAutoSize;
  36. import flash.text.TextFormat;
  37. import Escaper;
  38. import File;
  39. public class Main extends Sprite
  40. {
  41. public var options:Object = {
  42. typeFilter: null,
  43. typeFilterDescription: null,
  44. multiple: null,
  45. queued: null,
  46. verbose: null,
  47. width: 128,
  48. height: 32,
  49. passStatus: null,
  50. url: null,
  51. method: null,
  52. data: null,
  53. mergeData: null,
  54. fieldName: null,
  55. progressGraphSize: 10,
  56. fileSizeMin: 1,
  57. fileSizeMax: null,// Official 100 MB limit for FileReference
  58. allowDuplicates: false,
  59. timeLimit: null,
  60. policyFile: null,
  61. buttonImage: null
  62. };
  63. public var fileList:Array = new Array();
  64. public var uploading:uint = 0;
  65. public var size:uint = 0;
  66. public var bytesLoaded:uint = 0;
  67. public var rate:uint = 0;
  68. private var dialog:*= null;
  69. private var buttonLoader:Loader;
  70. private var buttonCursorSprite:Sprite;
  71. private var buttonState:uint = 0;
  72. const BUTTON_STATE_OVER = 1;
  73. const BUTTON_STATE_DOWN = 2;
  74. const BUTTON_STATE_DISABLED = 4;
  75. public function Main():void
  76. {
  77. if (stage) init();
  78. else addEventListener(Event.ADDED_TO_STAGE, init);
  79. }
  80. private function init(e:Event = null):void
  81. {
  82. removeEventListener(Event.ADDED_TO_STAGE, init);
  83. if (!flash.net.FileReference || !flash.external.ExternalInterface || !flash.external.ExternalInterface.available) {
  84. return;
  85. }
  86. // allow uploading to any domain
  87. Security.allowDomain("*");
  88. // ExternalInterface callback adding copied
  89. ExternalInterface.addCallback('initialize', xInitialize);
  90. ExternalInterface.addCallback('setOptions', xSetOptions);
  91. ExternalInterface.addCallback('start', xStart);
  92. ExternalInterface.addCallback('stop', xStop);
  93. ExternalInterface.addCallback('remove', xRemove);
  94. ExternalInterface.addCallback('setEnabled', xSetEnabled);
  95. ExternalInterface.addCallback('fileSetOptions', xFileSetOptions);
  96. ExternalInterface.addCallback('fileStart', xFileStart);
  97. ExternalInterface.addCallback('fileStop', xFileStop);
  98. ExternalInterface.addCallback('fileRemove', xFileRemove);
  99. ExternalInterface.addCallback('fileRequeue', xFileRequeue);
  100. fireEvent('load');
  101. }
  102. public function fireEvent(functionName:String, args:* = null):void
  103. {
  104. verboseLog('Main::fireEvent "' + functionName + '"', args);
  105. if (args !== null) {
  106. if (args is Array) args = Escaper.escapeArray(args);
  107. else args = [Escaper.escape(args)];
  108. }
  109. ExternalInterface.call(root.loaderInfo.parameters.fireCallback, functionName, args || []);
  110. }
  111. private function empty(fake:* = null):void
  112. {}
  113. // External Interface - JS to Flash - calls
  114. private function xInitialize(options_override:Object = null):void
  115. {
  116. xSetOptions(options_override);
  117. // Make the stage clickable and transparent.
  118. stage.align = StageAlign.TOP_LEFT;
  119. stage.scaleMode = StageScaleMode.NO_SCALE;
  120. stage.addEventListener(MouseEvent.CLICK, stageClick);
  121. stage.addEventListener(MouseEvent.MOUSE_DOWN, stageMouse);
  122. stage.addEventListener(MouseEvent.MOUSE_UP, stageMouse);
  123. stage.addEventListener(MouseEvent.MOUSE_OVER, stageMouse);
  124. stage.addEventListener(MouseEvent.MOUSE_OUT, stageMouse);
  125. stage.addEventListener(Event.MOUSE_LEAVE, stageLeave);
  126. stage.addEventListener(Event.RESIZE, stageResize);
  127. buttonLoader = new Loader();
  128. buttonLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, empty);
  129. buttonLoader.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, empty);
  130. stage.addChild(buttonLoader);
  131. buttonCursorSprite = new Sprite();
  132. buttonCursorSprite.graphics.beginFill(0xFFFFFF, 0);
  133. buttonCursorSprite.graphics.drawRect(0, 0, 1, 1);
  134. buttonCursorSprite.graphics.endFill();
  135. buttonCursorSprite.buttonMode = true;
  136. buttonCursorSprite.useHandCursor = true;
  137. buttonCursorSprite.x = 0;
  138. buttonCursorSprite.y = 0;
  139. buttonCursorSprite.addEventListener(MouseEvent.CLICK, empty);
  140. stage.addChild(buttonCursorSprite);
  141. initButton();
  142. verboseLog('initialized');
  143. }
  144. private function xSetOptions(options_override:Object = null):void
  145. {
  146. if (options_override != null) {
  147. for (var prop:String in options) {
  148. if (options_override.hasOwnProperty(prop)) {
  149. switch (prop) {
  150. case 'policyFile':
  151. if (options_override[prop] is String) Security.loadPolicyFile(options_override[prop]);
  152. break;
  153. }
  154. options[prop] = options_override[prop];
  155. }
  156. }
  157. }
  158. }
  159. private function xFileSetOptions(id:uint, options_init:Object = null):Boolean
  160. {
  161. var ref:File = getFileById(id);
  162. if (!ref) return false;
  163. ref.setOptions(options_init);
  164. return true;
  165. }
  166. private function xFileStart(id:uint):Boolean
  167. {
  168. var ref:File = getFileById(id);
  169. if (!ref) return false;
  170. return ref.start();
  171. }
  172. private function xFileStop(id:uint):Boolean
  173. {
  174. var ref:File = getFileById(id);
  175. if (!ref) return false;
  176. return ref.stop();
  177. }
  178. private function xFileRemove(id:uint):Boolean
  179. {
  180. var ref:File = getFileById(id);
  181. if (!ref) return false;
  182. return ref.remove();
  183. }
  184. public function queueUpdate():Object
  185. {
  186. return {
  187. uploading: uploading,
  188. size: size,
  189. bytesLoaded: bytesLoaded,
  190. percentLoaded: (size > 0) ? Math.ceil(bytesLoaded / size * 100) : 0,
  191. rate: rate
  192. };
  193. }
  194. private function xFileRequeue(id:uint):Boolean
  195. {
  196. var ref:File = getFileById(id);
  197. if (!ref) return false;
  198. return ref.requeue();
  199. }
  200. private function xStart():void
  201. {
  202. if (uploading) return;
  203. checkQueue();
  204. if (uploading) fireEvent('start');
  205. }
  206. private function xSetEnabled(status:* = null):void
  207. {
  208. if (status == null) status = !(buttonState & BUTTON_STATE_DISABLED);
  209. if (status) buttonState = buttonState &~ BUTTON_STATE_DISABLED;
  210. else buttonState = buttonState | BUTTON_STATE_DISABLED;
  211. buttonCursorSprite.useHandCursor = status;
  212. updateButton();
  213. }
  214. private function xStop():void
  215. {
  216. for (var i:uint = fileList.length - 1; i > 0; i--) {
  217. if (fileList[i].status != File.STATUS_RUNNING) continue;
  218. fileList[i].stop();
  219. }
  220. }
  221. private function xRemove():void
  222. {
  223. while (fileList.length) {
  224. fileList[0].remove();
  225. }
  226. }
  227. public function checkQueue(eventful:Boolean = false):void
  228. {
  229. var queued:uint = (options.queued) ? ((options.queued > 1) ? options.queued : 1) : 0;
  230. if (uploading && queued && uploading >= queued) return;
  231. var length:uint = fileList.length;
  232. for (var i:uint = 0; i < length; i++) {
  233. if (fileList[i].status != File.STATUS_QUEUED) continue;
  234. fileList[i].start();
  235. if (queued && uploading >= queued) break;
  236. }
  237. if (!uploading && eventful) fireEvent('complete', [queueUpdate()]);
  238. }
  239. private function stageClick(e:MouseEvent):void
  240. {
  241. if (buttonState & BUTTON_STATE_DISABLED) {
  242. fireEvent('disabledBrowse');
  243. return;
  244. }
  245. browse();
  246. }
  247. private function stageLeave(e:Event):void
  248. {
  249. buttonState = buttonState &~ BUTTON_STATE_DOWN;
  250. buttonState = buttonState &~ BUTTON_STATE_OVER;
  251. updateButton();
  252. }
  253. private function stageResize(e:Event):void
  254. {
  255. updateSize();
  256. }
  257. private function stageMouse(e:MouseEvent):void
  258. {
  259. switch (e.type) {
  260. case MouseEvent.MOUSE_DOWN:
  261. if (e.buttonDown) buttonState = buttonState | BUTTON_STATE_DOWN;
  262. break;
  263. case MouseEvent.MOUSE_UP:
  264. buttonState = buttonState &~ BUTTON_STATE_DOWN;
  265. break;
  266. case MouseEvent.MOUSE_OVER:
  267. buttonState = buttonState | BUTTON_STATE_OVER;
  268. break;
  269. case MouseEvent.MOUSE_OUT:
  270. buttonState = buttonState &~ BUTTON_STATE_OVER;
  271. }
  272. updateButton();
  273. }
  274. private function updateSize():void
  275. {
  276. buttonCursorSprite.width = stage.stageWidth;
  277. buttonCursorSprite.height = stage.stageHeight;
  278. }
  279. private function initButton():void
  280. {
  281. if (options.buttonImage) {
  282. buttonLoader.load(new URLRequest(options.buttonImage));
  283. } else {
  284. buttonLoader.unload();
  285. }
  286. updateSize();
  287. updateButton();
  288. }
  289. private function updateButton():void
  290. {
  291. var to_y:int = 0;
  292. var event:String = 'Leave';
  293. if (buttonState & BUTTON_STATE_DISABLED) {
  294. to_y = stage.stageHeight * -3;
  295. event = 'Disable';
  296. } else if (buttonState & BUTTON_STATE_DOWN) {
  297. to_y = stage.stageHeight * -2;
  298. event = 'Down';
  299. } else if (buttonState & BUTTON_STATE_OVER) {
  300. to_y = stage.stageHeight * -1;
  301. event = 'Enter';
  302. }
  303. if (to_y != buttonLoader.y) {
  304. buttonLoader.y = to_y;
  305. fireEvent('button' + event);
  306. }
  307. }
  308. private function browse():void
  309. {
  310. var filter:Array = new Array();
  311. if (options.typeFilter is String) {
  312. var description:String = options.typeFilterDescription || options.typeFilter;
  313. var type:FileFilter = new FileFilter(description, options.typeFilter + '; ' + options.typeFilter.toUpperCase() + '; ' + options.typeFilter.toLowerCase());
  314. filter.push(type);
  315. } else if (options.typeFilter is Object) {
  316. for (var key:String in options.typeFilter) {
  317. filter.push(new FileFilter(key, options.typeFilter[key] + '; ' + options.typeFilter[key].toUpperCase() + '; ' + options.typeFilter[key].toLowerCase()));
  318. }
  319. }
  320. fireEvent('browse');
  321. dialog = (options.multiple) ? new FileReferenceList() : new FileReference();
  322. dialog.addEventListener(Event.SELECT, handleSelect);
  323. dialog.addEventListener(Event.CANCEL, handleCancel);
  324. try {
  325. dialog.browse((filter.length) ? filter : null);
  326. } catch (e:Error) {
  327. verboseLog('Main::browse Exception', e.toString());
  328. }
  329. }
  330. private function handleSelect(event:Event):void
  331. {
  332. verboseLog('Main::handleSelect Adding Files');
  333. var added:Array = new Array();
  334. if (options.multiple) {
  335. var refList:FileReferenceList = dialog;
  336. for (var i:uint = 0; i < refList.fileList.length; i++) {
  337. var origin:FileReference = refList.fileList[i];
  338. var ref:File = new File(this, origin);
  339. added.push(ref);
  340. }
  341. } else {
  342. var origin:FileReference = dialog;
  343. var ref:File = new File(this, origin);
  344. added.push(ref);
  345. }
  346. var failed:Array = new Array();
  347. added = added.filter(function(ref:File, i:uint, self:Array) {
  348. if (!ref.validate()) {
  349. ref.id = 0; // invalidate file reference
  350. failed.push(ref);
  351. return false;
  352. }
  353. size += ref.reference.size;
  354. return true;
  355. });
  356. fileList = fileList.concat(added);
  357. fireEvent('select', [File.exportMany(added), File.exportMany(failed), queueUpdate()]);
  358. dialog = null;
  359. }
  360. private function handleCancel(event:Event):void
  361. {
  362. buttonState = buttonState & ~ BUTTON_STATE_OVER;
  363. updateButton();
  364. fireEvent('cancel');
  365. dialog = null;
  366. }
  367. private function getFileById(id:uint):File
  368. {
  369. if (id > 0) {
  370. for (var i:uint = 0; i < fileList.length; i++) {
  371. if (fileList[i].id == id) return fileList[i];
  372. }
  373. }
  374. verboseLog('Main::getFileById', 'File not found: ' + id);
  375. return null;
  376. }
  377. public function hasFile(check:File):Boolean
  378. {
  379. verboseLog('Main::hasFile', 'Checking ' + check.reference.name);
  380. return fileList.some(function(current:File, i:uint, self:Array) {
  381. if (current.reference == check.reference) return true;
  382. return (check.reference.name == current.reference.name && check.reference.size == current.reference.size && check.reference.creationDate.valueOf() == current.reference.creationDate.valueOf());
  383. }, this);
  384. }
  385. // Misc. helpers
  386. public function verboseLog(labelName:String, message:* = null):void
  387. {
  388. if (!options.verbose) return;
  389. if (message) ExternalInterface.call('Swiff.Uploader.log', labelName + ':', Escaper.escape(message));
  390. else ExternalInterface.call('Swiff.Uploader.log', labelName);
  391. }
  392. }
  393. }