1 // TODO: NEEDS FIXING!! //
  2 
  3 /**
  4  * The Render Engine
  5  * MultiResourceLoader
  6  *
  7  * @fileoverview A resource loader which can load resources of differing types.
  8  *
  9  * @author: Brett Fattori (brettf@renderengine.com)
 10  * @author: $Author: bfattori $
 11  * @version: $Revision: 1555 $
 12  *
 13  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 14  *
 15  * Permission is hereby granted, free of charge, to any person obtaining a copy
 16  * of this software and associated documentation files (the "Software"), to deal
 17  * in the Software without restriction, including without limitation the rights
 18  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 19  * copies of the Software, and to permit persons to whom the Software is
 20  * furnished to do so, subject to the following conditions:
 21  *
 22  * The above copyright notice and this permission notice shall be included in
 23  * all copies or substantial portions of the Software.
 24  *
 25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 28  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 30  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 31  * THE SOFTWARE.
 32  *
 33  */
 34 
 35 // The class this file defines and its required classes
 36 R.Engine.define({
 37     "class":"R.resources.loaders.MultiResourceLoader",
 38     "requires":[
 39         "R.resources.loaders.AbstractResourceLoader",
 40         "R.lang.OneShotTimeout"
 41     ]
 42 });
 43 
 44 /**
 45  * @class A resource loader which can load files of varying types from the
 46  *          server.
 47  *
 48  * @constructor
 49  * @param name {String=RemoteLoader} The name of the resource loader
 50  * @extends R.resources.loaders.AbstractResourceLoader
 51  * @description Loads files from multiple resource loaders based on a type specifier.  Each type of
 52  *                  loader is registered with the multiresource loader.
 53  */
 54 R.resources.loaders.MultiResourceLoader = function () {
 55     return R.resources.loaders.AbstractResourceLoader.extend(/** @scope R.resources.loaders.MultiResourceLoader.prototype */{
 56 
 57         loaderMappings:null,
 58         deferments:null,
 59         deferTimer:null,
 60         loaders:null,
 61 
 62         /**
 63          * private
 64          */
 65         constructor:function (name) {
 66             this.base(name || "MultiResourceLoader");
 67 
 68             // Map the types to their file names
 69             this.loaderMappings = {
 70                 "bitmapfont":{
 71                     clazz:"R.resources.loaders.BitmapFontLoader",
 72                     extension:".font,.fnt",
 73                     instance:null
 74                 },
 75                 "image":{
 76                     clazz:"R.resources.loaders.ImageLoader",
 77                     extension:".png,.jpg,.gif",
 78                     instance:null
 79                 },
 80                 "level":{
 81                     clazz:"R.resources.loaders.LevelLoader",
 82                     extension:".level,.lvl",
 83                     instance:null
 84                 },
 85                 "object":{
 86                     clazz:"R.resources.loaders.ObjectLoader",
 87                     extension:".json,.js,.ui",
 88                     instance:null
 89                 },
 90                 "text":{
 91                     clazz:"R.resources.loaders.RemoteFileLoader",
 92                     extension:".text,.txt",
 93                     instance:null
 94                 },
 95                 "sound":{
 96                     clazz:"R.resources.loaders.SoundLoader",
 97                     extension:".mp3,.mp4,.wav,.ogg",
 98                     instance:null
 99                 },
100                 "sprite":{
101                     clazz:"R.resources.loaders.SpriteLoader",
102                     extension:".sprite,.spr",
103                     instance:null
104                 },
105                 "xml":{
106                     clazz:"R.resources.loaders.XMLLoader",
107                     extension:".xml",
108                     instance:null
109                 }
110             };
111 
112             this.deferments = [];
113             this.loaders = [];
114         },
115 
116         /**
117          * Register a new loader type, or override an existing one.
118          *
119          * @param type {String} The resource loader type identifier.  This is used when adding a loader or
120          *             loading a resource with the multiresource loader.
121          * @param className {String} The class name of the of the resource loader
122          * @param extension {String} A comma separated list of possible extensions which can
123          *        be used to automatically determine the type of loader to use.
124          */
125         registerLoader:function (type, className, extensions) {
126             AssertWarn((this.loaderMappings[type] == null), "Overriding loader mapped to '" + type + "' with '" + filename + "'");
127             this.loaderMappings[type] = {
128                 clazz:className,
129                 extension:extensions,
130                 instance:null
131             };
132         },
133 
134         /**
135          * Add a loader type which the multiresource loader will user.  Calling this method will
136          * dynamically load the resource loader's class file and prepare it for loading objects.
137          * Supported default loaders are:
138          * <ul><li>bitmapfont</li>
139          * <li>image</li>
140          * <li>level</li>
141          * <li>object</li>
142          * <li>text</li>
143          * <li>sound</li>
144          * <li>sprite</li>
145          * <li>xml</li></ul>
146          *
147          * @param loaderType {String} The resource loader type to add
148          */
149         addLoader:function (loaderType) {
150             Assert((this.loaderMappings[loaderType] != null), "The specified resource loader type is unsupported!");
151             var f = this.loaderMappings[loaderType].file;
152 
153             // Load the resource loader class and remember the loader type being used
154             R.Engine.requires("/resourceloaders/" + f);
155             this.loaders.push(loaderType);
156         },
157 
158         /**
159          * Add multiple resource loaders at one time by their loader names.
160          * @param loaderArray {Array} An array of loader types
161          */
162         addLoaders:function (loaderArray) {
163             for (var l in loaderArray) {
164                 this.addLoader(loaderArray[l]);
165             }
166         },
167 
168         /**
169          * Get the type of loader for the given resource name
170          * @param name {String} The resource name
171          */
172         getLoaderForName:function (name) {
173             // Determine the loader type by file extension
174             var loaderType = "object";
175             for (var lm in this.loaderMappings) {
176                 var loader = this.loaderMappings[lm];
177                 var exts = loader.extension.split(",");
178                 for (var e in exts) {
179                     if (name.indexOf("." + exts[e]) != -1) {
180                         loaderType = lm;
181                         break;
182                     }
183                 }
184             }
185             return loaderType;
186         },
187 
188         /**
189          * Remove a loader type which the multiresource loader uses.
190          * @param loaderType {String} The loader type to remove
191          */
192         removeLoader:function (loaderType) {
193             Assert((this.loaderMappings[loaderType] != null), "The specified resource loader type is unsupported!");
194             this.loaders = R.engine.Support.filter(this.loaders, function (obj) {
195                 return obj !== loaderType;
196             });
197         },
198 
199         /**
200          * Determine if the argument is a path by checking for either the
201          * forward slash, or the dot.
202          * @private
203          */
204         isPath:function (arg) {
205             return (arg.indexOf("/") != -1 || arg.indexOf(".") != -1);
206         },
207 
208         /**
209          * Load a resource with the registered loader type.  Resource loaders are
210          * determined by the extension of the URL being loaded. If the loader
211          * class isn't loaded yet, the request will be deferred until the class is available.
212          *
213          * @param url {String} The url to load
214          * @param [args...] {Object} The arguments for the loader
215          * @return {String} The name given to the resource
216          */
217         load:function (url /*, args */) {
218             var argArray = [];
219 
220             // The name is the URL after the last slash
221             var sl = url.lastIndexOf("/");
222             var name = url.substring(sl + 1);
223             argArray.push(name);
224 
225             for (var a = 0; a < arguments.length; a++) {
226                 argArray.push(arguments[a]);
227             }
228 
229             var loaderType = this.getLoaderForName(name);
230 
231             var c = this.loaderMappings[loaderType].clazz;
232             if (!this.checkForClass(c)) {
233                 this.defer(loaderType, argArray);
234             }
235             else {
236                 // The class is loaded, load the resource
237                 this.getLoader(loaderType).load.apply(window, argArray);
238             }
239 
240             return name;
241         },
242 
243         /**
244          * Check for the existence of the loader's class object.
245          * @param loaderClass {String} The class name
246          * @private
247          */
248         checkForClass:function (loaderClass) {
249             return (typeof window[loaderClass] !== "undefined");
250         },
251 
252         /**
253          * Defer the loading of a resource until the resource loader class becomes
254          * available.
255          *
256          * @param type {String} The type of loader
257          * @param args {Array} The arguments array to pass to the load() method
258          * @private
259          */
260         defer:function (type, args) {
261             if (this.deferTimer == null) {
262                 var self = this;
263                 this.deferTimer = Timeout.create("multidefer", 150, function () {
264                     var processed = [];
265                     if (self.deferments.length > 0) {
266                         // Process deferments
267                         for (var d in self.deferments) {
268                             var defer = self.deferments[d];
269                             if (self.checkForClass(self.loaderMappings[defer.loader].clazz)) {
270                                 // The class is loaded, process the deferment and mark processed
271                                 processed.push(defer);
272                                 self.getLoader(defer.loader).load.apply(window, defer.args);
273                             }
274                         }
275 
276                         // Clear out processed deferments
277                         self.deferments = R.engine.Support.filter(self.deferments, function (obj) {
278                             var found = false;
279                             for (var p in processed) {
280                                 if (processed[p] === obj) {
281                                     found = true;
282                                 }
283                             }
284                             return !found;
285                         });
286 
287                         if (self.deferments.length > 0) {
288                             // If there are still deferments pending, restart the timer
289                             this.restart();
290                             return;
291                         }
292                     }
293 
294                     // Destroy the timer
295                     this.destroy();
296                     self.deferTimer = null;
297                 });
298             }
299 
300             this.deferments.push({
301                 loader:type,
302                 args:args
303             });
304         },
305 
306         /**
307          * Checks all of the resource loaders to see if the specified resource is ready.  If the name
308          * is used by multiple loaders, you can optionally specify the loader type the resource should
309          * be checked against.  If no arguments are passed, all resource loaders are checked to see if
310          * all resources are loaded.
311          *
312          * @param resource {String} The name of the resource to check
313          * @param [name] {String} The optional name within the resource
314          */
315         isReady:function (resource, name) {
316             var loaderType = this.getLoaderForName(resource);
317 
318             // Check a specific loader
319             if (!this.checkForClass(this.loaderMappings[loaderType].clazz)) {
320                 // The loader isn't even loaded itself yet
321                 return false;
322             }
323 
324             // The class is loaded, check for the resource
325             return this.getLoader(loaderType).isReady(name);
326         },
327 
328         /**
329          * Unload a resource, by its name, from the resource loader type specified, or from all
330          * loaders where the name might be in use.
331          * @param name {String} The resource name to unload
332          * @param [loaderType] {String} Optionally, the name of the resource loader
333          */
334         unload:function (resource, name) {
335             var loaderType = this.getLoaderForName(resource);
336 
337             if (loaderType != null && this.checkForClass(this.loaderMappings[loaderType].clazz)) {
338                 this.getLoader(loaderType).unload(name);
339             }
340         },
341 
342         /**
343          * Get the cached object from the specified loader, by name.
344          *
345          * @param resource {String} The resource to get the object from
346          * @param name {String} The name of the object to get from the cache
347          * @return {Object}
348          */
349         get:function (resource, name) {
350             var loaderType = this.getLoaderForName(resource);
351             if (!this.checkForClass(this.loaderMappings[loader].clazz)) {
352                 return null;
353             }
354             else {
355                 return this.getLoader(loaderType).get(name);
356             }
357         },
358 
359         /**
360          * Gets the loader object by its given type, allowing you to call its methods
361          * directly.
362          *
363          * @param resource {String} The resource which will determine the loader
364          * @return {ResourceLoader}
365          */
366         getLoader:function (resource) {
367             var loaderType = this.getLoaderForName(resource);
368             var inst = this.loaderMappings[loaderType].instance;
369             if (inst == null) {
370                 inst = this.loaderMappings[loaderType].instance = window[this.loaderMappings[loaderType].clazz].create();
371             }
372             return inst;
373         },
374 
375         /**
376          * The name of the resource this loader will get.
377          * @returns {String} The string "multi"
378          */
379         getResourceType:function () {
380             return "multi";
381         }
382 
383     }, /** @scope R.resources.loaders.MultiResourceLoader.prototype */ {
384         /**
385          * Get the class name of this object.
386          * @return {String} The string "R.resources.loaders.MultiResourceLoader"
387          */
388         getClassName:function () {
389             return "R.resources.loaders.MultiResourceLoader";
390         }
391     });
392 
393 }