blob: 37316fbbee4bef469fa5effd80de669cd2576bc8 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mx.preloaders
{
import flash.display.DisplayObject;
import flash.display.LoaderInfo;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.TimerEvent;
import flash.system.ApplicationDomain;
import flash.utils.Timer;
import mx.core.RSLItem;
import mx.core.RSLListLoader;
import mx.core.ResourceModuleRSLItem;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import mx.events.RSLEvent;
import mx.managers.SystemManagerGlobals;
import mx.resources.IResourceManager;
use namespace mx_internal;
/**
* The Preloader class is used by the SystemManager to monitor
* the download and initialization status of a Flex application.
* It is also responsible for downloading the runtime shared libraries (RSLs).
*
* <p>The Preloader class instantiates a download progress bar,
* which must implement the IPreloaderDisplay interface, and passes download
* and initialization events to the download progress bar.</p>
*
* @see mx.preloaders.DownloadProgressBar
* @see mx.preloaders.Preloader
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public class Preloader extends Sprite
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function Preloader()
{
super()
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var displayClass:IPreloaderDisplay = null;
/**
* @private
*/
private var timer:Timer;
/**
* @private
*/
private var showDisplay:Boolean;
/**
* @private
*/
private var rslListLoader:RSLListLoader;
/**
* @private
*/
private var resourceModuleListLoader:RSLListLoader;
/**
* @private
*/
private var rslDone:Boolean = false;
/**
* @private
*/
private var loadingRSLs:Boolean = false;
/**
* @private
*/
private var waitingToLoadResourceModules:Boolean = false;
/**
* @private
*/
private var sentDocFrameReady:Boolean = false;
/**
* @private
*/
private var app:IEventDispatcher = null;
/**
* @private
*/
private var applicationDomain:ApplicationDomain = null;
/**
* @private
*/
private var waitedAFrame:Boolean = false;
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* Called by the SystemManager to initialize a Preloader object.
*
* @param showDisplay Determines if the display class should be displayed.
*
* @param displayClassName The IPreloaderDisplay class to use
* for displaying the preloader status.
*
* @param backgroundColor Background color of the application.
*
* @param backgroundAlpha Background alpha of the application.
*
* @param backgroundImage Background image of the application.
*
* @param backgroundSize Background size of the application.
*
* @param displayWidth Width of the application.
*
* @param displayHeight Height of the application.
*
* @param libs Array of string URLs for the runtime shared libraries.
*
* @param sizes Array of uint values containing the byte size for each URL
* in the libs argument
*
* @param rslList Array of object of type RSLItem and CdRSLItem.
* This array describes all the RSLs to load.
* The libs and sizes parameters are ignored and must be set to null.
*
* @param resourceModuleURLs Array of Strings specifying URLs
* from which to preload resource modules.
*
* @param applicationDomain The application domain in which
* your code is executing.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function initialize(showDisplay:Boolean,
displayClassName:Class,
backgroundColor:uint,
backgroundAlpha:Number,
backgroundImage:Object,
backgroundSize:String,
displayWidth:Number,
displayHeight:Number,
libs:Array = null,
sizes:Array = null,
rslList:Array = null,
resourceModuleURLs:Array = null,
applicationDomain:ApplicationDomain = null):void
{
if ((libs != null || sizes != null) && rslList != null)
{
// both args can't be used at the same time
throw new Error("RSLs may only be specified by using libs and sizes or rslList, not both."); // $NON-NLS-1$
}
this.applicationDomain = applicationDomain;
root.loaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
var n:int;
var i:int;
// Store the RSL information.
// Keep this code for API backwards compatibility
if (libs && libs.length > 0)
{
if (rslList == null)
{
rslList = [];
}
n = libs.length;
for (i = 0; i < n; i++)
{
var node:RSLItem = new RSLItem(libs[i]);
rslList.push(node);
}
}
var resourceModuleList:Array = [];
// Preloading resource modules is similar enough to loading RSLs
// that we can simply create ResourceModuleRSLItems for them
if (resourceModuleURLs && resourceModuleURLs.length > 0)
{
n = resourceModuleURLs.length;
for (i = 0; i < n; i++)
{
var resourceModuleNode:ResourceModuleRSLItem =
new ResourceModuleRSLItem(resourceModuleURLs[i], applicationDomain);
resourceModuleList.push(resourceModuleNode);
}
}
rslListLoader = new RSLListLoader(rslList);
if (resourceModuleList.length)
resourceModuleListLoader = new RSLListLoader(resourceModuleList);
this.showDisplay = showDisplay;
// Create the timer (really should be adding event listeners to root.LoaderInfo)
timer = new Timer(10);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
// Create a new instance of the display class and attach it to the stage
if (showDisplay)
{
displayClass = new displayClassName();
// Listen for when the displayClass no longer needs to be on the stage
displayClass.addEventListener(Event.COMPLETE,
displayClassCompleteHandler);
// Add the display class as a child of the Preloader
addChild(DisplayObject(displayClass));
displayClass.backgroundColor = backgroundColor;
displayClass.backgroundAlpha = backgroundAlpha;
displayClass.backgroundImage = backgroundImage;
displayClass.backgroundSize = backgroundSize;
displayClass.stageWidth = displayWidth;
displayClass.stageHeight = displayHeight;
displayClass.initialize();
displayClass.preloader = this;
// Listen for ENTER_FRAME to make sure that we are going to render the displayClass first,
// before dispatching PRELOADER_DOC_FRAME_READY. This way we the run-time can render
// the displayClass as soon as possible, before advancing onto frame 2.
CONFIG::performanceInstrumentation
{
import mx.utils.PerfUtil;
PerfUtil.getInstance().markTime("Preloader.displayClass created");
}
this.addEventListener(Event.ENTER_FRAME, waitAFrame);
}
// move below showDisplay so error messages can be displayed
if (rslListLoader.getItemCount() > 0)
{
// Start loading the RSLs.
rslListLoader.load(rslProgressHandler,
rslCompleteHandler,
rslErrorHandler,
rslErrorHandler,
rslErrorHandler);
loadingRSLs = true;
}
else if (resourceModuleListLoader && resourceModuleListLoader.getItemCount() > 0)
{
if (applicationDomain.hasDefinition("mx.resources::ResourceManager"))
{
rslListLoader = resourceModuleListLoader;
// Start loading the resourceModules
rslListLoader.load(rslProgressHandler,
rslCompleteHandler,
rslErrorHandler,
rslErrorHandler,
rslErrorHandler);
}
else
{
waitingToLoadResourceModules = true;
rslDone = true;
}
}
else
{
rslDone = true;
}
}
/**
* Called by the SystemManager after it has finished instantiating
* an instance of the application class. Flex calls this method; you
* do not call it yourself.
*
* @param app The application object.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function registerApplication(app:IEventDispatcher):void
{
// Listen for events from the application.
app.addEventListener("validatePropertiesComplete", appProgressHandler);
app.addEventListener("validateSizeComplete", appProgressHandler);
app.addEventListener("validateDisplayListComplete", appProgressHandler);
app.addEventListener(FlexEvent.CREATION_COMPLETE, appCreationCompleteHandler);
// Cache for later cleanup.
this.app = app;
}
/**
* @private
* Return the number of bytes loaded and total for the SWF and any RSLs.
*/
private function getByteValues():Object
{
var li:LoaderInfo = root.loaderInfo;
var loaded:int = li.bytesLoaded;
var total:int = li.bytesTotal;
// Look up the rsl bytes and include those
var n:int = rslListLoader ? rslListLoader.getItemCount() : 0;
for (var i:int = 0; i < n; i++)
{
loaded += rslListLoader.getItem(i).loaded;
// If the rsl total is zero then provide an average rsl size
// to set rough expectations.
var rslTotal:int = rslListLoader.getItem(i).total;
total += rslTotal;
}
return { loaded: loaded, total: total };
}
/**
* @private
*/
private function dispatchAppEndEvent(event:Object = null):void
{
// Dispatch the application initialization end event
dispatchEvent(new FlexEvent(FlexEvent.INIT_COMPLETE));
if (!showDisplay)
displayClassCompleteHandler(null);
}
/**
* @private
* We don't listen for the events directly
* because we don't know which RSL is sending the event.
* So we have the RSLNode listen to the events
* and then pass them along to the Preloader.
*/
mx_internal function rslProgressHandler(event:ProgressEvent):void
{
var index:int = rslListLoader.getIndex();
var item:RSLItem = rslListLoader.getItem(index);
var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_PROGRESS);
rslEvent.isResourceModule = (rslListLoader == resourceModuleListLoader);
rslEvent.bytesLoaded = event.bytesLoaded;
rslEvent.bytesTotal = event.bytesTotal;
rslEvent.rslIndex = index;
rslEvent.rslTotal = rslListLoader.getItemCount();
rslEvent.url = item.urlRequest;
dispatchEvent(rslEvent);
}
/**
* @private
* Load the next RSL in the list and dispatch an event.
*/
mx_internal function rslCompleteHandler(event:Event):void
{
var index:int = rslListLoader.getIndex();
var item:RSLItem = rslListLoader.getItem(index);
var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_COMPLETE);
rslEvent.isResourceModule = (rslListLoader == resourceModuleListLoader);
rslEvent.bytesLoaded = item.total;
rslEvent.bytesTotal = item.total;
rslEvent.loaderInfo = event.target as LoaderInfo;
rslEvent.rslIndex = index;
rslEvent.rslTotal = rslListLoader.getItemCount();
rslEvent.url = item.urlRequest;
dispatchEvent(rslEvent);
if (loadingRSLs && resourceModuleListLoader && index + 1 == rslEvent.rslTotal)
{
loadingRSLs = false;
waitingToLoadResourceModules = true;
// timer will switch over to loading resource modules
}
rslDone = (index + 1 == rslEvent.rslTotal);
}
/**
* @private
*/
mx_internal function rslErrorHandler(event:ErrorEvent):void
{
// send an error event
var index:int = rslListLoader.getIndex();
var item:RSLItem = rslListLoader.getItem(index);
var rslEvent:RSLEvent = new RSLEvent(RSLEvent.RSL_ERROR);
rslEvent.isResourceModule = (rslListLoader == resourceModuleListLoader);
rslEvent.bytesLoaded = 0;
rslEvent.bytesTotal = 0;
rslEvent.rslIndex = index;
rslEvent.rslTotal = rslListLoader.getItemCount();
rslEvent.url = item.urlRequest;
rslEvent.errorText = decodeURI(event.text);
dispatchEvent(rslEvent);
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
* Listen or poll for progress events and dispatch events
* describing the current state of the download
*/
private function timerHandler(event:TimerEvent):void
{
// loaded swfs may not have root right away
if (!root)
return;
var bytes:Object = getByteValues();
var loaded:int = bytes.loaded;
var total:int = bytes.total;
// Dispatch a progress event (later we might conditionalize this
// so that it isn't sent on a cache load).
dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS,
false, false, loaded, total));
if (waitingToLoadResourceModules)
{
if (applicationDomain.hasDefinition("mx.resources::ResourceManager"))
{
waitingToLoadResourceModules = false;
rslListLoader = resourceModuleListLoader;
rslDone = false;
// Start loading the resourceModules
rslListLoader.load(rslProgressHandler,
rslCompleteHandler,
rslErrorHandler,
rslErrorHandler,
rslErrorHandler);
}
}
// Check if we are finished
if (rslDone &&
((loaded >= total && total > 0) || (total == 0 && loaded > 0) || (root is MovieClip && (MovieClip(root).totalFrames > 2) && (MovieClip(root).framesLoaded >= 2)) ))
{
if (!sentDocFrameReady)
{
// If there's a displayClass, don't send the PRELOADER_DOC_FRAME_READY
// event before we render at least one frame
if (showDisplay && !waitedAFrame)
return;
sentDocFrameReady = true;
// Dispatch a Frame1 done event.
dispatchEvent(new FlexEvent(FlexEvent.PRELOADER_DOC_FRAME_READY));
return;
}
if (waitingToLoadResourceModules)
{
if (applicationDomain.hasDefinition("mx.resources::ResourceManager"))
{
waitingToLoadResourceModules = false;
rslListLoader = resourceModuleListLoader;
rslDone = false;
// Start loading the resourceModules
rslListLoader.load(rslProgressHandler,
rslCompleteHandler,
rslErrorHandler,
rslErrorHandler,
rslErrorHandler);
return;
}
}
if (resourceModuleListLoader)
{
var resourceManager:IResourceManager;
// do this to prevent dependency on ResourceManager
if (applicationDomain.hasDefinition("mx.resources::ResourceManager"))
{
var resourceManagerClass:Class =
Class(applicationDomain.getDefinition("mx.resources::ResourceManager"));
resourceManager =
IResourceManager(resourceManagerClass["getInstance"]());
}
// The FlashVars of the SWF's HTML wrapper,
// or the query parameters of the SWF URL,
// can specify the ResourceManager's localeChain.
var localeChainList:String =
SystemManagerGlobals.parameters["localeChain"];
if (localeChainList != null && localeChainList != "")
resourceManager.localeChain = localeChainList.split(",");
}
timer.removeEventListener(TimerEvent.TIMER, timerHandler);
// Stop the timer.
timer.reset();
// Dispatch a complete event.
dispatchEvent(new Event(Event.COMPLETE));
// Dispatch an initProgress event.
dispatchEvent(new FlexEvent(FlexEvent.INIT_PROGRESS));
}
}
/**
* @private
*/
private function ioErrorHandler(event:IOErrorEvent):void
{
// Ignore the event
}
/**
* @private
* Called when the displayClass has finished animating
* and no longer needs to be displayed.
*/
private function displayClassCompleteHandler(event:Event):void
{
// Cleanup
if (displayClass)
displayClass.removeEventListener(Event.COMPLETE, displayClassCompleteHandler);
if (root)
root.loaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
if (app)
{
app.removeEventListener("validatePropertiesComplete", appProgressHandler);
app.removeEventListener("validateSizeComplete", appProgressHandler);
app.removeEventListener("validateDisplayListComplete", appProgressHandler);
app.removeEventListener(FlexEvent.CREATION_COMPLETE, appCreationCompleteHandler);
app = null;
}
// Send an event to the SystemManager that we are completely finished
dispatchEvent(new FlexEvent(FlexEvent.PRELOADER_DONE));
}
/**
* @private
*/
private function appCreationCompleteHandler(event:FlexEvent):void
{
dispatchAppEndEvent();
}
/**
* @private
*/
private function appProgressHandler(event:Event):void
{
dispatchEvent(new FlexEvent(FlexEvent.INIT_PROGRESS));
}
/**
* @private
*/
private function waitAFrame(event:Event):void
{
CONFIG::performanceInstrumentation
{
mx.utils.PerfUtil.getInstance().markTime("Preloader.displayClass rendered");
}
this.removeEventListener(Event.ENTER_FRAME, waitAFrame);
waitedAFrame = true;
}
}
}