package com.zehfernando.net { import flash.display.Loader; import flash.display.LoaderInfo; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IEventDispatcher; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.net.URLLoader; import flash.net.URLRequest; import flash.system.LoaderContext; /** * @author Zeh */ public class LoadingQueue extends EventDispatcher { // Properties public var maximumRetries:uint; protected var _slots:uint; // Maximum simultaneous loadings protected var _cumulativeBytesLoaded:uint; protected var _cumulativeSimulatedBytesLoaded:uint; //protected var _cumulativeTimeSpent:uint; // TOTAL time spent loading protected var queue:Vector.<LoadingQueueItemInfo>; // Items currently waiting in line protected var currentLoaders:Vector.<LoadingQueueItemInfo>; // Items currently loading protected var _paused:Boolean; // ================================================================================================================ // CONSTRUCTOR ---------------------------------------------------------------------------------------------------- public function LoadingQueue(target : IEventDispatcher = null) { super(target); setDefaultProperties(); //trace("LoadingQueue.LoadingQueue()"); } // ================================================================================================================ // INTERNAL functions --------------------------------------------------------------------------------------------- protected function setDefaultProperties(): void { _slots = 1; maximumRetries = 0; _paused = true; _cumulativeBytesLoaded = 0; _cumulativeSimulatedBytesLoaded = 0; //_cumulativeTimeSpent = 0; queue = new Vector.<LoadingQueueItemInfo>(); currentLoaders = new Vector.<LoadingQueueItemInfo>(); } protected function checkUnusedSpots(): void { // Check if there are remaining loading spots, //trace("LoadingQueue.checkUnusedSpots()"); if (!_paused) { while (currentLoaders.length < _slots && queue.length > 0) { loadNextItem(); } if (queue.length == 0) { //trace ("LoadingQueue.checkUnusedSpots() - All queued items have been fired already."); if (currentLoaders.length == 0) { //trace ("LoadingQueue.checkUnusedSpots() - All done!"); dispatchCompleteEvent(); } } } } protected function loadNextItem(): void { // Remove one item from the loading queue, and start loading it //trace("LoadingQueue.loadNextItem()"); var q:LoadingQueueItemInfo = queue.shift(); currentLoaders.push(q); if (q.targetObject is URLLoader) { var ul:URLLoader = q.targetObject as URLLoader; //ul.addEventListener(Event.OPEN, onURLLoaderOpen, false, 0, true); ul.addEventListener(Event.COMPLETE, onURLLoaderComplete, false, 0, true); ul.addEventListener(ProgressEvent.PROGRESS, onURLLoaderProgress, false, 0, true); //ul.addEventListener(HTTPStatusEvent.HTTP_STATUS, onURLLoaderHTTPStatus, false, 0, true); // TODO: set the http status? ul.addEventListener(IOErrorEvent.IO_ERROR, onURLLoaderIOError, false, 0, true); //l.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onURLLoaderSecurityError, false, 0, true); ul.load(q.request); } else if (q.targetObject is Loader) { var ld:Loader = q.targetObject as Loader; // TODO: set proper loader context? //ld.contentLoaderInfo.addEventListener(Event.OPEN, onLoaderOpen, false, 0, true); ld.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete, false, 0, true); ld.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoaderProgress, false, 0, true); //ld.contentLoaderInfo.addEventListener(HTTPStatusEvent.HTTP_STATUS, onLoaderHTTPStatus, false, 0, true); // TODO: set the http status? ld.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoaderIOError, false, 0, true); // Event.INIT // Event.UNLOAD var lc:LoaderContext = new LoaderContext(true); ld.load(q.request, lc); } else { throw new Error("LoadingQueue :: loadNextItem() tried loading an item '" + q.targetObject + "' of unknown type"); } } protected function getQueueItemInfoForObject(__targetObject:*):LoadingQueueItemInfo { // Based on a target object, returns the LoadingQueueItemInfo instance that is loading it var i:int; for (i = 0; i < currentLoaders.length; i++) { if (currentLoaders[i].targetObject == __targetObject) return currentLoaders[i]; } return null; } protected function getQueueItemInfoForObjectLoaderInfo(__targetObject:LoaderInfo):LoadingQueueItemInfo { // Based on a target object's contentLoaderInfo, returns the LoadingQueueItemInfo instance that is loading it var i:int; for (i = 0; i < currentLoaders.length; i++) { if (currentLoaders[i].targetObject["contentLoaderInfo"] == __targetObject) return currentLoaders[i]; } return null; } protected function removeItemFromCurrentLoaders(__q:LoadingQueueItemInfo): Boolean { var iq:int = currentLoaders.indexOf(__q); /* // Implied? if (__q.targetObject is URLLoader) { var l:URLLoader = __q.targetObject as URLLoader; l.removeEventListener(Event.OPEN, onURLLoaderOpen); l.removeEventListener(Event.COMPLETE, onURLLoaderComplete); l.removeEventListener(ProgressEvent.PROGRESS, onURLLoaderProgress); l.removeEventListener(HTTPStatusEvent.HTTP_STATUS, onURLLoaderHTTPStatus); l.removeEventListener(IOErrorEvent.IO_ERROR, onURLLoaderIOError); //l.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onURLLoaderSecurityError); } else { throw new Error("LoadingQueue :: removeItemFromCurrentLoaders() tried unregistering an item '" + q.targetObject + "' of unknown type"); } */ if (iq > -1) { currentLoaders.splice(iq, 1); return true; } return false; } protected function dispatchProgressEvent(): void { var bl:int = 0; var bt:int = 0; var i:int; bl += _cumulativeSimulatedBytesLoaded; bt += _cumulativeSimulatedBytesLoaded; for (i = 0; i < queue.length; i++) { bl += queue[i].simulatedBytesLoaded; bt += queue[i].simulatedBytesTotal; } for (i = 0;i < currentLoaders.length; i++) { bl += currentLoaders[i].simulatedBytesLoaded; bt += currentLoaders[i].simulatedBytesTotal; } var ne:Event = new ProgressEvent(ProgressEvent.PROGRESS, false, false, bl, bt); dispatchEvent(ne); } protected function dispatchCompleteEvent(): void { var ne:Event = new Event(Event.COMPLETE); dispatchEvent(ne); } /* protected function dispatchCompleteItemEvent(__queueItem:LoadingQueueItemInfo): void { var ne:Event = new LoadingQueueEvent(LoadingQueueEvent.COMPLETE_ITEM, this, __queueItem.targetObject); dispatchEvent(ne); } */ // ================================================================================================================ // EVENT functions ------------------------------------------------------------------------------------------------ // TODO: this is too redundant? // URLLoader protected function onURLLoaderProgress(e:ProgressEvent): void { var q:LoadingQueueItemInfo = getQueueItemInfoForObject(e.target); q.bytesLoaded = e.bytesLoaded; q.bytesTotal = e.bytesTotal; dispatchProgressEvent(); } protected function onURLLoaderComplete(e:Event): void { // Successfully loaded one item, remove it from the queue //trace ("LoadingQueue.onURLLoaderComplete :: Object '" + e.target); var q:LoadingQueueItemInfo = getQueueItemInfoForObject(e.target); //_cumulativeTimeSpent += (getTimer() - q.timeStarted); _cumulativeBytesLoaded += (q.targetObject as URLLoader).bytesTotal; _cumulativeSimulatedBytesLoaded += q.simulatedBytesTotal; removeItemFromCurrentLoaders(q); //dispatchCompleteItemEvent(q); checkUnusedSpots(); } protected function onURLLoaderIOError(e:IOErrorEvent): void { var q:LoadingQueueItemInfo = getQueueItemInfoForObject(e.target); //trace ("LoadingQueue :: onURLLoaderIOError :: Object '" + q.targetObject + "' has thrown an IOErrorEvent of " + e.text); removeItemFromCurrentLoaders(q); if (q.retries < maximumRetries) { // Try again q.retries++; queue.push(q); } else { // Maximum tries already trace ("LoadingQueue :: onURLLoaderIOError :: Can't load: reached maximum number of tries for [" +q.request.url + "] !"); } } // Loader protected function onLoaderProgress(e:ProgressEvent): void { var q:LoadingQueueItemInfo = getQueueItemInfoForObjectLoaderInfo(e.target as LoaderInfo); q.bytesLoaded = e.bytesLoaded; q.bytesTotal = e.bytesTotal; dispatchProgressEvent(); } protected function onLoaderComplete(e:Event): void { // Successfully loaded one item, remove it from the queue //trace ("LoadingQueue.onURLLoaderComplete :: Object '" + e.target); var q:LoadingQueueItemInfo = getQueueItemInfoForObjectLoaderInfo(e.target as LoaderInfo); //_cumulativeTimeSpent += (getTimer() - q.timeStarted); _cumulativeBytesLoaded += (q.targetObject as Loader).contentLoaderInfo.bytesTotal; _cumulativeSimulatedBytesLoaded += q.simulatedBytesTotal; removeItemFromCurrentLoaders(q); //dispatchCompleteItemEvent(q); checkUnusedSpots(); } protected function onLoaderIOError(e:IOErrorEvent): void { var q:LoadingQueueItemInfo = getQueueItemInfoForObjectLoaderInfo(e.target as LoaderInfo); //trace ("LoadingQueue :: onURLLoaderIOError :: Object '" + q.targetObject + "' has thrown an IOErrorEvent of " + e.text); removeItemFromCurrentLoaders(q); if (q.retries < maximumRetries) { // Try again q.retries++; queue.push(q); } else { // Maximum tries already trace ("LoadingQueue :: onLoaderIOError :: Reached maximum number of tries!"); } } // ================================================================================================================ // PUBLIC functions ----------------------------------------------------------------------------------------------- public function addURLLoader(__targetObject:URLLoader, __request:URLRequest, __simulatedBytesTotal:Number = 10000): void { //trace("LoadingQueue.addURLLoader("+__targetObject+", "+__request+", "+__simulatedBytesTotal+")"); var q:LoadingQueueItemInfo = new LoadingQueueItemInfo(__targetObject, __request); q.simulatedBytesTotal = __simulatedBytesTotal; queue.push(q); checkUnusedSpots(); // TODO: add priority! } public function addLoader(__targetObject:Loader, __request:URLRequest, __simulatedBytesTotal:Number = 100000): void { //trace("LoadingQueue.addURLLoader("+__targetObject+", "+__request+", "+__simulatedBytesTotal+")"); var q:LoadingQueueItemInfo = new LoadingQueueItemInfo(__targetObject, __request); q.simulatedBytesTotal = __simulatedBytesTotal; queue.push(q); checkUnusedSpots(); // TODO: add priority! } // ================================================================================================================ // ACCESSOR functions --------------------------------------------------------------------------------------------- public function get slots(): Number { return _slots; } public function set slots(__value:Number): void { if (_slots != __value) { _slots = __value; checkUnusedSpots(); } } public function get paused(): Boolean { return _paused; } public function pause(): void { if (!_paused) { _paused = true; } } public function resume(): void { if (_paused) { _paused = false; checkUnusedSpots(); } } } } import flash.net.URLRequest; class LoadingQueueItemInfo { // Properties public var targetObject:*; public var request:URLRequest; public var retries:uint; // Number of times this loading has been retried public var simulatedBytesTotal:Number; // Number of simulated bytes total public var bytesLoaded:Number; // Number of simulated bytes total public var bytesTotal:Number; // Number of simulated bytes total // ================================================================================================================ // PUBLIC functions ----------------------------------------------------------------------------------------------- public function LoadingQueueItemInfo(__targetObject:*, __request:URLRequest) { targetObject = __targetObject; request = __request; retries = 0; bytesLoaded = 0; bytesTotal = 0; simulatedBytesTotal = 10000; } // ================================================================================================================ // ACCESSOR functions --------------------------------------------------------------------------------------------- public function get simulatedBytesLoaded(): Number { return bytesTotal == 0 ? 0 : Math.round((bytesLoaded/bytesTotal) * simulatedBytesTotal); } }