blob: 071af451d2786f2b4a6d81269d2f3c04d9c08823 [file] [log] [blame]
/*
Copyright (c) 2004-2005, 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
*/
/**
* @file bootstrap1.js
*
* bootstrap file that runs before hostenv_*.js file.
*
* @author Copyright 2004 Mark D. Anderson (mda@discerning.com)
* @author Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
*
* $Id: bootstrap1.js 2836 2006-01-16 08:36:18Z alex $
*/
/**
* The global djConfig can be set prior to loading the library, to override
* certain settings. It does not exist under dojo.* so that it can be set
* before the dojo variable exists. Setting any of these variables *after* the
* library has loaded does nothing at all. The variables that can be set are
* as follows:
*/
/**
* dj_global is an alias for the top-level global object in the host
* environment (the "window" object in a browser).
*/
var dj_global = this; //typeof window == 'undefined' ? this : window;
function dj_undef(name, obj){
if(!obj){ obj = dj_global; }
return (typeof obj[name] == "undefined");
}
if(dj_undef("djConfig")){
var djConfig = {};
}
/**
* dojo is the root variable of (almost all) our public symbols.
*/
var dojo;
if(dj_undef("dojo")){ dojo = {}; }
dojo.version = {
major: 0, minor: 2, patch: 2, flag: "",
revision: Number("$Rev: 2836 $".match(/[0-9]+/)[0]),
toString: function() {
with (dojo.version) {
return major + "." + minor + "." + patch + flag + " (" + revision + ")";
}
}
};
/*
* evaluate a string like "A.B" without using eval.
*/
dojo.evalObjPath = function(objpath, create){
// fast path for no periods
if(typeof objpath != "string"){ return dj_global; }
if(objpath.indexOf('.') == -1){
if((dj_undef(objpath, dj_global))&&(create)){
dj_global[objpath] = {};
}
return dj_global[objpath];
}
var syms = objpath.split(/\./);
var obj = dj_global;
for(var i=0;i<syms.length;++i){
if(!create){
obj = obj[syms[i]];
if((typeof obj == 'undefined')||(!obj)){
return obj;
}
}else{
if(dj_undef(syms[i], obj)){
obj[syms[i]] = {};
}
obj = obj[syms[i]];
}
}
return obj;
};
// ****************************************************************
// global public utils
// ****************************************************************
/*
* utility to print an Error.
* TODO: overriding Error.prototype.toString won't accomplish this?
* ... since natively generated Error objects do not always reflect such things?
*/
dojo.errorToString = function(excep){
return ((!dj_undef("message", excep)) ? excep.message : (dj_undef("description", excep) ? excep : excep.description ));
};
/**
* Throws an Error object given the string err. For now, will also do a println
* to the user first.
*/
dojo.raise = function(message, excep){
if(excep){
message = message + ": "+dojo.errorToString(excep);
}
var he = dojo.hostenv;
if((!dj_undef("hostenv", dojo))&&(!dj_undef("println", dojo.hostenv))){
dojo.hostenv.println("FATAL: " + message);
}
throw Error(message);
};
dj_throw = dj_rethrow = function(m, e){
dojo.deprecated("dj_throw and dj_rethrow deprecated, use dojo.raise instead");
dojo.raise(m, e);
};
/**
* Produce a line of debug output.
* Does nothing unless djConfig.isDebug is true.
* varargs, joined with ''.
* Caller should not supply a trailing "\n".
*/
dojo.debug = function(){
if (!djConfig.isDebug) { return; }
var args = arguments;
if(dj_undef("println", dojo.hostenv)){
dojo.raise("dojo.debug not available (yet?)");
}
var isJUM = dj_global["jum"] && !dj_global["jum"].isBrowser;
var s = [(isJUM ? "": "DEBUG: ")];
for(var i=0;i<args.length;++i){
if(!false && args[i] instanceof Error){
var msg = "[" + args[i].name + ": " + dojo.errorToString(args[i]) +
(args[i].fileName ? ", file: " + args[i].fileName : "") +
(args[i].lineNumber ? ", line: " + args[i].lineNumber : "") + "]";
} else {
try {
var msg = String(args[i]);
} catch(e) {
if(dojo.render.html.ie) {
var msg = "[ActiveXObject]";
} else {
var msg = "[unknown]";
}
}
}
s.push(msg);
}
if(isJUM){ // this seems to be the only way to get JUM to "play nice"
jum.debug(s.join(" "));
}else{
dojo.hostenv.println(s.join(" "));
}
}
/**
* this is really hacky for now - just
* display the properties of the object
**/
dojo.debugShallow = function(obj){
if (!djConfig.isDebug) { return; }
dojo.debug('------------------------------------------------------------');
dojo.debug('Object: '+obj);
for(i in obj){
dojo.debug(i + ': ' + obj[i]);
}
dojo.debug('------------------------------------------------------------');
}
var dj_debug = dojo.debug;
/**
* We put eval() in this separate function to keep down the size of the trapped
* evaluation context.
*
* Note that:
* - JSC eval() takes an optional second argument which can be 'unsafe'.
* - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
* scope object for new symbols.
*/
function dj_eval(s){ return dj_global.eval ? dj_global.eval(s) : eval(s); }
/**
* Convenience for throwing an exception because some function is not
* implemented.
*/
dj_unimplemented = dojo.unimplemented = function(funcname, extra){
// FIXME: need to move this away from dj_*
var mess = "'" + funcname + "' not implemented";
if((!dj_undef(extra))&&(extra)){ mess += " " + extra; }
dojo.raise(mess);
}
/**
* Convenience for informing of deprecated behaviour.
*/
dj_deprecated = dojo.deprecated = function(behaviour, extra, removal){
var mess = "DEPRECATED: " + behaviour;
if(extra){ mess += " " + extra; }
if(removal){ mess += " -- will be removed in version: " + removal; }
dojo.debug(mess);
}
/**
* Does inheritance
*/
dojo.inherits = function(subclass, superclass){
if(typeof superclass != 'function'){
dojo.raise("superclass: "+superclass+" borken");
}
subclass.prototype = new superclass();
subclass.prototype.constructor = subclass;
subclass.superclass = superclass.prototype;
// DEPRICATED: super is a reserved word, use 'superclass'
subclass['super'] = superclass.prototype;
}
dj_inherits = function(subclass, superclass){
dojo.deprecated("dj_inherits deprecated, use dojo.inherits instead");
dojo.inherits(subclass, superclass);
}
// an object that authors use determine what host we are running under
dojo.render = (function(){
function vscaffold(prefs, names){
var tmp = {
capable: false,
support: {
builtin: false,
plugin: false
},
prefixes: prefs
};
for(var x in names){
tmp[x] = false;
}
return tmp;
}
return {
name: "",
ver: dojo.version,
os: { win: false, linux: false, osx: false },
html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
vml: vscaffold(["vml"], ["ie"]),
swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
swt: vscaffold(["Swt"], ["ibm"])
};
})();
// ****************************************************************
// dojo.hostenv methods that must be defined in hostenv_*.js
// ****************************************************************
/**
* The interface definining the interaction with the EcmaScript host environment.
*/
/*
* None of these methods should ever be called directly by library users.
* Instead public methods such as loadModule should be called instead.
*/
dojo.hostenv = (function(){
// default configuration options
var config = {
isDebug: false,
allowQueryConfig: false,
baseScriptUri: "",
baseRelativePath: "",
libraryScriptUri: "",
iePreventClobber: false,
ieClobberMinimal: true,
preventBackButtonFix: true,
searchIds: [],
parseWidgets: true
};
if (typeof djConfig == "undefined") { djConfig = config; }
else {
for (var option in config) {
if (typeof djConfig[option] == "undefined") {
djConfig[option] = config[option];
}
}
}
var djc = djConfig;
function _def(obj, name, def){
return (dj_undef(name, obj) ? def : obj[name]);
}
return {
name_: '(unset)',
version_: '(unset)',
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: [],
// lookup cache for modules.
// NOTE: this is partially redundant a private variable in the jsdown
// implementation, but we don't want to couple the two.
// modules_ : {},
post_load_: false,
modulesLoadedListeners: [],
/**
* Return the name of the hostenv.
*/
getName: function(){ return this.name_; },
/**
* Return the version of the hostenv.
*/
getVersion: function(){ return this.version_; },
/**
* Read the plain/text contents at the specified uri. If getText() is
* not implemented, then it is necessary to override loadUri() with an
* implementation that doesn't rely on it.
*/
getText: function(uri){
dojo.unimplemented('getText', "uri=" + uri);
},
/**
* return the uri of the script that defined this function
* private method that must be implemented by the hostenv.
*/
getLibraryScriptUri: function(){
// FIXME: need to implement!!!
dojo.unimplemented('getLibraryScriptUri','');
}
};
})();
/**
* Display a line of text to the user.
* The line argument should not contain a trailing "\n"; that is added by the
* implementation.
*/
//dojo.hostenv.println = function(line) {}
// ****************************************************************
// dojo.hostenv methods not defined in hostenv_*.js
// ****************************************************************
/**
* Return the base script uri that other scripts are found relative to.
* It is either the empty string, or a non-empty string ending in '/'.
*/
dojo.hostenv.getBaseScriptUri = function(){
if(djConfig.baseScriptUri.length){
return djConfig.baseScriptUri;
}
var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }
var lastslash = uri.lastIndexOf('/');
djConfig.baseScriptUri = djConfig.baseRelativePath;
return djConfig.baseScriptUri;
}
/**
* Set the base script uri.
*/
// In JScript .NET, see interface System._AppDomain implemented by
// System.AppDomain.CurrentDomain. Members include AppendPrivatePath,
// RelativeSearchPath, BaseDirectory.
dojo.hostenv.setBaseScriptUri = function(uri){ djConfig.baseScriptUri = uri }
/**
* 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.
*/
dojo.hostenv.loadPath = function(relpath, module /*optional*/, cb /*optional*/){
if((relpath.charAt(0) == '/')||(relpath.match(/^\w+:/))){
dojo.raise("relpath '" + relpath + "'; must be relative");
}
var 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.
*/
dojo.hostenv.loadUri = function(uri, cb){
if(this.loadedUris[uri]){
return;
}
var contents = this.getText(uri, null, true);
if(contents == null){ return 0; }
this.loadedUris[uri] = true;
var value = dj_eval(contents);
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.hostenv.loaded = function(){
this.post_load_ = true;
var mll = this.modulesLoadedListeners;
for(var x=0; x<mll.length; x++){
mll[x]();
}
dojo.loaded();
}
/*
Call styles:
dojo.addOnLoad(functionPointer)
dojo.addOnLoad(object, "functionName")
*/
dojo.addOnLoad = function(obj, fcnName) {
if(arguments.length == 1) {
dojo.hostenv.modulesLoadedListeners.push(obj);
} else if(arguments.length > 1) {
dojo.hostenv.modulesLoadedListeners.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;
}
if(typeof setTimeout == "object"){
setTimeout("dojo.hostenv.loaded();", 0);
}else{
dojo.hostenv.loaded();
}
}
}
dojo.hostenv.moduleLoaded = function(modulename){
var modref = dojo.evalObjPath((modulename.split(".").slice(0, -1)).join('.'));
this.loaded_modules_[(new String(modulename)).toLowerCase()] = modref;
}
/**
* 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 = modulename.split(".");
var nsyms = 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;
}
}
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
if(last=="*"){
modulename = (nsyms.slice(0, -1)).join('.');
while(syms.length){
syms.pop();
syms.push(this.pkgFileName);
relpath = syms.join("/") + '.js';
if(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(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
if(!omit_module_check){
// 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 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;
}