/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ /* * loader.js - runs before the hostenv_*.js file. Contains all of the package loading methods. */ //A semi-colon is at the start of the line because after doing a build, this function definition //get compressed onto the same line as the last line in bootstrap1.js. That list line is just a //curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it //here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using //the closure), and bootstrap1.js could change in the future. ;(function(){ //Additional properties for dojo.hostenv var _addHostEnv = { pkgFileName: "__package__", // for recursion protection loading_modules_: {}, loaded_modules_: {}, addedToLoadingCount: [], removedFromLoadingCount: [], inFlightCount: 0, // FIXME: it should be possible to pull module prefixes in from djConfig modulePrefixes_: { dojo: {name: "dojo", value: "src"} }, setModulePrefix: function(module, prefix){ this.modulePrefixes_[module] = {name: module, value: prefix}; }, getModulePrefix: function(module){ var mp = this.modulePrefixes_; if((mp[module])&&(mp[module]["name"])){ return mp[module].value; } return module; }, getTextStack: [], loadUriStack: [], loadedUris: [], //WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js post_load_: false, //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad. modulesLoadedListeners: [], unloadListeners: [], loadNotifying: false }; //Add all of these properties to dojo.hostenv for(var param in _addHostEnv){ dojo.hostenv[param] = _addHostEnv[param]; } })(); /** * Loads and interprets the script located at relpath, which is relative to the * script root directory. If the script is found but its interpretation causes * a runtime exception, that exception is not caught by us, so the caller will * see it. We return a true value if and only if the script is found. * * For now, we do not have an implementation of a true search path. We * consider only the single base script uri, as returned by getBaseScriptUri(). * * @param relpath A relative path to a script (no leading '/', and typically * ending in '.js'). * @param module A module whose existance to check for after loading a path. * Can be used to determine success or failure of the load. * @param cb a function to pass the result of evaluating the script (optional) */ dojo.hostenv.loadPath = function(relpath, module /*optional*/, cb /*optional*/){ var uri; if((relpath.charAt(0) == '/')||(relpath.match(/^\w+:/))){ // dojo.raise("relpath '" + relpath + "'; must be relative"); uri = relpath; }else{ uri = this.getBaseScriptUri() + relpath; } if(djConfig.cacheBust && dojo.render.html.capable){ uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,""); } try{ return ((!module) ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb)); }catch(e){ dojo.debug(e); return false; } } /** * Reads the contents of the URI, and evaluates the contents. * Returns true if it succeeded. Returns false if the URI reading failed. * Throws if the evaluation throws. * The result of the eval is not available to the caller TODO: now it is; was this a deliberate restriction? * * @param uri a uri which points at the script to be loaded * @param cb a function to process the result of evaluating the script as an expression (optional) */ dojo.hostenv.loadUri = function(uri, cb /*optional*/){ if(this.loadedUris[uri]){ return 1; } var contents = this.getText(uri, null, true); if(contents == null){ return 0; } this.loadedUris[uri] = true; if(cb){ contents = '('+contents+')'; } var value = dj_eval(contents); if(cb){ cb(value); } return 1; } // FIXME: probably need to add logging to this method dojo.hostenv.loadUriAndCheck = function(uri, module, cb){ var ok = true; try{ ok = this.loadUri(uri, cb); }catch(e){ dojo.debug("failed loading ", uri, " with error: ", e); } return ((ok)&&(this.findModule(module, false))) ? true : false; } dojo.loaded = function(){ } dojo.unloaded = function(){ } dojo.hostenv.loaded = function(){ this.loadNotifying = true; this.post_load_ = true; var mll = this.modulesLoadedListeners; for(var x=0; x 1) { dh.modulesLoadedListeners.push(function() { obj[fcnName](); }); } //Added for xdomain loading. dojo.addOnLoad is used to //indicate callbacks after doing some dojo.require() statements. //In the xdomain case, if all the requires are loaded (after initial //page load), then immediately call any listeners. if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){ dh.callLoaded(); } } dojo.addOnUnload = function(obj, fcnName){ var dh = dojo.hostenv; if(arguments.length == 1){ dh.unloadListeners.push(obj); } else if(arguments.length > 1) { dh.unloadListeners.push(function() { obj[fcnName](); }); } } dojo.hostenv.modulesLoaded = function(){ if(this.post_load_){ return; } if((this.loadUriStack.length==0)&&(this.getTextStack.length==0)){ if(this.inFlightCount > 0){ dojo.debug("files still in flight!"); return; } dojo.hostenv.callLoaded(); } } dojo.hostenv.callLoaded = function(){ if(typeof setTimeout == "object"){ setTimeout("dojo.hostenv.loaded();", 0); }else{ dojo.hostenv.loaded(); } } dojo.hostenv.getModuleSymbols = function(modulename) { var syms = modulename.split("."); for(var i = syms.length - 1; i > 0; i--){ var parentModule = syms.slice(0, i).join("."); var parentModulePath = this.getModulePrefix(parentModule); if(parentModulePath != parentModule){ syms.splice(0, i, parentModulePath); break; } } return syms; } /** * loadModule("A.B") first checks to see if symbol A.B is defined. * If it is, it is simply returned (nothing to do). * * If it is not defined, it will look for "A/B.js" in the script root directory, * followed by "A.js". * * It throws if it cannot find a file to load, or if the symbol A.B is not * defined after loading. * * It returns the object A.B. * * This does nothing about importing symbols into the current package. * It is presumed that the caller will take care of that. For example, to import * all symbols: * * with (dojo.hostenv.loadModule("A.B")) { * ... * } * * And to import just the leaf symbol: * * var B = dojo.hostenv.loadModule("A.B"); * ... * * dj_load is an alias for dojo.hostenv.loadModule */ dojo.hostenv._global_omit_module_check = false; dojo.hostenv.loadModule = function(modulename, exact_only, omit_module_check){ if(!modulename){ return; } omit_module_check = this._global_omit_module_check || omit_module_check; var module = this.findModule(modulename, false); if(module){ return module; } // protect against infinite recursion from mutual dependencies if(dj_undef(modulename, this.loading_modules_)){ this.addedToLoadingCount.push(modulename); } this.loading_modules_[modulename] = 1; // convert periods to slashes var relpath = modulename.replace(/\./g, '/') + '.js'; var syms = this.getModuleSymbols(modulename); var startedRelative = ((syms[0].charAt(0) != '/')&&(!syms[0].match(/^\w+:/))); var last = syms[syms.length - 1]; // figure out if we're looking for a full package, if so, we want to do // things slightly diffrently var nsyms = modulename.split("."); if(last=="*"){ modulename = (nsyms.slice(0, -1)).join('.'); while(syms.length){ syms.pop(); syms.push(this.pkgFileName); relpath = syms.join("/") + '.js'; if(startedRelative && (relpath.charAt(0)=="/")){ relpath = relpath.slice(1); } ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); if(ok){ break; } syms.pop(); } }else{ relpath = syms.join("/") + '.js'; modulename = nsyms.join('.'); var ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); if((!ok)&&(!exact_only)){ syms.pop(); while(syms.length){ relpath = syms.join('/') + '.js'; ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); if(ok){ break; } syms.pop(); relpath = syms.join('/') + '/'+this.pkgFileName+'.js'; if(startedRelative && (relpath.charAt(0)=="/")){ relpath = relpath.slice(1); } ok = this.loadPath(relpath, ((!omit_module_check) ? modulename : null)); if(ok){ break; } } } if((!ok)&&(!omit_module_check)){ dojo.raise("Could not load '" + modulename + "'; last tried '" + relpath + "'"); } } // check that the symbol was defined //Don't bother if we're doing xdomain (asynchronous) loading. if(!omit_module_check && !this["isXDomain"]){ // pass in false so we can give better error module = this.findModule(modulename, false); if(!module){ dojo.raise("symbol '" + modulename + "' is not defined after loading '" + relpath + "'"); } } return module; } /** * startPackage("A.B") follows the path, and at each level creates a new empty * object or uses what already exists. It returns the result. */ dojo.hostenv.startPackage = function(packname){ var modref = dojo.evalObjPath((packname.split(".").slice(0, -1)).join('.')); this.loaded_modules_[(new String(packname)).toLowerCase()] = modref; var syms = packname.split(/\./); if(syms[syms.length-1]=="*"){ syms.pop(); } return dojo.evalObjPath(syms.join("."), true); } /** * findModule("A.B") returns the object A.B if it exists, otherwise null. * @param modulename A string like 'A.B'. * @param must_exist Optional, defualt false. throw instead of returning null * if the module does not currently exist. */ dojo.hostenv.findModule = function(modulename, must_exist){ // check cache /* if(!dj_undef(modulename, this.modules_)){ return this.modules_[modulename]; } */ var lmn = (new String(modulename)).toLowerCase(); if(this.loaded_modules_[lmn]){ return this.loaded_modules_[lmn]; } // see if symbol is defined anyway var module = dojo.evalObjPath(modulename); if((modulename)&&(typeof module != 'undefined')&&(module)){ this.loaded_modules_[lmn] = module; return module; } if(must_exist){ dojo.raise("no loaded module named '" + modulename + "'"); } return null; } //Start of old bootstrap2: /* * This method taks a "map" of arrays which one can use to optionally load dojo * modules. The map is indexed by the possible dojo.hostenv.name_ values, with * two additional values: "default" and "common". The items in the "default" * array will be loaded if none of the other items have been choosen based on * the hostenv.name_ item. The items in the "common" array will _always_ be * loaded, regardless of which list is chosen. Here's how it's normally * called: * * dojo.kwCompoundRequire({ * browser: [ * ["foo.bar.baz", true, true], // an example that passes multiple args to loadModule() * "foo.sample.*", * "foo.test, * ], * default: [ "foo.sample.*" ], * common: [ "really.important.module.*" ] * }); */ dojo.kwCompoundRequire = function(modMap){ var common = modMap["common"]||[]; var result = (modMap[dojo.hostenv.name_]) ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]); for(var x=0; x