1 /** 2 * The Render Engine 3 * 4 * An extension to the engine for script loading and processing. 5 * 6 * @author: Brett Fattori (brettf@renderengine.com) 7 * 8 * @author: $Author$ 9 * @version: $Revision$ 10 * 11 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 12 * 13 * Permission is hereby granted, free of charge, to any person obtaining a copy 14 * of this software and associated documentation files (the "Software"), to deal 15 * in the Software without restriction, including without limitation the rights 16 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 * copies of the Software, and to permit persons to whom the Software is 18 * furnished to do so, subject to the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be included in 21 * all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 * THE SOFTWARE. 30 * 31 */ 32 33 //==================================================================================================== 34 //==================================================================================================== 35 // SCRIPT PROCESSING 36 //==================================================================================================== 37 //==================================================================================================== 38 /** 39 * @class A static class which is used to load new JavaScript into the browser. Methods are 40 * also provided to use AJAX to get text and JSON data on-the-fly, load stylesheets, 41 * and process script callbacks from a loader queue. 42 * @static 43 */ 44 R.engine.Script = Base.extend(/** @scope R.engine.Script.prototype */{ 45 46 constructor:null, 47 48 /* 49 * Script queue 50 */ 51 scriptQueue:[], 52 loadedScripts:{}, // Cache of loaded scripts 53 scriptLoadCount:0, // Number of queued scripts to load 54 scriptsProcessed:0, // Number of scripts processed 55 scriptRatio:0, // Ratio between processed/queued 56 queuePaused:false, // Script queue paused flag 57 pauseReps:0, // Queue run repetitions while paused 58 gameOptionsLoaded:false, // Whether the game options have loaded yet 59 gameOptionsObject:{}, // Options object for the game 60 uniqueRequest:true, 61 callbacks:{}, // Script callbacks 62 63 /** 64 * Status message when a script is not found 65 * @memberOf R.engine.Script 66 * @type {Boolean} 67 */ 68 SCRIPT_NOT_FOUND:false, 69 70 /** 71 * Status message when a script is successfully loaded 72 * @memberOf R.engine.Script 73 * @type {Boolean} 74 */ 75 SCRIPT_LOADED:true, 76 77 /** 78 * Include a script file. 79 * 80 * @param scriptURL {String} The URL of the script file 81 * @memberOf R.engine.Script 82 */ 83 include:function (scriptURL) { 84 R.engine.Script.loadNow(scriptURL); 85 }, 86 87 /** 88 * Perform an immediate load on the specified script. Objects within 89 * the script may not immediately initialize, unless their dependencies 90 * have been resolved. 91 * 92 * @param {String} scriptPath The path to the script to load 93 * @param {Function} [cb] The function to call when the script is loaded. 94 * the path of the script loaded and a status message 95 * will be passed as the two parameters. 96 * @memberOf R.engine.Script 97 * @private 98 */ 99 loadNow:function (scriptPath, cb) { 100 R.engine.Script.doLoad(R.Engine.getEnginePath() + scriptPath, scriptPath, cb); 101 }, 102 103 /** 104 * Queue a script to load from the server and append it to 105 * the head element of the browser. Script names are 106 * cached so they will not be loaded again. Each script in the 107 * queue is processed synchronously. 108 * 109 * @param scriptPath {String} The URL of a script to load. 110 * @memberOf R.engine.Script 111 */ 112 loadScript:function (scriptPath) { 113 // Put script into load queue 114 R.engine.Script.scriptQueue.push(scriptPath); 115 R.engine.Script.runScriptQueue(); 116 }, 117 118 /** 119 * Low-level method to call jQuery to use AJAX to load 120 * a file asynchronously. If a failure (such as a 404) occurs, 121 * it shouldn't fail silently. 122 * 123 * @param path {String} The url to load 124 * @param data {Object} Optional arguments to pass to server 125 * @param callback {Function} The callback method 126 * @memberOf R.engine.Script 127 */ 128 ajaxLoad:function (path, data, callback) { 129 /* pragma:DEBUG_START */ 130 // If we're in debug mode, force the browser to grab the latest 131 if (R.Engine.getDebugMode() && R.engine.Script.isMakeUnique()) { 132 path += (path.indexOf("?") == -1 ? "?" : "&") + "_debug=" + R.now(); 133 } 134 /* pragma:DEBUG_END */ 135 136 // Use our own internal method to load a file with the JSON 137 // data. This way, we don't fail silently when loading a file 138 // that doesn't exist. 139 var xhr = new XMLHttpRequest(); 140 xhr.open("GET", path, true); 141 xhr.onreadystatechange = function (evt) { 142 if (xhr.readyState == 4) { 143 callback(xhr, xhr.status); 144 } 145 }; 146 var rData = null; 147 if (data) { 148 rData = ""; 149 for (var i in data) { 150 rData += (rData.length == 0 ? "?" : "&") + i + "=" + encodeURIComponent(data[i]); 151 } 152 } 153 xhr.send(rData); 154 }, 155 156 /** 157 * Load text from the specified path. 158 * 159 * @param path {String} The url to load 160 * @param data {Object} Optional arguments to pass to server 161 * @param callback {Function} The callback method which is passed the 162 * text and status code (a number) of the request. 163 * @memberOf R.engine.Script 164 */ 165 loadText:function (path, data, callback) { 166 if (typeof data == "function") { 167 callback = data; 168 data = null; 169 } 170 R.engine.Script.ajaxLoad(path, data, function (xhr, result) { 171 callback(xhr.responseText, xhr.status); 172 }); 173 }, 174 175 /** 176 * Load text from the specified path and parse it as JSON. 177 * 178 * @param path {String} The url to load 179 * @param data {Object} Optional arguments to pass to server 180 * @param callback {Function} The callback method which is passed the 181 * JSON object and status code (a number) of the request. 182 * @memberOf R.engine.Script 183 */ 184 loadJSON:function (path, data, callback) { 185 if (typeof data == "function") { 186 callback = data; 187 data = null; 188 } 189 R.engine.Script.ajaxLoad(path, data, function (xhr, result) { 190 var json = null; 191 if (result != 404) { 192 try { 193 // Remove comments 194 json = R.engine.Support.parseJSON(xhr.responseText); 195 } catch (ex) { 196 R.debug.Console.error("Error parsing JSON at '" + path + "'"); 197 } 198 } 199 callback(json, xhr.status); 200 }); 201 }, 202 203 /** 204 * Internal method which runs the script queue to handle scripts and functions 205 * which are queued to run sequentially. 206 * @private 207 * @memberOf R.engine.Script 208 */ 209 runScriptQueue:function () { 210 if (!R.engine.Script.scriptQueueTimer) { 211 // Process any waiting scripts 212 R.engine.Script.scriptQueueTimer = setInterval(function () { 213 if (R.engine.Script.queuePaused) { 214 if (R.engine.Script.pauseReps++ > 500) { 215 // If after ~5 seconds the queue is still paused, unpause it and 216 // warn the user that the situation occurred 217 R.debug.Console.error("Script queue was paused for 5 seconds and not resumed -- restarting..."); 218 R.engine.Script.pauseReps = 0; 219 R.engine.Script.pauseQueue(false); 220 } 221 return; 222 } 223 224 R.engine.Script.pauseReps = 0; 225 226 if (R.engine.Script.scriptQueue.length > 0) { 227 R.engine.Script.processScriptQueue(); 228 } else { 229 // Stop the queue timer if there are no scripts 230 clearInterval(R.engine.Script.scriptQueueTimer); 231 R.engine.Script.scriptQueueTimer = null; 232 } 233 }, 10); 234 235 R.engine.Script.readyForNextScript = true; 236 } 237 }, 238 239 /** 240 * Put a callback into the script queue so that when a 241 * certain number of files has been loaded, we can call 242 * a method. Allows for functionality to start with 243 * incremental loading. 244 * 245 * @param cb {Function} A callback to execute 246 * @memberOf R.engine.Script 247 */ 248 setQueueCallback:function (cb) { 249 // Put callback into load queue 250 R.engine.Script.scriptQueue.push(cb); 251 R.engine.Script.runScriptQueue(); 252 }, 253 254 /** 255 * You can pause the queue from a callback function, then 256 * unpause it to continue processing queued scripts. This will 257 * allow you to wait for an event to occur before continuing to 258 * to load scripts. 259 * 260 * @param state {Boolean} <tt>true</tt> to put the queue processor 261 * in a paused state. 262 * @memberOf R.engine.Script 263 */ 264 pauseQueue:function (state) { 265 R.engine.Script.queuePaused = state; 266 }, 267 268 /** 269 * Process any scripts that are waiting to be loaded. 270 * @private 271 * @memberOf R.engine.Script 272 */ 273 processScriptQueue:function () { 274 if (R.engine.Script.scriptQueue.length > 0 && R.engine.Script.readyForNextScript) { 275 // Hold the queue until the script is loaded 276 R.engine.Script.readyForNextScript = false; 277 278 // Get next script... 279 var scriptPath = R.engine.Script.scriptQueue.shift(); 280 281 // If the queue element is a function, execute it and return 282 if (typeof scriptPath === "function") { 283 scriptPath(); 284 R.engine.Script.readyForNextScript = true; 285 return; 286 } 287 288 R.engine.Script.doLoad(scriptPath); 289 } 290 }, 291 292 /** 293 * This method performs the actual script loading. 294 * @private 295 * @memberOf R.engine.Script 296 */ 297 doLoad:function (scriptPath, simplePath, cb) { 298 if (!R.Engine.started) { 299 return; 300 } 301 302 var s = scriptPath.replace(/[\/\.]/g, "_"); 303 if (R.engine.Script.loadedScripts[s] == null) { 304 // Store the request in the cache 305 R.engine.Script.loadedScripts[s] = scriptPath; 306 307 R.engine.Script.scriptLoadCount++; 308 R.engine.Script.updateProgress(); 309 310 // If there's a callback for the script, store it 311 if (cb) { 312 R.debug.Console.log("Push callback for ", simplePath); 313 R.engine.Script.callbacks[simplePath] = cb; 314 } 315 316 /* pragma:DEBUG_START */ 317 // If we're in debug mode, force the browser to grab the latest 318 if (R.Engine.getDebugMode() && R.engine.Script.isMakeUnique()) { 319 scriptPath += (scriptPath.indexOf("?") == -1 ? "?" : "&") + "_debug=" + R.now(); 320 } 321 /* pragma:DEBUG_END */ 322 323 if (R.browser.Wii) { 324 325 $.get(scriptPath, function (data) { 326 327 // Parse script code for syntax errors 328 if (R.engine.Linker.parseSyntax(data)) { 329 var n = document.createElement("script"); 330 n.type = "text/javascript"; 331 $(n).text(data); 332 333 var h = document.getElementsByTagName("head")[0]; 334 h.appendChild(n); 335 R.engine.Script.readyForNextScript = true; 336 337 R.engine.Script.scriptLoadCount--; 338 R.engine.Script.updateProgress(); 339 R.debug.Console.debug("Loaded '" + scriptPath + "'"); 340 } 341 342 }, "text"); 343 } else { 344 345 // We'll use our own script loader so we can detect errors (i.e. missing files). 346 var n = document.createElement("script"); 347 n.src = scriptPath; 348 n.type = "text/javascript"; 349 350 // When the file is loaded 351 var scriptInfo = { 352 node:n, 353 fullPath:scriptPath, 354 simpPath:simplePath, 355 callback:cb 356 }, successCallback, errorCallback; 357 358 359 successCallback = R.bind(scriptInfo, function () { 360 if (!this.node.readyState || 361 this.node.readyState == "loaded" || 362 this.node.readyState == "complete") { 363 364 // If there was a callback, get it 365 var callBack = R.engine.Script.callbacks[this.simpPath]; 366 367 R.debug.Console.debug("Loaded '" + this.fullPath + "'"); 368 R.engine.Script.handleScriptDone(); 369 if ($.isFunction(callBack)) { 370 R.debug.Console.info("Callback for '" + this.fullPath + "'"); 371 callBack(this.simpPath, R.engine.Script.SCRIPT_LOADED); 372 373 // Delete the callback 374 delete R.engine.Script.callbacks[this.simpPath]; 375 } 376 377 if (!R.Engine.localMode) { 378 // Delete the script node 379 $(this.node).remove(); 380 } 381 } 382 R.engine.Script.readyForNextScript = true; 383 }); 384 385 // When an error occurs 386 errorCallback = R.bind(scriptInfo, function (msg) { 387 var callBack = this.callback; 388 R.debug.Console.error("File not found: ", this.fullPath); 389 if (callBack) { 390 callBack(this.simpPath, R.engine.Script.SCRIPT_NOT_FOUND); 391 } 392 R.engine.Script.readyForNextScript = true; 393 }); 394 395 if (R.browser.msie) { 396 n.defer = true; 397 n.onreadystatechange = successCallback; 398 n.onerror = errorCallback; 399 } else { 400 n.onload = successCallback; 401 n.onerror = errorCallback; 402 } 403 404 var h = document.getElementsByTagName("head")[0]; 405 h.appendChild(n); 406 } 407 408 } else { 409 // Already have this script 410 R.engine.Script.readyForNextScript = true; 411 } 412 }, 413 414 /** 415 * Loads a game's script. This will wait until the specified 416 * <tt>gameObjectName</tt> is available before running it. Doing so will 417 * ensure that all dependencies have been resolved before starting a game. 418 * Also creates the default rendering context for the engine. 419 * <p/> 420 * All games should execute this method to start their processing, rather than 421 * using the script loading mechanism for engine or game scripts. This is used 422 * for the main game script only. Normally it would appear in the game's "index" file. 423 * <pre> 424 * <script type="text/javascript"> 425 * // Load the game script 426 * Engine.loadGame('game.js','Spaceroids'); 427 * </script> 428 * </pre> 429 * <p/> 430 * The game can provide configuration files which will be loaded and passed to the 431 * game's <tt>setup()</tt> method. The name of the configuration file is the game 432 * as the game's main JavaScript file. If your JavaScript file is "game.js", the 433 * format for the config files are: 434 * <ul> 435 * <li><tt>game.config</tt> - General game configuration</li> 436 * <li><tt>game_[browser].config</tt> - Browser specific configuration</li> 437 * <li><tt>game_[browser]_[platform].config</tt> - Platform specific configuration</li> 438 * </ul> 439 * Examples: <tt>game_mobilesafari.config</tt>, <tt>game_mobilesafari_ipad.config</tt> 440 * 441 * @param gameSource {String} The URL of the game script. 442 * @param gameObjectName {String} The string name of the game object to execute. When 443 * the framework if ready, the <tt>startup()</tt> method of this 444 * object will be called. 445 * @param [gameDisplayName] {String} An optional string to display in the loading dialog 446 * @memberOf R.engine.Script 447 */ 448 loadGame:function (gameSource, gameObjectName/* , gameDisplayName */) { 449 if (!R.Engine.startup()) { 450 return; 451 } 452 453 var gameDisplayName = arguments[2] || gameObjectName; 454 455 $(document).ready(function () { 456 // Determine if the developer has provided a "loading" element of their own 457 if ($("span.loading").length == 0) { 458 // They haven't, so create one for them 459 $("head").append($(R.Engine.loadingCSS)); 460 461 var loadingDialog = "<span id='loading' class='intrinsic'><table border='0' style='width:100%;height:100%;'><tr>"; 462 loadingDialog += "<td style='width:100%;height:100%;' valign='middle' align='center'><div class='loadbox'>Loading "; 463 loadingDialog += gameDisplayName + "...<div id='engine-load-progress'></div><span id='engine-load-info'></span></div>"; 464 loadingDialog += "</td></tr></table></span>"; 465 466 $("body", document).append($(loadingDialog)); 467 } 468 }); 469 470 // We'll wait for the Engine to be ready before we load the game 471 // Load engine options for browsers 472 R.engine.Script.loadEngineOptions(); 473 474 // Load the config object for the game, if it exists 475 R.engine.Script.loadGameOptions(gameSource); 476 477 R.engine.Script.gameLoadTimer = setInterval(function () { 478 if (R.engine.Script.optionsLoaded && 479 R.engine.Script.gameOptionsLoaded && 480 R.rendercontexts.DocumentContext && 481 R.rendercontexts.DocumentContext.started) { 482 483 // Show the virtual D-pad if the option is on 484 R.engine.Support.showDPad(); 485 486 // Start the engine 487 R.Engine.run(); 488 489 // Stop the timer 490 clearInterval(R.engine.Script.gameLoadTimer); 491 R.engine.Script.gameLoadTimer = null; 492 493 // Load the game 494 R.debug.Console.debug("Loading '" + gameSource + "'"); 495 R.engine.Script.loadScript(gameSource); 496 497 // Start the game when it's ready 498 if (gameObjectName) { 499 R.engine.Script.gameRunTimer = setInterval(function () { 500 var gameObj = R.getClassForName(gameObjectName); 501 if (gameObj !== undefined && gameObj.setup) { 502 clearInterval(R.engine.Script.gameRunTimer); 503 504 R.debug.Console.warn("Starting: " + gameObjectName); 505 506 // Remove the "loading" message (if we provided it) 507 $("#loading.intrinsic").remove(); 508 509 // Store the game object when it's ready 510 R.Engine.$GAME = gameObj; 511 512 // Start the game 513 gameObj.setup(R.engine.Script.gameOptionsObject); 514 } 515 }, 100); 516 } 517 } 518 }, 2); 519 }, 520 521 /** 522 * Load the engine options object for the current browser and OS 523 * @memberOf R.engine.Script 524 * @private 525 */ 526 loadEngineOptions:function () { 527 // Load the specific config for the browser type 528 R.engine.Script.optionsLoaded = false; 529 530 // Load the options specific to the browser. Whether they load, or not, 531 // the game will continue to load. 532 R.engine.Script.loadJSON(R.Engine.getEnginePath() + "/configs/" + R.engine.Support.sysInfo().browser + ".config", function (bData, status) { 533 if (status == 200 || status == 304) { 534 R.debug.Console.debug("Engine options loaded for: " + R.engine.Support.sysInfo().browser); 535 R.Engine.setOptions(bData); 536 } else { 537 // Log an error (most likely a 404) 538 R.debug.Console.log("Engine options for: " + R.engine.Support.sysInfo().browser + " responded with " + status); 539 } 540 541 // Allow a game to override engine options 542 R.engine.Script.loadJSON("engine.config", function (bData, status) { 543 if (status == 200 || status == 304) { 544 R.debug.Console.debug("Engine option overrides loaded for game."); 545 R.Engine.options = $.extend(R.Engine.options, bData); 546 } 547 548 R.engine.Script.optionsLoaded = true; 549 }); 550 }); 551 }, 552 553 /** 554 * Load the the options object for the current game being loaded. 555 * @param gameSource {String} The game source file 556 * @memberOf R.engine.Script 557 * @private 558 */ 559 loadGameOptions:function (gameSource) { 560 var file = gameSource.split(".")[0]; 561 R.engine.Script.gameOptionsLoaded = false; 562 R.engine.Script.gameOptionsObject = {}; 563 564 // Attempt three loads for game options... First for the game in general, then 565 // for the browser, and finally for the browser and platform. The objects will be 566 // merged together and passed to the setup() method of the game. 567 R.engine.Script.loadJSON(file + ".config", function (bData, status) { 568 if (status == 200 || status == 304) { 569 R.debug.Console.debug("Game options loaded from '" + file + ".config'"); 570 R.engine.Script.gameOptionsObject = $.extend(R.engine.Script.gameOptionsObject, bData); 571 } 572 573 // Now try to load a browser specific object 574 file += "_" + R.engine.Support.sysInfo().browser; 575 R.engine.Script.loadJSON(file + ".config", function (bData, status) { 576 if (status == 200 || status == 304) { 577 R.debug.Console.debug("Browser specific game options loaded from '" + file + ".config'"); 578 R.engine.Script.gameOptionsObject = $.extend(R.engine.Script.gameOptionsObject, bData); 579 } 580 581 // Finally try to load a browser and platform specific object 582 file += "_" + R.engine.Support.sysInfo().platform.toLowerCase(); 583 R.engine.Script.loadJSON(file + ".config", function (bData, status) { 584 if (status == 200 || status == 304) { 585 R.debug.Console.debug("Platform specific game options loaded from '" + file + ".config'"); 586 R.engine.Script.gameOptionsObject = $.extend(R.engine.Script.gameOptionsObject, bData); 587 } 588 589 R.engine.Script.gameOptionsLoaded = true; 590 }); 591 }); 592 }); 593 }, 594 595 /** 596 * Load a script relative to the engine path. A simple helper method which calls 597 * {@link #loadScript} and prepends the engine path to the supplied script source. 598 * 599 * @param scriptSource {String} A URL to load that is relative to the engine path. 600 * @memberOf R.engine.Script 601 */ 602 load:function (scriptSource) { 603 R.engine.Script.loadScript(R.Engine.getEnginePath() + scriptSource); 604 }, 605 606 /** 607 * After a script has been loaded, updates the progress 608 * @private 609 * @memberOf R.engine.Script 610 */ 611 handleScriptDone:function () { 612 R.engine.Script.scriptsProcessed++; 613 R.engine.Script.scriptRatio = R.engine.Script.scriptsProcessed / R.engine.Script.scriptLoadCount; 614 R.engine.Script.scriptRatio = R.engine.Script.scriptRatio > 1 ? 1 : R.engine.Script.scriptRatio; 615 R.engine.Script.updateProgress(); 616 }, 617 618 /** 619 * Updates the progress bar (if available) 620 * @private 621 * @memberOf R.engine.Script 622 */ 623 updateProgress:function () { 624 var pBar = jQuery("#engine-load-progress"); 625 if (pBar.length > 0) { 626 // Update their progress bar 627 if (pBar.css("position") != "relative" || pBar.css("position") != "absolute") { 628 pBar.css("position", "relative"); 629 } 630 var pW = pBar.width(); 631 var fill = Math.floor(pW * R.engine.Script.scriptRatio); 632 var fBar = jQuery("#engine-load-progress .bar"); 633 if (fBar.length == 0) { 634 fBar = jQuery("<div class='bar' style='position: absolute; top: 0px; left: 0px; height: 100%;'></div>"); 635 pBar.append(fBar); 636 } 637 fBar.width(fill); 638 jQuery("#engine-load-info").text(R.engine.Script.scriptsProcessed + " of " + R.engine.Script.scriptLoadCount); 639 } 640 }, 641 642 /** 643 * Load a stylesheet and append it to the document. Allows for 644 * scripts to specify additional stylesheets that can be loaded 645 * as needed. Additionally, you can use thise method to inject 646 * the engine path into the css being loaded. Using the variable 647 * <tt>$<enginePath></tt>, you can load css relative to the 648 * engine's path. For example: 649 * <pre> 650 * .foo { 651 * background: url('$<enginePath>/myGame/images/bar.png') no-repeat 50% 50%; 652 * } 653 * </pre> 654 * 655 * @param stylesheetPath {String} Path to the stylesheet, relative to 656 * the engine path. 657 * @param relative {Boolean} Relative to the current path, or from the engine path 658 * @param noInject {Boolean} <code>true</code> to bypass engine path injection and use 659 * a <tt><link /> tag to load the styles instead. 660 * @memberOf R.engine.Script 661 */ 662 loadStylesheet:function (stylesheetPath, relative, noInject) { 663 stylesheetPath = (relative ? "" : R.Engine.getEnginePath()) + stylesheetPath; 664 665 /* pragma:DEBUG_START */ 666 // If we're in debug mode, force the browser to grab the latest 667 if (R.Engine.getDebugMode() && R.engine.Script.isMakeUnique()) { 668 stylesheetPath += (stylesheetPath.indexOf("?") == -1 ? "?" : "&") + "_debug=" + R.now(); 669 } 670 /* pragma:DEBUG_END */ 671 672 var f = function () { 673 if (noInject) { 674 $("head", document).append($("<link type='text/css' rel='stylesheet' href='" + stylesheetPath + "'/>")); 675 } else { 676 $.get(stylesheetPath, function (data) { 677 // process the data to replace the "enginePath" variable 678 var epRE = /(\$<enginePath>)/g; 679 data = data.replace(epRE, R.Engine.getEnginePath()); 680 if (R.engine.Support.sysInfo().browser == "msie") { 681 // IE likes it this way... 682 $("head", document).append($("<style type='text/css'>" + data + "</style>")); 683 } else { 684 $("head", document).append($("<style type='text/css'/>").text(data)); 685 } 686 R.debug.Console.debug("Stylesheet loaded '" + stylesheetPath + "'"); 687 }, "text"); 688 } 689 }; 690 691 R.engine.Script.setQueueCallback(f); 692 }, 693 694 /** 695 * Output the list of scripts loaded by the Engine to the console. 696 * @memberOf R.engine.Script 697 */ 698 dumpScripts:function () { 699 for (var f in this.loadedScripts) { 700 R.debug.Console.debug(R.engine.Script.loadedScripts[f]); 701 } 702 }, 703 704 /** 705 * Clears the script name cache. Allows scripts to be loaded 706 * again. Use this method with caution, as it is not recommended 707 * to load a script if the object is in use. May cause unexpected 708 * results. 709 * @memberOf R.engine.Script 710 */ 711 clearScriptCache:function () { 712 R.engine.Script.loadedScripts = {}; 713 }, 714 715 isMakeUnique:function () { 716 return R.engine.Script.uniqueRequest; 717 }, 718 719 setUniqueRequest:function (state) { 720 R.engine.Script.uniqueRequest = state; 721 } 722 723 }); 724