File:AssetManager.js

/**
 * @module Core
 * @namespace springroll
 */
(function(undefined)
{
	var AssetLoad = include('springroll.AssetLoad'),
		AssetCache = include('springroll.AssetCache'),
		AssetSizes = include('springroll.AssetSizes'),
		Task = include('springroll.Task'),
		Debug;

	/**
	 * Handle the asynchronous loading of multiple assets.
	 * @class AssetManager
	 * @constructor
	 */
	var AssetManager = function()
	{
		if (DEBUG)
		{
			Debug = include('springroll.Debug', false);
		}

		/**
		 * The collection of current multiloads
		 * @property {Array} loads
		 * @private
		 */
		this.loads = [];

		/**
		 * The expired loads to reuse.
		 * @property {Array} loadPool
		 * @private
		 */
		this.loadPool = [];

		/**
		 * The collection of task definitions
		 * @property {Array} taskDefs
		 * @readOnly
		 */
		this.taskDefs = [];

		/**
		 * The cache of assets
		 * @property {springroll.AssetCache} cache
		 * @readOnly
		 */
		this.cache = new AssetCache();

		/**
		 * Handle multiple asset sizes. Defaults are 'full' at a scale of 1, and
		 * 'half' at a scale of 0.5 (under 400 points).
		 * @property {springroll.AssetSizes} sizes
		 * @readOnly
		 */
		this.sizes = new AssetSizes();

		/**
		 * The default asset type
		 * @property {String} defaultType
		 * @readOnly
		 */
		this.defaultType = null;

		// Add the default built-in sizes for "half" and "full"
		this.sizes.define('half', 400, 0.5, ['full']);
		this.sizes.define('full', 10000, 1, ['half']);
	};

	// reference to prototype
	var p = extend(AssetManager);

	/**
	 * Register new tasks types, these tasks must extend Task
	 * @method register
	 * @private
	 * @param {Function|String} TaskClass The class task reference
	 * @param {int} [priority=0] The priority. Higher priority tasks
	 *      are tested first. More general tasks should be lower
	 *      and more specific tasks should be higher.
	 */
	p.register = function(TaskClass, priority)
	{
		if (typeof TaskClass == "string")
		{
			TaskClass = include(TaskClass, false);
		}

		if (!TaskClass) return;

		TaskClass.priority = priority || 0;

		if (DEBUG && Debug)
		{
			if (!(TaskClass.prototype instanceof Task))
			{
				Debug.error("Registering task much extend Task", TaskClass);
			}
			else if (!TaskClass.test)
			{
				Debug.error("Registering task much have test method");
			}
		}
		this.taskDefs.push(TaskClass);

		// Sort definitions by priority
		// where the higher priorities are first
		this.taskDefs.sort(function(a, b)
		{
			return b.priority - a.priority;
		});
	};

	/**
	 * Load a bunch of assets, can only call one load at a time
	 * @method load
	 * @param {Object|Array} asset The assets to load
	 * @param {Object} [options] The loading options
	 * @param {function} [options.complete] The callback when finished
	 * @param {function} [options.progress] The callback when loading percentage is updated
	 * @param {function} [options.taskDone] The callback when finished with each individual task
	 * @param {Boolean} [options.autoStart=true] If we should start running right away
	 * @param {Boolean} [options.startAll=true] If we should run all the tasks at once, in parallel
	 * @param {Boolean} [options.cacheAll=false] If we should cache all files
	 * @param {String} [options.type] The type of assets to load, defaults to AssetManager.prototype.defaultType
	 * @return {springroll.AssetLoad} The reference to the current load
	 */
	p.load = function(assets, options)
	{
		// Apply defaults to options
		options = Object.merge(
		{
			complete: null,
			progress: null,
			taskDone: null,
			cacheAll: false,
			startAll: true,
			autoStart: true,
			type: this.defaultType
		}, options);

		var load = this.getLoad();

		// Add to the stack of current loads
		this.loads.push(load);

		// Override the complete callback with a bind of the
		// original callback with the task
		options.complete = this._onLoaded.bind(
			this,
			options.complete,
			load
		);

		// Handle the finish
		load.once('complete', options.complete);

		// Optional loaded amount event
		if (options.progress)
			load.on('progress', options.progress);

		// Called when a task is complete
		if (options.taskDone)
			load.on('taskDone', options.taskDone);

		// Start the load
		load.setup(assets, options);

		return load;
	};

	/**
	 * Stash the load for use later
	 * @method poolLoad
	 * @private
	 * @param {springroll.AssetLoad} load The load to recycle
	 */
	p.poolLoad = function(load)
	{
		load.off('complete progress taskDone');
		load.reset();
		this.loadPool.push(load);
	};

	/**
	 * Get either a new AssetLoad or a recycled one
	 * @method getLoad
	 * @private
	 * @return {springroll.AssetLoad} The load to use
	 */
	p.getLoad = function()
	{
		if (this.loadPool.length > 0)
		{
			return this.loadPool.pop();
		}
		return new AssetLoad(this);
	};

	/**
	 * Handler when a load is finished
	 * @method _onLoaded
	 * @private
	 * @param {function} complete The function to call when done
	 * @param {springroll.AssetLoad} load The current load
	 * @param {*} The returned results
	 */
	p._onLoaded = function(complete, load, results)
	{
		var index = this.loads.indexOf(load);
		if (index > -1)
		{
			this.loads.splice(index, 1);
		}
		if (complete) complete(results);
		this.poolLoad(load);
	};

	/**
	 * Destroy the AssetManager
	 * @method destroy
	 */
	p.destroy = function()
	{
		this.sizes.destroy();
		this.sizes = null;

		this.cache.destroy();
		this.cache = null;

		this.loadPool = null;
		this.loads = null;
		this.taskDefs = null;
	};

	// Assign to namespace
	namespace('springroll').AssetManager = AssetManager;

}());