API Docs for: 1.4.0
Show:

File: src\file\Loader.ts

/**
* 
* @module Kiwi
* @submodule Files 
* 
*/

module Kiwi.Files {

	/**
	* Used for the loading of files and game assets. This usually happens when a State is at the 'loading' stage (executing the 'preload' method).
	* 
	* @class Loader
	* @namespace Kiwi.Files
	* @constructor
	* @param game {Kiwi.Game} The game that this loader belongs to.
	* @return {Kiwi.Files.Loader} This Object
	*
	*/
	export class Loader {
 
		constructor(game: Kiwi.Game) {

			this.game = game;

		}

		/**
		* The type of object this is.
		* @method objType
		* @return {String} "Loader"
		* @public
		*/
		public objType() {
			return "Loader";
		}

		/**
		* The game this loader is attached to.
		* @property game 
		* @type Kiwi.Game
		* @public
		*/
		public game: Kiwi.Game;

		/**
		* A list of files that can be loaded in parallel to one another.
		* This list of files are currently being loaded. 
		* 
		* @property _loadingParallel
		* @type Array
		* @since 1.2.0
		* @private 
		*/
		private _loadingParallel: Kiwi.Files.File[];

		/**
		* List of files that cannot load in parallel to one another
		* and so need to wait for previous files to load first. 
		* Generally files loaded via XHR.
		* 
		* @property _loadingQueue
		* @type Array
		* @since 1.2.0
		* @private 
		*/
		private _loadingQueue: Kiwi.Files.File[];

		/**
		* List of files that are to be loaded. 
		* These files will be placed in the '_loadingQueue' or '_loadingParallel' 
		* lists when the queue is told to 'start' loading.
		* 
		* @property _loadingList
		* @type Array
		* @since 1.2.0
		* @private 
		*/
		private _loadingList: Kiwi.Files.File[];

		/**
		* A Signal which dispatches callbacks when all files in the 'loadingList' have been loaded.
		* When adding callbacks make sure to 'remove' them (or to use the 'addOnce' method) 
		* otherwise will fire when other sections use the loader.
		* 
		* @method onQueueComplete
		* @type Kiwi.Signal
		* @since 1.2.0
		* @public
		*/
		public onQueueComplete: Kiwi.Signal;

		/**
		* A Signal which dispatches callbacks each time a file in the 'loadingList' have been loaded.
		* Callbacks dispatched are passed the following arguments in order.
		* 1. percent - The percentage of files loaded. A number from 0 - 100
		* 2. bytesLoaded - The number of bytes loaded 
		* 3. file - The latest file that was loaded. First call will be null.
		* 
		* When adding callbacks make sure to 'remove' them (or to use the 'addOnce' method) 
		* otherwise will fire when other sections use the loader.
		* 
		* @method onQueueProgress
		* @type Kiwi.Signal
		* @since 1.2.0
		* @public
		*/
		public onQueueProgress: Kiwi.Signal;


		/**
		* A flag indicating if the files inside the file queue are loading or not.
		* 
		* @property _fileQueueLoading
		* @type Boolean
		* @default false
		* @since 1.2.0
		* @private
		*/
		private _queueLoading: boolean = false;

		/**
		* READ ONLY: A flag indicating if the files inside the file queue are loading or not.
		* 
		* @property fileQueueLoading
		* @type Boolean
		* @default false
		* @readOnly
		* @since 1.2.0
		* @public
		*/
		public get queueLoading(): boolean {
			return this._queueLoading;
		}


		/**
		* When 'calculateBytes' is true the percentLoaded will be the `bytesLoaded / bytesTotal`. 
		* Otherwise it is based on the `filesLoaded / numberOfFilesToLoad`. 
		*
		* @property percentLoaded
		* @type Number
		* @since 1.2.0
		* @readOnly
		* @public
		*/
		public percentLoaded: number = 0;


		/**
		* When enabled, files which can be loaded in parallel (those which are loaded via tags) 
		* will be loaded at the same time. 
		* 
		* The default behaviour is to have the files loading in a queued fashion instead of one after another.
		* 
		* @property enableParallelLoading
		* @type Boolean
		* @default false
		* @since 1.2.0
		* @public
		*/
		public enableParallelLoading: boolean = false; 


		/**
		* The boot method is executed when the DOM has successfully loaded and we can now start the game.
		* @method boot
		* @public
		*/
		public boot() {

			this._loadingList = [];

			this._loadingParallel = [];

			this._loadingQueue = [];

			this.onQueueComplete = new Kiwi.Signal();

			this.onQueueProgress = new Kiwi.Signal();

		}

		/**
		* Starts loading all the files which are in the file queue. 
		*
		* To accurately use the bytesLoaded or bytesTotal properties you will need to set the 'calculateBytes' boolean to true.
		* This may increase load times, as each file in the queue will firstly make XHR HEAD requests for information.
		*
		* When 'calculateBytes' is true the percentLoaded will be the `bytesLoaded / bytesTotal`. 
		* Otherwise it is based on the `filesLoaded / numberOfFilesToLoad`. 
		*
		* @method start
		* @param [calculateBytes] {Boolean} Setter for the 'calculateBytes' property.
		* @since 1.2.0
		* @public
		*/ 
		public start(calculateBytes: boolean = null) {

			if (calculateBytes !== null) {
				this._calculateBytes = calculateBytes;
			}

			if (this._queueLoading) {
				Kiwi.Log.warn('Kiwi.Files.Loader: Files in the queue are already being loaded');
				return;
			}

			//Reset the number of bytes laoded
			this._bytesLoaded = 0;
			this._bytesTotal = 0;
			this.percentLoaded = 0;

			if (this._calculateBytes) {
				this.calculateQueuedSize(this._startLoading, this);
			} else {
				this._startLoading();
			}

		}

		/**
		* Loops through the file queue and starts the loading process. 
		* 
		* @method _startLoading
		* @private 
		*/
		private _startLoading() {

			//Any files to load?
			if (this._loadingList.length <= 0) {
				Kiwi.Log.log('Kiwi.Files.Loader: No files are to load have been found.', '#loading');
				this.onQueueProgress.dispatch(100, 0, null);
				this.onQueueComplete.dispatch();
				return;
			}

			//There are files to load
			var i = 0,
				file: Kiwi.Files.File;

			while (i < this._loadingList.length) {

				if (this._calculateBytes) {
					this._loadingList[i].onProgress.add(this._updateFileListInformation, this);
				}

                this._sortFile(this._loadingList[i]);

                this._loadingList[i].onComplete.addOnce(this._fileQueueUpdate, this);

				i++;
			}

			this._queueLoading = true;
			this._bytesLoaded = 0;

			this._startLoadingQueue();
			this._startLoadingAllParallel();

			this._fileQueueUpdate(null, true);

		}

		/**
		* Adds a file to the queue of files to be loaded.
		* Files cannot be added whilst the queue is currently loading, 
		* the file to add is currently loading, or has been loaded before.
		* 
		* @method addFileToQueue
		* @param file {Kiwi.Files.File} The file to add.
		* @return {Boolean} If the file was added to the queue or not. 
		* @since 1.2.0
		* @public
		*/
		public addFileToQueue(file: Kiwi.Files.File) {

			if (this._queueLoading) {
				Kiwi.Log.warn('Kiwi.Files.Loader: File cannot be added to the queue whilst the queue is currently loading.', '#loading', '#filequeue');
				return false;
			}

			if (file.loading || file.complete) {
				Kiwi.Log.warn('Kiwi.Files.Loader: File could not be added as it is currently loading or has already loaded.', '#loading', '#filequeue');
				return false;
			}

			this._loadingList.push(file);
			return true;
		}

		/**
		* Removes a file from the file queue. 
		* Files cannot be removed whilst the queue is loading.
		*
		* @method removeFileFromQueue
		* @param file {Kiwi.Files.File} The file to remove.
		* @return {Boolean} If the file was added to the queue or not.
		* @since 1.2.0
		* @public
		*/
		public removeFileFromQueue(file: Kiwi.Files.File): boolean {

			if (this._queueLoading) {
				Kiwi.Log.warn('Kiwi.Files.Loader: File cannot be remove from the queue whilst the queue is currently loading.', '#loading', '#filequeue');
				return false;
			}

			var index = this._loadingList.indexOf(file);

			if (index === -1) {
				return false;
			} 

			if (file.loading) {
				Kiwi.Log.warn('Kiwi.Files.Loader: Cannot remove the file from the list as it is currently loading.', '#loading', '#filequeue');
				return false;
			}

			this._loadingList.splice(index, 1);
			return true;
		}

		/**
		* Clears the file queue of all files. 
		* 
		* @method clearQueue
		* @since 1.2.0
		* @public
		*/
		public clearQueue() {

			if (!this._queueLoading) {
				this._loadingList.length = 0;
			} else {
				Kiwi.Log.error('Kiwi.Files.Loader: Cannot clear the file queue whilst the files are being loaded.', '#loading', '#filequeue');
			}
		}

		/**
		* Starts the process of loading a file outside of the regular queue loading process.
		* Callbacks for load completion need to be added onto the file via 'onComplete' Signal.
		*
		* @method loadFile
		* @public
		*/
		public loadFile(file: Kiwi.Files.File) {

			if ( file.loading || file.complete ) {
				Kiwi.Log.error('Kiwi.Files.Loader: Could not add file. File is already loading or has completed loading.');
				return;
			}

			this._sortFile(file, true);

		}

		/**
		* Sorts a file and places it into either the 'loadingParallel' or 'loadingQueue' 
		* depending on the method of loading it is using.
		* 
		* @method _sortFile
		* @param file {Kiwi.Files.File}
		* @since 1.2.0
		* @private
		*/
		private _sortFile(file: Kiwi.Files.File, startLoading: boolean = false) {

			if (this.enableParallelLoading && file.loadInParallel) {
				//Push into the tag loader queue
				this._loadingParallel.push(file);

				if (startLoading) {
					this._startLoadingParallel(file);
				}

			} else {
				//Push into the xhr queue
				this._loadingQueue.push(file);

				if (startLoading) {
					this._startLoadingQueue();
				}
			}

		}

		/**
		* The number of files in the file queue that have been updated.
		* 
		* @property _completeFiles
		* @type number
		* @default 0
		* @private
		*/
		private _completedFiles: number = 0;

		/**
		* Called each time a file has processed whilst loading, or has just completed loading.
		* 
		* Calculates the new number of bytes loaded and 
		* the percentage of loading done by looping through all of the files.
		* 
		* @method _updateFileListInformation
		* @private
		*/
		private _updateFileListInformation() {

			var i = 0;

			this._completedFiles = 0;
			this._bytesLoaded = 0;

			while (i < this._loadingList.length) {

				//Was the file loaded, but we have no bytes (must have used the tag loader) and we have their details?
				if (this._loadingList[i].bytesLoaded === 0 && this._loadingList[i].success && this._loadingList[i].detailsReceived) {
					this._bytesLoaded += this._loadingList[i].size;

				} else {
					//Add the bytes loaded to the list 
					this._bytesLoaded += this._loadingList[i].bytesLoaded;
				}

				//Calculate percentage
				if (this._loadingList[i].complete) {
					this._completedFiles++;
				}

				i++;
			}

			//Calculate the percentage depending on how accurate we can be.
			if (this._calculateBytes) {
				this.percentLoaded = (this._bytesLoaded / this._bytesTotal) * 100;
			} else {
				this.percentLoaded = (this._completedFiles / this._loadingList.length) * 100;
			}

		}

		/**
		* Executed by files when they have successfully been loaded.
		* This method checks to see if the files are in the file queue, and dispatches the appropriate events.
		* 
		* @method _fileQueueUpdate
		* @param file {Kiwi.Files.File} The file which has been recently loaded.
		* @param [forceProgressCheck=false] {Boolean} If the progress of file loading should be checked, regardless of the file being in the queue or not.
		* @since 1.2.0
		* @private
		*/
		private _fileQueueUpdate(file: Kiwi.Files.File, forceProgressCheck: boolean = false) {

			//If the file loaded is in the loadingList
			if (!forceProgressCheck && this._loadingList.indexOf(file) === -1) {
				return;
			}

			//Update the file information.
			this._updateFileListInformation();

			//Dispatch progress event.
			this.onQueueProgress.dispatch(this.percentLoaded, this.bytesLoaded, file);

			if (this._completedFiles >= this._loadingList.length) {
				//Clear the file queue and dispatch the loaded event
				this._queueLoading = false;
				this.clearQueue();
				this.onQueueComplete.dispatch();
			}

		}

		/**
		* Starts the loading process in the loadingQueue. 
		* @method _startLoadingQueue
		* @return {Boolean}
		* @private
		* @since 1.2.0
		* @return {boolean} Whether the first file is loading
		*/
		private _startLoadingQueue(): boolean {

			//Any files to load?
			if (this._loadingQueue.length <= 0) {
				Kiwi.Log.log('Kiwi.Files.Loader: No queued files to load.', '#loading');
				return false;
			}

			//Is the first file currently loading?
			if (this._loadingQueue[0].loading) {
				return false;
			}

			//Attempt to load the file!
			this._loadingQueue[0].onComplete.addOnce(this._queueFileComplete, this, 1);
			this._loadingQueue[0].load();
			return true;
		}

		/**
		* Executed when a file in the 'loadingQueue' has been successfully loaded.
		* Removes the file from the loadingQueue and executes the '_startLoadingQueue' to start loading the next file. 
		* 
		* @method _queueFileComplete
		* @param file {Kiwi.Files.File}
		* @since 1.2.0
		* @private
		*/
		private _queueFileComplete(file: Kiwi.Files.File) {

			//Remove from the loadingQueue
			var index = this._loadingQueue.indexOf(file);
			if (index === -1) {
				Kiwi.Log.warn("Something has gone wrong? The file which executed this method doesn't exist in the loadingQueue.", '#loading', '#error');
				return;
			}

			this._loadingQueue.splice(index, 1);

			//Start loading the next file
			this._startLoadingQueue();
		}

		/**
		* Starts loading a file which can be loaded in parallel.
		* @method _startLoadingParallel
		* @param params file {Kiwi.Files.File}
		* @since 1.2.0
		* @private 
		*/
		private _startLoadingParallel( file: Kiwi.Files.File) {

			if (!file.loading) {
                file.onComplete.add(this._parallelFileComplete, this, 1);
				file.load();
			}

		}
		
		/**
		* Starts loading all files which can be loaded in parallel.
		* @method _startLoadingAllParallel
		* @since 1.2.0
		* @private 
		*/
		private _startLoadingAllParallel() {

			var i = this._loadingParallel.length,
				file: Kiwi.Files.File;

			while( i-- ) {
				this._startLoadingParallel( this._loadingParallel[ i ] );
			}
		}
		
		/**
		* Executed when a file in the 'loadingParallel' lsit has been successfully loaded.
		* Removes the file from the list and get the fileQueue to check its progress.
		* 
		* @method _parallelFileComplete
		* @param file {Kiwi.Files.File}
		* @since 1.2.0
		* @private 
		*/
		private _parallelFileComplete(file: Kiwi.Files.File) {

			var index = this._loadingParallel.indexOf(file);
			if (index === -1) {
				Kiwi.Log.warn("Something has gone wrong? The file which executed this method doesn't exist in the loadingParallel.", '#loading', '#error');
				return;
            }

			this._loadingParallel.splice(index, 1);
		}


		/**
		* -----------------------------
		* Bytes Loaded Methods 
		* -----------------------------
		**/
		
		/**
		* If the number of bytes for each file should be calculated before the queue starts loading.
		* If true each file in the queue makes a XHR HEAD request first to get the total values.
		*
		* @property _calculateBytes
		* @type Boolean
		* @private 
		*/
		private _calculateBytes: boolean = false;

		/**
		* Callback for when the total number of bytes of the files in the file list has been calculated.
		* 
		* @property onQueueSizeCalculate
		* @type any
		* @private
		*/
		private onSizeCallback: any;
		
		/**
		* Context that the onSizeCallback should be executed in.
		* 
		* @property onSizeContext
		* @type any
		* @private
		*/
		private onSizeContext: any;

		/**
		* The index of the current file in the filelist thats size is being retrieved.
		* @property _currentFileIndex
		* @type number
		* @private
		*/
		private _currentFileIndex: number = 0;

		/**
		* Total file size (in bytes) of all files in the queue to be loaded. 
		* 
		* @property _bytesTotal
		* @type Number
		* @private
		*/
		private _bytesTotal: number = 0;

		/**
		* READ ONLY: Returns the total number of bytes for the files in the file queue.
		* Only contains a value if you use the 'calculateBytes' and are loading files
		* OR if you use the 'calculateQueuedSize' method.
		* 
		* @property bytesTotal
		* @readOnly
		* @default 0
		* @since 1.2.0
		* @type Number
		* @public
		*/
		public get bytesTotal(): number {
			return this._bytesTotal;
		}

		/**
		* The number of bytes loaded of files in the file queue. 
		* 
		* @property _bytesLoaded
		* @type Number
		* @private 
		*/
		private _bytesLoaded: number = 0;

		/**
		* READ ONLY: Returns the total number of bytes for the files in the file queue.
		*
		* If you are using this make sure you set the 'calculateBytes' property to true OR execute the 'calculateQueuedSize' method. 
		* Otherwise files that are loaded via tags will not be accurate!
		* 
		* @property bytesLoaded
		* @readOnly
		* @default 0
		* @since 1.2.0
		* @type Number
		* @public
		*/
		public get bytesLoaded(): number {
			return this._bytesLoaded;
		}

		/**
		* Loops through the file queue and gets file information (filesize, ETag, filetype) for each.
		* 
		* To get accurate information about the bytesLoaded, bytesTotal, and the percentLoaded 
		* set the 'calculateBytes' property to true, as the loader will automatically execute this method before hand.
		* 
		* Can only be executed when the file queue is not currently loading.
		* 
		* @method calculateQueuedSize
		* @param callback {any}
		* @param [context=null] {any}
		* @public
		*/
		public calculateQueuedSize(callback: any, context: any = null) {

			//Is the queue currently loading files?
			if (this._queueLoading) {
				Kiwi.Log.warn('Kiwi.Files.Loader: Cannot calculate the size of the files in the filequeue whilst they are loading. ');
				return;
			}

			//Set the callbacks
			this.onSizeCallback = callback;
			this.onSizeContext = context;

			// Start the process
			this._currentFileIndex = 0;
			this._bytesTotal = 0;
			this._queueLoading = true;

			this._calculateNextFileSize();
		}

		/**
		* Checks to see if all the file sizes have been retrieved. 
		* If so completes the "calculateQueuedSize" call.
		* Otherwise requests the next file's details.
		*
		* @method _calculateNextFileSize
		* @private
		*/
		private _calculateNextFileSize() {

			if (this._currentFileIndex >= this._loadingList.length) {
				this._queueLoading = false;
				this.onSizeCallback.call(this.onSizeContext, this._bytesTotal);;
				return;
			}
			
			var file = this._loadingList[this._currentFileIndex];

			//Have we already got the details for this file?
			if (file.detailsReceived) {
				this._detailsReceived();
			} else {
				var details = file.loadDetails(this._detailsReceived, this);

				//Skip to the next file if the request could not be made.
				//Shouldn't happen.
				if (!details) {
					this._detailsReceived();
				}
			}

		}

		/**
		* Executed when by '_calculateNextFileSize' when the files information has been retrieved.
		* Adds its calculated size to the _bytesTotal and executes the 'nextFileSize' method.
		* 
		* @method _detailsReceived
		* @private 
		*/
		private _detailsReceived() {

			var file = this._loadingList[this._currentFileIndex];

			if (file.detailsReceived) {
				this._bytesTotal += file.size;
			}

			this._currentFileIndex++;
			this._calculateNextFileSize();

		}


		/**
		* -----------------------------
		* File Addition Methods
		* -----------------------------
		*/


		/**
		* Creates a new file for an image and adds a the file to loading queue. 
		* @method addImage
		* @param key {String} The key for the file.
		* @param url {String} The url of the image to load.
		* @param [width] {number} The width of the cell on the image to use once the image is loaded.
		* @param [height] {number} The height of the cell on the image to use once the image is loaded.
		* @param [offsetX] {number} An offset on the x axis of the cell.
		* @param [offsetY] {number} An offset of the y axis of the cell.
		* @param [storeAsGlobal=true] {boolean} If the image should be stored globally or not.
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addImage(key: string, url: string, width?: number, height?: number, offsetX?: number, offsetY?: number, storeAsGlobal: boolean = true): Kiwi.Files.File {

			var params:any = {
				type: Kiwi.Files.File.IMAGE,
				key: null,
				url: null,
				fileStore: this.game.fileStore,
				metadata: {}
			};

			if ( Kiwi.Utils.Common.isObject(key) ) {
				var p: any = key;

				params.key = p.key;
				params.url = p.url;
				params.metadata = {
					width: p.width,
					height: p.height,
					offsetX: p.offsetX,
					offsetY: p.offsetY
				};

				if (p.xhrLoading) params.xhrLoading = p.xhrLoading;//forces blob loading
				if (p.state) params.state = p.state;
				if (p.tags) params.tags = p.tags;

			} else {

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

				params.key = key;
				params.url = url;
				params.metadata = {
					width: width,
					height: height,
					offsetX: offsetX,
					offsetY: offsetY
				};

			}

			var file: Kiwi.Files.File = new Kiwi.Files.TextureFile(this.game, params);
			this.addFileToQueue(file);

			return file;
		}

		/**
		* Creates a new file for a spritesheet and adds the file to the loading queue.
		* @method addSpriteSheet
		* @param key {String} The key for the file.
		* @param url {String} The url of the image to load.
		* @param frameWidth {number} The width of a single cell in the spritesheet.
		* @param frameHeight {number} The height of a single cell in the spritesheet.
		* @param [numCells] {number} The number of cells that are in this spritesheet.
		* @param [rows] {number} The number of cells that are in a row.
		* @param [cols] {number} The number of cells that are in a column.
		* @param [sheetOffsetX] {number} The offset of the whole spritesheet on the x axis.
		* @param [sheetOffsetY] {number} The offset of the whole spritesheet on the y axis.
		* @param [cellOffsetX] {number} The spacing between each cell on the x axis.
		* @param [cellOffsetY] {number} The spacing between each cell on the y axis.
		* @param [storeAsGlobal=true] {boolean} 
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addSpriteSheet(key: string, url: string, frameWidth: number, frameHeight: number, numCells?: number, rows?: number, cols?: number, sheetOffsetX?: number, sheetOffsetY?: number, cellOffsetX?: number, cellOffsetY?: number, storeAsGlobal: boolean = true) {

			var params: any = {
				type: Kiwi.Files.File.SPRITE_SHEET,
				key: null,
				url: null,
				fileStore: this.game.fileStore,
				metadata: {}
			};


			if (Kiwi.Utils.Common.isObject(key)) {
				var p: any = key;

				params.key = p.key;
				params.url = p.url;
				params.metadata = {
					frameWidth: p.frameWidth,
					frameHeight: p.frameHeight,
					numCells: p.numCells,
					rows: p.rows,
					cols: p.cols,
					sheetOffsetX: p.sheetOffsetX,
					sheetOffsetY: p.sheetOffsetY,
					cellOffsetX: p.cellOffsetX,
					cellOffsetY: p.cellOffsetY
				};

				if (p.xhrLoading) params.xhrLoading = p.xhrLoading;//forces blob loading
				if (p.state) params.state = p.state;
				if (p.tags) params.tags = p.tags;

			} else {

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

				params.key = key;
				params.url = url;
				params.metadata = {
					frameWidth: frameWidth,
					frameHeight: frameHeight,
					numCells: numCells,
					rows: rows,
					cols: cols,
					sheetOffsetX: sheetOffsetX,
					sheetOffsetY: sheetOffsetY,
					cellOffsetX: cellOffsetX,
					cellOffsetY: cellOffsetY
				};

			}


			var file = new Kiwi.Files.TextureFile(this.game, params);
			this.addFileToQueue(file);

			return file;
		}

		/**
		* Creates new file's for loading a texture atlas and adds those files to the loading queue.
		* @method addTextureAtlas
		* @param key {String} The key for the image file.
		* @param imageUrl {String} The url of the image to load.
		* @param jsonID {String} A key for the JSON file.
		* @param jsonURL {String} The url of the json file to load.
		* @param [storeAsGlobal=true] {Boolean} If hte files should be stored globally or not.
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addTextureAtlas(key: string, imageURL: string, jsonID: string, jsonURL: string, storeAsGlobal: boolean = true) {

			var textureParams: any = {
				type: Kiwi.Files.File.TEXTURE_ATLAS,
				key: key,
				url: imageURL,
				fileStore: this.game.fileStore,
				metadata: {
					jsonID: jsonID
				}
			};
			var jsonParams: any = {
				type: Kiwi.Files.File.JSON,
				key: jsonID,
				url: jsonURL,
				fileStore: this.game.fileStore,
				metadata: {
					imageID: key
				}
			};

			if (!storeAsGlobal && this.game.states.current) {
				textureParams.state = this.game.states.current;
				jsonParams.state = this.game.states.current;
			}

			if (Kiwi.Utils.Common.isObject(key)) {
				var p: any = key;

				textureParams.key = p.textureAtlasKey;
				textureParams.url = p.textureAtlasURL;
				jsonParams.key = p.jsonKey;
				jsonParams.url = p.jsonURL;

				textureParams.metadata.jsonID = jsonParams.key;
				jsonParams.metadata.imageID = textureParams.key;

				if (p.state) {
					textureParams.state = p.state;
					jsonParams.state = p.state;
				}

				if (p.xhrLoading) textureParams.xhrLoading = p.xhrLoading; //forces blob loading
				if (p.tags) {
					jsonParams.tags = p.tags;
					textureParams.tags = p.tags;
				}

			}

			var imageFile = new Kiwi.Files.TextureFile(this.game, textureParams);
			var jsonFile = new Kiwi.Files.DataFile(this.game, jsonParams);

			this.addFileToQueue(imageFile);
			this.addFileToQueue(jsonFile);

			return imageFile;
		}

		/**
		* Creates a new File to store a audio piece. 
		* This method firstly checks to see if the AUDIO file being loaded is supported or not by the browser/device before adding it to the loading queue.
		* You can override this behaviour and tell the audio data to load even if not supported by setting the 'onlyIfSupported' boolean to false.
		* Also you can now pass an array of filepaths, and the first audio filetype that is supported will be loaded.
		*
		* @method addAudio
		* @param key {String} The key for the audio file.
		* @param url {String} The url of the audio to load. You can pass an array of URLs, in which case the first supported audio filetype in the array will be loaded.
		* @param [storeAsGlobal=true] {Boolean} If the file should be stored globally.
		* @param [onlyIfSupported=true] {Boolean} If the audio file should only be loaded if Kiwi detects that the audio file could be played. 
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addAudio(key: string, url: any, storeAsGlobal: boolean = true, onlyIfSupported: boolean = true) {

			var params = {
				type: Kiwi.Files.File.AUDIO,
				key: null,
				url: null,
				state: null,
				fileStore: this.game.fileStore
			};

			if (Kiwi.Utils.Common.isObject(key)) {
				params = (<any>key);
				params.type = Kiwi.Files.File.AUDIO;
				params.fileStore = this.game.fileStore;

			} else {
				params.key = key;
				params.url = url;

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

			}

			var i = 0,
				urls, file;

			//If it is a string then try to load that file
			if (Kiwi.Utils.Common.isString(params.url)) {
				urls = [params.url];
			} else {
				urls = params.url;
			}

			while (i < urls.length) {

				params.url = urls[i]
				file = this._attemptToAddAudio(params, onlyIfSupported);
				if (file) {
					return file;
				}

				i++;
			}

			return null;
		}

		/**
		* This method firstly checks to see if the AUDIO file being loaded is supported or not by the browser/device before adding it to the loading queue.
		* Returns a boolean if the audio file was successfully added or not to the file directory.
		* @method _attemptToAddAudio
		* @param params {Object} 
		*   @param params.key {String} The key for the audio file.
		*   @param params.url {String} The url of the audio to load. 
		*   @param [params.state=true] {Kiwi.State} The state this file should be for.
		*   @param [params.fileStore] {Kiwi.Files.FileStore} 
		* @param [onlyIfSupported=true] {Boolean} If the audio file should only be loaded if Kiwi detects that the audio file could be played. 
		* @return {Kiwi.Files.File} The file which was created.
		* @private
		*/
		private _attemptToAddAudio(params:any, onlyIfSupported: boolean): Kiwi.Files.File {

			var file = new Kiwi.Files.AudioFile(this.game, params);
			var support = false;

			switch (file.extension) {
				case 'mp3':
					support = Kiwi.DEVICE.mp3;
					break;

				case 'ogg':
				case 'oga':
					support = Kiwi.DEVICE.ogg;
					break;

				case 'm4a':
					support = Kiwi.DEVICE.m4a;
					break;

				case 'wav':
				case 'wave':
					support = Kiwi.DEVICE.wav;
					break;
			}

			if (support == true || onlyIfSupported == false) {
				this.addFileToQueue(file);
				return file;
			} else {
				Kiwi.Log.error('Kiwi.Loader: Audio Format not supported on this Device/Browser.', '#audio', '#unsupported');
				return null;
			}

		}

		/**
		* Creates a new File to store JSON and adds it to the loading queue.
		* @method addJSON
		* @param key {String} The key for the file.
		* @param url {String} The url to the json file.
		* @param [storeAsGlobal=true] {Boolean} If the file should be stored globally.
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addJSON(key: string, url: string, storeAsGlobal: boolean = true) {

			var params: any = {
				type: Kiwi.Files.File.JSON,
				key: key, 
				url: url,
				fileStore: this.game.fileStore
			};

			if (Kiwi.Utils.Common.isObject(key)) {
				var p: any = key;

				params.key = p.key;
				params.url = p.url;

				if (p.parse) params.parse = p.parse;
				if (p.state) params.state = p.state;
				if (p.tags) params.tags = p.tags;

			} else {

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

			}

			var file = new Kiwi.Files.DataFile(this.game, params);
			this.addFileToQueue(file);
			return file;

		}

		/**
		* Creates a new File to store XML and adds it to the loading queue.
		* @method addXML
		* @param key {String} The key for the file.
		* @param url {String} The url to the xml file.
		* @param [storeAsGlobal=true] {Boolean} If the file should be stored globally.
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addXML(key: string, url: string, storeAsGlobal: boolean = true) {
			
			var params:any = {
				type: Kiwi.Files.File.XML,
				key: key,
				url: url,
				fileStore: this.game.fileStore
			};

			if (Kiwi.Utils.Common.isObject(key)) {
				var p: any = key;

				params.key = p.key;
				params.url = p.url;

				if (p.parse) params.parse = p.parse;
				if (p.state) params.state = p.state;
				if (p.tags) params.tags = p.tags;

			} else {

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

			}

			var file = new Kiwi.Files.DataFile(this.game, params);
			this.addFileToQueue(file);
			return file;

		}

		/**
		* Creates a new File for a Binary file and adds it to the loading queue.
		* @method addBinaryFile
		* @param key {String} The key for the file.
		* @param url {String} The url to the Binary file.
		* @param [storeAsGlobal=true] {Boolean} If the file should be stored globally.
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addBinaryFile(key: string, url: string, storeAsGlobal: boolean = true) {

			var params: any = {
				type: Kiwi.Files.File.BINARY_DATA,
				key: key,
				url: url,
				fileStore: this.game.fileStore
			};

			if (Kiwi.Utils.Common.isObject(key)) {
				var p: any = key;

				params.key = p.key;
				params.url = p.url;

				if (p.parse) params.parse = p.parse;
				if (p.state) params.state = p.state;
				if (p.tags) params.tags = p.tags;

			} else {

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

			}

			var file = new Kiwi.Files.DataFile(this.game, params);
			this.addFileToQueue(file);
			return file;

		}

		/**
		* Creates a new File to store a text file and adds it to the loading queue.
		* @method addTextFile
		* @param key {String} The key for the file.
		* @param url {String} The url to the text file.
		* @param [storeAsGlobal=true] {Boolean} If the file should be stored globally.
		* @return {Kiwi.Files.File} The file which was created.
		* @public
		*/
		public addTextFile(key: string, url: string, storeAsGlobal: boolean = true) {

			var params: any = {
				type: Kiwi.Files.File.TEXT_DATA,
				key: key,
				url: url,
				fileStore: this.game.fileStore
			};

			if (Kiwi.Utils.Common.isObject(key)) {
				var p: any = key;

				params.key = p.key;
				params.url = p.url;

				if (p.parse) params.parse = p.parse;
				if (p.state) params.state = p.state;
				if (p.tags) params.tags = p.tags;

			} else {

				if (!storeAsGlobal && this.game.states.current) {
					params.state = this.game.states.current;
				}

			}

			var file = new Kiwi.Files.DataFile(this.game, params);
			this.addFileToQueue(file);
			return file;

		}

		/**
		* Flags this loader for garbage collection. Only use this method if you are SURE you will no longer need it. 
		* Otherwise it is best to leave it alone.
		* 
		* @method destroy
		* @public
		*/
		public destroy() {

			this.onQueueComplete.dispose();
			this.onQueueProgress.dispose();

			delete this.game;

			delete this.onQueueComplete;
			delete this.onQueueProgress;

			var i = 0;
			while (i < this._loadingList.length) {
				this._loadingList[i].destroy();
				i++;
			}
			this._loadingList = [];

			var i = 0;
			while (i < this._loadingQueue.length) {
				this._loadingQueue[i].destroy();
				i++;
			}
			this._loadingQueue = [];

			var i = 0;
			while (i < this._loadingParallel.length) {
				this._loadingParallel[i].destroy();
				i++;
			}
			this._loadingParallel = [];


		}

		
		/**
		* -----------------------
		* Deprecated - Functionality exists. Maps to its equalvent
		* -----------------------
		**/


		/**
		* Initialise the properities that are needed on this loader.
		* Recommended you use the 'onQueueProgress' / 'onQueueComplete' signals instead.
		* 
		* @method init
		* @param [progress=null] {Any} Progress callback method.
		* @param [complete=null] {Any} Complete callback method.
		* @param [calculateBytes=false] {boolean} 
		* @deprecated Deprecated as of 1.2.0
		* @public
		*/
		public init(progress: any = null, complete: any = null, calculateBytes: boolean=null) {

			if (calculateBytes !== null) {
				this._calculateBytes = calculateBytes;
			} 

			if (progress !== null) {
				this.onQueueProgress.addOnce( progress );
			}

			if (complete !== null) {
				this.onQueueComplete.addOnce( complete );
			}

		}

		/**
		* Loops through all of the files that need to be loaded and start the load event on them. 
		* @method startLoad
		* @deprecated Use 'start' instead. Deprecated as of 1.2.0
		* @public
		*/
		public startLoad() {
			this.start();
		}

		/**
		* Returns a percentage of the amount that has been loaded so far.
		* @method getPercentLoaded
		* @return {Number}
		* @deprecated Use 'percentLoaded' instead. Deprecated as of 1.2.0
		* @public
		*/
		public getPercentLoaded(): number {
			return this.percentLoaded;
		}


		/**
		* Returns a boolean indicating if everything in the loading que has been loaded or not.
		* @method complete
		* @return {boolean}
		* @deprecated Use 'percentLoaded' instead. Deprecated as of 1.2.0
		* @public
		*/
		public complete(): boolean {
			return ( this.percentLoaded === 100 );
		}

		/**
		* Quick way of getting / setting the private variable 'calculateBytes'
		* To be made into a public variable once removed.
		* @method calculateBytes
		* @param [value] {boolean}
		* @return {boolean}
		* @public
		*/
		public calculateBytes(value?: boolean): boolean {

			if (typeof value !== "undefined") {
				this._calculateBytes = value;
			}

			return this._calculateBytes;
		}

		/**
		* Returns the total number of bytes that have been loaded so far from files in the file queue.
		* 
		* @method getBytesLoaded
		* @return {Number}
		* @readOnly
		* @deprecated Use 'bytesLoaded' instead. Deprecated as of 1.2.0
		* @public
		*/
		public getBytesLoaded(): number {

			return this.bytesLoaded;
		}

	}

}