1 // ========================================================================== 2 // Project: SproutCore - JavaScript Application Framework 3 // Copyright: ©2006-2011 Strobe Inc. and contributors. 4 // Portions ©2008-2011 Apple Inc. All rights reserved. 5 // License: Licensed under MIT license (see license.js) 6 // ========================================================================== 7 8 /*globals jQuery */ 9 10 sc_require('tasks/task'); 11 12 //@if(debug) 13 SC.LOG_MODULE_LOADING = YES; 14 //@endif 15 16 /** 17 SC.Module is responsible for dynamically loading in JavaScript and other 18 resources. These packages of code and resources, called bundles, can be 19 loaded by your application once it has finished loading, allowing you to 20 reduce the time taken for it to launch. 21 22 You can explicitly load a module by calling SC.Module.loadModule(), or you 23 can mark a module as prefetched in your Buildfile. In those cases, 24 SproutCore will automatically start to load the bundle once the application 25 has loaded and the user has remained idle for more than one second. 26 */ 27 28 SC.Module = SC.Object.create(/** @scope SC.Module */ { 29 30 /** 31 Returns YES if the module is ready; NO if it is not loaded or its 32 dependencies have not yet loaded. 33 34 @param {String} moduleName the name of the module to check 35 @returns {Boolean} 36 */ 37 isModuleReady: function (moduleName) { 38 var moduleInfo = SC.MODULE_INFO[moduleName]; 39 return moduleInfo ? !!moduleInfo.isReady : NO; 40 }, 41 42 /** 43 Asynchronously loads a module if it is not already loaded. If you pass 44 a function, or a target and action, it will be called once the module 45 has finished loading. 46 47 If the module you request has dependencies (as specified in the Buildfile) 48 that are not yet loaded, it will load them first before executing the 49 requested module. 50 51 @param moduleName {String} 52 @param target {Function} 53 @param method {Function} 54 @returns {Boolean} YES if already loaded, NO otherwise 55 */ 56 loadModule: function (moduleName, target, method) { 57 var module = SC.MODULE_INFO[moduleName], 58 //@if(debug) 59 log = SC.LOG_MODULE_LOADING, 60 //@endif 61 args; 62 63 if (arguments.length > 3) { 64 // Fast arguments access. 65 // Accessing `arguments.length` is just a Number and doesn't materialize the `arguments` object, which is costly. 66 args = new Array(arguments.length - 3); // SC.A(arguments).slice(3) 67 for (var i = 0, len = args.length; i < len; i++) { args[i] = arguments[i + 3]; } 68 } else { 69 args = []; 70 } 71 72 // Treat the first parameter as the callback if the target is a function and there is 73 // no method supplied. 74 if (method === undefined && SC.typeOf(target) === SC.T_FUNCTION) { 75 method = target; 76 target = null; 77 } 78 79 //@if(debug) 80 if (log) SC.debug("SC.Module: Attempting to load '%@'", moduleName); 81 //@endif 82 83 // If we couldn't find anything in the SC.MODULE_INFO hash, we don't have any record of the 84 // requested module. 85 if (!module) { 86 throw new Error("SC.Module: could not find module '%@'".fmt(moduleName)); 87 } 88 89 // If this module was in the middle of being prefetched, we now need to 90 // execute it immediately when it loads. 91 module.isPrefetching = NO; 92 93 // If the module is already loaded, execute the callback immediately if SproutCore is loaded, 94 // or else as soon as SC has finished loading. 95 if (module.isLoaded && !module.isWaitingForRunLoop) { 96 //@if(debug) 97 if (log) SC.debug("SC.Module: Module '%@' already loaded.", moduleName); 98 //@endif 99 100 // we can't just eval it if its dependencies have not been met... 101 if (!this._dependenciesMetForModule(moduleName)) { 102 // we can't let it return normally here, because we need the module to wait until the end of the run loop. 103 // This is because the module may set up bindings. 104 this._addCallbackForModule(moduleName, target, method, args); 105 106 this._loadDependenciesForModule(moduleName); 107 108 return NO; 109 } 110 111 // If the module has finished loading and we have the string 112 // representation, try to evaluate it now. 113 if (module.source) { 114 //@if(debug) 115 if (log) SC.debug("SC.Module: Evaluating JavaScript for module '%@'.", moduleName); 116 //@endif 117 this._evaluateStringLoadedModule(module); 118 119 // we can't let it return normally here, because we need the module to wait until the end of the run loop. 120 // This is because the module may set up bindings. 121 this._addCallbackForModule(moduleName, target, method, args); 122 123 this.invokeLast(function () { 124 module.isReady = YES; 125 this._moduleDidBecomeReady(moduleName); 126 }); 127 128 return NO; 129 } 130 131 if (method) { 132 if (SC.isReady) { 133 SC.Module._invokeCallback(moduleName, target, method, args); 134 } else { 135 // Queue callback for when SC has finished loading. 136 SC.ready(SC.Module, function () { 137 SC.Module._invokeCallback(moduleName, target, method, args); 138 }); 139 } 140 } 141 142 return YES; 143 } 144 145 // The module has loaded, but is waiting for the end of the run loop before it is "ready"; 146 // we just need to add the callback. 147 else if (module.isWaitingForRunLoop) { 148 this._addCallbackForModule(moduleName, target, method, args); 149 } 150 // The module is not yet loaded, so register the callback and, if necessary, begin loading 151 // the code. 152 else { 153 154 //@if(debug) 155 if (log) SC.debug("SC.Module: Module '%@' is not loaded, loading now.", moduleName); 156 //@endif 157 158 // If this method is called more than once for the same module before it is finished 159 // loading, we might have multiple callbacks that need to be executed once it loads. 160 this._addCallbackForModule(moduleName, target, method, args); 161 162 // If this is the first time the module has been requested, determine its dependencies 163 // and begin loading them as well as the JavaScript for this module. 164 if (!module.isLoading) { 165 this._loadDependenciesForModule(moduleName); 166 this._loadCSSForModule(moduleName); 167 this._loadJavaScriptForModule(moduleName); 168 module.isLoading = YES; 169 } 170 171 return NO; 172 } 173 }, 174 175 _addCallbackForModule: function (moduleName, target, method, args) { 176 var module = SC.MODULE_INFO[moduleName]; 177 178 // Retrieve array of callbacks from MODULE_INFO hash. 179 var callbacks = module.callbacks || []; 180 181 if (method) { 182 callbacks.push(function () { 183 SC.Module._invokeCallback(moduleName, target, method, args); 184 }); 185 } 186 187 module.callbacks = callbacks; 188 }, 189 190 /** 191 @private 192 193 Loads a module in string form. If you prefetch a module, its source will 194 be held as a string in memory until SC.Module.loadModule() is called, at 195 which time its JavaScript will be evaluated. 196 197 You shouldn't call this method directly; instead, mark modules as 198 prefetched in your Buildfile. SproutCore will automatically prefetch those 199 modules once your application has loaded and the user is idle. 200 201 @param {String} moduleName the name of the module to prefetch 202 */ 203 prefetchModule: function (moduleName) { 204 var module = SC.MODULE_INFO[moduleName]; 205 206 if (module.isLoading || module.isLoaded) return; 207 208 if (SC.LOG_MODULE_LOADING) SC.debug("SC.Module: Prefetching module '%@'.", moduleName); 209 this._loadDependenciesForModule(moduleName); 210 this._loadCSSForModule(moduleName); 211 this._loadJavaScriptForModule(moduleName); 212 module.isLoading = YES; 213 module.isPrefetching = YES; 214 }, 215 216 // .......................................................... 217 // INTERNAL SUPPORT 218 // 219 220 /** @private 221 If a module is marked for lazy instantiation, this method will execute the closure and call 222 any registered callbacks. 223 */ 224 _executeLazilyInstantiatedModule: function (moduleName, targetName, methodName) { 225 var lazyInfo = SC.LAZY_INSTANTIATION[moduleName]; 226 var target; 227 var method; 228 var idx, len; 229 230 if (SC.LOG_MODULE_LOADING) { 231 SC.debug("SC.Module: Module '%@' is marked for lazy instantiation, instantiating it now…", moduleName); 232 } 233 234 len = lazyInfo.length; 235 for (idx = 0; idx < len; idx++) { 236 // Iterate through each function associated with this module, and attempt to execute it. 237 try { 238 lazyInfo[idx](); 239 } catch (e) { 240 SC.Logger.error("SC.Module: Failed to lazily instatiate entry for '%@'".fmt(moduleName)); 241 } 242 } 243 244 // Free up memory containing the functions once they have been executed. 245 delete SC.LAZY_INSTANTIATION[moduleName]; 246 247 // Now that we have executed the functions, try to find the target and action for the callback. 248 target = this._targetForTargetName(targetName); 249 method = this._methodForMethodNameInTarget(methodName, target); 250 251 if (!method) { 252 throw new Error("SC.Module: could not find callback for lazily instantiated module '%@'".fmt(moduleName)); 253 } 254 }, 255 256 /** 257 Evaluates a module's JavaScript if it is stored in string format, then 258 deletes that code from memory. 259 260 @param {Hash} module the module to evaluate 261 */ 262 _evaluateStringLoadedModule: function (module) { 263 var moduleSource = module.source; 264 265 // so, force a run loop. 266 jQuery.globalEval(moduleSource); 267 268 delete module.source; 269 270 if (module.cssSource) { 271 var el = document.createElement('style'); 272 el.setAttribute('type', 'text/css'); 273 if (el.styleSheet) { 274 el.styleSheet.cssText = module.cssSource; 275 } else { 276 var content = document.createTextNode(module.cssSource); 277 el.appendChild(content); 278 } 279 280 document.getElementsByTagName('head')[0].appendChild(el); 281 } 282 283 module.isReady = YES; 284 }, 285 286 /** 287 @private 288 289 Creates <link> tags for every CSS resource in a module. 290 291 @param {String} moduleName the name of the module whose CSS should be loaded 292 */ 293 _loadCSSForModule: function (moduleName) { 294 var head = document.getElementsByTagName('head')[0]; 295 var module = SC.MODULE_INFO[moduleName]; 296 var styles = module.styles || []; 297 var len = styles.length; 298 var url; 299 var el; 300 var idx; 301 302 if (!head) head = document.documentElement; // fix for Opera 303 len = styles.length; 304 305 for (idx = 0; idx < len; idx++) { 306 url = styles[idx]; 307 308 if (url.length > 0) { 309 if (SC.LOG_MODULE_LOADING) SC.debug("SC.Module: Loading CSS file in '%@' -> '%@'", moduleName, url); 310 311 // if we are on a retina device lets load the retina style sheet instead 312 if (window.devicePixelRatio > 1 || window.location.search.indexOf("2x") > -1) { 313 url = url.replace('.css', '@2x.css'); 314 } 315 316 el = document.createElement('link'); 317 el.setAttribute('href', url); 318 el.setAttribute('rel', "stylesheet"); 319 el.setAttribute('type', "text/css"); 320 head.appendChild(el); 321 } 322 } 323 324 el = null; 325 }, 326 327 _loadJavaScriptForModule: function (moduleName) { 328 var module = SC.MODULE_INFO[moduleName]; 329 var el; 330 var url; 331 var dependencies = module.dependencies; 332 var dependenciesAreLoaded = YES; 333 334 // If this module has dependencies, determine if they are loaded. 335 if (dependencies && dependencies.length > 0) { 336 dependenciesAreLoaded = this._dependenciesMetForModule(moduleName); 337 } 338 339 // If the module is prefetched, always load the string representation. 340 if (module.isPrefetched) { 341 url = module.stringURL; 342 } else { 343 if (dependenciesAreLoaded) { 344 // Either we have no dependencies or they've all loaded already, 345 // so just execute the code immediately once it loads. 346 url = module.scriptURL; 347 } else { 348 // Because the dependencies might load after this module, load the 349 // string representation so we can execute it once all dependencies 350 // are in place. 351 url = module.stringURL; 352 } 353 } 354 355 if (url.length > 0) { 356 if (SC.LOG_MODULE_LOADING) SC.debug("SC.Module: Loading JavaScript file in '%@' -> '%@'", moduleName, url); 357 358 el = document.createElement('script'); 359 el.setAttribute('type', "text/javascript"); 360 el.setAttribute('src', url); 361 362 if (el.addEventListener) { 363 el.addEventListener('load', function () { 364 SC.run(function () { 365 SC.Module._moduleDidLoad(moduleName); 366 }); 367 }, false); 368 } else if (el.readyState) { 369 el.onreadystatechange = function () { 370 if (this.readyState === 'complete' || this.readyState === 'loaded') { 371 SC.run(function () { 372 SC.Module._moduleDidLoad(moduleName); 373 }); 374 } 375 }; 376 } 377 378 document.body.appendChild(el); 379 } 380 }, 381 382 /** 383 @private 384 385 Returns YES if all of the dependencies for a module are ready. 386 387 @param {String} moduleName the name of the module being checked 388 @returns {Boolean} whether the dependencies are loaded 389 */ 390 _dependenciesMetForModule: function (moduleName) { 391 var dependencies = SC.MODULE_INFO[moduleName].dependencies || []; 392 var idx, len = dependencies.length; 393 var dependencyName; 394 var module; 395 396 for (idx = 0; idx < len; idx++) { 397 dependencyName = dependencies[idx]; 398 module = SC.MODULE_INFO[dependencyName]; 399 400 if (!module) throw new Error("SC.loadModule: Unable to find dependency %@ for module %@.".fmt(dependencyName, moduleName)); 401 402 if (!module.isReady) { 403 return NO; 404 } 405 } 406 407 return YES; 408 }, 409 410 /** 411 Loads all unloaded dependencies for a module, then creates the <script> and <link> tags to 412 load the JavaScript and CSS for the module. 413 */ 414 _loadDependenciesForModule: function (moduleName) { 415 // Load module's dependencies first. 416 var moduleInfo = SC.MODULE_INFO[moduleName]; 417 418 //@if(debug) 419 var log = SC.LOG_MODULE_LOADING; 420 //@endif 421 var dependencies = moduleInfo.dependencies || []; 422 var dependenciesMet = YES; 423 var len = dependencies.length; 424 var idx; 425 var requiredModuleName; 426 var requiredModule; 427 var dependents; 428 429 for (idx = 0; idx < len; idx++) { 430 requiredModuleName = dependencies[idx]; 431 requiredModule = SC.MODULE_INFO[requiredModuleName]; 432 433 // Try to find dependent module in MODULE_INFO 434 if (!requiredModule) { 435 throw new Error("SC.Module: could not find required module '%@' for module '%@'".fmt(requiredModuleName, moduleName)); 436 } else { 437 438 // Required module has been requested but hasn't loaded yet. 439 if (requiredModule.isLoading) { 440 dependenciesMet = NO; 441 442 dependents = requiredModule.dependents; 443 if (!dependents) requiredModule.dependents = dependents = []; 444 dependents.push(moduleName); 445 } 446 447 // Required module has already been loaded and evaluated, no need to worry about it. 448 else if (requiredModule.isReady) { 449 continue; 450 } 451 // Required module has not been loaded nor requested yet. 452 else { 453 dependenciesMet = NO; 454 455 // Register this as a dependent module (used by SC._moduleDidLoad()...) 456 dependents = requiredModule.dependents; 457 if (!dependents) requiredModule.dependents = dependents = []; 458 459 dependents.push(moduleName); 460 461 //@if(debug) 462 if (log) SC.debug("SC.Module: '%@' depends on '%@', loading dependency…", moduleName, requiredModuleName); 463 //@endif 464 465 // Load dependencies 466 SC.Module.loadModule(requiredModuleName); 467 } 468 } 469 } 470 }, 471 472 /** 473 @private 474 475 Calls an action on a target to notify the target that a module has loaded. 476 */ 477 _invokeCallback: function (moduleName, targetName, methodName, args) { 478 var method; 479 var target; 480 481 target = this._targetForTargetName(targetName); 482 method = this._methodForMethodNameInTarget(methodName, target); 483 484 // If we weren't able to find the callback, this module may be lazily instantiated and 485 // the callback won't exist until we execute the closure that it is wrapped in. 486 if (!method) { 487 if (SC.LAZY_INSTANTIATION[moduleName]) { 488 this._executeLazilyInstantiatedModule(moduleName, targetName, methodName); 489 490 target = this._targetForTargetName(targetName); 491 method = this._methodForMethodNameInTarget(methodName, target); 492 } else { 493 throw new Error("SC.Module: could not find callback for '%@'".fmt(moduleName)); 494 } 495 } 496 497 if (!args) { 498 args = []; 499 } 500 501 // The first parameter passed to the callback is the name of the module. 502 args.unshift(moduleName); 503 504 // Invoke the callback. Wrap it in a run loop if we are not in a runloop already. 505 var needsRunLoop = !!SC.RunLoop.currentRunLoop; 506 if (needsRunLoop) { 507 SC.run(function () { 508 method.apply(target, args); 509 }); 510 } else { 511 method.apply(target, args); 512 } 513 }, 514 515 /** @private 516 Given a module name, iterates through all registered callbacks and calls them. 517 */ 518 _invokeCallbacksForModule: function (moduleName) { 519 var moduleInfo = SC.MODULE_INFO[moduleName], callbacks; 520 if (!moduleInfo) return; // shouldn't happen, but recover anyway 521 522 //@if(debug) 523 if (SC.LOG_MODULE_LOADING) SC.debug("SC.Module: Module '%@' has completed loading, invoking callbacks.", moduleName); 524 //@endif 525 526 callbacks = moduleInfo.callbacks || []; 527 528 for (var idx = 0, len = callbacks.length; idx < len; ++idx) { 529 callbacks[idx](); 530 } 531 }, 532 533 _evaluateAndInvokeCallbacks: function (moduleName) { 534 var moduleInfo = SC.MODULE_INFO; 535 var module = moduleInfo[moduleName]; 536 //@if(debug) 537 var log = SC.LOG_MODULE_LOADING; 538 539 if (log) SC.debug("SC.Module: Evaluating and invoking callbacks for '%@'.", moduleName); 540 //@endif 541 542 if (module.source) { 543 this._evaluateStringLoadedModule(module); 544 } 545 546 // this is ugly, but a module evaluated late like this won't be done instantiating 547 // until the end of a run loop. Also, the code here is not structured in a way that makes 548 // it easy to "add a step" before saying a module is ready. And finally, invokeLater doesn't 549 // accept arguments; hence, the closure. 550 module.isWaitingForRunLoop = YES; 551 this.invokeLast(function () { 552 module.isReady = YES; 553 this._moduleDidBecomeReady(moduleName); 554 }); 555 }, 556 557 _moduleDidBecomeReady: function (moduleName) { 558 var moduleInfo = SC.MODULE_INFO; 559 var module = moduleInfo[moduleName]; 560 //@if(debug) 561 var log = SC.LOG_MODULE_LOADING; 562 //@endif 563 564 module.isWaitingForRunLoop = NO; 565 566 if (SC.isReady) { 567 SC.Module._invokeCallbacksForModule(moduleName); 568 delete module.callbacks; 569 } else { 570 SC.ready(SC, function () { 571 SC.Module._invokeCallbacksForModule(moduleName); 572 delete module.callbacks; 573 }); 574 } 575 576 // for each dependent module, try and load them again... 577 var dependents = module.dependents || []; 578 var dependentName, dependent; 579 580 for (var idx = 0, len = dependents.length; idx < len; idx++) { 581 dependentName = dependents[idx]; 582 dependent = moduleInfo[dependentName]; 583 if (dependent.isLoaded && this._dependenciesMetForModule(dependentName)) { 584 //@if(debug) 585 if (log) SC.debug("SC.Module: Now that %@ has loaded, all dependencies for a dependent %@ are met.", moduleName, dependentName); 586 //@endif 587 this._evaluateAndInvokeCallbacks(dependentName); 588 } 589 } 590 591 }, 592 593 /** @private 594 Called when the JavaScript for a module finishes loading. 595 596 Any pending callbacks are called (if SC.isReady), and any dependent 597 modules which were waiting for this module to load are notified so they 598 can continue loading. 599 600 @param moduleName {String} the name of the module that just loaded 601 */ 602 _moduleDidLoad: function (moduleName) { 603 var module = SC.MODULE_INFO[moduleName]; 604 //@if(debug) 605 var log = SC.LOG_MODULE_LOADING; 606 //@endif 607 var dependenciesMet; 608 609 //@if(debug) 610 if (log) SC.debug("SC.Module: Module '%@' finished loading.", moduleName); 611 //@endif 612 613 if (!module) { 614 //@if(debug) 615 if (log) SC.debug("SC._moduleDidLoad() called for unknown module '@'.", moduleName); 616 //@endif 617 module = SC.MODULE_INFO[moduleName] = { isLoaded: YES, isReady: YES }; 618 return; 619 } 620 621 if (module.isLoaded) { 622 //@if(debug) 623 if (log) SC.debug("SC._moduleDidLoad() called more than once for module '%@'. Skipping.", moduleName); 624 //@endif 625 return; 626 } 627 628 // Remember that we're loaded. 629 delete module.isLoading; 630 module.isLoaded = YES; 631 632 if (!module.isPrefetching) { 633 dependenciesMet = this._dependenciesMetForModule(moduleName); 634 if (dependenciesMet) { 635 this._evaluateAndInvokeCallbacks(moduleName); 636 } else { 637 //@if(debug) 638 if (log) SC.debug("SC.Module: Dependencies for '%@' not met yet, waiting to evaluate.", moduleName); 639 //@endif 640 } 641 } else { 642 delete module.isPrefetching; 643 //@if(debug) 644 if (log) SC.debug("SC.Module: Module '%@' was prefetched, not evaluating until needed.", moduleName); 645 //@endif 646 } 647 }, 648 649 /** 650 @private 651 652 If necessary, converts a property path into a target object. 653 654 @param {String|Object} targetName the string or object representing the target 655 @returns Object 656 */ 657 _targetForTargetName: function (targetName) { 658 if (SC.typeOf(targetName) === SC.T_STRING) { 659 return SC.objectForPropertyPath(targetName); 660 } 661 662 return targetName; 663 }, 664 665 /** 666 @private 667 668 If necessary, converts a property path into a method object. 669 670 @param {String|Object} methodName the string or object representing the method 671 @param {Object} target the target from which to retrieve the method 672 @returns Object 673 */ 674 _methodForMethodNameInTarget: function (methodName, target) { 675 if (SC.typeOf(methodName) === SC.T_STRING) { 676 return SC.objectForPropertyPath(methodName, target); 677 } 678 679 return methodName; 680 }, 681 682 /** 683 A list of the methods to temporarily disable (and buffer calls for) when we are suspended. 684 */ 685 methodsForSuspend: "loadModule _moduleDidLoad prefetchModule _moduleDidBecomeReady".w(), 686 687 /** 688 Call this in order to prevent expensive tasks from occurring at inopportune times. 689 */ 690 suspend: function () { 691 692 //Increment the suspension count, to support nested suspend()/resume() pairs. 693 //We only do anything if the suspend count ends up at 1, as that implies it's 694 //the first suspend() call. 695 this._suspendCount = (this._suspendCount || 0) + 1; 696 if (this._suspendCount !== 1) return; 697 698 //Yummy variables. 699 var methods = this.get('methodsForSuspend'), 700 replaceKey, saveKey, key, i; 701 702 //Now we go through the list of methods to suspend, and overwrite them with 703 //versions that will buffer their calls in a _bufferedCalls array. 704 for (i = 0; (key = methods[i]); i++) { 705 //jshint loopfunc: true 706 //Ensure the replacement function exists at a key where it'll be cached. 707 if (!this[replaceKey = "__replacement_" + key + "__"]) { 708 (this[replaceKey] = function () { 709 (this._bufferedCalls || (this._bufferedCalls = [])).push({ 710 method: arguments.callee.methodName, 711 arguments: arguments 712 }); 713 }).methodName = key; 714 } 715 716 //Ensure the original function exists at a key where it'll be cached. 717 if (!this[saveKey = "__saved_" + key + "__"]) this[saveKey] = this[key]; 718 719 //Ensure that the replacement function exists where the rest of the 720 //code expects the original. 721 this[key] = this[replaceKey]; 722 } 723 }, 724 725 /** 726 Call this in order to resume normal behavior of the methods here, and to 727 finally perform any calls that may have occurred during suspension. Calls 728 will run in the order they were received. 729 */ 730 resume: function () { 731 732 //First, we need to decrement the suspension count, and warn if the suspension 733 //count implied that we weren't already suspended. Furthermore, if the suspend 734 //count is not zero, then we haven't tackled the last suspend() call with a resume(), 735 //and should therefore not resume. 736 this._suspendCount = (this._suspendCount || 0) - 1; 737 if (this._suspendCount < 0) { 738 SC.warn("SC.Module.resume() was called without SC.Module having been in a suspended state. Call aborted."); 739 this._suspendCount = 0; 740 return; 741 } 742 if (this._suspendCount > 0) return; 743 744 //Yummy variables. 745 var methods = this.get('methodsForSuspend'), 746 calls = this._bufferedCalls, 747 key, i, call; 748 749 //Restore the original methods to where they belong for normal functionality. 750 for (i = 0; (key = methods[i]); i++) this[key] = this["__saved_" + key + "__"]; 751 752 //Perform any buffered calls that built up during the suspended period. 753 for (i = 0; (call = calls) && calls[i]; i++) this[call.method].apply(this, call.arguments); 754 755 //Clear the list of calls, so subsequent resume() calls won't flush them again. 756 if (calls) calls.length = 0; 757 } 758 }); 759 760 /** 761 Inspect the list of modules and, for every prefetched module, create a 762 background task to load the module when the user remains idle. 763 */ 764 SC.ready(function () { 765 var moduleInfo = SC.MODULE_INFO; 766 var moduleName; 767 var module; 768 var task; 769 770 // Iterate through all known modules and look for those that are marked 771 // as prefetched. 772 for (moduleName in moduleInfo) { 773 module = moduleInfo[moduleName]; 774 775 if (module.isPrefetched) { 776 // Create a task that will load the module, and then register it with 777 // the global background task queue. 778 task = SC.Module.PrefetchModuleTask.create({ prefetchedModuleName: moduleName }); 779 SC.backgroundTaskQueue.push(task); 780 } 781 } 782 }); 783 784 SC.Module.PrefetchModuleTask = SC.Task.extend({ 785 prefetchedModuleName: null, 786 run: function () { 787 SC.Module.prefetchModule(this.prefetchedModuleName); 788 } 789 }); 790