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 }