/* BulkLoader: manage multiple loadings in Actioncript 3. * * * @author Arthur Debert * @version 0.9.1 */ /* * Licensed under the MIT License * * Copyright (c) 2006-2007 Arthur Debert * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * http://code.google.com/p/bulk-loader/ * http://www.opensource.org/licenses/mit-license.php * */ package br.com.stimuli.loading.loadingtypes { import br.com.stimuli.loading.BulkLoader; import br.com.stimuli.loading.BulkProgressEvent; import br.com.stimuli.loading.utils.SmartURL; import flash.display.*; import flash.events.*; import flash.net.*; import flash.utils.*; /** * Dispatched on download progress. * * @eventType flash.events.ProgressEvent.PROGRESS */ [Event(name="progress", type="flash.events.ProgressEvent")]; /** * Dispatched when all the item has been fully downloaded and is ready for use.. * * @eventType flash.events.ProgressEvent.COMPLETE */ [Event(name="complete", type="flash.events.Event")]; /** * Dispatched when the connection has been stablished and the download has * begun. For types that can be streamed such as videos (NetStream * ) and sound(Sound), it's content is will be available * after this event has fired. * * @eventType flash.events.Event.OPEN */ [Event(name="open", type="flash.events.Event")]; /** * Dispatched when the ImageItem (swf or image) has inited. * * @eventType flash.events.Event.INIT */ [Event(name="init", type="flash.events.Event")]; /** * Dispatched when the the netStream can be played until the end with no * interuption expected. Only fires for TYPE_VIDEO items and will only fire * once. * * @eventType br.com.stimuli.loading.BulkLoader.CAN_BEGIN_PLAYING */ [Event(name="canBeginPlaying", type="br.com.stimuli.loading.BulkLoader")]; /** * An class used in BulkLoader instances.

. * A reference to a LoadingItem object can be used to attach * events for an individual item, read it's id, type, content and urls from * inside event handlers. * When implementing custom loading types, the new type should be a subclass * of LoadingItem. This class is just the base for specific loading types, * but should provide the external interface for end users. * @langversion ActionScript 3.0 * @playerversion Flash 9.0 * * @author Arthur Debert * @since 15.09.2007 */ public class LoadingItem extends EventDispatcher { /** @private */ public static const STATUS_STOPPED : String = "stopped"; /** @private */ public static const STATUS_STARTED : String = "started"; /** @private */ public static const STATUS_FINISHED : String = "finished"; /** @private */ public static const STATUS_ERROR : String = "error"; /** The type of loading to perform (see BulkLoader.TYPES). * @private */ public var _type : String; // The url to load the asset from. /** @private */ public var url : URLRequest; /** @private */ public var _id : String; /** @private */ public var _uid : String; /** @private */ public var _additionIndex : int ; /** @private */ public var _priority : int = 0; /** @private */ ///**Indicated if item is loaded and ready to use..*/ /** @private */ public var _isLoaded : Boolean; /**Indicated if loading has stated. * @private */ public var _isLoading : Boolean; /** @private If the gzip compression status has been determined */ //public var _gzipDetermined : int = -1; /** @private The http response headers, will be null until received */ //public var _responseHeaders : Array = null; /** @private * At what stage this item is at ( canceled, started, finished or error). */ public var status : String; // /** @private * Maximun number of tries in case it fails. * */ public var maxTries : int = 3; /**Current try number. * @private */ public var numTries : int = 0; /**A relative unit of size, so that preloaders can show relative * progress before all connections have started. * @private */ public var weight : int = 1; /** * If a random string should be appended to the end of the url to * prevent caching. * @private */ public var preventCache : Boolean; /**the number of bytes to load. Starts at -1. * @private */ public var _bytesTotal : int = -1; /**the number of bytes loaded so far. Starts at -1. * @private */ public var _bytesLoaded : int = 0; /** @private */ public var _bytesRemaining : int = int.MAX_VALUE; /**The percentage of loading done (from 0 to 1). * @private */ public var _percentLoaded : Number; /** The percentage of loading done relative to the weight of this * item(from 0 to 1). * @private */ public var _weightPercentLoaded : Number; /** * @private */ public var _addedTime : int ; /** @private */ public var _startTime : int ; /** @private */ public var _responseTime : Number; /** The time (in seconds) that the server took and send begin * streaming content. * @private */ public var _latency : Number; /** @private */ public var _totalTime : int; /** The total time (in seconds) this item took to load.*/ public var _timeToDownload : Number; /** The speed (in kbs) for this download. * @private * */ public var _speed : Number; /** @private */ public var _content : *; /** @private */ public var _httpStatus : int = -1; /** * @private */ public var _context : * = null; /** * @private */ public var _parsedURL : SmartURL /** @private */ public var specificAvailableProps : Array ; /** @private */ public var propertyParsingErrors : Array; /** * Stores any erros (if any), such as IOError and SecurityError for this item */ public var errorEvent : ErrorEvent; public function LoadingItem(url : URLRequest, type : String, _uid : String){ this._type = type; this.url = url; _parsedURL = new SmartURL(url.url); if(!specificAvailableProps){ specificAvailableProps = []; } this._uid = _uid; } /** * @private */ public function _parseOptions(props : Object) : Array{ preventCache = props[BulkLoader.PREVENT_CACHING]; _id = props[BulkLoader.ID]; _priority = int(props[BulkLoader.PRIORITY]) || 0; maxTries = props[BulkLoader.MAX_TRIES] || 3; weight = int(props[BulkLoader.WEIGHT]) || 1; // checks that we are not adding any inexistent props, // aka, typos on props : var allowedProps : Array = BulkLoader.GENERAL_AVAILABLE_PROPS.concat(specificAvailableProps); propertyParsingErrors = []; for (var propName :String in props){ if (allowedProps.indexOf(propName) == -1){ propertyParsingErrors.push(this + ": got a wrong property name: " + propName + ", with value:" + props[propName]); } } return propertyParsingErrors; } /** The content resulting from this download. The data type for the * content depends on the myme-type of the downloaded asset. * For types that can be streamed such as videos (NetStream) * and sound(Sound), it's content is available as soon as * the connection is open. Otherwiser the content will be available * after the download is done and the Event.COMPLETE is * fired. * @return An object whose type depends on what the asset is. */ public function get content() : * { return _content; } /** * @private */ public function load() : void{ if (preventCache){ var cacheString : String = "BulkLoaderNoCache=" + _uid + "_" + int(Math.random() * 100 * getTimer()); if(url.url.indexOf("?") == -1){ url.url += "?" + cacheString; }else{ url.url += "&" + cacheString; } } _isLoading = true; _startTime = getTimer(); } /** * @private */ public function onHttpStatusHandler(evt : HTTPStatusEvent) : void{ _httpStatus = evt.status; //_responseHeaders = evt.responseHeaders; waiting for decend http support in as3 dispatchEvent(evt); } /** * @private */ public function onProgressHandler(evt : ProgressEvent) : void { _bytesLoaded = evt.bytesLoaded; _bytesTotal = evt.bytesTotal; _bytesRemaining = _bytesTotal - bytesLoaded; // truncate correct numbers, since gzip compression might // create nonsensical values if (_bytesRemaining < 0 ){ _bytesRemaining = int.MAX_VALUE; } if (_bytesTotal < 4 && _bytesLoaded > 0 ){ _bytesTotal = int.MAX_VALUE; } _percentLoaded = _bytesLoaded / _bytesTotal; _weightPercentLoaded = _percentLoaded * weight; var e : BulkProgressEvent = new BulkProgressEvent(BulkLoader.PROGRESS, true, false); e._percentLoaded = _percentLoaded; e.bytesTotal = _bytesTotal; e.bytesLoaded = _bytesLoaded; evt.stopImmediatePropagation(); dispatchEvent(e); } /** * @private */ public function onCompleteHandler(evt : Event) : void { _totalTime = getTimer(); _timeToDownload = ((_totalTime - _responseTime) /1000); if(_timeToDownload == 0){ _timeToDownload = 0.1; } // check for gzip compression as these will give bad results _bytesTotal = _bytesLoaded; _bytesRemaining = 0; _speed = BulkLoader.truncateNumber((bytesTotal / 1024) / (_timeToDownload)); status = STATUS_FINISHED; _isLoaded = true; dispatchEvent(evt); evt.stopPropagation(); } /** * @private */ public function onErrorHandler(evt : ErrorEvent) : void{ numTries ++; evt.stopPropagation(); if(numTries < maxTries){ status = null load(); }else{ status = STATUS_ERROR; errorEvent = evt; _dispatchErrorEvent(errorEvent); } } /** @private */ public function _dispatchErrorEvent (evt : ErrorEvent) : void{ // we are dispatching here so we can have all error events catched by addEventListener("error"), regardless of event type. status = STATUS_ERROR; dispatchEvent(new ErrorEvent(BulkLoader.ERROR, true, false, evt.text)); } /** private */ public function _createErrorEvent(e : Error) : ErrorEvent{ return new ErrorEvent(BulkLoader.ERROR, false, false, e.message); } /** * @private * @param A SecurityErrorEvent. */ public function onSecurityErrorHandler(e : ErrorEvent) : void{ status = STATUS_ERROR; errorEvent = e as ErrorEvent; e.stopPropagation(); _dispatchErrorEvent(errorEvent); } /** * @private */ public function onStartedHandler(evt : Event) : void{ _responseTime = getTimer(); _latency = BulkLoader.truncateNumber((_responseTime - _startTime)/1000); status = STATUS_STARTED; dispatchEvent(evt); } /* If we have enough information (HttpHeaders available) to determine if * the response has been gzip enconded. */ //public function get gzipDetermined() : Boolean{ //if (_gzipDetermined > -1) return true; //return false //} /* If the response has gzip compression. This is important since progress * report is unreliable for gzip compressed responses. This only returns * a meaniful value if the headers have been returned, which means that * to be sure of the result you must check for gzipCompressed and gzipDetermined. * Once determined, the response will be cached. * @returns True if the response is compressed. And false if it is not * OR if the headers haven't been parsed yet. * @see #gzipDetermined */ //public function get gzipCompressed (): Boolean{ //if (_gzipDetermined > -1){ //return Boolean(_gzipDetermined); //} //if (_responseHeaders){ //for each(var header : URLRequestHeader in _responseHeaders){ //trace("\n", _parsedURL.fileName, header.name, header.value); //if (header.name.indexOf("Content-Encoding") > -1 && //header.value.match(/gzip|compress/i)) { //_gzipDetermined = 1; //trace("gzip found"); //return gzipCompressed; //} //} //_gzipDetermined = 0; //} //return false; //} public override function toString() : String{ return "LoadingItem url: " + url.url + ", type:" + _type + ", status: " + status; } /** * @private */ public function stop() : void{ if(_isLoaded){ return; } status = STATUS_STOPPED; _isLoading = false; } /** * @private */ public function cleanListeners() : void { } /** Returns true if content is of type video. */ public function isVideo(): Boolean{ return false; } /** Returns true if content is of type sound. */ public function isSound(): Boolean{ return false; } /** Returns true if content is of type text. */ public function isText(): Boolean{ return false; } /** Returns true if content is of type xml. */ public function isXML(): Boolean{ return false; } /** Returns true if content is of type image. */ public function isImage() : Boolean{ return false; } /** Returns true if content is of type movieclip. */ public function isSWF() : Boolean{ return false; } /** Returns true if uses a Loader instance internally. (such as swfs and images). */ public function isLoader(): Boolean{ return false; } /** Returns true if this loading type should allow it's content to be accessed as soon as the server response starts. Should be true for sound and video types. */ public function isStreamable() : Boolean{ return false; } /** * @private */ public function destroy() : void{ _content = null; } /** Public accessors * @private */ public function get bytesTotal() : int { return _bytesTotal; } /** * @private */ public function get bytesLoaded() : int { return _bytesLoaded; } /** * @private */ public function get bytesRemaining() : int { return _bytesRemaining; } /** * @private */ public function get percentLoaded() : Number { return _percentLoaded; } /** * @private */ public function get weightPercentLoaded() : Number { return _weightPercentLoaded; } /** The priority at which this item will be downloaded. Items with a higher priority will be downloaded first. * @private */ public function get priority() : int { return _priority; } /** The type of this item. * @see BulkLoader.AVAILABLE_EXTENSIONS */ public function get type() : String{ return _type; } /** A Boolean that indicates if the item is fully loaded and ready for consumption. */ public function get isLoaded() : Boolean { return _isLoaded; } /** * @private */ public function get addedTime() : int { return _addedTime; } /** * @private */ public function get startTime() : int { return _startTime; } /** * @private */ public function get responseTime() : Number { return _responseTime; } /** The time (in seconds) that the server took and send begin streaming content. * @private */ public function get latency() : Number { return _latency; } /** * @private */ public function get totalTime() : int { return _totalTime; } /** The total time (in seconds) this item took to load. * @private */ public function get timeToDownload() : int { return _timeToDownload; } /** The speed (in kbs) for this download. * @private */ public function get speed() : Number { return _speed; } /** The httpStatus of the LoadingItem, as in int (0 if no status has been received). * @private */ public function get httpStatus() : int { return _httpStatus; } /** The id this item was assigned. This is use in all of BulkLoader.getXXX(key) functions */ public function get id() : String { return _id; } /** The hostname, as a string for this item's URL. If no hostname is specified, then an empty string is returned */ public function get hostName():String { return _parsedURL.host; } public function get humanFiriendlySize():String { var kb : Number = _bytesTotal/1024; if (kb < 1024){ return Math.ceil(kb) + " kb" }else{ return (kb/1024).toPrecision(3) + " mb" } } /** Returns a string with time stats for this loading item. */ public function getStats() : String{ return "Item url: " + url.url + "(s), total time: " + (_totalTime/1000).toPrecision(3) + "(s), download time: " + (_timeToDownload).toPrecision(3) + "(s), latency:" + _latency + "(s), speed: " + _speed + " kb/s, size: " + humanFiriendlySize; } }}