/*
	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
*/

if(typeof dojo == "undefined"){

// TODOC: HOW TO DOC THE BELOW?
// @global: djConfig
// summary:
//		Application code can set the global 'djConfig' prior to loading
//		the library to override certain global settings for how dojo works.
// description:  The variables that can be set are as follows:
//			- isDebug: false
//			- allowQueryConfig: false
//			- baseScriptUri: ""
//			- baseRelativePath: ""
//			- libraryScriptUri: ""
//			- iePreventClobber: false
//			- ieClobberMinimal: true
//			- locale: undefined
//			- extraLocale: undefined
//			- preventBackButtonFix: true
//			- searchIds: []
//			- parseWidgets: true
// TODOC: HOW TO DOC THESE VARIABLES?
// TODOC: IS THIS A COMPLETE LIST?
// note:
//		'djConfig' does not exist under 'dojo.*' so that it can be set before the
//		'dojo' variable exists.
// note:
//		Setting any of these variables *after* the library has loaded does nothing at all.
// TODOC: is this still true?  Release notes for 0.3 indicated they could be set after load.
//


//TODOC:  HOW TO DOC THIS?
// @global: dj_global
// summary:
//		an alias for the top-level global object in the host environment
//		(e.g., the window object in a browser).
// description:
//		Refer to 'dj_global' rather than referring to window to ensure your
//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
var dj_global = this;

//TODOC:  HOW TO DOC THIS?
// @global: dj_currentContext
// summary:
//		Private global context object. Where 'dj_global' always refers to the boot-time
//    global context, 'dj_currentContext' can be modified for temporary context shifting.
//    dojo.global() returns dj_currentContext.
// description:
//		Refer to dojo.global() rather than referring to dj_global to ensure your
//		code runs correctly in managed contexts.
var dj_currentContext = this;


// ****************************************************************
// global public utils
// TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS?
// ****************************************************************

function dj_undef(/*String*/ name, /*Object?*/ object){
	//summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null).
	//description: Note that 'defined' and 'exists' are not the same concept.
	return (typeof (object || dj_currentContext)[name] == "undefined");	// Boolean
}

// make sure djConfig is defined
if(dj_undef("djConfig", this)){
	var djConfig = {};
}

//TODOC:  HOW TO DOC THIS?
// dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
if(dj_undef("dojo", this)){
	var dojo = {};
}

dojo.global = function(){
	// summary:
	//		return the current global context object
	//		(e.g., the window object in a browser).
	// description:
	//		Refer to 'dojo.global()' rather than referring to window to ensure your
	//		code runs correctly in contexts other than web browsers (eg: Rhino on a server).
	return dj_currentContext;
}

// Override locale setting, if specified
dojo.locale  = djConfig.locale;

//TODOC:  HOW TO DOC THIS?
dojo.version = {
	// summary: version number of this instance of dojo.
	major: 0, minor: 4, patch: 3, flag: "",
	revision: Number("$Rev: 8617 $".match(/[0-9]+/)[0]),
	toString: function(){
		with(dojo.version){
			return major + "." + minor + "." + patch + flag + " (" + revision + ")";	// String
		}
	}
}

dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){
	// summary: Returns 'object[name]'.  If not defined and 'create' is true, will return a new Object.
	// description:
	//		Returns null if 'object[name]' is not defined and 'create' is not true.
	// 		Note: 'defined' and 'exists' are not the same concept.
	if((!object)||(!name)) return undefined; // undefined
	if(!dj_undef(name, object)) return object[name]; // mixed
	return (create ? (object[name]={}) : undefined);	// mixed
}

dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){
	// summary: Parse string path to an object, and return corresponding object reference and property name.
	// description:
	//		Returns an object with two properties, 'obj' and 'prop'.
	//		'obj[prop]' is the reference indicated by 'path'.
	// path: Path to an object, in the form "A.B.C".
	// context: Object to use as root of path.  Defaults to 'dojo.global()'.
	// create: If true, Objects will be created at any point along the 'path' that is undefined.
	var object = (context || dojo.global());
	var names = path.split('.');
	var prop = names.pop();
	for (var i=0,l=names.length;i<l && object;i++){
		object = dojo.evalProp(names[i], object, create);
	}
	return {obj: object, prop: prop};	// Object: {obj: Object, prop: String}
}

dojo.evalObjPath = function(/*String*/ path, /*Boolean?*/ create){
	// summary: Return the value of object at 'path' in the global scope, without using 'eval()'.
	// path: Path to an object, in the form "A.B.C".
	// create: If true, Objects will be created at any point along the 'path' that is undefined.
	if(typeof path != "string"){
		return dojo.global();
	}
	// fast path for no periods
	if(path.indexOf('.') == -1){
		return dojo.evalProp(path, dojo.global(), create);		// mixed
	}

	//MOW: old 'with' syntax was confusing and would throw an error if parseObjPath returned null.
	var ref = dojo.parseObjPath(path, dojo.global(), create);
	if(ref){
		return dojo.evalProp(ref.prop, ref.obj, create);	// mixed
	}
	return null;
}

dojo.errorToString = function(/*Error*/ exception){
	// summary: Return an exception's 'message', 'description' or text.

	// TODO: overriding Error.prototype.toString won't accomplish this?
 	// 		... since natively generated Error objects do not always reflect such things?
	if(!dj_undef("message", exception)){
		return exception.message;		// String
	}else if(!dj_undef("description", exception)){
		return exception.description;	// String
	}else{
		return exception;				// Error
	}
}

dojo.raise = function(/*String*/ message, /*Error?*/ exception){
	// summary: Common point for raising exceptions in Dojo to enable logging.
	//	Throws an error message with text of 'exception' if provided, or
	//	rethrows exception object.

	if(exception){
		message = message + ": "+dojo.errorToString(exception);
	}else{
		message = dojo.errorToString(message);
	}

	// print the message to the user if hostenv.println is defined
	try { if(djConfig.isDebug){ dojo.hostenv.println("FATAL exception raised: "+message); } } catch (e) {}

	throw exception || Error(message);
}

//Stub functions so things don't break.
//TODOC:  HOW TO DOC THESE?
dojo.debug = function(){};
dojo.debugShallow = function(obj){};
dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };

function dj_eval(/*String*/ scriptFragment){
	// summary: Perform an evaluation in the global scope.  Use this rather than calling 'eval()' directly.
	// description: Placed in a separate function to minimize size of trapped evaluation context.
	// note:
	//	 - 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.
	return dj_global.eval ? dj_global.eval(scriptFragment) : eval(scriptFragment); 	// mixed
}

dojo.unimplemented = function(/*String*/ funcname, /*String?*/ extra){
	// summary: Throw an exception because some function is not implemented.
	// extra: Text to append to the exception message.
	var message = "'" + funcname + "' not implemented";
	if (extra != null) { message += " " + extra; }
	dojo.raise(message);
}

dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
	// summary: Log a debug message to indicate that a behavior has been deprecated.
	// extra: Text to append to the message.
	// removal: Text to indicate when in the future the behavior will be removed.
	var message = "DEPRECATED: " + behaviour;
	if(extra){ message += " " + extra; }
	if(removal){ message += " -- will be removed in version: " + removal; }
	dojo.debug(message);
}

dojo.render = (function(){
	//TODOC: HOW TO DOC THIS?
	// summary: Details rendering support, OS and browser of the current environment.
	// TODOC: is this something many folks will interact with?  If so, we should doc the structure created...
	function vscaffold(prefs, names){
		var tmp = {
			capable: false,
			support: {
				builtin: false,
				plugin: false
			},
			prefixes: prefs
		};
		for(var i=0; i<names.length; i++){
			tmp[names[i]] = 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(){
	// TODOC:  HOW TO DOC THIS?
	// summary: Provides encapsulation of behavior that changes across different 'host environments'
	//			(different browsers, server via Rhino, etc).
	// description: None of these methods should ever be called directly by library users.
	//				Use public methods such as 'loadModule' instead.

	// default configuration options
	var config = {
		isDebug: false,
		allowQueryConfig: false,
		baseScriptUri: "",
		baseRelativePath: "",
		libraryScriptUri: "",
		iePreventClobber: false,
		ieClobberMinimal: true,
		preventBackButtonFix: true,
		delayMozLoadingFix: false,
		searchIds: [],
		parseWidgets: true
	};

	if (typeof djConfig == "undefined") { djConfig = config; }
	else {
		for (var option in config) {
			if (typeof djConfig[option] == "undefined") {
				djConfig[option] = config[option];
			}
		}
	}

	return {
		name_: '(unset)',
		version_: '(unset)',


		getName: function(){
			// sumary: Return the name of the host environment.
			return this.name_; 	// String
		},


		getVersion: function(){
			// summary: Return the version of the hostenv.
			return this.version_; // String
		},

		getText: function(/*String*/ uri){
			// summary:	Read the plain/text contents at the specified 'uri'.
			// description:
			//			If 'getText()' is not implemented, then it is necessary to override
			//			'loadUri()' with an implementation that doesn't rely on it.

			dojo.unimplemented('getText', "uri=" + uri);
		}
	};
})();


dojo.hostenv.getBaseScriptUri = function(){
	// summary: Return the base script uri that other scripts are found relative to.
	// TODOC: HUH?  This comment means nothing to me.  What other scripts? Is this the path to other dojo libraries?
	//		MAYBE:  Return the base uri to scripts in the dojo library.	 ???
	// return: Empty string or a path ending in '/'.
	if(djConfig.baseScriptUri.length){
		return djConfig.baseScriptUri;
	}

	// MOW: Why not:
	//			uri = djConfig.libraryScriptUri || djConfig.baseRelativePath
	//		??? Why 'new String(...)'
	var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
	if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }

	// MOW: uri seems to not be actually used.  Seems to be hard-coding to djConfig.baseRelativePath... ???
	var lastslash = uri.lastIndexOf('/');		// MOW ???
	djConfig.baseScriptUri = djConfig.baseRelativePath;
	return djConfig.baseScriptUri;	// String
}

/*
 * loader.js - A bootstrap module.  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(/*String*/module, /*String*/prefix){
			// summary: establishes module/prefix pair
			this.modulePrefixes_[module] = {name: module, value: prefix};
		},

		moduleHasPrefix: function(/*String*/module){
			// summary: checks to see if module has been established
			var mp = this.modulePrefixes_;
			return Boolean(mp[module] && mp[module].value); // Boolean
		},

		getModulePrefix: function(/*String*/module){
			// summary: gets the prefix associated with module
			if(this.moduleHasPrefix(module)){
				return this.modulePrefixes_[module].value; // String
			}
			return module; // String
		},

		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];
	}
})();

dojo.hostenv.loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
// summary:
//	Load a Javascript module given a relative path
//
// description:
//	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().
//
// relpath: A relative path to a script (no leading '/', and typically
// 	ending in '.js').
// module: A module whose existance to check for after loading a path.
//	Can be used to determine success or failure of the load.
// cb: a callback function to pass the result of evaluating the script

	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); // Boolean
	}catch(e){
		dojo.debug(e);
		return false; // Boolean
	}
}

dojo.hostenv.loadUri = function(/*String (URL)*/uri, /*Function?*/cb){
// summary:
//	Loads JavaScript from a URI
//
// description:
//	Reads the contents of the URI, and evaluates the contents.  This is used to load modules as well
//	as resource bundles.  Returns true if it succeeded. Returns false if the URI reading failed.
//	Throws if the evaluation throws.
//
// uri: a uri which points at the script to be loaded
// cb: a callback function to process the result of evaluating the script as an expression, typically
//	used by the resource bundle loader to load JSON-style resources

	if(this.loadedUris[uri]){
		return true; // Boolean
	}
	var contents = this.getText(uri, null, true);
	if(!contents){ return false; } // Boolean
	this.loadedUris[uri] = true;
	if(cb){ contents = '('+contents+')'; }
	var value = dj_eval(contents);
	if(cb){ cb(value); }
	return true; // Boolean
}

// FIXME: probably need to add logging to this method
dojo.hostenv.loadUriAndCheck = function(/*String (URL)*/uri, /*String*/moduleName, /*Function?*/cb){
	// summary: calls loadUri then findModule and returns true if both succeed
	var ok = true;
	try{
		ok = this.loadUri(uri, cb);
	}catch(e){
		dojo.debug("failed loading ", uri, " with error: ", e);
	}
	return Boolean(ok && this.findModule(moduleName, false)); // Boolean
}

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<mll.length; x++){
		mll[x]();
	}

	//Clear listeners so new ones can be added
	//For other xdomain package loads after the initial load.
	this.modulesLoadedListeners = [];
	this.loadNotifying = false;

	dojo.loaded();
}

dojo.hostenv.unloaded = function(){
	var mll = this.unloadListeners;
	while(mll.length){
		(mll.pop())();
	}
	dojo.unloaded();
}

dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName) {
// summary:
//	Registers a function to be triggered after the DOM has finished loading 
//	and widgets declared in markup have been instantiated.  Images and CSS files
//	may or may not have finished downloading when the specified function is called.
//	(Note that widgets' CSS and HTML code is guaranteed to be downloaded before said
//	widgets are instantiated.)
//
// usage:
//	dojo.addOnLoad(functionPointer)
//	dojo.addOnLoad(object, "functionName")

	var dh = dojo.hostenv;
	if(arguments.length == 1) {
		dh.modulesLoadedListeners.push(obj);
	} else if(arguments.length > 1) {
		dh.modulesLoadedListeners.push(function() {
			obj[functionName]();
		});
	}

	//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(/*Object?*/obj, /*String|Function?*/functionName){
// summary: registers a function to be triggered when the page unloads
//
// usage:
//	dojo.addOnLoad(functionPointer)
//	dojo.addOnLoad(object, "functionName")
	var dh = dojo.hostenv;
	if(arguments.length == 1){
		dh.unloadListeners.push(obj);
	} else if(arguments.length > 1) {
		dh.unloadListeners.push(function() {
			obj[functionName]();
		});
	}
}

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(){
	//The "object" check is for IE, and the other opera check fixes an issue
	//in Opera where it could not find the body element in some widget test cases.
	//For 0.9, maybe route all browsers through the setTimeout (need protection
	//still for non-browser environments though). This might also help the issue with
	//FF 2.0 and freezing issues where we try to do sync xhr while background css images
	//are being loaded (trac #2572)? Consider for 0.9.
	if(typeof setTimeout == "object" || (djConfig["useXDomain"] && dojo.render.html.opera)){
		setTimeout("dojo.hostenv.loaded();", 0);
	}else{
		dojo.hostenv.loaded();
	}
}

dojo.hostenv.getModuleSymbols = function(/*String*/modulename){
// summary:
//	Converts a module name in dotted JS notation to an array representing the path in the source tree
	var syms = modulename.split(".");
	for(var i = syms.length; i>0; i--){
		var parentModule = syms.slice(0, i).join(".");
		if((i==1) && !this.moduleHasPrefix(parentModule)){		
			// Support default module directory (sibling of dojo) for top-level modules 
			syms[0] = "../" + syms[0];
		}else{
			var parentModulePath = this.getModulePrefix(parentModule);
			if(parentModulePath != parentModule){
				syms.splice(0, i, parentModulePath);
				break;
			}
		}
	}
	return syms; // Array
}

dojo.hostenv._global_omit_module_check = false;
dojo.hostenv.loadModule = function(/*String*/moduleName, /*Boolean?*/exactOnly, /*Boolean?*/omitModuleCheck){
// summary:
//	loads a Javascript module from the appropriate URI
//
// description:
//	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

	if(!moduleName){ return; }
	omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
	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 nsyms = moduleName.split(".");
	
	// this line allowed loading of a module manifest as if it were a namespace
	// it's an interesting idea, but shouldn't be combined with 'namespaces' proper
	// and leads to unwanted dependencies
	// the effect can be achieved in other (albeit less-flexible) ways now, so I am
	// removing this pending further design work
	// perhaps we can explicitly define this idea of a 'module manifest', and subclass
	// 'namespace manifest' from that
	//dojo.getNamespace(nsyms[0]);

	var syms = this.getModuleSymbols(moduleName);
	var startedRelative = ((syms[0].charAt(0) != '/') && !syms[0].match(/^\w+:/));
	var last = syms[syms.length - 1];
	var ok;
	// 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(startedRelative && relpath.charAt(0)=="/"){
				relpath = relpath.slice(1);
			}
			ok = this.loadPath(relpath, !omitModuleCheck ? moduleName : null);
			if(ok){ break; }
			//debugger;
			syms.pop();
		}
	}else{
		relpath = syms.join("/") + '.js';
		moduleName = nsyms.join('.');
		var modArg = !omitModuleCheck ? moduleName : null;
		ok = this.loadPath(relpath, modArg);
		if(!ok && !exactOnly){
			//debugger;
			syms.pop();
			while(syms.length){
				relpath = syms.join('/') + '.js';
				ok = this.loadPath(relpath, modArg);
				if(ok){ break; }
				//debugger;
				syms.pop();
				relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
				if(startedRelative && relpath.charAt(0)=="/"){
					relpath = relpath.slice(1);
				}
				ok = this.loadPath(relpath, modArg);
				if(ok){ break; }
				//debugger;
			}
		}

		if(!ok && !omitModuleCheck){
			//debugger;
			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(!omitModuleCheck && !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;
}

dojo.hostenv.startPackage = function(/*String*/packageName){
// summary:
//	Creates a JavaScript package
//
// description:
//	startPackage("A.B") follows the path, and at each level creates a new empty
//	object or uses what already exists. It returns the result.
//
// packageName: the package to be created as a String in dot notation

	//Make sure we have a string.
	var fullPkgName = String(packageName);
	var strippedPkgName = fullPkgName;

	var syms = packageName.split(/\./);
	if(syms[syms.length-1]=="*"){
		syms.pop();
		strippedPkgName = syms.join(".");
	}
	var evaledPkg = dojo.evalObjPath(strippedPkgName, true);
	this.loaded_modules_[fullPkgName] = evaledPkg;
	this.loaded_modules_[strippedPkgName] = evaledPkg;
	
	return evaledPkg; // Object
}

dojo.hostenv.findModule = function(/*String*/moduleName, /*Boolean?*/mustExist){
// summary:
//	Returns the Object representing the module, if it exists, otherwise null.
//
// moduleName A fully qualified module including package name, like 'A.B'.
// mustExist Optional, default false. throw instead of returning null
//	if the module does not currently exist.

	var lmn = String(moduleName);

	if(this.loaded_modules_[lmn]){
		return this.loaded_modules_[lmn]; // Object
	}

	if(mustExist){
		dojo.raise("no loaded module named '" + moduleName + "'");
	}
	return null; // null
}

//Start of old bootstrap2:

dojo.kwCompoundRequire = function(/*Object containing Arrays*/modMap){
// description:
//	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.*" ]
//	});

	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<result.length; x++){
		var curr = result[x];
		if(curr.constructor == Array){
			dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
		}else{
			dojo.hostenv.loadModule(curr);
		}
	}
}

dojo.require = function(/*String*/ resourceName){
	// summary
	//	Ensure that the given resource (ie, javascript
	//	source file) has been loaded.
	// description
	//	dojo.require() is similar to C's #include command or java's "import" command.
	//	You call dojo.require() to pull in the resources (ie, javascript source files)
	//	that define the functions you are using. 
	//
	//	Note that in the case of a build, many resources have already been included
	//	into dojo.js (ie, many of the javascript source files have been compressed and
	//	concatened into dojo.js), so many dojo.require() calls will simply return
	//	without downloading anything.
	dojo.hostenv.loadModule.apply(dojo.hostenv, arguments);
}

dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
	// summary
	//	If the condition is true then call dojo.require() for the specified resource
	var arg0 = arguments[0];
	if((arg0 === true)||(arg0=="common")||(arg0 && dojo.render[arg0].capable)){
		var args = [];
		for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
		dojo.require.apply(dojo, args);
	}
}

dojo.requireAfterIf = dojo.requireIf;

dojo.provide = function(/*String*/ resourceName){
	// summary
	//	Each javascript source file must have (exactly) one dojo.provide()
	//	call at the top of the file, corresponding to the file name.
	//	For example, dojo/src/foo.js must have dojo.provide("dojo.foo"); at the top of the file.
	//
	// description
	//	Each javascript source file is called a resource.  When a resource
	//	is loaded by the browser, dojo.provide() registers that it has
	//	been loaded.
	//	
	//	For backwards compatibility reasons, in addition to registering the resource,
	//	dojo.provide() also ensures that the javascript object for the module exists.  For
	//	example, dojo.provide("dojo.html.common"), in addition to registering that common.js
	//	is a resource for the dojo.html module, will ensure that the dojo.html javascript object
	//	exists, so that calls like dojo.html.foo = function(){ ... } don't fail.
	//
	//	In the case of a build (or in the future, a rollup), where multiple javascript source
	//	files are combined into one bigger file (similar to a .lib or .jar file), that file
	//	will contain multiple dojo.provide() calls, to note that it includes
	//	multiple resources.
	return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
}

dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
	// summary: maps a module name to a path
	// description: An unregistered module is given the default path of ../<module>,
	//	relative to Dojo root. For example, module acme is mapped to ../acme.
	//	If you want to use a different module name, use dojo.registerModulePath. 
	return dojo.hostenv.setModulePrefix(module, prefix);
}

if(djConfig["modulePaths"]){
	for(var param in djConfig["modulePaths"]){
		dojo.registerModulePath(param, djConfig["modulePaths"][param]);
	}
}

dojo.setModulePrefix = function(/*String*/module, /*String*/prefix){
	// summary: maps a module name to a path
	dojo.deprecated('dojo.setModulePrefix("' + module + '", "' + prefix + '")', "replaced by dojo.registerModulePath", "0.5");
	return dojo.registerModulePath(module, prefix);
}

dojo.exists = function(/*Object*/obj, /*String*/name){
	// summary: determine if an object supports a given method
	// description: useful for longer api chains where you have to test each object in the chain
	var p = name.split(".");
	for(var i = 0; i < p.length; i++){
		if(!obj[p[i]]){ return false; } // Boolean
		obj = obj[p[i]];
	}
	return true; // Boolean
}

// Localization routines

dojo.hostenv.normalizeLocale = function(/*String?*/locale){
//	summary:
//		Returns canonical form of locale, as used by Dojo.  All variants are case-insensitive and are separated by '-'
//		as specified in RFC 3066. If no locale is specified, the user agent's default is returned.

	var result = locale ? locale.toLowerCase() : dojo.locale;
	if(result == "root"){
		result = "ROOT";
	}
	return result;// String
};

dojo.hostenv.searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
//	summary:
//		A helper method to assist in searching for locale-based resources.  Will iterate through
//		the variants of a particular locale, either up or down, executing a callback function.
//		For example, "en-us" and true will try "en-us" followed by "en" and finally "ROOT".

	locale = dojo.hostenv.normalizeLocale(locale);

	var elements = locale.split('-');
	var searchlist = [];
	for(var i = elements.length; i > 0; i--){
		searchlist.push(elements.slice(0, i).join('-'));
	}
	searchlist.push(false);
	if(down){searchlist.reverse();}

	for(var j = searchlist.length - 1; j >= 0; j--){
		var loc = searchlist[j] || "ROOT";
		var stop = searchFunc(loc);
		if(stop){ break; }
	}
}

//These two functions are placed outside of preloadLocalizations
//So that the xd loading can use/override them.
dojo.hostenv.localesGenerated /***BUILD:localesGenerated***/; // value will be inserted here at build time, if necessary

dojo.hostenv.registerNlsPrefix = function(){
// summary:
//	Register module "nls" to point where Dojo can find pre-built localization files
	dojo.registerModulePath("nls","nls");	
}

dojo.hostenv.preloadLocalizations = function(){
// summary:
//	Load built, flattened resource bundles, if available for all locales used in the page.
//	Execute only once.  Note that this is a no-op unless there is a build.

	if(dojo.hostenv.localesGenerated){
		dojo.hostenv.registerNlsPrefix();

		function preload(locale){
			locale = dojo.hostenv.normalizeLocale(locale);
			dojo.hostenv.searchLocalePath(locale, true, function(loc){
				for(var i=0; i<dojo.hostenv.localesGenerated.length;i++){
					if(dojo.hostenv.localesGenerated[i] == loc){
						dojo["require"]("nls.dojo_"+loc);
						return true; // Boolean
					}
				}
				return false; // Boolean
			});
		}
		preload();
		var extra = djConfig.extraLocale||[];
		for(var i=0; i<extra.length; i++){
			preload(extra[i]);
		}
	}
	dojo.hostenv.preloadLocalizations = function(){};
}

dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
// summary:
//	Declares translated resources and loads them if necessary, in the same style as dojo.require.
//	Contents of the resource bundle are typically strings, but may be any name/value pair,
//	represented in JSON format.  See also dojo.i18n.getLocalization.
//
// moduleName: name of the package containing the "nls" directory in which the bundle is found
// bundleName: bundle name, i.e. the filename without the '.js' suffix
// locale: the locale to load (optional)  By default, the browser's user locale as defined by dojo.locale
// availableFlatLocales: A comma-separated list of the available, flattened locales for this bundle.
// This argument should only be set by the build process.
//
// description:
//	Load translated resource bundles provided underneath the "nls" directory within a package.
//	Translated resources may be located in different packages throughout the source tree.  For example,
//	a particular widget may define one or more resource bundles, structured in a program as follows,
//	where moduleName is mycode.mywidget and bundleNames available include bundleone and bundletwo:
//	...
//	mycode/
//	 mywidget/
//	  nls/
//	   bundleone.js (the fallback translation, English in this example)
//	   bundletwo.js (also a fallback translation)
//	   de/
//	    bundleone.js
//	    bundletwo.js
//	   de-at/
//	    bundleone.js
//	   en/
//	    (empty; use the fallback translation)
//	   en-us/
//	    bundleone.js
//	   en-gb/
//	    bundleone.js
//	   es/
//	    bundleone.js
//	    bundletwo.js
//	  ...etc
//	...
//	Each directory is named for a locale as specified by RFC 3066, (http://www.ietf.org/rfc/rfc3066.txt),
//	normalized in lowercase.  Note that the two bundles in the example do not define all the same variants.
//	For a given locale, bundles will be loaded for that locale and all more general locales above it, including
//	a fallback at the root directory.  For example, a declaration for the "de-at" locale will first
//	load nls/de-at/bundleone.js, then nls/de/bundleone.js and finally nls/bundleone.js.  The data will
//	be flattened into a single Object so that lookups will follow this cascading pattern.  An optional build
//	step can preload the bundles to avoid data redundancy and the multiple network hits normally required to
//	load these resources.

	dojo.hostenv.preloadLocalizations();
	var targetLocale = dojo.hostenv.normalizeLocale(locale);
 	var bundlePackage = [moduleName, "nls", bundleName].join(".");
//NOTE: When loading these resources, the packaging does not match what is on disk.  This is an
// implementation detail, as this is just a private data structure to hold the loaded resources.
// e.g. tests/hello/nls/en-us/salutations.js is loaded as the object tests.hello.nls.salutations.en_us={...}
// The structure on disk is intended to be most convenient for developers and translators, but in memory
// it is more logical and efficient to store in a different order.  Locales cannot use dashes, since the
// resulting path will not evaluate as valid JS, so we translate them to underscores.
	
	//Find the best-match locale to load if we have available flat locales.
	var bestLocale = "";
	if(availableFlatLocales){
		var flatLocales = availableFlatLocales.split(",");
		for(var i = 0; i < flatLocales.length; i++){
			//Locale must match from start of string.
			if(targetLocale.indexOf(flatLocales[i]) == 0){
				if(flatLocales[i].length > bestLocale.length){
					bestLocale = flatLocales[i];
				}
			}
		}
		if(!bestLocale){
			bestLocale = "ROOT";
		}		
	}

	//See if the desired locale is already loaded.
	var tempLocale = availableFlatLocales ? bestLocale : targetLocale;
	var bundle = dojo.hostenv.findModule(bundlePackage);
	var localizedBundle = null;
	if(bundle){
		if(djConfig.localizationComplete && bundle._built){return;}
		var jsLoc = tempLocale.replace('-', '_');
		var translationPackage = bundlePackage+"."+jsLoc;
		localizedBundle = dojo.hostenv.findModule(translationPackage);
	}

	if(!localizedBundle){
		bundle = dojo.hostenv.startPackage(bundlePackage);
		var syms = dojo.hostenv.getModuleSymbols(moduleName);
		var modpath = syms.concat("nls").join("/");
		var parent;

		dojo.hostenv.searchLocalePath(tempLocale, availableFlatLocales, function(loc){
			var jsLoc = loc.replace('-', '_');
			var translationPackage = bundlePackage + "." + jsLoc;
			var loaded = false;
			if(!dojo.hostenv.findModule(translationPackage)){
				// Mark loaded whether it's found or not, so that further load attempts will not be made
				dojo.hostenv.startPackage(translationPackage);
				var module = [modpath];
				if(loc != "ROOT"){module.push(loc);}
				module.push(bundleName);
				var filespec = module.join("/") + '.js';
				loaded = dojo.hostenv.loadPath(filespec, null, function(hash){
					// Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
					var clazz = function(){};
					clazz.prototype = parent;
					bundle[jsLoc] = new clazz();
					for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
				});
			}else{
				loaded = true;
			}
			if(loaded && bundle[jsLoc]){
				parent = bundle[jsLoc];
			}else{
				bundle[jsLoc] = parent;
			}
			
			if(availableFlatLocales){
				//Stop the locale path searching if we know the availableFlatLocales, since
				//the first call to this function will load the only bundle that is needed.
				return true;
			}
		});
	}

	//Save the best locale bundle as the target locale bundle when we know the
	//the available bundles.
	if(availableFlatLocales && targetLocale != bestLocale){
		bundle[targetLocale.replace('-', '_')] = bundle[bestLocale.replace('-', '_')];
	}
};

(function(){
	// If other locales are used, dojo.requireLocalization should load them as well, by default.
	// Override dojo.requireLocalization to do load the default bundle, then iterate through the
	// extraLocale list and load those translations as well, unless a particular locale was requested.

	var extra = djConfig.extraLocale;
	if(extra){
		if(!extra instanceof Array){
			extra = [extra];
		}

		var req = dojo.requireLocalization;
		dojo.requireLocalization = function(m, b, locale, availableFlatLocales){
			req(m,b,locale, availableFlatLocales);
			if(locale){return;}
			for(var i=0; i<extra.length; i++){
				req(m,b,extra[i], availableFlatLocales);
			}
		};
	}
})();

};

if(typeof window != 'undefined'){

	// attempt to figure out the path to dojo if it isn't set in the config
	(function(){
		// before we get any further with the config options, try to pick them out
		// of the URL. Most of this code is from NW
		if(djConfig.allowQueryConfig){
			var baseUrl = document.location.toString(); // FIXME: use location.query instead?
			var params = baseUrl.split("?", 2);
			if(params.length > 1){
				var paramStr = params[1];
				var pairs = paramStr.split("&");
				for(var x in pairs){
					var sp = pairs[x].split("=");
					// FIXME: is this eval dangerous?
					if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
						var opt = sp[0].substr(9);
						try{
							djConfig[opt]=eval(sp[1]);
						}catch(e){
							djConfig[opt]=sp[1];
						}
					}
				}
			}
		}

		if(
			((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) && 
			(document && document.getElementsByTagName)
		){
			var scripts = document.getElementsByTagName("script");
			var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
			for(var i = 0; i < scripts.length; i++) {
				var src = scripts[i].getAttribute("src");
				if(!src) { continue; }
				var m = src.match(rePkg);
				if(m) {
					var root = src.substring(0, m.index);
					if(src.indexOf("bootstrap1") > -1) { root += "../"; }
					if(!this["djConfig"]) { djConfig = {}; }
					if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
					if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
					break;
				}
			}
		}

		// fill in the rendering support information in dojo.render.*
		var dr = dojo.render;
		var drh = dojo.render.html;
		var drs = dojo.render.svg;
		var dua = (drh.UA = navigator.userAgent);
		var dav = (drh.AV = navigator.appVersion);
		var t = true;
		var f = false;
		drh.capable = t;
		drh.support.builtin = t;

		dr.ver = parseFloat(drh.AV);
		dr.os.mac = dav.indexOf("Macintosh") >= 0;
		dr.os.win = dav.indexOf("Windows") >= 0;
		// could also be Solaris or something, but it's the same browser
		dr.os.linux = dav.indexOf("X11") >= 0;

		drh.opera = dua.indexOf("Opera") >= 0;
		drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
		drh.safari = dav.indexOf("Safari") >= 0;
		var geckoPos = dua.indexOf("Gecko");
		drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
		if (drh.mozilla) {
			// gecko version is YYYYMMDD
			drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
		}
		drh.ie = (document.all)&&(!drh.opera);
		drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
		drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
		drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;
		drh.ie70 = drh.ie && dav.indexOf("MSIE 7.0")>=0;

		var cm = document["compatMode"];
		drh.quirks = (cm == "BackCompat")||(cm == "QuirksMode")||drh.ie55||drh.ie50;

		// TODO: is the HTML LANG attribute relevant?
		dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase();

		dr.vml.capable=drh.ie;
		drs.capable = f;
		drs.support.plugin = f;
		drs.support.builtin = f;
		var tdoc = window["document"];
		var tdi = tdoc["implementation"];

		if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg", "1.0"))){
			drs.capable = t;
			drs.support.builtin = t;
			drs.support.plugin = f;
		}
		// webkits after 420 support SVG natively. The test string is "AppleWebKit/420+"
		if(drh.safari){
			var tmp = dua.split("AppleWebKit/")[1];
			var ver = parseFloat(tmp.split(" ")[0]);
			if(ver >= 420){
				drs.capable = t;
				drs.support.builtin = t;
				drs.support.plugin = f;
			}
		}else{
		}
	})();

	dojo.hostenv.startPackage("dojo.hostenv");

	dojo.render.name = dojo.hostenv.name_ = 'browser';
	dojo.hostenv.searchIds = [];

	// These are in order of decreasing likelihood; this will change in time.
	dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];

	dojo.hostenv.getXmlhttpObject = function(){
		// summary: does the work of portably generating a new XMLHTTPRequest object.
		var http = null;
		var last_e = null;
		try{ http = new XMLHttpRequest(); }catch(e){}
		if(!http){
			for(var i=0; i<3; ++i){
				var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
				try{
					http = new ActiveXObject(progid);
				}catch(e){
					last_e = e;
				}

				if(http){
					dojo.hostenv._XMLHTTP_PROGIDS = [progid];  // so faster next time
					break;
				}
			}

			/*if(http && !http.toString) {
				http.toString = function() { "[object XMLHttpRequest]"; }
			}*/
		}

		if(!http){
			return dojo.raise("XMLHTTP not available", last_e);
		}

		return http; // XMLHTTPRequest instance
	}

	dojo.hostenv._blockAsync = false;
	dojo.hostenv.getText = function(uri, async_cb, fail_ok){
		// summary: Read the contents of the specified uri and return those contents.
		// uri:
		//		A relative or absolute uri. If absolute, it still must be in
		//		the same "domain" as we are.
		// async_cb:
		//		If not specified, load synchronously. If specified, load
		//		asynchronously, and use async_cb as the progress handler which
		//		takes the xmlhttp object as its argument. If async_cb, this
		//		function returns null.
		// fail_ok:
		//		Default false. If fail_ok and !async_cb and loading fails,
		//		return null instead of throwing.

		// need to block async callbacks from snatching this thread as the result
		// of an async callback might call another sync XHR, this hangs khtml forever
		// hostenv._blockAsync must also be checked in BrowserIO's watchInFlight()
		// NOTE: must be declared before scope switches ie. this.getXmlhttpObject()
		if(!async_cb){ this._blockAsync = true; }

		var http = this.getXmlhttpObject();

		function isDocumentOk(http){
			var stat = http["status"];
			// allow a 304 use cache, needed in konq (is this compliant with the http spec?)
			return Boolean((!stat)||((200 <= stat)&&(300 > stat))||(stat==304));
		}

		if(async_cb){
			var _this = this, timer = null, gbl = dojo.global();
			var xhr = dojo.evalObjPath("dojo.io.XMLHTTPTransport");
			http.onreadystatechange = function(){
				if(timer){ gbl.clearTimeout(timer); timer = null; }
				if(_this._blockAsync || (xhr && xhr._blockAsync)){
					timer = gbl.setTimeout(function () { http.onreadystatechange.apply(this); }, 10);
				}else{
					if(4==http.readyState){
						if(isDocumentOk(http)){
							// dojo.debug("LOADED URI: "+uri);
							async_cb(http.responseText);
						}
					}
				}
			}
		}

		http.open('GET', uri, async_cb ? true : false);
		try{
			http.send(null);
			if(async_cb){
				return null;
			}
			if(!isDocumentOk(http)){
				var err = Error("Unable to load "+uri+" status:"+ http.status);
				err.status = http.status;
				err.responseText = http.responseText;
				throw err;
			}
		}catch(e){
			this._blockAsync = false;
			if((fail_ok)&&(!async_cb)){
				return null;
			}else{
				throw e;
			}
		}

		this._blockAsync = false;
		return http.responseText; // String
	}

	dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
	dojo.hostenv._println_buffer = [];
	dojo.hostenv._println_safe = false;
	dojo.hostenv.println = function(/*String*/line){
		// summary:
		//		prints the provided line to whatever logging container is
		//		available. If the page isn't loaded yet, the line may be added
		//		to a buffer for printing later.
		if(!dojo.hostenv._println_safe){
			dojo.hostenv._println_buffer.push(line);
		}else{
			try {
				var console = document.getElementById(djConfig.debugContainerId ?
					djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
				if(!console) { console = dojo.body(); }

				var div = document.createElement("div");
				div.appendChild(document.createTextNode(line));
				console.appendChild(div);
			} catch (e) {
				try{
					// safari needs the output wrapped in an element for some reason
					document.write("<div>" + line + "</div>");
				}catch(e2){
					window.status = line;
				}
			}
		}
	}

	dojo.addOnLoad(function(){
		dojo.hostenv._println_safe = true;
		while(dojo.hostenv._println_buffer.length > 0){
			dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
		}
	});

	function dj_addNodeEvtHdlr(/*DomNode*/node, /*String*/evtName, /*Function*/fp){
		// summary:
		//		non-destructively adds the specified function to the node's
		//		evtName handler.
		// node: the DomNode to add the handler to
		// evtName: should be in the form "click" for "onclick" handlers
		var oldHandler = node["on"+evtName] || function(){};
		node["on"+evtName] = function(){
			fp.apply(node, arguments);
			oldHandler.apply(node, arguments);
		}
		return true;
	}

	dojo.hostenv._djInitFired = false;
	//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
	function dj_load_init(e){
		dojo.hostenv._djInitFired = true;
		// allow multiple calls, only first one will take effect
		// A bug in khtml calls events callbacks for document for event which isnt supported
		// for example a created contextmenu event calls DOMContentLoaded, workaround
		var type = (e && e.type) ? e.type.toLowerCase() : "load";
		if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }
		arguments.callee.initialized = true;
		if(typeof(_timer) != 'undefined'){
			clearInterval(_timer);
			delete _timer;
		}

		var initFunc = function(){
			//perform initialization
			if(dojo.render.html.ie){
				dojo.hostenv.makeWidgets();
			}
		};

		if(dojo.hostenv.inFlightCount == 0){
			initFunc();
			dojo.hostenv.modulesLoaded();
		}else{
			//This else case should be xdomain loading.
			//Make sure this is the first thing in the load listener array.
			//Part of the dojo.addOnLoad guarantee is that when the listeners are notified,
			//It means the DOM (or page) has loaded and that widgets have been parsed.
			dojo.hostenv.modulesLoadedListeners.unshift(initFunc);
		}
	}

	//	START DOMContentLoaded
	// Mozilla and Opera 9 expose the event we could use
	if(document.addEventListener){
		// NOTE: 
		//		due to a threading issue in Firefox 2.0, we can't enable
		//		DOMContentLoaded on that platform. For more information, see:
		//		http://trac.dojotoolkit.org/ticket/1704
		if(dojo.render.html.opera || (dojo.render.html.moz && (djConfig["enableMozDomContentLoaded"] === true))){
			document.addEventListener("DOMContentLoaded", dj_load_init, null);
		}

		//	mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
		//  also used for Mozilla because of trac #1640
		window.addEventListener("load", dj_load_init, null);
	}

	// 	for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
	//	however, we'll include it because we don't know if there are other functions added that might.
	//	Note that this has changed because the build process strips all comments--including conditional
	//		ones.
	if(dojo.render.html.ie && dojo.render.os.win){
		document.attachEvent("onreadystatechange", function(e){
			if(document.readyState == "complete"){
				dj_load_init();
			}
		});
	}

	if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff
		var _timer = setInterval(function() {
			if (/loaded|complete/.test(document.readyState)) {
				dj_load_init(); // call the onload handler
			}
		}, 10);
	}
	//	END DOMContentLoaded

	// IE WebControl hosted in an application can fire "beforeunload" and "unload"
	// events when control visibility changes, causing Dojo to unload too soon. The
	// following code fixes the problem
	// Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
	if(dojo.render.html.ie){
		dj_addNodeEvtHdlr(window, "beforeunload", function(){
			dojo.hostenv._unloading = true;
			window.setTimeout(function() {
				dojo.hostenv._unloading = false;
			}, 0);
		});
	}

	dj_addNodeEvtHdlr(window, "unload", function(){
		dojo.hostenv.unloaded();
		if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){
			dojo.hostenv.unloaded();
		}
	});

	dojo.hostenv.makeWidgets = function(){
		// you can put searchIds in djConfig and dojo.hostenv at the moment
		// we should probably eventually move to one or the other
		var sids = [];
		if(djConfig.searchIds && djConfig.searchIds.length > 0) {
			sids = sids.concat(djConfig.searchIds);
		}
		if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
			sids = sids.concat(dojo.hostenv.searchIds);
		}

		if((djConfig.parseWidgets)||(sids.length > 0)){
			if(dojo.evalObjPath("dojo.widget.Parse")){
				// we must do this on a delay to avoid:
				//	http://www.shaftek.org/blog/archives/000212.html
				// (IE bug)
					var parser = new dojo.xml.Parse();
					if(sids.length > 0){
						for(var x=0; x<sids.length; x++){
							var tmpNode = document.getElementById(sids[x]);
							if(!tmpNode){ continue; }
							var frag = parser.parseElement(tmpNode, null, true);
							dojo.widget.getParser().createComponents(frag);
						}
					}else if(djConfig.parseWidgets){
						var frag  = parser.parseElement(dojo.body(), null, true);
						dojo.widget.getParser().createComponents(frag);
					}
			}
		}
	}

	dojo.addOnLoad(function(){
		if(!dojo.render.html.ie) {
			dojo.hostenv.makeWidgets();
		}
	});

	try{
		if(dojo.render.html.ie){
			document.namespaces.add("v","urn:schemas-microsoft-com:vml");
			document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
		}
	}catch(e){ }

	// stub, over-ridden by debugging code. This will at least keep us from
	// breaking when it's not included
	dojo.hostenv.writeIncludes = function(){}

	//TODOC:  HOW TO DOC THIS?
	// @global: dj_currentDocument
	// summary:
	//		Current document object. 'dj_currentDocument' can be modified for temporary context shifting.
	// description:
	//    dojo.doc() returns dojo.currentDocument.
	//		Refer to dojo.doc() rather than referring to 'window.document' to ensure your
	//		code runs correctly in managed contexts.
	if(!dj_undef("document", this)){
		dj_currentDocument = this.document;
	}

	dojo.doc = function(){
		// summary:
		//		return the document object associated with the dojo.global()
		return dj_currentDocument;
	}

	dojo.body = function(){
		// summary:
		//		return the body object associated with dojo.doc()
		// Note: document.body is not defined for a strict xhtml document
		return dojo.doc().body || dojo.doc().getElementsByTagName("body")[0];
	}

	dojo.byId = function(/*String*/id, /*DocumentElement*/doc){
		// summary:
		// 		similar to other library's "$" function, takes a string
		// 		representing a DOM id or a DomNode and returns the
		// 		corresponding DomNode. If a Node is passed, this function is a
		// 		no-op. Returns a single DOM node or null, working around
		// 		several browser-specific bugs to do so.
		// id: DOM id or DOM Node
		// doc:
		//		optional, defaults to the current value of dj_currentDocument.
		//		Can be used to retreive node references from other documents.
		if((id)&&((typeof id == "string")||(id instanceof String))){
			if(!doc){ doc = dj_currentDocument; }
			var ele = doc.getElementById(id);
			// workaround bug in IE and Opera 8.2 where getElementById returns wrong element
			if(ele && (ele.id != id) && doc.all){
				ele = null;
				// get all matching elements with this id
				eles = doc.all[id];
				if(eles){
					// if more than 1, choose first with the correct id
					if(eles.length){
						for(var i=0; i<eles.length; i++){
							if(eles[i].id == id){
								ele = eles[i];
								break;
							}
						}
					// return 1 and only element
					}else{
						ele = eles;
					}
				}
			}
			return ele; // DomNode
		}
		return id; // DomNode
	}

	dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){
		// summary:
		//		changes the behavior of many core Dojo functions that deal with
		//		namespace and DOM lookup, changing them to work in a new global
		//		context. The varibles dj_currentContext and dj_currentDocument
		//		are modified as a result of calling this function.
		dj_currentContext = globalObject;
		dj_currentDocument = globalDocument;
	};

	dojo._fireCallback = function(callback, context, cbArguments){
		if((context)&&((typeof callback == "string")||(callback instanceof String))){
			callback=context[callback];
		}
		return (context ? callback.apply(context, cbArguments || [ ]) : callback());
	}

	dojo.withGlobal = function(/*Object*/globalObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments){
		// summary:
		//		Call callback with globalObject as dojo.global() and globalObject.document
		//		as dojo.doc(). If provided, globalObject will be executed in the context of
		//		object thisObject
		// description:
		//		When callback() returns or throws an error, the dojo.global() and dojo.doc() will
		//		be restored to its previous state.
		var rval;
		var oldGlob = dj_currentContext;
		var oldDoc = dj_currentDocument;
		try{
			dojo.setContext(globalObject, globalObject.document);
			rval = dojo._fireCallback(callback, thisObject, cbArguments);
		}finally{
			dojo.setContext(oldGlob, oldDoc);
		}
		return rval;
	}

	dojo.withDoc = function (/*Object*/documentObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments) {
		// summary:
		//		Call callback with documentObject as dojo.doc(). If provided, callback will be executed
		//		in the context of object thisObject
		// description:
		//		When callback() returns or throws an error, the dojo.doc() will
		//		be restored to its previous state.
		var rval;
		var oldDoc = dj_currentDocument;
		try{
			dj_currentDocument = documentObject;
			rval = dojo._fireCallback(callback, thisObject, cbArguments);
		}finally{
			dj_currentDocument = oldDoc;
		}
		return rval;
	}

} //if (typeof window != 'undefined')

//Load debug code if necessary.
dojo.requireIf((djConfig["isDebug"] || djConfig["debugAtAllCosts"]), "dojo.debug");

//window.widget is for Dashboard detection
//The full conditionals are spelled out to avoid issues during builds.
//Builds may be looking for require/requireIf statements and processing them.
dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && !djConfig["useXDomain"], "dojo.browser_debug");
dojo.requireIf(djConfig["debugAtAllCosts"] && !window.widget && djConfig["useXDomain"], "dojo.browser_debug_xd");

dojo.provide("dojo.string.common");

dojo.string.trim = function(/* string */str, /* integer? */wh){
	//	summary
	//	Trim whitespace from str.  If wh > 0, trim from start, if wh < 0, trim from end, else both
	if(!str.replace){ return str; }
	if(!str.length){ return str; }
	var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
	return str.replace(re, "");	//	string
}

dojo.string.trimStart = function(/* string */str) {
	//	summary
	//	Trim whitespace at the beginning of 'str'
	return dojo.string.trim(str, 1);	//	string
}

dojo.string.trimEnd = function(/* string */str) {
	//	summary
	//	Trim whitespace at the end of 'str'
	return dojo.string.trim(str, -1);
}

dojo.string.repeat = function(/* string */str, /* integer */count, /* string? */separator) {
	//	summary
	//	Return 'str' repeated 'count' times, optionally placing 'separator' between each rep
	var out = "";
	for(var i = 0; i < count; i++) {
		out += str;
		if(separator && i < count - 1) {
			out += separator;
		}
	}
	return out;	//	string
}

dojo.string.pad = function(/* string */str, /* integer */len/*=2*/, /* string */ c/*='0'*/, /* integer */dir/*=1*/) {
	//	summary
	//	Pad 'str' to guarantee that it is at least 'len' length with the character 'c' at either the 
	//	start (dir=1) or end (dir=-1) of the string
	var out = String(str);
	if(!c) {
		c = '0';
	}
	if(!dir) {
		dir = 1;
	}
	while(out.length < len) {
		if(dir > 0) {
			out = c + out;
		} else {
			out += c;
		}
	}
	return out;	//	string
}

dojo.string.padLeft = function(/* string */str, /* integer */len, /* string */c) {
	//	summary
	//	same as dojo.string.pad(str, len, c, 1)
	return dojo.string.pad(str, len, c, 1);	//	string
}

dojo.string.padRight = function(/* string */str, /* integer */len, /* string */c) {
	//	summary
	//	same as dojo.string.pad(str, len, c, -1)
	return dojo.string.pad(str, len, c, -1);	//	string
}

dojo.provide("dojo.string");


dojo.provide("dojo.lang.common");

dojo.lang.inherits = function(/*Function*/subclass, /*Function*/superclass){
	// summary: Set up inheritance between two classes.
	if(!dojo.lang.isFunction(superclass)){ 
		dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: ["+subclass+"']");
	}
	subclass.prototype = new superclass();
	subclass.prototype.constructor = subclass;
	subclass.superclass = superclass.prototype;
	// DEPRECATED: super is a reserved word, use 'superclass'
	subclass['super'] = superclass.prototype;
}

dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
	// summary:
	//		Adds all properties and methods of props to obj. This addition is
	//		"prototype extension safe", so that instances of objects will not
	//		pass along prototype defaults.
	var tobj = {};
	for(var x in props){
		// the "tobj" condition avoid copying properties in "props"
		// inherited from Object.prototype.  For example, if obj has a custom
		// toString() method, don't overwrite it with the toString() method
		// that props inherited from Object.protoype
		if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
			obj[x] = props[x];
		}
	}
	// IE doesn't recognize custom toStrings in for..in
	if(dojo.render.html.ie 
		&& (typeof(props["toString"]) == "function")
		&& (props["toString"] != obj["toString"])
		&& (props["toString"] != tobj["toString"]))
	{
		obj.toString = props.toString;
	}
	return obj; // Object
}

dojo.lang.mixin = function(/*Object*/obj, /*Object...*/props){
	// summary:	Adds all properties and methods of props to obj. 
	for(var i=1, l=arguments.length; i<l; i++){
		dojo.lang._mixin(obj, arguments[i]);
	}
	return obj; // Object
}

dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
	// summary:
	//		Adds all properties and methods of props to constructor's
	//		prototype, making them available to all instances created with
	//		constructor.
	for(var i=1, l=arguments.length; i<l; i++){
		dojo.lang._mixin(constructor.prototype, arguments[i]);
	}
	return constructor; // Object
}

// Promote to dojo module
dojo.inherits = dojo.lang.inherits;
//dojo.lang._mixin = dojo.lang._mixin;
dojo.mixin = dojo.lang.mixin;
dojo.extend = dojo.lang.extend;

dojo.lang.find = function(	/*Array*/		array, 
							/*Object*/		value,
							/*Boolean?*/	identity,
							/*Boolean?*/	findLast){
	// summary:	
	//		Return the index of value in array, returning -1 if not found.
	// array: just what you think
	// value: the value to locate
	// identity: 
	//		If true, matches with identity comparison (===). If false, uses
	//		normal comparison (==).
	// findLast: 
	//		If true, returns index of last instance of value.
	// examples:
	//		find(array, value[, identity [findLast]]) // recommended
 	//		find(value, array[, identity [findLast]]) // deprecated
							
	// support both (array, value) and (value, array)
	if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) {
		dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5");
		var temp = array;
		array = value;
		value = temp;
	}
	var isString = dojo.lang.isString(array);
	if(isString) { array = array.split(""); }

	if(findLast) {
		var step = -1;
		var i = array.length - 1;
		var end = -1;
	} else {
		var step = 1;
		var i = 0;
		var end = array.length;
	}
	if(identity){
		while(i != end) {
			if(array[i] === value){ return i; }
			i += step;
		}
	}else{
		while(i != end) {
			if(array[i] == value){ return i; }
			i += step;
		}
	}
	return -1;	// number
}

dojo.lang.indexOf = dojo.lang.find;

dojo.lang.findLast = function(/*Array*/array, /*Object*/value, /*boolean?*/identity){
	// summary:
	//		Return index of last occurance of value in array, returning -1 if
	//		not found. This is a shortcut for dojo.lang.find() with a true
	//		value for its "findLast" parameter.
	// identity:
	//		If true, matches with identity comparison (===). If false, uses
	//		normal comparison (==).
	return dojo.lang.find(array, value, identity, true); // number
}

dojo.lang.lastIndexOf = dojo.lang.findLast;

dojo.lang.inArray = function(array /*Array*/, value /*Object*/){
	// summary:	Return true if value is present in array.
	return dojo.lang.find(array, value) > -1; // boolean
}

/**
 * Partial implmentation of is* functions from
 * http://www.crockford.com/javascript/recommend.html
 * NOTE: some of these may not be the best thing to use in all situations
 * as they aren't part of core JS and therefore can't work in every case.
 * See WARNING messages inline for tips.
 *
 * The following is* functions are fairly "safe"
 */

dojo.lang.isObject = function(/*anything*/ it){
	// summary:	Return true if it is an Object, Array or Function.
	if(typeof it == "undefined"){ return false; }
	return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean
}

dojo.lang.isArray = function(/*anything*/ it){
	// summary:	Return true if it is an Array.
	return (it && it instanceof Array || typeof it == "array"); // Boolean
}

dojo.lang.isArrayLike = function(/*anything*/ it){
	// summary:	
	//		Return true if it can be used as an array (i.e. is an object with
	//		an integer length property).
	if((!it)||(dojo.lang.isUndefined(it))){ return false; }
	if(dojo.lang.isString(it)){ return false; }
	if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties
	if(dojo.lang.isArray(it)){ return true; }
	// form node itself is ArrayLike, but not always iterable. Use form.elements instead.
	if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; }
	if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; }
	return false; // Boolean
}

dojo.lang.isFunction = function(/*anything*/ it){
	// summary:	Return true if it is a Function.
	return (it instanceof Function || typeof it == "function"); // Boolean
};

(function(){
	// webkit treats NodeList as a function, which is bad
	if((dojo.render.html.capable)&&(dojo.render.html["safari"])){
		dojo.lang.isFunction = function(/*anything*/ it){
			if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; }
			return (it instanceof Function || typeof it == "function"); // Boolean
		}
	}
})();

dojo.lang.isString = function(/*anything*/ it){
	// summary:	Return true if it is a String.
	return (typeof it == "string" || it instanceof String);
}

dojo.lang.isAlien = function(/*anything*/ it){
	// summary: Return true if it is not a built-in function. False if not.
	if(!it){ return false; }
	return !dojo.lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
}

dojo.lang.isBoolean = function(/*anything*/ it){
	// summary:	Return true if it is a Boolean.
	return (it instanceof Boolean || typeof it == "boolean"); // Boolean
}

/**
 * The following is***() functions are somewhat "unsafe". Fortunately,
 * there are workarounds the the language provides and are mentioned
 * in the WARNING messages.
 *
 */
dojo.lang.isNumber = function(/*anything*/ it){
	// summary:	Return true if it is a number.
	// description: 
	//		WARNING - In most cases, isNaN(it) is sufficient to determine whether or not
	// 		something is a number or can be used as such. For example, a number or string
	// 		can be used interchangably when accessing array items (array["1"] is the same as
	// 		array[1]) and isNaN will return false for both values ("1" and 1). However,
	// 		isNumber("1")  will return false, which is generally not too useful.
	// 		Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
	// 		are corner cases (like when you want to make sure that two things are really
	// 		the same type of thing). That is really where isNumber "shines".
	//
	// Recommendation - Use isNaN(it) when possible
	
	return (it instanceof Number || typeof it == "number"); // Boolean
}

/*
 * FIXME: Should isUndefined go away since it is error prone?
 */
dojo.lang.isUndefined = function(/*anything*/ it){
	// summary: Return true if it is not defined.
	// description: 
	//		WARNING - In some cases, isUndefined will not behave as you
	// 		might expect. If you do isUndefined(foo) and there is no earlier
	// 		reference to foo, an error will be thrown before isUndefined is
	// 		called. It behaves correctly if you scope yor object first, i.e.
	// 		isUndefined(foo.bar) where foo is an object and bar isn't a
	// 		property of the object.
	//
	// Recommendation - Use typeof foo == "undefined" when possible

	return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean
}

// end Crockford functions

dojo.provide("dojo.lang.extras");



dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){
	// summary:
	//		Sets a timeout in milliseconds to execute a function in a given
	//		context with optional arguments.
	// usage:
	//		dojo.lang.setTimeout(Object context, function func, number delay[, arg1[, ...]]);
	//		dojo.lang.setTimeout(function func, number delay[, arg1[, ...]]);

	var context = window, argsStart = 2;
	if(!dojo.lang.isFunction(func)){
		context = func;
		func = delay;
		delay = arguments[2];
		argsStart++;
	}

	if(dojo.lang.isString(func)){
		func = context[func];
	}
	
	var args = [];
	for (var i = argsStart; i < arguments.length; i++){
		args.push(arguments[i]);
	}
	return dojo.global().setTimeout(function(){ func.apply(context, args); }, delay); // int
}

dojo.lang.clearTimeout = function(/*int*/timer){
	// summary: clears timer by number from the execution queue

	// FIXME:
	//		why do we have this function? It's not portable outside of browser
	//		environments and it's a stupid wrapper on something that browsers
	//		provide anyway.
	dojo.global().clearTimeout(timer);
}

dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){
	// summary: 
	//		looks for a value in the object ns with a value matching item and
	//		returns the property name
	// ns: if null, dj_global is used
	// item: value to return a name for
	if(!ns){ ns = dj_global; }

	for(var x in ns){
		if(ns[x] === item){
			return new String(x); // String
		}
	}
	return null; // null
}

dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){
	// summary:
	//		copies object obj one level deep, or full depth if deep is true
	var i, ret;	

	if(obj === null){ /*obj: null*/ return null; } // null
	
	if(dojo.lang.isObject(obj)){
		// obj: Object	
		ret = new obj.constructor();
		for(i in obj){
			if(dojo.lang.isUndefined(ret[i])){
				ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
			}
		}
	}else if(dojo.lang.isArray(obj)){
		// obj: Array
		ret = [];
		for(i=0; i<obj.length; i++){
			ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
		}
	}else{
		// obj: Object
		ret = obj;
	}

	return ret; // Object
}

dojo.lang.firstValued = function(/* ... */){
	// summary: Return the first argument that isn't undefined

	for(var i = 0; i < arguments.length; i++){
		if(typeof arguments[i] != "undefined"){
			return arguments[i]; // Object
		}
	}
	return undefined; // undefined
}

dojo.lang.getObjPathValue = function(/*String*/objpath, /*Object?*/context, /*Boolean?*/create){
	// summary:
	//		Gets a value from a reference specified as a string descriptor,
	//		(e.g. "A.B") in the given context.
	// context: if not specified, dj_global is used
	// create: if true, undefined objects in the path are created.
	with(dojo.parseObjPath(objpath, context, create)){
		return dojo.evalProp(prop, obj, create); // Object
	}
}

dojo.lang.setObjPathValue = function(/*String*/objpath, /*anything*/value, /*Object?*/context, /*Boolean?*/create){
	// summary:
	//		Sets a value on a reference specified as a string descriptor.
	//		(e.g. "A.B") in the given context. This is similar to straight
	//		assignment, except that the object structure in question can
	//		optionally be created if it does not exist.
	//	context: if not specified, dj_global is used
	//	create: if true, undefined objects in the path are created.

	// FIXME: why is this function valuable? It should be scheduled for
	// removal on the grounds that dojo.parseObjPath does most of it's work and
	// is more straightforward and has fewer dependencies. Also, the order of
	// arguments is bone-headed. "context" should clearly come after "create".
	// *sigh*
	dojo.deprecated("dojo.lang.setObjPathValue", "use dojo.parseObjPath and the '=' operator", "0.6");

	if(arguments.length < 4){
		create = true;
	}
	with(dojo.parseObjPath(objpath, context, create)){
		if(obj && (create || (prop in obj))){
			obj[prop] = value;
		}
	}
}

dojo.provide("dojo.io.common");



/******************************************************************************
 *	Notes about dojo.io design:
 *	
 *	The dojo.io.* package has the unenviable task of making a lot of different
 *	types of I/O feel natural, despite a universal lack of good (or even
 *	reasonable!) I/O capability in the host environment. So lets pin this down
 *	a little bit further.
 *
 *	Rhino:
 *		perhaps the best situation anywhere. Access to Java classes allows you
 *		to do anything one might want in terms of I/O, both synchronously and
 *		async. Can open TCP sockets and perform low-latency client/server
 *		interactions. HTTP transport is available through Java HTTP client and
 *		server classes. Wish it were always this easy.
 *
 *	xpcshell:
 *		XPCOM for I/O.
 *
 *	spidermonkey:
 *		S.O.L.
 *
 *	Browsers:
 *		Browsers generally do not provide any useable filesystem access. We are
 *		therefore limited to HTTP for moving information to and from Dojo
 *		instances living in a browser.
 *
 *		XMLHTTP:
 *			Sync or async, allows reading of arbitrary text files (including
 *			JS, which can then be eval()'d), writing requires server
 *			cooperation and is limited to HTTP mechanisms (POST and GET).
 *
 *		<iframe> hacks:
 *			iframe document hacks allow browsers to communicate asynchronously
 *			with a server via HTTP POST and GET operations. With significant
 *			effort and server cooperation, low-latency data transit between
 *			client and server can be acheived via iframe mechanisms (repubsub).
 *
 *		SVG:
 *			Adobe's SVG viewer implements helpful primitives for XML-based
 *			requests, but receipt of arbitrary text data seems unlikely w/o
 *			<![CDATA[]]> sections.
 *
 *
 *	A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
 *	the IO API interface. A transcript of it can be found at:
 *		http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
 *	
 *	Also referenced in the design of the API was the DOM 3 L&S spec:
 *		http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
 ******************************************************************************/

// a map of the available transport options. Transports should add themselves
// by calling add(name)
dojo.io.transports = [];
dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now

dojo.io.Request = function(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){
// summary:
//		Constructs a Request object that is used by dojo.io.bind().
// description:
//		dojo.io.bind() will create one of these for you if
//		you call dojo.io.bind() with an plain object containing the bind parameters.
//		This method can either take the arguments specified, or an Object containing all of the parameters that you
//		want to use to create the dojo.io.Request (similar to how dojo.io.bind() is called.
//		The named parameters to this constructor represent the minimum set of parameters need
	if((arguments.length == 1)&&(arguments[0].constructor == Object)){
		this.fromKwArgs(arguments[0]);
	}else{
		this.url = url;
		if(mimetype){ this.mimetype = mimetype; }
		if(transport){ this.transport = transport; }
		if(arguments.length >= 4){ this.changeUrl = changeUrl; }
	}
}

dojo.lang.extend(dojo.io.Request, {

	/** The URL to hit */
	url: "",
	
	/** The mime type used to interrpret the response body */
	mimetype: "text/plain",
	
	/** The HTTP method to use */
	method: "GET",
	
	/** An Object containing key-value pairs to be included with the request */
	content: undefined, // Object
	
	/** The transport medium to use */
	transport: undefined, // String
	
	/** If defined the URL of the page is physically changed */
	changeUrl: undefined, // String
	
	/** A form node to use in the request */
	formNode: undefined, // HTMLFormElement
	
	/** Whether the request should be made synchronously */
	sync: false,
	
	bindSuccess: false,

	/** Cache/look for the request in the cache before attempting to request?
	 *  NOTE: this isn't a browser cache, this is internal and would only cache in-page
	 */
	useCache: false,

	/** Prevent the browser from caching this by adding a query string argument to the URL */
	preventCache: false,

	jsonFilter: function(value){
		if(	(this.mimetype == "text/json-comment-filtered")||
			(this.mimetype == "application/json-comment-filtered")
		){
			var cStartIdx = value.indexOf("\/*");
			var cEndIdx = value.lastIndexOf("*\/");
			if((cStartIdx == -1)||(cEndIdx == -1)){
				dojo.debug("your JSON wasn't comment filtered!"); // FIXME: throw exception instead?
				return "";
			}
			return value.substring(cStartIdx+2, cEndIdx);
		}
		dojo.debug("please consider using a mimetype of text/json-comment-filtered to avoid potential security issues with JSON endpoints");
		return value;
	},
	
	// events stuff
	load: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
		// summary:
		//		Called on successful completion of a bind.
		//		type: String
		//				A string with value "load"
		//		data: Object
		//				The object representing the result of the bind. The actual structure
		//				of the data object will depend on the mimetype that was given to bind
		//				in the bind arguments.
		//		transportImplementation: Object
		//				The object that implements a particular transport. Structure is depedent
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
		//				XMLHttpRequest object from the browser.
		//		kwArgs: Object
		//				Object that contains the request parameters that were given to the
		//				bind call. Useful for storing and retrieving state from when bind
		//				was called.
	},
	error: function(/*String*/type, /*Object*/error, /*Object*/transportImplementation, /*Object*/kwArgs){
		// summary:
		//		Called when there is an error with a bind.
		//		type: String
		//				A string with value "error"
		//		error: Object
		//				The error object. Should be a dojo.io.Error object, but not guaranteed.
		//		transportImplementation: Object
		//				The object that implements a particular transport. Structure is depedent
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
		//				XMLHttpRequest object from the browser.
		//		kwArgs: Object
		//				Object that contains the request parameters that were given to the
		//				bind call. Useful for storing and retrieving state from when bind
		//				was called.
	},
	timeout: function(/*String*/type, /*Object*/empty, /*Object*/transportImplementation, /*Object*/kwArgs){
		// summary:
		//		Called when there is an error with a bind. Only implemented in certain transports at this time.
		//		type: String
		//				A string with value "timeout"
		//		empty: Object
		//				Should be null. Just a spacer argument so that load, error, timeout and handle have the
		//				same signatures.
		//		transportImplementation: Object
		//				The object that implements a particular transport. Structure is depedent
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
		//				XMLHttpRequest object from the browser. May be null for the timeout case for
		//				some transports.
		//		kwArgs: Object
		//				Object that contains the request parameters that were given to the
		//				bind call. Useful for storing and retrieving state from when bind
		//				was called.
	},
	handle: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){
		// summary:
		//		The handle method can be defined instead of defining separate load, error and timeout
		//		callbacks.
		//		type: String
		//				A string with the type of callback: "load", "error", or "timeout".
		//		data: Object
		//				See the above callbacks for what this parameter could be.
		//		transportImplementation: Object
		//				The object that implements a particular transport. Structure is depedent
		//				on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
		//				XMLHttpRequest object from the browser.
		//		kwArgs: Object
		//				Object that contains the request parameters that were given to the
		//				bind call. Useful for storing and retrieving state from when bind
		//				was called.	
	},

	//FIXME: change IframeIO.js to use timeouts?
	// The number of seconds to wait until firing a timeout callback.
	// If it is zero, that means, don't do a timeout check.
	timeoutSeconds: 0,
	
	// the abort method needs to be filled in by the transport that accepts the
	// bind() request
	abort: function(){ },
	
	// backButton: function(){ },
	// forwardButton: function(){ },

	fromKwArgs: function(/*Object*/ kwArgs){
		// summary:
		//		Creates a dojo.io.Request from a simple object (kwArgs object).

		// normalize args
		if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
		if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
		if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
			kwArgs.method = kwArgs["formNode"].method;
		}
		
		// backwards compatibility
		if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
		if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
		if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }

		// encoding fun!
		kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");

		kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);

		var isFunction = dojo.lang.isFunction;
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
			var fn = dojo.io.hdlrFuncNames[x];
			if(kwArgs[fn] && isFunction(kwArgs[fn])){ continue; }
			if(kwArgs["handle"] && isFunction(kwArgs["handle"])){
				kwArgs[fn] = kwArgs.handle;
			}
			// handler is aliased above, shouldn't need this check
			/* else if(dojo.lang.isObject(kwArgs.handler)){
				if(isFunction(kwArgs.handler[fn])){
					kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
				}
			}*/
		}
		dojo.lang.mixin(this, kwArgs);
	}

});

dojo.io.Error = function(/*String*/ msg, /*String*/ type, /*Number*/num){
	// summary:
	//		Constructs an object representing a bind error.
	this.message = msg;
	this.type =  type || "unknown"; // must be one of "io", "parse", "unknown"
	this.number = num || 0; // per-substrate error number, not normalized
}

dojo.io.transports.addTransport = function(/*String*/name){
	// summary:
	//		Used to register transports that can support bind calls.
	this.push(name);
	// FIXME: do we need to handle things that aren't direct children of the
	// dojo.io module? (say, dojo.io.foo.fooTransport?)
	this[name] = dojo.io[name];
}

// binding interface, the various implementations register their capabilities
// and the bind() method dispatches
dojo.io.bind = function(/*dojo.io.Request or Object*/request){
	// summary:
	//		Binding interface for IO. Loading different IO transports, like
	//		dojo.io.BrowserIO or dojo.io.IframeIO, will register with bind
	//		to handle particular types of bind calls.
	//		request: Object
	//				Object containing bind arguments. This object is converted to
	//				a dojo.io.Request object, and that request object is the return
	//				value for this method.
	if(!(request instanceof dojo.io.Request)){
		try{
			request = new dojo.io.Request(request);
		}catch(e){ dojo.debug(e); }
	}

	// if the request asks for a particular implementation, use it
	var tsName = "";
	if(request["transport"]){
		tsName = request["transport"];
		if(!this[tsName]){
			dojo.io.sendBindError(request, "No dojo.io.bind() transport with name '"
				+ request["transport"] + "'.");
			return request; //dojo.io.Request
		}
		if(!this[tsName].canHandle(request)){
			dojo.io.sendBindError(request, "dojo.io.bind() transport with name '"
				+ request["transport"] + "' cannot handle this type of request.");
			return request;	//dojo.io.Request
		}
	}else{
		// otherwise we do our best to auto-detect what available transports
		// will handle 
		for(var x=0; x<dojo.io.transports.length; x++){
			var tmp = dojo.io.transports[x];
			if((this[tmp])&&(this[tmp].canHandle(request))){
				tsName = tmp;
				break;
			}
		}
		if(tsName == ""){
			dojo.io.sendBindError(request, "None of the loaded transports for dojo.io.bind()"
				+ " can handle the request.");
			return request; //dojo.io.Request
		}
	}
	this[tsName].bind(request);
	request.bindSuccess = true;
	return request; //dojo.io.Request
}

dojo.io.sendBindError = function(/* Object */request, /* String */message){
	// summary:
	//		Used internally by dojo.io.bind() to return/raise a bind error.

	//Need to be careful since not all hostenvs support setTimeout.
	if((typeof request.error == "function" || typeof request.handle == "function")
		&& (typeof setTimeout == "function" || typeof setTimeout == "object")){
		var errorObject = new dojo.io.Error(message);
		setTimeout(function(){
			request[(typeof request.error == "function") ? "error" : "handle"]("error", errorObject, null, request);
		}, 50);
	}else{
		dojo.raise(message);
	}
}

dojo.io.queueBind = function(/*dojo.io.Request or Object*/request){
	// summary:
	//		queueBind will use dojo.io.bind() but guarantee that only one bind
	//		call is handled at a time.
	// description:
	//		If queueBind is called while a bind call
	//		is in process, it will queue up the other calls to bind and call them
	//		in order as bind calls complete.
	//		request: Object
	//			Same sort of request object as used for dojo.io.bind().
	if(!(request instanceof dojo.io.Request)){
		try{
			request = new dojo.io.Request(request);
		}catch(e){ dojo.debug(e); }
	}

	// make sure we get called if/when we get a response
	var oldLoad = request.load;
	request.load = function(){
		dojo.io._queueBindInFlight = false;
		var ret = oldLoad.apply(this, arguments);
		dojo.io._dispatchNextQueueBind();
		return ret;
	}

	var oldErr = request.error;
	request.error = function(){
		dojo.io._queueBindInFlight = false;
		var ret = oldErr.apply(this, arguments);
		dojo.io._dispatchNextQueueBind();
		return ret;
	}

	dojo.io._bindQueue.push(request);
	dojo.io._dispatchNextQueueBind();
	return request; //dojo.io.Request
}

dojo.io._dispatchNextQueueBind = function(){
	// summary:
	//	Private method used by dojo.io.queueBind().
	if(!dojo.io._queueBindInFlight){
		dojo.io._queueBindInFlight = true;
		if(dojo.io._bindQueue.length > 0){
			dojo.io.bind(dojo.io._bindQueue.shift());
		}else{
			dojo.io._queueBindInFlight = false;
		}
	}
}
dojo.io._bindQueue = [];
dojo.io._queueBindInFlight = false;

dojo.io.argsFromMap = function(/*Object*/map, /*String?*/encoding, /*String?*/last){
	// summary:
	//		Converts name/values pairs in the map object to an URL-encoded string
	//		with format of name1=value1&name2=value2...
	//		map: Object
	//			Object that has the contains the names and values.
	//		encoding: String?
	//			String to specify how to encode the name and value. If the encoding string
	//			contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise
	//			dojo.string.encodeAscii is used.
	//		last: String?
	//			The last parameter in the list. Helps with final string formatting?
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
	var mapped = [];
	var control = new Object();
	for(var name in map){
		var domap = function(elt){
			var val = enc(name)+"="+enc(elt);
			mapped[(last == name) ? "push" : "unshift"](val);
		}
		if(!control[name]){
			var value = map[name];
			// FIXME: should be isArrayLike?
			if (dojo.lang.isArray(value)){
				dojo.lang.forEach(value, domap);
			}else{
				domap(value);
			}
		}
	}
	return mapped.join("&"); //String
}

dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){
	//summary:
	//		Sets the URL that is loaded in an IFrame. The replace parameter indicates whether
	//		location.replace() should be used when changing the location of the iframe.
	try{
		var r = dojo.render.html;
		// dojo.debug(iframe);
		if(!replace){
			if(r.safari){
				iframe.location = src;
			}else{
				frames[iframe.name].location = src;
			}
		}else{
			// Fun with DOM 0 incompatibilities!
			var idoc;
			if(r.ie){
				idoc = iframe.contentWindow.document;
			}else if(r.safari){
				idoc = iframe.document;
			}else{ //  if(r.moz){
				idoc = iframe.contentWindow;
			}

			//For Safari (at least 2.0.3) and Opera, if the iframe
			//has just been created but it doesn't have content
			//yet, then iframe.document may be null. In that case,
			//use iframe.location and return.
			if(!idoc){
				iframe.location = src;
				return;
			}else{
				idoc.location.replace(src);
			}
		}
	}catch(e){ 
		dojo.debug(e); 
		dojo.debug("setIFrameSrc: "+e); 
	}
}

/*
dojo.io.sampleTranport = new function(){
	this.canHandle = function(kwArgs){
		// canHandle just tells dojo.io.bind() if this is a good transport to
		// use for the particular type of request.
		if(	
			(
				(kwArgs["mimetype"] == "text/plain") ||
				(kwArgs["mimetype"] == "text/html") ||
				(kwArgs["mimetype"] == "text/javascript")
			)&&(
				(kwArgs["method"] == "get") ||
				( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
			)
		){
			return true;
		}

		return false;
	}

	this.bind = function(kwArgs){
		var hdlrObj = {};

		// set up a handler object
		for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
			var fn = dojo.io.hdlrFuncNames[x];
			if(typeof kwArgs.handler == "object"){
				if(typeof kwArgs.handler[fn] == "function"){
					hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
				}
			}else if(typeof kwArgs[fn] == "function"){
				hdlrObj[fn] = kwArgs[fn];
			}else{
				hdlrObj[fn] = kwArgs["handle"]||function(){};
			}
		}

		// build a handler function that calls back to the handler obj
		var hdlrFunc = function(evt){
			if(evt.type == "onload"){
				hdlrObj.load("load", evt.data, evt);
			}else if(evt.type == "onerr"){
				var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
				hdlrObj.error("error", errObj);
			}
		}

		// the sample transport would attach the hdlrFunc() when sending the
		// request down the pipe at this point
		var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
	}

	dojo.io.transports.addTransport("sampleTranport");
}
*/

dojo.provide("dojo.lang.array");



// FIXME: Is this worthless since you can do: if(name in obj)
// is this the right place for this?

dojo.lang.mixin(dojo.lang, {
	has: function(/*Object*/obj, /*String*/name){
		// summary: is there a property with the passed name in obj?
		try{
			return typeof obj[name] != "undefined"; // Boolean
		}catch(e){ return false; } // Boolean
	},

	isEmpty: function(/*Object*/obj){
		// summary:
		//		can be used to determine if the passed object is "empty". In
		//		the case of array-like objects, the length, property is
		//		examined, but for other types of objects iteration is used to
		//		examine the iterable "surface area" to determine if any
		//		non-prototypal properties have been assigned. This iteration is
		//		prototype-extension safe.
		if(dojo.lang.isObject(obj)){
			var tmp = {};
			var count = 0;
			for(var x in obj){
				if(obj[x] && (!tmp[x])){
					count++;
					break;
				} 
			}
			return count == 0; // boolean
		}else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){
			return obj.length == 0; // boolean
		}
	},

	map: function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){
		// summary:
		//		returns a new array constituded from the return values of
		//		passing each element of arr into unary_func. The obj parameter
		//		may be passed to enable the passed function to be called in
		//		that scope. In environments that support JavaScript 1.6, this
		//		function is a passthrough to the built-in map() function
		//		provided by Array instances. For details on this, see:
		// 			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map
		// examples:
		//		dojo.lang.map([1, 2, 3, 4], function(item){ return item+1 });
		//		// returns [2, 3, 4, 5]
		var isString = dojo.lang.isString(arr);
		if(isString){
			// arr: String
			arr = arr.split("");
		}
		if(dojo.lang.isFunction(obj)&&(!unary_func)){
			unary_func = obj;
			obj = dj_global;
		}else if(dojo.lang.isFunction(obj) && unary_func){
			// ff 1.5 compat
			var tmpObj = obj;
			obj = unary_func;
			unary_func = tmpObj;
		}
		if(Array.map){
			var outArr = Array.map(arr, unary_func, obj);
		}else{
			var outArr = [];
			for(var i=0;i<arr.length;++i){
				outArr.push(unary_func.call(obj, arr[i]));
			}
		}
		if(isString) {
			return outArr.join(""); // String
		} else {
			return outArr; // Array
		}
	},

	reduce: function(/*Array*/arr, initialValue, /*Object|Function*/obj, /*Function*/binary_func){
		// summary:
		// 		similar to Python's builtin reduce() function. The result of
		// 		the previous computation is passed as the first argument to
		// 		binary_func along with the next value from arr. The result of
		// 		this call is used along with the subsequent value from arr, and
		// 		this continues until arr is exhausted. The return value is the
		// 		last result. The "obj" and "initialValue" parameters may be
		// 		safely omitted and the order of obj and binary_func may be
		// 		reversed. The default order of the obj and binary_func argument
		// 		will probably be reversed in a future release, and this call
		// 		order is supported today.
		// examples:
		//		dojo.lang.reduce([1, 2, 3, 4], function(last, next){ return last+next});
		//		returns 10
		var reducedValue = initialValue;
		if(arguments.length == 2){
			binary_func = initialValue;
			reducedValue = arr[0];
			arr = arr.slice(1);
		}else if(arguments.length == 3){
			if(dojo.lang.isFunction(obj)){
				binary_func = obj;
				obj = null;
			}
		}else{
			// un-fsck the default order
			// FIXME:
			//		could be wrong for some strange function object cases. Not
			//		sure how to test for them.
			if(dojo.lang.isFunction(obj)){
				var tmp = binary_func;
				binary_func = obj;
				obj = tmp;
			}
		}

		var ob = obj || dj_global;
		dojo.lang.map(arr, 
			function(val){
				reducedValue = binary_func.call(ob, reducedValue, val);
			}
		);
		return reducedValue;
	},

	forEach: function(/*Array*/anArray, /*Function*/callback, /*Object?*/thisObject){
		// summary:
		//		for every item in anArray, call callback with that item as its
		//		only parameter. Return values are ignored. This funciton
		//		corresponds (and wraps) the JavaScript 1.6 forEach method. For
		//		more details, see:
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
		if(dojo.lang.isString(anArray)){
			// anArray: String
			anArray = anArray.split(""); 
		}
		if(Array.forEach){
			Array.forEach(anArray, callback, thisObject);
		}else{
			// FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
			if(!thisObject){
				thisObject=dj_global;
			}
			for(var i=0,l=anArray.length; i<l; i++){ 
				callback.call(thisObject, anArray[i], i, anArray);
			}
		}
	},

	_everyOrSome: function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
		if(dojo.lang.isString(arr)){ 
			//arr: String
			arr = arr.split(""); 
		}
		if(Array.every){
			return Array[ every ? "every" : "some" ](arr, callback, thisObject);
		}else{
			if(!thisObject){
				thisObject = dj_global;
			}
			for(var i=0,l=arr.length; i<l; i++){
				var result = callback.call(thisObject, arr[i], i, arr);
				if(every && !result){
					return false; // Boolean
				}else if((!every)&&(result)){
					return true; // Boolean
				}
			}
			return Boolean(every); // Boolean
		}
	},

	every: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
		// summary:
		//		determines whether or not every item in the array satisfies the
		//		condition implemented by callback. thisObject may be used to
		//		scope the call to callback. The function signature is derived
		//		from the JavaScript 1.6 Array.every() function. More
		//		information on this can be found here:
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every
		// examples:
		//		dojo.lang.every([1, 2, 3, 4], function(item){ return item>1; });
		//		// returns false
		//		dojo.lang.every([1, 2, 3, 4], function(item){ return item>0; });
		//		// returns true 
		return this._everyOrSome(true, arr, callback, thisObject); // Boolean
	},

	some: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
		// summary:
		//		determines whether or not any item in the array satisfies the
		//		condition implemented by callback. thisObject may be used to
		//		scope the call to callback. The function signature is derived
		//		from the JavaScript 1.6 Array.some() function. More
		//		information on this can be found here:
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
		// examples:
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; });
		//		// returns true
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item<1; });
		//		// returns false
		return this._everyOrSome(false, arr, callback, thisObject); // Boolean
	},

	filter: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
		// summary:
		//		returns a new Array with those items from arr that match the
		//		condition implemented by callback.thisObject may be used to
		//		scope the call to callback. The function signature is derived
		//		from the JavaScript 1.6 Array.filter() function, although
		//		special accomidation is made in our implementation for strings.
		//		More information on the JS 1.6 API can be found here:
		//			http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter
		// examples:
		//		dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; });
		//		// returns [2, 3, 4]
		var isString = dojo.lang.isString(arr);
		if(isString){ /*arr: String*/arr = arr.split(""); }
		var outArr;
		if(Array.filter){
			outArr = Array.filter(arr, callback, thisObject);
		}else{
			if(!thisObject){
				if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); }
				thisObject = dj_global;
			}

			outArr = [];
			for(var i = 0; i < arr.length; i++){
				if(callback.call(thisObject, arr[i], i, arr)){
					outArr.push(arr[i]);
				}
			}
		}
		if(isString){
			return outArr.join(""); // String
		} else {
			return outArr; // Array
		}
	},

	unnest: function(/* ... */){
		// summary:
		//		Creates a 1-D array out of all the arguments passed,
		//		unravelling any array-like objects in the process
		// usage:
		//		unnest(1, 2, 3) ==> [1, 2, 3]
		//		unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]

		var out = [];
		for(var i = 0; i < arguments.length; i++){
			if(dojo.lang.isArrayLike(arguments[i])){
				var add = dojo.lang.unnest.apply(this, arguments[i]);
				out = out.concat(add);
			}else{
				out.push(arguments[i]);
			}
		}
		return out; // Array
	},

	toArray: function(/*Object*/arrayLike, /*Number*/startOffset){
		// summary:
		//		Converts an array-like object (i.e. arguments, DOMCollection)
		//		to an array. Returns a new Array object.
		var array = [];
		for(var i = startOffset||0; i < arrayLike.length; i++){
			array.push(arrayLike[i]);
		}
		return array; // Array
	}
});

dojo.provide("dojo.lang.func");


dojo.lang.hitch = function(/*Object*/thisObject, /*Function|String*/method /*, ...*/){
	// summary: 
	//		Returns a function that will only ever execute in the a given scope
	//		(thisObject). This allows for easy use of object member functions
	//		in callbacks and other places in which the "this" keyword may
	//		otherwise not reference the expected scope. Any number of default
	//		positional arguments may be passed as parameters beyond "method".
	//		Each of these values will be used to "placehold" (similar to curry)
	//		for the hitched function. Note that the order of arguments may be
	//		reversed in a future version.
	// thisObject: the scope to run the method in
	// method:
	//		a function to be "bound" to thisObject or the name of the method in
	//		thisObject to be used as the basis for the binding
	// usage:
	//		dojo.lang.hitch(foo, "bar")(); // runs foo.bar() in the scope of foo
	//		dojo.lang.hitch(foo, myFunction); // returns a function that runs myFunction in the scope of foo

	var args = [];
	for(var x=2; x<arguments.length; x++){
		args.push(arguments[x]);
	}
	var fcn = (dojo.lang.isString(method) ? thisObject[method] : method) || function(){};
	return function(){
		var ta = args.concat([]); // make a copy
		for(var x=0; x<arguments.length; x++){
			ta.push(arguments[x]);
		}
		return fcn.apply(thisObject, ta); // Function
		// return fcn.apply(thisObject, arguments); // Function
	};
}

dojo.lang.anonCtr = 0;
dojo.lang.anon = {};

dojo.lang.nameAnonFunc = function(/*Function*/anonFuncPtr, /*Object*/thisObj, /*Boolean*/searchForNames){
	// summary:
	//		Creates a reference to anonFuncPtr in thisObj with a completely
	//		unique name. The new name is returned as a String.  If
	//		searchForNames is true, an effort will be made to locate an
	//		existing reference to anonFuncPtr in thisObj, and if one is found,
	//		the existing name will be returned instead. The default is for
	//		searchForNames to be false.
	var nso = (thisObj|| dojo.lang.anon);
	if( (searchForNames) ||
		((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
		for(var x in nso){
			try{
				if(nso[x] === anonFuncPtr){
					return x;
				}
			}catch(e){} // window.external fails in IE embedded in Eclipse (Eclipse bug #151165)
		}
	}
	var ret = "__"+dojo.lang.anonCtr++;
	while(typeof nso[ret] != "undefined"){
		ret = "__"+dojo.lang.anonCtr++;
	}
	nso[ret] = anonFuncPtr;
	return ret; // String
}

dojo.lang.forward = function(funcName){
	// summary:
	// 		Returns a function that forwards a method call to
	// 		this.funcName(...).  Unlike dojo.lang.hitch(), the "this" scope is
	// 		not fixed on a single object. Ported from MochiKit.
	return function(){
		return this[funcName].apply(this, arguments);
	}; // Function
}

dojo.lang.curry = function(thisObj, func /* args ... */){
	// summary:
	//		similar to the curry() method found in many functional programming
	//		environments, this function returns an "argument accumulator"
	//		function, bound to a particular scope, and "primed" with a variable
	//		number of arguments. The curry method is unique in that it returns
	//		a function that may return other "partial" function which can be
	//		called repeatedly. New functions are returned until the arity of
	//		the original function is reached, at which point the underlying
	//		function (func) is called in the scope thisObj with all of the
	//		accumulated arguments (plus any extras) in positional order.
	// examples:
	//		assuming a function defined like this:
	//			var foo = {
	//				bar: function(arg1, arg2, arg3){
	//					dojo.debug.apply(dojo, arguments);
	//				}
	//			};
	//		
	//		dojo.lang.curry() can be used most simply in this way:
	//		
	//			tmp = dojo.lang.curry(foo, foo.bar, "arg one", "thinger");
	//			tmp("blah", "this is superfluous");
	//			// debugs: "arg one thinger blah this is superfluous"
	//			tmp("blah");
	//			// debugs: "arg one thinger blah"
	//			tmp();
	//			// returns a function exactly like tmp that expects one argument
	//
	//		other intermittent functions could be created until the 3
	//		positional arguments are filled:
	//
	//			tmp = dojo.lang.curry(foo, foo.bar, "arg one");
	//			tmp2 = tmp("arg two");
	//			tmp2("blah blah");
	//			// debugs: "arg one arg two blah blah"
	//			tmp2("oy");
	//			// debugs: "arg one arg two oy"
	//
	//		curry() can also be used to call the function if enough arguments
	//		are passed in the initial invocation:
	//
	//			dojo.lang.curry(foo, foo.bar, "one", "two", "three", "four");
	//			// debugs: "one two three four"
	//			dojo.lang.curry(foo, foo.bar, "one", "two", "three");
	//			// debugs: "one two three"


	// FIXME: the order of func and thisObj should be changed!!!
	var outerArgs = [];
	thisObj = thisObj||dj_global;
	if(dojo.lang.isString(func)){
		func = thisObj[func];
	}
	for(var x=2; x<arguments.length; x++){
		outerArgs.push(arguments[x]);
	}
	// since the event system replaces the original function with a new
	// join-point runner with an arity of 0, we check to see if it's left us
	// any clues about the original arity in lieu of the function's actual
	// length property
	var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
	// borrowed from svend tofte
	function gather(nextArgs, innerArgs, expected){
		var texpected = expected;
		var totalArgs = innerArgs.slice(0); // copy
		for(var x=0; x<nextArgs.length; x++){
			totalArgs.push(nextArgs[x]);
		}
		// check the list of provided nextArgs to see if it, plus the
		// number of innerArgs already supplied, meets the total
		// expected.
		expected = expected-nextArgs.length;
		if(expected<=0){
			var res = func.apply(thisObj, totalArgs);
			expected = texpected;
			return res;
		}else{
			return function(){
				return gather(arguments,// check to see if we've been run
										// with enough args
							totalArgs,	// a copy
							expected);	// how many more do we need to run?;
			};
		}
	}
	return gather([], outerArgs, ecount);
}

dojo.lang.curryArguments = function(/*Object*/thisObj, /*Function*/func, /*Array*/args, /*Integer, optional*/offset){
	// summary:
	//		similar to dojo.lang.curry(), except that a list of arguments to
	//		start the curry with may be provided as an array instead of as
	//		positional arguments. An offset may be specified from the 0 index
	//		to skip some elements in args.
	var targs = [];
	var x = offset||0;
	for(x=offset; x<args.length; x++){
		targs.push(args[x]); // ensure that it's an arr
	}
	return dojo.lang.curry.apply(dojo.lang, [thisObj, func].concat(targs));
}

dojo.lang.tryThese = function(/*...*/){
	// summary:
	//		executes each function argument in turn, returning the return value
	//		from the first one which does not throw an exception in execution.
	//		Any number of functions may be passed.
	for(var x=0; x<arguments.length; x++){
		try{
			if(typeof arguments[x] == "function"){
				var ret = (arguments[x]());
				if(ret){
					return ret;
				}
			}
		}catch(e){
			dojo.debug(e);
		}
	}
}

dojo.lang.delayThese = function(/*Array*/farr, /*Function, optional*/cb, /*Integer*/delay, /*Function, optional*/onend){
	// summary:
	//		executes a series of functions contained in farr, but spaces out
	//		calls to each function by the millisecond delay provided. If cb is
	//		provided, it will be called directly after each item in farr is
	//		called and if onend is passed, it will be called when all items
	//		have completed executing.

	/**
	 * alternate: (array funcArray, function callback, function onend)
	 * alternate: (array funcArray, function callback)
	 * alternate: (array funcArray)
	 */
	if(!farr.length){ 
		if(typeof onend == "function"){
			onend();
		}
		return;
	}
	if((typeof delay == "undefined")&&(typeof cb == "number")){
		delay = cb;
		cb = function(){};
	}else if(!cb){
		cb = function(){};
		if(!delay){ delay = 0; }
	}
	setTimeout(function(){
		(farr.shift())();
		cb();
		dojo.lang.delayThese(farr, cb, delay, onend);
	}, delay);
}

dojo.provide("dojo.string.extras");





//TODO: should we use ${} substitution syntax instead, like widgets do?
dojo.string.substituteParams = function(/*string*/template, /* object - optional or ... */hash){
// summary:
//	Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched.
//
// description:
//	For example,
//		dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp");
//	returns
//		"File 'foo.html' is not found in directory '/temp'."
//
// template: the original string template with %{values} to be replaced
// hash: name/value pairs (type object) to provide substitutions.  Alternatively, substitutions may be
//	included as arguments 1..n to this function, corresponding to template parameters 0..n-1

	var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1);

	return template.replace(/\%\{(\w+)\}/g, function(match, key){
		if(typeof(map[key]) != "undefined" && map[key] != null){
			return map[key];
		}
		dojo.raise("Substitution not found: " + key);
	}); // string
};

dojo.string.capitalize = function(/*string*/str){
// summary:
//	Uppercases the first letter of each word

	if(!dojo.lang.isString(str)){ return ""; }
	if(arguments.length == 0){ str = this; }

	var words = str.split(' ');
	for(var i=0; i<words.length; i++){
		words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
	}
	return words.join(" "); // string
}

dojo.string.isBlank = function(/*string*/str){
// summary:
//	Return true if the entire string is whitespace characters

	if(!dojo.lang.isString(str)){ return true; }
	return (dojo.string.trim(str).length == 0); // boolean
}

//FIXME: not sure exactly what encodeAscii is trying to do, or if it's working right
dojo.string.encodeAscii = function(/*string*/str){
	if(!dojo.lang.isString(str)){ return str; } // unknown
	var ret = "";
	var value = escape(str);
	var match, re = /%u([0-9A-F]{4})/i;
	while((match = value.match(re))){
		var num = Number("0x"+match[1]);
		var newVal = escape("&#" + num + ";");
		ret += value.substring(0, match.index) + newVal;
		value = value.substring(match.index+match[0].length);
	}
	ret += value.replace(/\+/g, "%2B");
	return ret; // string
}

dojo.string.escape = function(/*string*/type, /*string*/str){
// summary:
//	Adds escape sequences for special characters according to the convention of 'type'
//
// type: one of xml|html|xhtml|sql|regexp|regex|javascript|jscript|js|ascii
// str: the string to be escaped

	var args = dojo.lang.toArray(arguments, 1);
	switch(type.toLowerCase()){
		case "xml":
		case "html":
		case "xhtml":
			return dojo.string.escapeXml.apply(this, args); // string
		case "sql":
			return dojo.string.escapeSql.apply(this, args); // string
		case "regexp":
		case "regex":
			return dojo.string.escapeRegExp.apply(this, args); // string
		case "javascript":
		case "jscript":
		case "js":
			return dojo.string.escapeJavaScript.apply(this, args); // string
		case "ascii":
			// so it's encode, but it seems useful
			return dojo.string.encodeAscii.apply(this, args); // string
		default:
			return str; // string
	}
}

dojo.string.escapeXml = function(/*string*/str, /*boolean*/noSingleQuotes){
//summary:
//	Adds escape sequences for special characters in XML: &<>"'
//  Optionally skips escapes for single quotes

	str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
		.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
	if(!noSingleQuotes){ str = str.replace(/'/gm, "&#39;"); }
	return str; // string
}

dojo.string.escapeSql = function(/*string*/str){
//summary:
//	Adds escape sequences for single quotes in SQL expressions

	return str.replace(/'/gm, "''"); //string
}

dojo.string.escapeRegExp = function(/*string*/str){
//summary:
//	Adds escape sequences for special characters in regular expressions

	return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
}

//FIXME: should this one also escape backslash?
dojo.string.escapeJavaScript = function(/*string*/str){
//summary:
//	Adds escape sequences for single and double quotes as well
//	as non-visible characters in JavaScript string literal expressions

	return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); // string
}

//FIXME: looks a lot like escapeJavaScript, just adds quotes? deprecate one?
dojo.string.escapeString = function(/*string*/str){
//summary:
//	Adds escape sequences for non-visual characters, double quote and backslash
//	and surrounds with double quotes to form a valid string literal.
	return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
		).replace(/[\f]/g, "\\f"
		).replace(/[\b]/g, "\\b"
		).replace(/[\n]/g, "\\n"
		).replace(/[\t]/g, "\\t"
		).replace(/[\r]/g, "\\r"); // string
}

// TODO: make an HTML version
dojo.string.summary = function(/*string*/str, /*number*/len){
// summary:
//	Truncates 'str' after 'len' characters and appends periods as necessary so that it ends with "..."

	if(!len || str.length <= len){
		return str; // string
	}

	return str.substring(0, len).replace(/\.+$/, "") + "..."; // string
}

dojo.string.endsWith = function(/*string*/str, /*string*/end, /*boolean*/ignoreCase){
// summary:
//	Returns true if 'str' ends with 'end'

	if(ignoreCase){
		str = str.toLowerCase();
		end = end.toLowerCase();
	}
	if((str.length - end.length) < 0){
		return false; // boolean
	}
	return str.lastIndexOf(end) == str.length - end.length; // boolean
}

dojo.string.endsWithAny = function(/*string*/str /* , ... */){
// summary:
//	Returns true if 'str' ends with any of the arguments[2 -> n]

	for(var i = 1; i < arguments.length; i++) {
		if(dojo.string.endsWith(str, arguments[i])) {
			return true; // boolean
		}
	}
	return false; // boolean
}

dojo.string.startsWith = function(/*string*/str, /*string*/start, /*boolean*/ignoreCase){
// summary:
//	Returns true if 'str' starts with 'start'

	if(ignoreCase) {
		str = str.toLowerCase();
		start = start.toLowerCase();
	}
	return str.indexOf(start) == 0; // boolean
}

dojo.string.startsWithAny = function(/*string*/str /* , ... */){
// summary:
//	Returns true if 'str' starts with any of the arguments[2 -> n]

	for(var i = 1; i < arguments.length; i++) {
		if(dojo.string.startsWith(str, arguments[i])) {
			return true; // boolean
		}
	}
	return false; // boolean
}

dojo.string.has = function(/*string*/str /* , ... */) {
// summary:
//	Returns true if 'str' contains any of the arguments 2 -> n

	for(var i = 1; i < arguments.length; i++) {
		if(str.indexOf(arguments[i]) > -1){
			return true; // boolean
		}
	}
	return false; // boolean
}

dojo.string.normalizeNewlines = function(/*string*/text, /*string? (\n or \r)*/newlineChar){
// summary:
//	Changes occurences of CR and LF in text to CRLF, or if newlineChar is provided as '\n' or '\r',
//	substitutes newlineChar for occurrences of CR/LF and CRLF

	if (newlineChar == "\n"){
		text = text.replace(/\r\n/g, "\n");
		text = text.replace(/\r/g, "\n");
	} else if (newlineChar == "\r"){
		text = text.replace(/\r\n/g, "\r");
		text = text.replace(/\n/g, "\r");
	}else{
		text = text.replace(/([^\r])\n/g, "$1\r\n").replace(/\r([^\n])/g, "\r\n$1");
	}
	return text; // string
}

dojo.string.splitEscaped = function(/*string*/str, /*string of length=1*/charac){
// summary:
//	Splits 'str' into an array separated by 'charac', but skips characters escaped with a backslash

	var components = [];
	for (var i = 0, prevcomma = 0; i < str.length; i++){
		if (str.charAt(i) == '\\'){ i++; continue; }
		if (str.charAt(i) == charac){
			components.push(str.substring(prevcomma, i));
			prevcomma = i + 1;
		}
	}
	components.push(str.substr(prevcomma));
	return components; // array
}

dojo.provide("dojo.dom");

dojo.dom.ELEMENT_NODE                  = 1;
dojo.dom.ATTRIBUTE_NODE                = 2;
dojo.dom.TEXT_NODE                     = 3;
dojo.dom.CDATA_SECTION_NODE            = 4;
dojo.dom.ENTITY_REFERENCE_NODE         = 5;
dojo.dom.ENTITY_NODE                   = 6;
dojo.dom.PROCESSING_INSTRUCTION_NODE   = 7;
dojo.dom.COMMENT_NODE                  = 8;
dojo.dom.DOCUMENT_NODE                 = 9;
dojo.dom.DOCUMENT_TYPE_NODE            = 10;
dojo.dom.DOCUMENT_FRAGMENT_NODE        = 11;
dojo.dom.NOTATION_NODE                 = 12;
	
dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";

/**
 *	comprehensive list of XML namespaces
**/
dojo.dom.xmlns = {
	//	summary
	//	aliases for various common XML namespaces
	svg : "http://www.w3.org/2000/svg",
	smil : "http://www.w3.org/2001/SMIL20/",
	mml : "http://www.w3.org/1998/Math/MathML",
	cml : "http://www.xml-cml.org",
	xlink : "http://www.w3.org/1999/xlink",
	xhtml : "http://www.w3.org/1999/xhtml",
	xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
	xbl : "http://www.mozilla.org/xbl",
	fo : "http://www.w3.org/1999/XSL/Format",
	xsl : "http://www.w3.org/1999/XSL/Transform",
	xslt : "http://www.w3.org/1999/XSL/Transform",
	xi : "http://www.w3.org/2001/XInclude",
	xforms : "http://www.w3.org/2002/01/xforms",
	saxon : "http://icl.com/saxon",
	xalan : "http://xml.apache.org/xslt",
	xsd : "http://www.w3.org/2001/XMLSchema",
	dt: "http://www.w3.org/2001/XMLSchema-datatypes",
	xsi : "http://www.w3.org/2001/XMLSchema-instance",
	rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
	rdfs : "http://www.w3.org/2000/01/rdf-schema#",
	dc : "http://purl.org/dc/elements/1.1/",
	dcq: "http://purl.org/dc/qualifiers/1.0",
	"soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
	wsdl : "http://schemas.xmlsoap.org/wsdl/",
	AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
};

dojo.dom.isNode = function(/* object */wh){
	//	summary:
	//		checks to see if wh is actually a node.
	if(typeof Element == "function") {
		try {
			return wh instanceof Element;	//	boolean
		} catch(e) {}
	} else {
		// best-guess
		return wh && !isNaN(wh.nodeType);	//	boolean
	}
}

dojo.dom.getUniqueId = function(){
	//	summary:
	//		returns a unique string for use with any DOM element
	var _document = dojo.doc();
	do {
		var id = "dj_unique_" + (++arguments.callee._idIncrement);
	}while(_document.getElementById(id));
	return id;	//	string
}
dojo.dom.getUniqueId._idIncrement = 0;

dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
	//	summary:
	//		returns the first child element matching tagName
	var node = parentNode.firstChild;
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
		node = node.nextSibling;
	}
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
		node = dojo.dom.nextElement(node, tagName);
	}
	return node;	//	Element
}

dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
	//	summary:
	//		returns the last child element matching tagName
	var node = parentNode.lastChild;
	while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
		node = node.previousSibling;
	}
	if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
		node = dojo.dom.prevElement(node, tagName);
	}
	return node;	//	Element
}

dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
	//	summary:
	//		returns the next sibling element matching tagName
	if(!node) { return null; }
	do {
		node = node.nextSibling;
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);

	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
		return dojo.dom.nextElement(node, tagName);
	}
	return node;	//	Element
}

dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
	//	summary:
	//		returns the previous sibling element matching tagName
	if(!node) { return null; }
	if(tagName) { tagName = tagName.toLowerCase(); }
	do {
		node = node.previousSibling;
	} while(node && node.nodeType != dojo.dom.ELEMENT_NODE);

	if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
		return dojo.dom.prevElement(node, tagName);
	}
	return node;	//	Element
}

// TODO: hmph
/*this.forEachChildTag = function(node, unaryFunc) {
	var child = this.getFirstChildTag(node);
	while(child) {
		if(unaryFunc(child) == "break") { break; }
		child = this.getNextSiblingTag(child);
	}
}*/

dojo.dom.moveChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
	//	summary:
	//		Moves children from srcNode to destNode and returns the count of
	//		children moved; will trim off text nodes if trim == true
	var count = 0;
	if(trim) {
		while(srcNode.hasChildNodes() &&
			srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
			srcNode.removeChild(srcNode.firstChild);
		}
		while(srcNode.hasChildNodes() &&
			srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
			srcNode.removeChild(srcNode.lastChild);
		}
	}
	while(srcNode.hasChildNodes()){
		destNode.appendChild(srcNode.firstChild);
		count++;
	}
	return count;	//	number
}

dojo.dom.copyChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){
	//	summary:
	//		Copies children from srcNde to destNode and returns the count of
	//		children copied; will trim off text nodes if trim == true
	var clonedNode = srcNode.cloneNode(true);
	return this.moveChildren(clonedNode, destNode, trim);	//	number
}

dojo.dom.replaceChildren = function(/*Element*/node, /*Node*/newChild){
	//	summary:
	//		Removes all children of node and appends newChild. All the existing
	//		children will be destroyed.
	// FIXME: what if newChild is an array-like object?
	var nodes = [];
	if(dojo.render.html.ie){
		for(var i=0;i<node.childNodes.length;i++){
			nodes.push(node.childNodes[i]);
		}
	}
	dojo.dom.removeChildren(node);
	node.appendChild(newChild);
	for(var i=0;i<nodes.length;i++){
		dojo.dom.destroyNode(nodes[i]);
	}
}

dojo.dom.removeChildren = function(/*Element*/node){
	//	summary:
	//		removes all children from node and returns the count of children removed.
	//		The children nodes are not destroyed. Be sure to call destroyNode on them
	//		after they are not used anymore.
	var count = node.childNodes.length;
	while(node.hasChildNodes()){ dojo.dom.removeNode(node.firstChild); }
	return count; // int
}

dojo.dom.replaceNode = function(/*Element*/node, /*Element*/newNode){
	//	summary:
	//		replaces node with newNode and returns a reference to the removed node.
	//		To prevent IE memory leak, call destroyNode on the returned node when
	//		it is no longer needed.
	return node.parentNode.replaceChild(newNode, node); // Node
}

dojo.dom.destroyNode = function(/*Node*/node){
	// summary:
	//		destroy a node (it can not be used any more). For IE, this is the
	//		right function to call to prevent memory leaks. While for other
	//		browsers, this is identical to dojo.dom.removeNode
	if(node.parentNode){
		node = dojo.dom.removeNode(node);
	}
	if(node.nodeType != 3){ // ingore TEXT_NODE
		if(dojo.evalObjPath("dojo.event.browser.clean", false)){
			dojo.event.browser.clean(node);
		}
		if(dojo.render.html.ie){
			node.outerHTML=''; //prevent ugly IE mem leak associated with Node.removeChild (ticket #1727)
		}
	}
}

dojo.dom.removeNode = function(/*Node*/node){
	// summary:
	//		if node has a parent, removes node from parent and returns a
	//		reference to the removed child.
	//		To prevent IE memory leak, call destroyNode on the returned node when
	//		it is no longer needed.
	//	node:
	//		the node to remove from its parent.

	if(node && node.parentNode){
		// return a ref to the removed child
		return node.parentNode.removeChild(node); //Node
	}
}

dojo.dom.getAncestors = function(/*Node*/node, /*function?*/filterFunction, /*boolean?*/returnFirstHit){
	//	summary:
	//		returns all ancestors matching optional filterFunction; will return
	//		only the first if returnFirstHit
	var ancestors = [];
	var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
	while(node){
		if(!isFunction || filterFunction(node)){
			ancestors.push(node);
		}
		if(returnFirstHit && ancestors.length > 0){ 
			return ancestors[0]; 	//	Node
		}
		
		node = node.parentNode;
	}
	if(returnFirstHit){ return null; }
	return ancestors;	//	array
}

dojo.dom.getAncestorsByTag = function(/*Node*/node, /*String*/tag, /*boolean?*/returnFirstHit){
	//	summary:
	//		returns all ancestors matching tag (as tagName), will only return
	//		first one if returnFirstHit
	tag = tag.toLowerCase();
	return dojo.dom.getAncestors(node, function(el){
		return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
	}, returnFirstHit);	//	Node || array
}

dojo.dom.getFirstAncestorByTag = function(/*Node*/node, /*string*/tag){
	//	summary:
	//		Returns first ancestor of node with tag tagName
	return dojo.dom.getAncestorsByTag(node, tag, true);	//	Node
}

dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
	//	summary
	//	Returns boolean if node is a descendant of ancestor
	// guaranteeDescendant allows us to be a "true" isDescendantOf function
	if(guaranteeDescendant && node) { node = node.parentNode; }
	while(node) {
		if(node == ancestor){ 
			return true; 	//	boolean
		}
		node = node.parentNode;
	}
	return false;	//	boolean
}

dojo.dom.innerXML = function(/*Node*/node){
	//	summary:
	//		Implementation of MS's innerXML function.
	if(node.innerXML){
		return node.innerXML;	//	string
	}else if (node.xml){
		return node.xml;		//	string
	}else if(typeof XMLSerializer != "undefined"){
		return (new XMLSerializer()).serializeToString(node);	//	string
	}
}

dojo.dom.createDocument = function(){
	//	summary:
	//		cross-browser implementation of creating an XML document object.
	var doc = null;
	var _document = dojo.doc();

	if(!dj_undef("ActiveXObject")){
		var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
		for(var i = 0; i<prefixes.length; i++){
			try{
				doc = new ActiveXObject(prefixes[i]+".XMLDOM");
			}catch(e){ /* squelch */ };

			if(doc){ break; }
		}
	}else if((_document.implementation)&&
		(_document.implementation.createDocument)){
		doc = _document.implementation.createDocument("", "", null);
	}
	
	return doc;	//	DOMDocument
}

dojo.dom.createDocumentFromText = function(/*string*/str, /*string?*/mimetype){
	//	summary:
	//		attempts to create a Document object based on optional mime-type,
	//		using str as the contents of the document
	if(!mimetype){ mimetype = "text/xml"; }
	if(!dj_undef("DOMParser")){
		var parser = new DOMParser();
		return parser.parseFromString(str, mimetype);	//	DOMDocument
	}else if(!dj_undef("ActiveXObject")){
		var domDoc = dojo.dom.createDocument();
		if(domDoc){
			domDoc.async = false;
			domDoc.loadXML(str);
			return domDoc;	//	DOMDocument
		}else{
			dojo.debug("toXml didn't work?");
		}
	/*
	}else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
		// FIXME: this doesn't appear to work!
		// from: http://web-graphics.com/mtarchive/001606.php
		// var xml = '<?xml version="1.0"?>'+str;
		var mtype = "text/xml";
		var xml = '<?xml version="1.0"?>'+str;
		var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
		var req = new XMLHttpRequest();
		req.open("GET", url, false);
		req.overrideMimeType(mtype);
		req.send(null);
		return req.responseXML;
	*/
	}else{
		var _document = dojo.doc();
		if(_document.createElement){
			// FIXME: this may change all tags to uppercase!
			var tmp = _document.createElement("xml");
			tmp.innerHTML = str;
			if(_document.implementation && _document.implementation.createDocument){
				var xmlDoc = _document.implementation.createDocument("foo", "", null);
				for(var i = 0; i < tmp.childNodes.length; i++) {
					xmlDoc.importNode(tmp.childNodes.item(i), true);
				}
				return xmlDoc;	//	DOMDocument
			}
			// FIXME: probably not a good idea to have to return an HTML fragment
			// FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
			// work that way across the board
			return ((tmp.document)&&
				(tmp.document.firstChild ?  tmp.document.firstChild : tmp));	//	DOMDocument
		}
	}
	return null;
}

dojo.dom.prependChild = function(/*Element*/node, /*Element*/parent){
	//	summary:
	//		prepends node to parent's children nodes
	if(parent.firstChild) {
		parent.insertBefore(node, parent.firstChild);
	} else {
		parent.appendChild(node);
	}
	return true;	//	boolean
}

dojo.dom.insertBefore = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
	//	summary:
	//		Try to insert node before ref
	if(	(force != true)&&
		(node === ref || node.nextSibling === ref)){ return false; }
	var parent = ref.parentNode;
	parent.insertBefore(node, ref);
	return true;	//	boolean
}

dojo.dom.insertAfter = function(/*Node*/node, /*Node*/ref, /*boolean?*/force){
	//	summary:
	//		Try to insert node after ref
	var pn = ref.parentNode;
	if(ref == pn.lastChild){
		if((force != true)&&(node === ref)){
			return false;	//	boolean
		}
		pn.appendChild(node);
	}else{
		return this.insertBefore(node, ref.nextSibling, force);	//	boolean
	}
	return true;	//	boolean
}

dojo.dom.insertAtPosition = function(/*Node*/node, /*Node*/ref, /*string*/position){
	//	summary:
	//		attempt to insert node in relation to ref based on position
	if((!node)||(!ref)||(!position)){ 
		return false;	//	boolean 
	}
	switch(position.toLowerCase()){
		case "before":
			return dojo.dom.insertBefore(node, ref);	//	boolean
		case "after":
			return dojo.dom.insertAfter(node, ref);		//	boolean
		case "first":
			if(ref.firstChild){
				return dojo.dom.insertBefore(node, ref.firstChild);	//	boolean
			}else{
				ref.appendChild(node);
				return true;	//	boolean
			}
			break;
		default: // aka: last
			ref.appendChild(node);
			return true;	//	boolean
	}
}

dojo.dom.insertAtIndex = function(/*Node*/node, /*Element*/containingNode, /*number*/insertionIndex){
	//	summary:
	//		insert node into child nodes nodelist of containingNode at
	//		insertionIndex. insertionIndex should be between 0 and 
	//		the number of the childNodes in containingNode. insertionIndex
	//		specifys after how many childNodes in containingNode the node
	//		shall be inserted. If 0 is given, node will be appended to 
	//		containingNode.
	var siblingNodes = containingNode.childNodes;

	// if there aren't any kids yet, just add it to the beginning

	if (!siblingNodes.length || siblingNodes.length == insertionIndex){
		containingNode.appendChild(node);
		return true;	//	boolean
	}

	if(insertionIndex == 0){
		return dojo.dom.prependChild(node, containingNode);	//	boolean
	}
	// otherwise we need to walk the childNodes
	// and find our spot

	return dojo.dom.insertAfter(node, siblingNodes[insertionIndex-1]);	//	boolean
}
	
dojo.dom.textContent = function(/*Node*/node, /*string*/text){
	//	summary:
	//		implementation of the DOM Level 3 attribute; scan node for text
	if (arguments.length>1) {
		var _document = dojo.doc();
		dojo.dom.replaceChildren(node, _document.createTextNode(text));
		return text;	//	string
	} else {
		if(node.textContent != undefined){ //FF 1.5
			return node.textContent;	//	string
		}
		var _result = "";
		if (node == null) { return _result; }
		for (var i = 0; i < node.childNodes.length; i++) {
			switch (node.childNodes[i].nodeType) {
				case 1: // ELEMENT_NODE
				case 5: // ENTITY_REFERENCE_NODE
					_result += dojo.dom.textContent(node.childNodes[i]);
					break;
				case 3: // TEXT_NODE
				case 2: // ATTRIBUTE_NODE
				case 4: // CDATA_SECTION_NODE
					_result += node.childNodes[i].nodeValue;
					break;
				default:
					break;
			}
		}
		return _result;	//	string
	}
}

dojo.dom.hasParent = function(/*Node*/node){
	//	summary:
	//		returns whether or not node is a child of another node.
	return Boolean(node && node.parentNode && dojo.dom.isNode(node.parentNode));	//	boolean
}

/**
 * Examples:
 *
 * myFooNode = <foo />
 * isTag(myFooNode, "foo"); // returns "foo"
 * isTag(myFooNode, "bar"); // returns ""
 * isTag(myFooNode, "FOO"); // returns ""
 * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
**/
dojo.dom.isTag = function(/* Node */node /* ... */){
	//	summary:
	//		determines if node has any of the provided tag names and returns
	//		the tag name that matches, empty string otherwise.
	if(node && node.tagName) {
		for(var i=1; i<arguments.length; i++){
			if(node.tagName==String(arguments[i])){
				return String(arguments[i]);	//	string
			}
		}
	}
	return "";	//	string
}

dojo.dom.setAttributeNS = function(	/*Element*/elem, /*string*/namespaceURI, 
									/*string*/attrName, /*string*/attrValue){
	//	summary:
	//		implementation of DOM2 setAttributeNS that works cross browser.
	if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
		dojo.raise("No element given to dojo.dom.setAttributeNS");
	}
	
	if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
		elem.setAttributeNS(namespaceURI, attrName, attrValue);
	}else{ // IE
		// get a root XML document
		var ownerDoc = elem.ownerDocument;
		var attribute = ownerDoc.createNode(
			2, // node type
			attrName,
			namespaceURI
		);
		
		// set value
		attribute.nodeValue = attrValue;
		
		// attach to element
		elem.setAttributeNode(attribute);
	}
}

dojo.provide("dojo.undo.browser");


try{
	if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
		document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='" + (djConfig["dojoIframeHistoryUrl"] || dojo.hostenv.getBaseScriptUri()+'iframe_history.html') + "'></iframe>");
	}
}catch(e){/* squelch */}

if(dojo.render.html.opera){
	dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work.");
}

dojo.undo.browser = {
	initialHref: (!dj_undef("window")) ? window.location.href : "",
	initialHash: (!dj_undef("window")) ? window.location.hash : "",

	moveForward: false,
	historyStack: [],
	forwardStack: [],
	historyIframe: null,
	bookmarkAnchor: null,
	locationTimer: null,

	/**
	 * 
	 */
	setInitialState: function(/*Object*/args){
		//summary: Sets the state object and back callback for the very first page that is loaded.
		//description: It is recommended that you call this method as part of an event listener that is registered via
		//dojo.addOnLoad().
		//args: Object
		//		See the addToHistory() function for the list of valid args properties.
		this.initialState = this._createState(this.initialHref, args, this.initialHash);
	},

	//FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
	//FIXME: is there a slight race condition in moz using change URL with the timer check and when
	//       the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
	addToHistory: function(args){
		//summary: adds a state object (args) to the history list. You must set
		//djConfig.preventBackButtonFix = false to use dojo.undo.browser.

		//args: Object
		//		args can have the following properties:
		//		To support getting back button notifications, the object argument should implement a
		//		function called either "back", "backButton", or "handle". The string "back" will be
		//		passed as the first and only argument to this callback.
		//		- To support getting forward button notifications, the object argument should implement a
		//		function called either "forward", "forwardButton", or "handle". The string "forward" will be
		//		passed as the first and only argument to this callback.
		//		- If you want the browser location string to change, define "changeUrl" on the object. If the
		//		value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
		//		identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
		//		not evaluate to false, that value will be used as the fragment identifier. For example,
		//		if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
	 	//		Full example:
		//		dojo.undo.browser.addToHistory({
		//		  back: function() { alert('back pressed'); },
		//		  forward: function() { alert('forward pressed'); },
		//		  changeUrl: true
		//		});
		//
		//	BROWSER NOTES:
		//  Safari 1.2: 
		//	back button "works" fine, however it's not possible to actually
		//	DETECT that you've moved backwards by inspecting window.location.
		//	Unless there is some other means of locating.
		//	FIXME: perhaps we can poll on history.length?
		//	Safari 2.0.3+ (and probably 1.3.2+):
		//	works fine, except when changeUrl is used. When changeUrl is used,
		//	Safari jumps all the way back to whatever page was shown before
		//	the page that uses dojo.undo.browser support.
		//	IE 5.5 SP2:
		//	back button behavior is macro. It does not move back to the
		//	previous hash value, but to the last full page load. This suggests
		//	that the iframe is the correct way to capture the back button in
		//	these cases.
		//	Don't test this page using local disk for MSIE. MSIE will not create 
		//	a history list for iframe_history.html if served from a file: URL. 
		//	The XML served back from the XHR tests will also not be properly 
		//	created if served from local disk. Serve the test pages from a web 
		//	server to test in that browser.
		//	IE 6.0:
		//	same behavior as IE 5.5 SP2
		//	Firefox 1.0+:
		//	the back button will return us to the previous hash on the same
		//	page, thereby not requiring an iframe hack, although we do then
		//	need to run a timer to detect inter-page movement.

		//If addToHistory is called, then that means we prune the
		//forward stack -- the user went back, then wanted to
		//start a new forward path.
		this.forwardStack = []; 

		var hash = null;
		var url = null;
		if(!this.historyIframe){
			if(djConfig["useXDomain"] && !djConfig["dojoIframeHistoryUrl"]){
				dojo.debug("dojo.undo.browser: When using cross-domain Dojo builds,"
					+ " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
					+ " to the path on your domain to iframe_history.html");
			}
			this.historyIframe = window.frames["djhistory"];
		}
		if(!this.bookmarkAnchor){
			this.bookmarkAnchor = document.createElement("a");
			dojo.body().appendChild(this.bookmarkAnchor);
			this.bookmarkAnchor.style.display = "none";
		}
		if(args["changeUrl"]){
			hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
			
			//If the current hash matches the new one, just replace the history object with
			//this new one. It doesn't make sense to track different state objects for the same
			//logical URL. This matches the browser behavior of only putting in one history
			//item no matter how many times you click on the same #hash link, at least in Firefox
			//and Safari, and there is no reliable way in those browsers to know if a #hash link
			//has been clicked on multiple times. So making this the standard behavior in all browsers
			//so that dojo.undo.browser's behavior is the same in all browsers.
			if(this.historyStack.length == 0 && this.initialState.urlHash == hash){
				this.initialState = this._createState(url, args, hash);
				return;
			}else if(this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash){
				this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash);
				return;
			}

			this.changingUrl = true;
			setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1);
			this.bookmarkAnchor.href = hash;
			
			if(dojo.render.html.ie){
				url = this._loadIframeHistory();

				var oldCB = args["back"]||args["backButton"]||args["handle"];

				//The function takes handleName as a parameter, in case the
				//callback we are overriding was "handle". In that case,
				//we will need to pass the handle name to handle.
				var tcb = function(handleName){
					if(window.location.hash != ""){
						setTimeout("window.location.href = '"+hash+"';", 1);
					}
					//Use apply to set "this" to args, and to try to avoid memory leaks.
					oldCB.apply(this, [handleName]);
				}
		
				//Set interceptor function in the right place.
				if(args["back"]){
					args.back = tcb;
				}else if(args["backButton"]){
					args.backButton = tcb;
				}else if(args["handle"]){
					args.handle = tcb;
				}
		
				var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
		
				//The function takes handleName as a parameter, in case the
				//callback we are overriding was "handle". In that case,
				//we will need to pass the handle name to handle.
				var tfw = function(handleName){
					if(window.location.hash != ""){
						window.location.href = hash;
					}
					if(oldFW){ // we might not actually have one
						//Use apply to set "this" to args, and to try to avoid memory leaks.
						oldFW.apply(this, [handleName]);
					}
				}

				//Set interceptor function in the right place.
				if(args["forward"]){
					args.forward = tfw;
				}else if(args["forwardButton"]){
					args.forwardButton = tfw;
				}else if(args["handle"]){
					args.handle = tfw;
				}

			}else if(dojo.render.html.moz){
				// start the timer
				if(!this.locationTimer){
					this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200);
				}
			}
		}else{
			url = this._loadIframeHistory();
		}

		this.historyStack.push(this._createState(url, args, hash));
	},

	checkLocation: function(){
		//summary: private method. Do not call this directly.
		if (!this.changingUrl){
			var hsl = this.historyStack.length;

			if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){
				// FIXME: could this ever be a forward button?
				// we can't clear it because we still need to check for forwards. Ugg.
				// clearInterval(this.locationTimer);
				this.handleBackButton();
				return;
			}
			
			// first check to see if we could have gone forward. We always halt on
			// a no-hash item.
			if(this.forwardStack.length > 0){
				if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){
					this.handleForwardButton();
					return;
				}
			}
	
			// ok, that didn't work, try someplace back in the history stack
			if((hsl >= 2)&&(this.historyStack[hsl-2])){
				if(this.historyStack[hsl-2].urlHash==window.location.hash){
					this.handleBackButton();
					return;
				}
			}
		}
	},

	iframeLoaded: function(evt, ifrLoc){
		//summary: private method. Do not call this directly.
		if(!dojo.render.html.opera){
			var query = this._getUrlQuery(ifrLoc.href);
			if(query == null){ 
				// alert("iframeLoaded");
				// we hit the end of the history, so we should go back
				if(this.historyStack.length == 1){
					this.handleBackButton();
				}
				return;
			}
			if(this.moveForward){
				// we were expecting it, so it's not either a forward or backward movement
				this.moveForward = false;
				return;
			}
	
			//Check the back stack first, since it is more likely.
			//Note that only one step back or forward is supported.
			if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
				this.handleBackButton();
			}
			else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
				this.handleForwardButton();
			}
		}
	},

	handleBackButton: function(){
		//summary: private method. Do not call this directly.

		//The "current" page is always at the top of the history stack.
		var current = this.historyStack.pop();
		if(!current){ return; }
		var last = this.historyStack[this.historyStack.length-1];
		if(!last && this.historyStack.length == 0){
			last = this.initialState;
		}
		if (last){
			if(last.kwArgs["back"]){
				last.kwArgs["back"]();
			}else if(last.kwArgs["backButton"]){
				last.kwArgs["backButton"]();
			}else if(last.kwArgs["handle"]){
				last.kwArgs.handle("back");
			}
		}
		this.forwardStack.push(current);
	},

	handleForwardButton: function(){
		//summary: private method. Do not call this directly.

		var last = this.forwardStack.pop();
		if(!last){ return; }
		if(last.kwArgs["forward"]){
			last.kwArgs.forward();
		}else if(last.kwArgs["forwardButton"]){
			last.kwArgs.forwardButton();
		}else if(last.kwArgs["handle"]){
			last.kwArgs.handle("forward");
		}
		this.historyStack.push(last);
	},

	_createState: function(url, args, hash){
		//summary: private method. Do not call this directly.

		return {"url": url, "kwArgs": args, "urlHash": hash};	//Object
	},

	_getUrlQuery: function(url){
		//summary: private method. Do not call this directly.
		var segments = url.split("?");
		if (segments.length < 2){
			return null; //null
		}
		else{
			return segments[1]; //String
		}
	},
	
	_loadIframeHistory: function(){
		//summary: private method. Do not call this directly.
		var url = (djConfig["dojoIframeHistoryUrl"] || dojo.hostenv.getBaseScriptUri()+'iframe_history.html')
			+ "?" + (new Date()).getTime();
		this.moveForward = true;
		dojo.io.setIFrameSrc(this.historyIframe, url, false);	
		return url; //String
	}
}

dojo.provide("dojo.io.BrowserIO");








if(!dj_undef("window")) {

dojo.io.checkChildrenForFile = function(/*DOMNode*/node){
	//summary: Checks any child nodes of node for an input type="file" element.
	var hasFile = false;
	var inputs = node.getElementsByTagName("input");
	dojo.lang.forEach(inputs, function(input){
		if(hasFile){ return; }
		if(input.getAttribute("type")=="file"){
			hasFile = true;
		}
	});
	return hasFile; //boolean
}

dojo.io.formHasFile = function(/*DOMNode*/formNode){
	//summary: Just calls dojo.io.checkChildrenForFile().
	return dojo.io.checkChildrenForFile(formNode); //boolean
}

dojo.io.updateNode = function(/*DOMNode*/node, /*String or Object*/urlOrArgs){
	//summary: Updates a DOMnode with the result of a dojo.io.bind() call.
	//node: DOMNode
	//urlOrArgs: String or Object
	//		Either a String that has an URL, or an object containing dojo.io.bind()
	//		arguments.
	node = dojo.byId(node);
	var args = urlOrArgs;
	if(dojo.lang.isString(urlOrArgs)){
		args = { url: urlOrArgs };
	}
	args.mimetype = "text/html";
	args.load = function(t, d, e){
		while(node.firstChild){
			dojo.dom.destroyNode(node.firstChild);
		}
		node.innerHTML = d;
	};
	dojo.io.bind(args);
}

dojo.io.formFilter = function(/*DOMNode*/node) {
	//summary: Returns true if the node is an input element that is enabled, has
	//a name, and whose type is one of the following values: ["file", "submit", "image", "reset", "button"]
	var type = (node.type||"").toLowerCase();
	return !node.disabled && node.name
		&& !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type); //boolean
}

// TODO: Move to htmlUtils
dojo.io.encodeForm = function(/*DOMNode*/formNode, /*String?*/encoding, /*Function?*/formFilter){
	//summary: Converts the names and values of form elements into an URL-encoded
	//string (name=value&name=value...).
	//formNode: DOMNode
	//encoding: String?
	//		The encoding to use for the values. Specify a string that starts with
	//		"utf" (for instance, "utf8"), to use encodeURIComponent() as the encoding
	//		function. Otherwise, dojo.string.encodeAscii will be used.
	//formFilter: Function?
	//	A function used to filter out form elements. The element node will be passed
	//	to the formFilter function, and a boolean result is expected (true indicating
	//	indicating that the element should have its name/value included in the output).
	//	If no formFilter is specified, then dojo.io.formFilter() will be used.
	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
		dojo.raise("Attempted to encode a non-form element.");
	}
	if(!formFilter) { formFilter = dojo.io.formFilter; }
	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
	var values = [];

	for(var i = 0; i < formNode.elements.length; i++){
		var elm = formNode.elements[i];
		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
		var name = enc(elm.name);
		var type = elm.type.toLowerCase();

		if(type == "select-multiple"){
			for(var j = 0; j < elm.options.length; j++){
				if(elm.options[j].selected) {
					values.push(name + "=" + enc(elm.options[j].value));
				}
			}
		}else if(dojo.lang.inArray(["radio", "checkbox"], type)){
			if(elm.checked){
				values.push(name + "=" + enc(elm.value));
			}
		}else{
			values.push(name + "=" + enc(elm.value));
		}
	}

	// now collect input type="image", which doesn't show up in the elements array
	var inputs = formNode.getElementsByTagName("input");
	for(var i = 0; i < inputs.length; i++) {
		var input = inputs[i];
		if(input.type.toLowerCase() == "image" && input.form == formNode
			&& formFilter(input)) {
			var name = enc(input.name);
			values.push(name + "=" + enc(input.value));
			values.push(name + ".x=0");
			values.push(name + ".y=0");
		}
	}
	return values.join("&") + "&"; //String
}

dojo.io.FormBind = function(/*DOMNode or Object*/args) {
	//summary: constructor for a dojo.io.FormBind object. See the Dojo Book for
	//some information on usage: http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book23
	//args: DOMNode or Object
	//		args can either be the DOMNode for a form element, or an object containing
	//		dojo.io.bind() arguments, one of which should be formNode with the value of
	//		a form element DOMNode.
	this.bindArgs = {};

	if(args && args.formNode) {
		this.init(args);
	} else if(args) {
		this.init({formNode: args});
	}
}
dojo.lang.extend(dojo.io.FormBind, {
	form: null,

	bindArgs: null,

	clickedButton: null,

	init: function(/*DOMNode or Object*/args) {
		//summary: Internal function called by the dojo.io.FormBind() constructor
		//do not call this method directly.
		var form = dojo.byId(args.formNode);

		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
			throw new Error("FormBind: Couldn't apply, invalid form");
		} else if(this.form == form) {
			return;
		} else if(this.form) {
			throw new Error("FormBind: Already applied to a form");
		}

		dojo.lang.mixin(this.bindArgs, args);
		this.form = form;

		this.connect(form, "onsubmit", "submit");

		for(var i = 0; i < form.elements.length; i++) {
			var node = form.elements[i];
			if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) {
				this.connect(node, "onclick", "click");
			}
		}

		var inputs = form.getElementsByTagName("input");
		for(var i = 0; i < inputs.length; i++) {
			var input = inputs[i];
			if(input.type.toLowerCase() == "image" && input.form == form) {
				this.connect(input, "onclick", "click");
			}
		}
	},

	onSubmit: function(/*DOMNode*/form) {
		//summary: Function used to verify that the form is OK to submit.
		//Override this function if you want specific form validation done.
		return true; //boolean
	},

	submit: function(/*Event*/e) {
		//summary: internal function that is connected as a listener to the
		//form's onsubmit event.
		e.preventDefault();
		if(this.onSubmit(this.form)) {
			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
				formFilter: dojo.lang.hitch(this, "formFilter")
			}));
		}
	},

	click: function(/*Event*/e) {
		//summary: internal method that is connected as a listener to the
		//form's elements whose click event can submit a form.
		var node = e.currentTarget;
		if(node.disabled) { return; }
		this.clickedButton = node;
	},

	formFilter: function(/*DOMNode*/node) {
		//summary: internal function used to know which form element values to include
		//		in the dojo.io.bind() request.
		var type = (node.type||"").toLowerCase();
		var accept = false;
		if(node.disabled || !node.name) {
			accept = false;
		} else if(dojo.lang.inArray(["submit", "button", "image"], type)) {
			if(!this.clickedButton) { this.clickedButton = node; }
			accept = node == this.clickedButton;
		} else {
			accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type);
		}
		return accept; //boolean
	},

	// in case you don't have dojo.event.* pulled in
	connect: function(/*Object*/srcObj, /*Function*/srcFcn, /*Function*/targetFcn) {
		//summary: internal function used to connect event listeners to form elements
		//that trigger events. Used in case dojo.event is not loaded.
		if(dojo.evalObjPath("dojo.event.connect")) {
			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
		} else {
			var fcn = dojo.lang.hitch(this, targetFcn);
			srcObj[srcFcn] = function(e) {
				if(!e) { e = window.event; }
				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
				fcn(e);
			}
		}
	}
});

dojo.io.XMLHTTPTransport = new function(){
	//summary: The object that implements the dojo.io.bind transport for XMLHttpRequest.
	var _this = this;

	var _cache = {}; // FIXME: make this public? do we even need to?
	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
	this.preventCache = false; // if this is true, we'll always force GET requests to cache

	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
	function getCacheKey(url, query, method) {
		return url + "|" + query + "|" + method.toLowerCase();
	}

	function addToCache(url, query, method, http) {
		_cache[getCacheKey(url, query, method)] = http;
	}

	function getFromCache(url, query, method) {
		return _cache[getCacheKey(url, query, method)];
	}

	this.clearCache = function() {
		_cache = {};
	}

	// moved successful load stuff here
	function doLoad(kwArgs, http, url, query, useCache) {
		if(	((http.status>=200)&&(http.status<300))|| 	// allow any 2XX response code
			(http.status==304)|| 						// get it out of the cache
			(http.status==1223)|| 						// Internet Explorer mangled the status code
			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
		){
			var ret;
			if(kwArgs.method.toLowerCase() == "head"){
				var headers = http.getAllResponseHeaders();
				ret = {};
				ret.toString = function(){ return headers; }
				var values = headers.split(/[\r\n]+/g);
				for(var i = 0; i < values.length; i++) {
					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
					if(pair) {
						ret[pair[1]] = pair[2];
					}
				}
			}else if(kwArgs.mimetype == "text/javascript"){
				try{
					ret = dj_eval(http.responseText);
				}catch(e){
					dojo.debug(e);
					dojo.debug(http.responseText);
					ret = null;
				}
			}else if(kwArgs.mimetype.substr(0, 9) == "text/json" || kwArgs.mimetype.substr(0, 16) == "application/json"){
				try{
					ret = dj_eval("("+kwArgs.jsonFilter(http.responseText)+")");
				}catch(e){
					dojo.debug(e);
					dojo.debug(http.responseText);
					ret = false;
				}
			}else if((kwArgs.mimetype == "application/xml")||
						(kwArgs.mimetype == "text/xml")){
				ret = http.responseXML;
				if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
					ret = dojo.dom.createDocumentFromText(http.responseText);
				}
			}else{
				ret = http.responseText;
			}

			if(useCache){ // only cache successful responses
				addToCache(url, query, kwArgs.method, http);
			}
			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
		}else{
			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
		}
	}

	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
	function setHeaders(http, kwArgs){
		if(kwArgs["headers"]) {
			for(var header in kwArgs["headers"]) {
				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
					kwArgs["contentType"] = kwArgs["headers"][header];
				} else {
					http.setRequestHeader(header, kwArgs["headers"][header]);
				}
			}
		}
	}

	this.inFlight = [];
	this.inFlightTimer = null;

	this.startWatchingInFlight = function(){
		//summary: internal method used to trigger a timer to watch all inflight
		//XMLHttpRequests.
		if(!this.inFlightTimer){
			// setInterval broken in mozilla x86_64 in some circumstances, see
			// https://bugzilla.mozilla.org/show_bug.cgi?id=344439
			// using setTimeout instead
			this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
		}
	}

	this.watchInFlight = function(){
		//summary: internal method that checks each inflight XMLHttpRequest to see
		//if it has completed or if the timeout situation applies.
		var now = null;
		// make sure sync calls stay thread safe, if this callback is called during a sync call
		// and this results in another sync call before the first sync call ends the browser hangs
		if(!dojo.hostenv._blockAsync && !_this._blockAsync){
			for(var x=this.inFlight.length-1; x>=0; x--){
				try{
					var tif = this.inFlight[x];
					if(!tif || tif.http._aborted || !tif.http.readyState){
						this.inFlight.splice(x, 1); continue; 
					}
					if(4==tif.http.readyState){
						// remove it so we can clean refs
						this.inFlight.splice(x, 1);
						doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
					}else if (tif.startTime){
						//See if this is a timeout case.
						if(!now){
							now = (new Date()).getTime();
						}
						if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
							//Stop the request.
							if(typeof tif.http.abort == "function"){
								tif.http.abort();
							}
		
							// remove it so we can clean refs
							this.inFlight.splice(x, 1);
							tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
						}
					}
				}catch(e){
					try{
						var errObj = new dojo.io.Error("XMLHttpTransport.watchInFlight Error: " + e);
						tif.req[(typeof tif.req.error == "function") ? "error" : "handle"]("error", errObj, tif.http, tif.req);
					}catch(e2){
						dojo.debug("XMLHttpTransport error callback failed: " + e2);
					}
				}
			}
		}

		clearTimeout(this.inFlightTimer);
		if(this.inFlight.length == 0){
			this.inFlightTimer = null;
			return;
		}
		this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
	}

	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
	this.canHandle = function(/*dojo.io.Request*/kwArgs){
		//summary: Tells dojo.io.bind() if this is a good transport to
		//use for the particular type of request. This type of transport cannot
		//handle forms that have an input type="file" element.

		// FIXME: we need to determine when form values need to be
		// multi-part mime encoded and avoid using this transport for those
		// requests.
		var mlc = kwArgs["mimetype"].toLowerCase()||"";
		return hasXmlHttp
			&& (
				(
					dojo.lang.inArray([
						"text/plain", "text/html", "application/xml", 
						"text/xml", "text/javascript"
						], mlc
					)
				) || (
					mlc.substr(0, 9) == "text/json" || mlc.substr(0, 16) == "application/json"
				)
			)
			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) ); //boolean
	}

	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts

	this.bind = function(/*dojo.io.Request*/kwArgs){
		//summary: function that sends the request to the server.

		//This function will attach an abort() function to the kwArgs dojo.io.Request object,
		//so if you need to abort the request, you can call that method on the request object.
		//The following are acceptable properties in kwArgs (in addition to the
		//normal dojo.io.Request object properties).
		//url: String: URL the server URL to use for the request.
		//method: String: the HTTP method to use (GET, POST, etc...).
		//mimetype: Specifies what format the result data should be given to the load/handle callback. Valid values are:
		//		text/javascript, text/json, application/json, application/xml, text/xml. Any other mimetype will give back a text
		//		string.
		//transport: String: specify "XMLHTTPTransport" to force the use of this XMLHttpRequest transport.
		//headers: Object: The object property names and values will be sent as HTTP request header
		//		names and values.
		//sendTransport: boolean: If true, then dojo.transport=xmlhttp will be added to the request.
		//encoding: String: The type of encoding to use when dealing with the content kwArgs property.
		//content: Object: The content object is converted into a name=value&name=value string, by
		//		using dojo.io.argsFromMap(). The encoding kwArgs property is passed to dojo.io.argsFromMap()
		//		for use in encoding the names and values. The resulting string is added to the request.
		//formNode: DOMNode: a form element node. This should not normally be used. Use new dojo.io.FormBind() instead.
		//		If formNode is used, then the names and values of the form elements will be converted
		//		to a name=value&name=value string and added to the request. The encoding kwArgs property is used
		//		to encode the names and values.
		//postContent: String: Raw name=value&name=value string to be included as part of the request.
		//back or backButton: Function: A function to be called if the back button is pressed. If this kwArgs property
		//		is used, then back button support via dojo.undo.browser will be used. See notes for dojo.undo.browser on usage.
		//		You need to set djConfig.preventBackButtonFix = false to enable back button support.
		//changeUrl: boolean or String: Used as part of back button support. See notes for dojo.undo.browser on usage.
		//user: String: The user name. Used in conjuction with password. Passed to XMLHttpRequest.open().
		//password: String: The user's password. Used in conjuction with user. Passed to XMLHttpRequest.open().
		//file: Object or Array of Objects: an object simulating a file to be uploaded. file objects should have the following properties:
		//		name or fileName: the name of the file
		//		contentType: the MIME content type for the file.
		//		content: the actual content of the file.
		//multipart: boolean: indicates whether this should be a multipart mime request. If kwArgs.file exists, then this
		//		property is set to true automatically.
		//sync: boolean: if true, then a synchronous XMLHttpRequest call is done,
		//		if false (the default), then an asynchronous call is used.
		//preventCache: boolean: If true, then a cache busting parameter is added to the request URL.
		//		default value is false.
		//useCache: boolean: If true, then XMLHttpTransport will keep an internal cache of the server
		//		response and use that response if a similar request is done again.
		//		A similar request is one that has the same URL, query string and HTTP method value.
		//		default is false.
		if(!kwArgs["url"]){
			// are we performing a history action?
			if( !kwArgs["formNode"]
				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
				&& (!djConfig.preventBackButtonFix)) {
        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
        				"Use dojo.undo.browser.addToHistory() instead.", "0.4");
				dojo.undo.browser.addToHistory(kwArgs);
				return true;
			}
		}

		// build this first for cache purposes
		var url = kwArgs.url;
		var query = "";
		if(kwArgs["formNode"]){
			var ta = kwArgs.formNode.getAttribute("action");
			if((ta)&&(!kwArgs["url"])){ url = ta; }
			var tp = kwArgs.formNode.getAttribute("method");
			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
		}

		if(url.indexOf("#") > -1) {
			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
			url = url.split("#")[0];
		}

		if(kwArgs["file"]){
			// force post for file transfer
			kwArgs.method = "post";
		}

		if(!kwArgs["method"]){
			kwArgs.method = "get";
		}

		// guess the multipart value
		if(kwArgs.method.toLowerCase() == "get"){
			// GET cannot use multipart
			kwArgs.multipart = false;
		}else{
			if(kwArgs["file"]){
				// enforce multipart when sending files
				kwArgs.multipart = true;
			}else if(!kwArgs["multipart"]){
				// default 
				kwArgs.multipart = false;
			}
		}

		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
			dojo.undo.browser.addToHistory(kwArgs);
		}

		var content = kwArgs["content"] || {};

		if(kwArgs.sendTransport) {
			content["dojo.transport"] = "xmlhttp";
		}

		do { // break-block
			if(kwArgs.postContent){
				query = kwArgs.postContent;
				break;
			}

			if(content) {
				query += dojo.io.argsFromMap(content, kwArgs.encoding);
			}
			
			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
				break;
			}

			var	t = [];
			if(query.length){
				var q = query.split("&");
				for(var i = 0; i < q.length; ++i){
					if(q[i].length){
						var p = q[i].split("=");
						t.push(	"--" + this.multipartBoundary,
								"Content-Disposition: form-data; name=\"" + p[0] + "\"", 
								"",
								p[1]);
					}
				}
			}

			if(kwArgs.file){
				if(dojo.lang.isArray(kwArgs.file)){
					for(var i = 0; i < kwArgs.file.length; ++i){
						var o = kwArgs.file[i];
						t.push(	"--" + this.multipartBoundary,
								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
								"",
								o.content);
					}
				}else{
					var o = kwArgs.file;
					t.push(	"--" + this.multipartBoundary,
							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
							"",
							o.content);
				}
			}

			if(t.length){
				t.push("--"+this.multipartBoundary+"--", "");
				query = t.join("\r\n");
			}
		}while(false);

		// kwArgs.Connection = "close";

		var async = kwArgs["sync"] ? false : true;

		var preventCache = kwArgs["preventCache"] ||
			(this.preventCache == true && kwArgs["preventCache"] != false);
		var useCache = kwArgs["useCache"] == true ||
			(this.useCache == true && kwArgs["useCache"] != false );

		// preventCache is browser-level (add query string junk), useCache
		// is for the local cache. If we say preventCache, then don't attempt
		// to look in the cache, but if useCache is true, we still want to cache
		// the response
		if(!preventCache && useCache){
			var cachedHttp = getFromCache(url, query, kwArgs.method);
			if(cachedHttp){
				doLoad(kwArgs, cachedHttp, url, query, false);
				return;
			}
		}

		// much of this is from getText, but reproduced here because we need
		// more flexibility
		var http = dojo.hostenv.getXmlhttpObject(kwArgs);	
		var received = false;

		// build a handler function that calls back to the handler obj
		if(async){
			var startTime = 
			// FIXME: setting up this callback handler leaks on IE!!!
			this.inFlight.push({
				"req":		kwArgs,
				"http":		http,
				"url":	 	url,
				"query":	query,
				"useCache":	useCache,
				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
			});
			this.startWatchingInFlight();
		}else{
			// block async callbacks until sync is in, needed in khtml, others?
			_this._blockAsync = true;
		}

		if(kwArgs.method.toLowerCase() == "post"){
			// FIXME: need to hack in more flexible Content-Type setting here!
			if (!kwArgs.user) {
				http.open("POST", url, async);
			}else{
        http.open("POST", url, async, kwArgs.user, kwArgs.password);
			}
			setHeaders(http, kwArgs);
			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) : 
				(kwArgs.contentType || "application/x-www-form-urlencoded"));
			try{
				http.send(query);
			}catch(e){
				if(typeof http.abort == "function"){
					http.abort();
				}
				doLoad(kwArgs, {status: 404}, url, query, useCache);
			}
		}else{
			var tmpUrl = url;
			if(query != "") {
				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
			}
			if(preventCache) {
				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
			}
			if (!kwArgs.user) {
				http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
			}else{
				http.open(kwArgs.method.toUpperCase(), tmpUrl, async, kwArgs.user, kwArgs.password);
			}
			setHeaders(http, kwArgs);
			try {
				http.send(null);
			}catch(e)	{
				if(typeof http.abort == "function"){
					http.abort();
				}
				doLoad(kwArgs, {status: 404}, url, query, useCache);
			}
		}

		if( !async ) {
			doLoad(kwArgs, http, url, query, useCache);
			_this._blockAsync = false;
		}

		kwArgs.abort = function(){
			try{// khtml doesent reset readyState on abort, need this workaround
				http._aborted = true; 
			}catch(e){/*squelsh*/}
			return http.abort();
		}

		return;
	}
	dojo.io.transports.addTransport("XMLHTTPTransport");
}

}

dojo.provide("dojo.io.cookie");

dojo.io.cookie.setCookie = function(/*String*/name, /*String*/value, 
									/*Number?*/days, /*String?*/path, 
									/*String?*/domain, /*boolean?*/secure){
	//summary: sets a cookie.
	var expires = -1;
	if((typeof days == "number")&&(days >= 0)){
		var d = new Date();
		d.setTime(d.getTime()+(days*24*60*60*1000));
		expires = d.toGMTString();
	}
	value = escape(value);
	document.cookie = name + "=" + value + ";"
		+ (expires != -1 ? " expires=" + expires + ";" : "")
		+ (path ? "path=" + path : "")
		+ (domain ? "; domain=" + domain : "")
		+ (secure ? "; secure" : "");
}

dojo.io.cookie.set = dojo.io.cookie.setCookie;

dojo.io.cookie.getCookie = function(/*String*/name){
	//summary: Gets a cookie with the given name.

	// FIXME: Which cookie should we return?
	//        If there are cookies set for different sub domains in the current
	//        scope there could be more than one cookie with the same name.
	//        I think taking the last one in the list takes the one from the
	//        deepest subdomain, which is what we're doing here.
	var idx = document.cookie.lastIndexOf(name+'=');
	if(idx == -1) { return null; }
	var value = document.cookie.substring(idx+name.length+1);
	var end = value.indexOf(';');
	if(end == -1) { end = value.length; }
	value = value.substring(0, end);
	value = unescape(value);
	return value; //String
}

dojo.io.cookie.get = dojo.io.cookie.getCookie;

dojo.io.cookie.deleteCookie = function(/*String*/name){
	//summary: Deletes a cookie with the given name.
	dojo.io.cookie.setCookie(name, "-", 0);
}

dojo.io.cookie.setObjectCookie = function(	/*String*/name, /*Object*/obj, 
											/*Number?*/days, /*String?*/path, 
											/*String?*/domain, /*boolean?*/secure, 
											/*boolean?*/clearCurrent){
	//summary: Takes an object, serializes it to a cookie value, and either
	//sets a cookie with the serialized value.
	//description: If clearCurrent is true, then any current cookie value
	//for this object will be replaced with the the new serialized object value.
	//If clearCurrent is false, then the existing cookie value will be modified
	//with any changes from the new object value.
	//Objects must be simple name/value pairs where the value is either a string
	//or a number. Any other value will be ignored.
	if(arguments.length == 5){ // for backwards compat
		clearCurrent = domain;
		domain = null;
		secure = null;
	}
	var pairs = [], cookie, value = "";
	if(!clearCurrent){
		cookie = dojo.io.cookie.getObjectCookie(name);
	}
	if(days >= 0){
		if(!cookie){ cookie = {}; }
		for(var prop in obj){
			if(obj[prop] == null){
				delete cookie[prop];
			}else if((typeof obj[prop] == "string")||(typeof obj[prop] == "number")){
				cookie[prop] = obj[prop];
			}
		}
		prop = null;
		for(var prop in cookie){
			pairs.push(escape(prop) + "=" + escape(cookie[prop]));
		}
		value = pairs.join("&");
	}
	dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
}

dojo.io.cookie.getObjectCookie = function(/*String*/name){
	//summary: Gets an object value for the given cookie name. The complement of
	//dojo.io.cookie.setObjectCookie().
	var values = null, cookie = dojo.io.cookie.getCookie(name);
	if(cookie){
		values = {};
		var pairs = cookie.split("&");
		for(var i = 0; i < pairs.length; i++){
			var pair = pairs[i].split("=");
			var value = pair[1];
			if( isNaN(value) ){ value = unescape(pair[1]); }
			values[ unescape(pair[0]) ] = value;
		}
	}
	return values;
}

dojo.io.cookie.isSupported = function(){
	//summary: Tests the browser to see if cookies are enabled.
	if(typeof navigator.cookieEnabled != "boolean"){
		dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
			"CookiesAllowed", 90, null);
		var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
		navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
		if(navigator.cookieEnabled){
			// FIXME: should we leave this around?
			this.deleteCookie("__TestingYourBrowserForCookieSupport__");
		}
	}
	return navigator.cookieEnabled; //boolean
}

// need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
if(!dojo.io.cookies){ dojo.io.cookies = dojo.io.cookie; }

dojo.kwCompoundRequire({
	common: ["dojo.io.common"],
	rhino: ["dojo.io.RhinoIO"],
	browser: ["dojo.io.BrowserIO", "dojo.io.cookie"],
	dashboard: ["dojo.io.BrowserIO", "dojo.io.cookie"]
});
dojo.provide("dojo.io.*");

dojo.provide("dojo.event.common");





// TODO: connection filter functions
//			these are functions that accept a method invocation (like around
//			advice) and return a boolean based on it. That value determines
//			whether or not the connection proceeds. It could "feel" like around
//			advice for those who know what it is (calling proceed() or not),
//			but I think presenting it as a "filter" and/or calling it with the
//			function args and not the MethodInvocation might make it more
//			palletable to "normal" users than around-advice currently is
// TODO: execution scope mangling
//			YUI's event facility by default executes listeners in the context
//			of the source object. This is very odd, but should probably be
//			supported as an option (both for the source and for the dest). It
//			can be thought of as a connection-specific hitch().
// TODO: more resiliency for 4+ arguments to connect()

dojo.event = new function(){
	this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);

	// FIXME: where should we put this method (not here!)?
	function interpolateArgs(args, searchForNames){
		var dl = dojo.lang;
		var ao = {
			srcObj: dj_global,
			srcFunc: null,
			adviceObj: dj_global,
			adviceFunc: null,
			aroundObj: null,
			aroundFunc: null,
			adviceType: (args.length>2) ? args[0] : "after",
			precedence: "last",
			once: false,
			delay: null,
			rate: 0,
			adviceMsg: false,
			maxCalls: -1
		};

		switch(args.length){
			case 0: return;
			case 1: return;
			case 2:
				ao.srcFunc = args[0];
				ao.adviceFunc = args[1];
				break;
			case 3:
				if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
					ao.adviceType = "after";
					ao.srcObj = args[0];
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
				}else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
				}else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
					ao.adviceType = "after";
					ao.srcObj = args[0];
					ao.srcFunc = args[1];
					var tmpName  = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames);
					ao.adviceFunc = tmpName;
				}else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
					ao.adviceType = "after";
					ao.srcObj = dj_global;
					var tmpName  = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames);
					ao.srcFunc = tmpName;
					ao.adviceObj = args[1];
					ao.adviceFunc = args[2];
				}
				break;
			case 4:
				if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
					// we can assume that we've got an old-style "connect" from
					// the sigslot school of event attachment. We therefore
					// assume after-advice.
					ao.adviceType = "after";
					ao.srcObj = args[0];
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
					ao.adviceType = args[0];
					ao.srcObj = dj_global;
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
					ao.adviceType = args[0];
					ao.srcObj = dj_global;
					var tmpName  = dl.nameAnonFunc(args[1], dj_global, searchForNames);
					ao.srcFunc = tmpName;
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
					ao.srcObj = args[1];
					ao.srcFunc = args[2];
					var tmpName  = dl.nameAnonFunc(args[3], dj_global, searchForNames);
					ao.adviceObj = dj_global;
					ao.adviceFunc = tmpName;
				}else if(dl.isObject(args[1])){
					ao.srcObj = args[1];
					ao.srcFunc = args[2];
					ao.adviceObj = dj_global;
					ao.adviceFunc = args[3];
				}else if(dl.isObject(args[2])){
					ao.srcObj = dj_global;
					ao.srcFunc = args[1];
					ao.adviceObj = args[2];
					ao.adviceFunc = args[3];
				}else{
					ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
					ao.srcFunc = args[1];
					ao.adviceFunc = args[2];
					ao.aroundFunc = args[3];
				}
				break;
			case 6:
				ao.srcObj = args[1];
				ao.srcFunc = args[2];
				ao.adviceObj = args[3]
				ao.adviceFunc = args[4];
				ao.aroundFunc = args[5];
				ao.aroundObj = dj_global;
				break;
			default:
				ao.srcObj = args[1];
				ao.srcFunc = args[2];
				ao.adviceObj = args[3]
				ao.adviceFunc = args[4];
				ao.aroundObj = args[5];
				ao.aroundFunc = args[6];
				ao.once = args[7];
				ao.delay = args[8];
				ao.rate = args[9];
				ao.adviceMsg = args[10];
				ao.maxCalls = (!isNaN(parseInt(args[11]))) ? args[11] : -1;
				break;
		}

		if(dl.isFunction(ao.aroundFunc)){
			var tmpName  = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
			ao.aroundFunc = tmpName;
		}

		if(dl.isFunction(ao.srcFunc)){
			ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc);
		}

		if(dl.isFunction(ao.adviceFunc)){
			ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc);
		}

		if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
			ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc);
		}

		if(!ao.srcObj){
			dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
		}
		if(!ao.adviceObj){
			dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
		}
		
		if(!ao.adviceFunc){
			dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
			dojo.debugShallow(ao);
		} 
		
		return ao;
	}

	this.connect = function(/*...*/){
		// summary:
		//		dojo.event.connect is the glue that holds most Dojo-based
		//		applications together. Most combinations of arguments are
		//		supported, with the connect() method attempting to disambiguate
		//		the implied types of positional parameters. The following will
		//		all work:
		//			dojo.event.connect("globalFunctionName1", "globalFunctionName2");
		//			dojo.event.connect(functionReference1, functionReference2);
		//			dojo.event.connect("globalFunctionName1", functionReference2);
		//			dojo.event.connect(functionReference1, "globalFunctionName2");
		//			dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
		//			dojo.event.connect("globalFunctionName1", scope2, "functionName2");
		//			dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
		//			dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
		//			dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");
		//			dojo.event.connect("around", 	scope1, "functionName1", 
		//											scope2, "functionName2",
		//											aroundFunctionReference);
		//			dojo.event.connect("around", 	scope1, "functionName1", 
		//											scope2, "functionName2",
		//											scope3, "aroundFunctionName");
		//			dojo.event.connect("before-around", 	scope1, "functionName1", 
		//													scope2, "functionName2",
		//													aroundFunctionReference);
		//			dojo.event.connect("after-around", 		scope1, "functionName1", 
		//													scope2, "functionName2",
		//													aroundFunctionReference);
		//			dojo.event.connect("after-around", 		scope1, "functionName1", 
		//													scope2, "functionName2",
		//													scope3, "aroundFunctionName");
		//			dojo.event.connect("around", 	scope1, "functionName1", 
		//											scope2, "functionName2",
		//											scope3, "aroundFunctionName", true, 30);
		//			dojo.event.connect("around", 	scope1, "functionName1", 
		//											scope2, "functionName2",
		//											scope3, "aroundFunctionName", null, null, 10);
		// adviceType: 
		//		Optional. String. One of "before", "after", "around",
		//		"before-around", or "after-around". FIXME
		// srcObj:
		//		the scope in which to locate/execute the named srcFunc. Along
		//		with srcFunc, this creates a way to dereference the function to
		//		call. So if the function in question is "foo.bar", the
		//		srcObj/srcFunc pair would be foo and "bar", where "bar" is a
		//		string and foo is an object reference.
		// srcFunc:
		//		the name of the function to connect to. When it is executed,
		//		the listener being registered with this call will be called.
		//		The adviceType defines the call order between the source and
		//		the target functions.
		// adviceObj:
		//		the scope in which to locate/execute the named adviceFunc.
		// adviceFunc:
		//		the name of the function being conected to srcObj.srcFunc
		// aroundObj:
		//		the scope in which to locate/execute the named aroundFunc.
		// aroundFunc:
		//		the name of, or a reference to, the function that will be used
		//		to mediate the advice call. Around advice requires a special
		//		unary function that will be passed a "MethodInvocation" object.
		//		These objects have several important properties, namely:
		//			- args
		//				a mutable array of arguments to be passed into the
		//				wrapped function
		//			- proceed
		//				a function that "continues" the invocation. The result
		//				of this function is the return of the wrapped function.
		//				You can then manipulate this return before passing it
		//				back out (or take further action based on it).
		// once:
		//		boolean that determines whether or not this connect() will
		//		create a new connection if an identical connect() has already
		//		been made. Defaults to "false".
		// delay:
		//		an optional delay (in ms), as an integer, for dispatch of a
		//		listener after the source has been fired.
		// rate:
		//		an optional rate throttling parameter (integer, in ms). When
		//		specified, this particular connection will not fire more than
		//		once in the interval specified by the rate
		// adviceMsg:
		//		boolean. Should the listener have all the parameters passed in
		//		as a single argument?

		/*
				ao.adviceType = args[0];
				ao.srcObj = args[1];
				ao.srcFunc = args[2];
				ao.adviceObj = args[3]
				ao.adviceFunc = args[4];
				ao.aroundObj = args[5];
				ao.aroundFunc = args[6];
				ao.once = args[7];
				ao.delay = args[8];
				ao.rate = args[9];
				ao.adviceMsg = args[10];
				ao.maxCalls = args[11];
		*/
		if(arguments.length == 1){
			var ao = arguments[0];
		}else{
			var ao = interpolateArgs(arguments, true);
		}
		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
			if(dojo.render.html.ie){
				ao.srcFunc = "onkeydown";
				this.connect(ao);
			}
			ao.srcFunc = "onkeypress";
		}

		if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){
			var tmpAO = {};
			for(var x in ao){
				tmpAO[x] = ao[x];
			}
			var mjps = [];
			dojo.lang.forEach(ao.srcObj, function(src){
				if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
					src = dojo.byId(src);
					// dojo.debug(src);
				}
				tmpAO.srcObj = src;
				// dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
				// dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
				mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
			});
			return mjps;
		}

		// FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
		if(ao.adviceFunc){
			var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
		}

		try{
		mjp.kwAddAdvice(ao);
		}catch(e){}

		// advanced users might want to fsck w/ the join point manually
		return mjp; // a MethodJoinPoint object
	}

	this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){
		// summary:
		//		a function that will wrap and log all calls to the specified
		//		a1.a2() function. If only a1 is passed, it'll be used as a
		//		function or function name on the global context. Logging will
		//		be sent to dojo.debug
		// a1:
		//		if a2 is passed, this should be an object. If not, it can be a
		//		function or function name.
		// a2:
		//		a function name
		var kwArgs;
		if((arguments.length == 1)&&(typeof a1 == "object")){
			kwArgs = a1;
		}else{
			kwArgs = {
				srcObj: a1,
				srcFunc: a2
			};
		}
		kwArgs.adviceFunc = function(){
			var argsStr = [];
			for(var x=0; x<arguments.length; x++){
				argsStr.push(arguments[x]);
			}
			dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
		};
		this.kwConnect(kwArgs);
	}

	this.connectBefore = function(){
		// summary:
		//	 	takes the same parameters as dojo.event.connect(), except that
		//	 	the advice type will always be "before"
		var args = ["before"];
		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
		return this.connect.apply(this, args); // a MethodJoinPoint object
	}

	this.connectAround = function(){
		// summary:
		//	 	takes the same parameters as dojo.event.connect(), except that
		//	 	the advice type will always be "around"
		var args = ["around"];
		for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
		return this.connect.apply(this, args); // a MethodJoinPoint object
	}

	this.connectOnce = function(){
		// summary:
		//	 	takes the same parameters as dojo.event.connect(), except that
		//	 	the "once" flag will always be set to "true"
		var ao = interpolateArgs(arguments, true);
		ao.once = true;
		return this.connect(ao); // a MethodJoinPoint object
	}

	this.connectRunOnce = function(){
		// summary:
		//	 	takes the same parameters as dojo.event.connect(), except that
		//	 	the "maxCalls" flag will always be set to 1
		var ao = interpolateArgs(arguments, true);
		ao.maxCalls = 1;
		return this.connect(ao); // a MethodJoinPoint object
	}

	this._kwConnectImpl = function(kwArgs, disconnect){
		var fn = (disconnect) ? "disconnect" : "connect";
		if(typeof kwArgs["srcFunc"] == "function"){
			kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
			kwArgs.srcFunc = tmpName;
		}
		if(typeof kwArgs["adviceFunc"] == "function"){
			kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
			var tmpName  = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
			kwArgs.adviceFunc = tmpName;
		}
		kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
		kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global;
		kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"];
		// pass kwargs to avoid unrolling/repacking
		return dojo.event[fn](kwArgs);
	}

	this.kwConnect = function(/*Object*/ kwArgs){
		// summary:
		//		A version of dojo.event.connect() that takes a map of named
		//		parameters instead of the positional parameters that
		//		dojo.event.connect() uses. For many advanced connection types,
		//		this can be a much more readable (and potentially faster)
		//		alternative.
		// kwArgs:
		// 		An object that can have the following properties:
		//			- adviceType
		//			- srcObj
		//			- srcFunc
		//			- adviceObj
		//			- adviceFunc 
		//			- aroundObj
		//			- aroundFunc
		//			- once
		//			- delay
		//			- rate
		//			- adviceMsg
		//		As with connect, only srcFunc and adviceFunc are generally
		//		required

		return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object

	}

	this.disconnect = function(){
		// summary:
		//		Takes the same parameters as dojo.event.connect() but destroys
		//		an existing connection instead of building a new one. For
		//		multiple identical connections, multiple disconnect() calls
		//		will unroll one each time it's called.
		if(arguments.length == 1){
			var ao = arguments[0];
		}else{
			var ao = interpolateArgs(arguments, true);
		}
		if(!ao.adviceFunc){ return; } // nothing to disconnect
		if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
			if(dojo.render.html.ie){
				ao.srcFunc = "onkeydown";
				this.disconnect(ao);
			}
			ao.srcFunc = "onkeypress";
		}
		if(!ao.srcObj[ao.srcFunc]){ return null; } // prevent un-necessaray joinpoint creation
		var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc, true);
		try{
			mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object
		}
		catch(e){;}
		return mjp;
	}

	this.kwDisconnect = function(kwArgs){
		// summary:
		//		Takes the same parameters as dojo.event.kwConnect() but
		//		destroys an existing connection instead of building a new one.
		return this._kwConnectImpl(kwArgs, true);
	}
}

// exactly one of these is created whenever a method with a joint point is run,
// if there is at least one 'around' advice.
dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){
	// summary:
	//		a class the models the call into a function. This is used under the
	//		covers for all method invocations on both ends of a
	//		connect()-wrapped function dispatch. This allows us to "pickle"
	//		calls, such as in the case of around advice.
	// join_point:
	//		a dojo.event.MethodJoinPoint object that represents a connection
	// obj:
	//		the scope the call will execute in
	// args:
	//		an array of parameters that will get passed to the callee
	this.jp_ = join_point;
	this.object = obj;
	this.args = [];
	// make sure we don't lock into a mutable object which can change under us.
	// It's ok if the individual items change, though.
	for(var x=0; x<args.length; x++){
		this.args[x] = args[x];
	}
	// the index of the 'around' that is currently being executed.
	this.around_index = -1;
}

dojo.event.MethodInvocation.prototype.proceed = function(){
	// summary:
	//		proceed with the method call that's represented by this invocation
	//		object
	this.around_index++;
	if(this.around_index >= this.jp_.around.length){
		return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
		// return this.jp_.run_before_after(this.object, this.args);
	}else{
		var ti = this.jp_.around[this.around_index];
		var mobj = ti[0]||dj_global;
		var meth = ti[1];
		return mobj[meth].call(mobj, this);
	}
} 


dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){
	this.object = obj||dj_global;
	this.methodname = funcName;
	this.methodfunc = this.object[funcName];
	this.squelch = false;
	// this.before = [];
	// this.after = [];
	// this.around = [];
}

dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){
	// summary:
	//		"static" class function for returning a MethodJoinPoint from a
	//		scoped function. If one doesn't exist, one is created.
	// obj:
	//		the scope to search for the function in
	// funcName:
	//		the name of the function to return a MethodJoinPoint for
	if(!obj){ obj = dj_global; }
	var ofn = obj[funcName];
	if(!ofn){
		// supply a do-nothing method implementation
		ofn = obj[funcName] = function(){};
		if(!obj[funcName]){
			// e.g. cannot add to inbuilt objects in IE6
			dojo.raise("Cannot set do-nothing method on that object "+funcName);
		}
	}else if((typeof ofn != "function")&&(!dojo.lang.isFunction(ofn))&&(!dojo.lang.isAlien(ofn))){
		// FIXME: should we throw an exception here instead?
		return null; 
	}
	// we hide our joinpoint instance in obj[funcName + '$joinpoint']
	var jpname = funcName + "$joinpoint";
	var jpfuncname = funcName + "$joinpoint$method";
	var joinpoint = obj[jpname];
	if(!joinpoint){
		var isNode = false;
		if(dojo.event["browser"]){
			if( (obj["attachEvent"])||
				(obj["nodeType"])||
				(obj["addEventListener"]) ){
				isNode = true;
				dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
			}
		}
		var origArity = ofn.length;
		obj[jpfuncname] = ofn;
		// joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName);
		joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);

		if(!isNode){
			obj[funcName] = function(){ 
				// var args = [];
				// for(var x=0; x<arguments.length; x++){
					// args.push(arguments[x]);
				// }
				// return joinpoint.run.apply(joinpoint, args); 
				return joinpoint.run.apply(joinpoint, arguments); 
			}
		}else{
			obj[funcName] = function(){ 
				var args = [];

				if(!arguments.length){
					var evt = null;
					try{
						if(obj.ownerDocument){
							evt = obj.ownerDocument.parentWindow.event;
						}else if(obj.documentElement){
							evt = obj.documentElement.ownerDocument.parentWindow.event;
						}else if(obj.event){ //obj is a window
							evt = obj.event;
						}else{
							evt = window.event;
						}
					}catch(e){
						evt = window.event;
					}

					if(evt){
						args.push(dojo.event.browser.fixEvent(evt, this));
					}
				}else{
					for(var x=0; x<arguments.length; x++){
						if((x==0)&&(dojo.event.browser.isEvent(arguments[x]))){
							args.push(dojo.event.browser.fixEvent(arguments[x], this));
						}else{
							args.push(arguments[x]);
						}
					}
				}
				// return joinpoint.run.apply(joinpoint, arguments); 
				return joinpoint.run.apply(joinpoint, args); 
			}
		}
		obj[funcName].__preJoinArity = origArity;
	}
	return joinpoint; // dojo.event.MethodJoinPoint
}

dojo.lang.extend(dojo.event.MethodJoinPoint, {
	squelch: false,

	unintercept: function(){
		// summary: 
		//		destroy the connection to all listeners that may have been
		//		registered on this joinpoint
		this.object[this.methodname] = this.methodfunc;
		this.before = [];
		this.after = [];
		this.around = [];
	},

	disconnect: dojo.lang.forward("unintercept"),

	run: function(){
		// summary:
		//		execute the connection represented by this join point. The
		//		arguments passed to run() will be passed to the function and
		//		its listeners.
		var obj = this.object||dj_global;
		var args = arguments;

		// optimization. We only compute once the array version of the arguments
		// pseudo-arr in order to prevent building it each time advice is unrolled.
		var aargs = [];
		for(var x=0; x<args.length; x++){
			aargs[x] = args[x];
		}

		var unrollAdvice  = function(marr){ 
			if(!marr){
				dojo.debug("Null argument to unrollAdvice()");
				return;
			}
		  
			var callObj = marr[0]||dj_global;
			var callFunc = marr[1];
			
			if(!callObj[callFunc]){
				dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
			}
			
			var aroundObj = marr[2]||dj_global;
			var aroundFunc = marr[3];
			var msg = marr[6];
			var maxCount = marr[7];
			if(maxCount > -1){
				if(maxCount == 0){
					return;
				}
				marr[7]--;
			}
			var undef;

			var to = {
				args: [],
				jp_: this,
				object: obj,
				proceed: function(){
					return callObj[callFunc].apply(callObj, to.args);
				}
			};
			to.args = aargs;

			var delay = parseInt(marr[4]);
			var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
			if(marr[5]){
				var rate = parseInt(marr[5]);
				var cur = new Date();
				var timerSet = false;
				if((marr["last"])&&((cur-marr.last)<=rate)){
					if(dojo.event._canTimeout){
						if(marr["delayTimer"]){
							clearTimeout(marr.delayTimer);
						}
						var tod = parseInt(rate*2); // is rate*2 naive?
						var mcpy = dojo.lang.shallowCopy(marr);
						marr.delayTimer = setTimeout(function(){
							// FIXME: on IE at least, event objects from the
							// browser can go out of scope. How (or should?) we
							// deal with it?
							mcpy[5] = 0;
							unrollAdvice(mcpy);
						}, tod);
					}
					return;
				}else{
					marr.last = cur;
				}
			}

			// FIXME: need to enforce rates for a connection here!

			if(aroundFunc){
				// NOTE: around advice can't delay since we might otherwise depend
				// on execution order!
				aroundObj[aroundFunc].call(aroundObj, to);
			}else{
				// var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
				if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){  // FIXME: the render checks are grotty!
					dj_global["setTimeout"](function(){
						if(msg){
							callObj[callFunc].call(callObj, to); 
						}else{
							callObj[callFunc].apply(callObj, args); 
						}
					}, delay);
				}else{ // many environments can't support delay!
					if(msg){
						callObj[callFunc].call(callObj, to); 
					}else{
						callObj[callFunc].apply(callObj, args); 
					}
				}
			}
		};

		var unRollSquelch = function(){
			if(this.squelch){
				try{
					return unrollAdvice.apply(this, arguments);
				}catch(e){ 
					dojo.debug(e);
				}
			}else{
				return unrollAdvice.apply(this, arguments);
			}
		};

		if((this["before"])&&(this.before.length>0)){
			// pass a cloned array, if this event disconnects this event forEach on this.before wont work
			dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch);
		}

		var result;
		
		try{
			if((this["around"])&&(this.around.length>0)){
				var mi = new dojo.event.MethodInvocation(this, obj, args);
				result = mi.proceed();
			}else if(this.methodfunc){
				result = this.object[this.methodname].apply(this.object, args);
			}
		}catch(e){ 
			if(!this.squelch){ 
				dojo.debug(e,"when calling",this.methodname,"on",this.object,"with arguments",args);
				dojo.raise(e);
			} 
		}

		if((this["after"])&&(this.after.length>0)){
			// see comment on this.before above
			dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch);
		}

		return (this.methodfunc) ? result : null;
	},

	getArr: function(/*String*/kind){
		// summary: return a list of listeners of the past "kind"
		// kind:
		//		can be one of: "before", "after", "around", "before-around", or
		//		"after-around"
		var type = "after";
		// FIXME: we should be able to do this through props or Array.in()
		if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
			type = "before";
		}else if(kind=="around"){
			type = "around";
		}
		if(!this[type]){ this[type] = []; }
		return this[type]; // Array
	},

	kwAddAdvice: function(/*Object*/args){
		// summary:
		//		adds advice to the joinpoint with arguments in a map
		// args:
		// 		An object that can have the following properties:
		//			- adviceType
		//			- adviceObj
		//			- adviceFunc 
		//			- aroundObj
		//			- aroundFunc
		//			- once
		//			- delay
		//			- rate
		//			- adviceMsg
		//			- maxCalls
		this.addAdvice(	args["adviceObj"], args["adviceFunc"], 
						args["aroundObj"], args["aroundFunc"], 
						args["adviceType"], args["precedence"], 
						args["once"], args["delay"], args["rate"], 
						args["adviceMsg"], args["maxCalls"]);
	},

	addAdvice: function(	thisAdviceObj, thisAdvice, 
							thisAroundObj, thisAround, 
							adviceType, precedence, 
							once, delay, rate, asMessage,
							maxCalls){
		// summary:
		//		add advice to this joinpoint using positional parameters
		// thisAdviceObj:
		//		the scope in which to locate/execute the named adviceFunc.
		// thisAdviceFunc:
		//		the name of the function being conected
		// thisAroundObj:
		//		the scope in which to locate/execute the named aroundFunc.
		// thisAroundFunc:
		//		the name of the function that will be used to mediate the
		//		advice call.
		// adviceType: 
		//		Optional. String. One of "before", "after", "around",
		//		"before-around", or "after-around". FIXME
		// once:
		//		boolean that determines whether or not this advice will create
		//		a new connection if an identical advice set has already been
		//		provided. Defaults to "false".
		// delay:
		//		an optional delay (in ms), as an integer, for dispatch of a
		//		listener after the source has been fired.
		// rate:
		//		an optional rate throttling parameter (integer, in ms). When
		//		specified, this particular connection will not fire more than
		//		once in the interval specified by the rate
		// adviceMsg:
		//		boolean. Should the listener have all the parameters passed in
		//		as a single argument?
		// maxCalls:
		//		Integer. The maximum number of times this connection can be
		//		used before being auto-disconnected. -1 signals that the
		//		connection should never be disconnected.
		var arr = this.getArr(adviceType);
		if(!arr){
			dojo.raise("bad this: " + this);
		}

		var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage, maxCalls];
		
		if(once){
			if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){
				return;
			}
		}

		if(precedence == "first"){
			arr.unshift(ao);
		}else{
			arr.push(ao);
		}
	},

	hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){
		// summary:
		//		returns the array index of the first existing connection
		//		betweened the passed advice and this joinpoint. Will be -1 if
		//		none exists.
		// thisAdviceObj:
		//		the scope in which to locate/execute the named adviceFunc.
		// thisAdviceFunc:
		//		the name of the function being conected
		// adviceType: 
		//		Optional. String. One of "before", "after", "around",
		//		"before-around", or "after-around". FIXME
		// arr:
		//		Optional. The list of advices to search. Will be found via
		//		adviceType if not passed
		if(!arr){ arr = this.getArr(adviceType); }
		var ind = -1;
		for(var x=0; x<arr.length; x++){
			var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
			var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
			if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
				ind = x;
			}
		}
		return ind; // Integer
	},

	removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){
		// summary:
		//		returns the array index of the first existing connection
		//		betweened the passed advice and this joinpoint. Will be -1 if
		//		none exists.
		// thisAdviceObj:
		//		the scope in which to locate/execute the named adviceFunc.
		// thisAdviceFunc:
		//		the name of the function being conected
		// adviceType: 
		//		Optional. String. One of "before", "after", "around",
		//		"before-around", or "after-around". FIXME
		// once:
		//		Optional. Should this only remove the first occurance of the
		//		connection?
		var arr = this.getArr(adviceType);
		var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
		if(ind == -1){
			return false;
		}
		while(ind != -1){
			arr.splice(ind, 1);
			if(once){ break; }
			ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
		}
		return true;
	}
});


dojo.provide("dojo.event.topic");

dojo.event.topic = new function(){
	this.topics = {};

	this.getTopic = function(/*String*/topic){
		// summary:
		//		returns a topic implementation object of type
		//		dojo.event.topic.TopicImpl
		// topic:
		//		a unique, opaque string that names the topic
		if(!this.topics[topic]){
			this.topics[topic] = new this.TopicImpl(topic);
		}
		return this.topics[topic]; // a dojo.event.topic.TopicImpl object
	}

	this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
		// summary:
		//		registers a function as a publisher on a topic. Subsequent
		//		calls to the function will cause a publish event on the topic
		//		with the arguments passed to the function passed to registered
		//		listeners.
		// topic: 
		//		a unique, opaque string that names the topic
		// obj:
		//		the scope to locate the function in
		// funcName:
		//		the name of the function to register
		var topic = this.getTopic(topic);
		topic.registerPublisher(obj, funcName);
	}

	this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
		// summary:
		//		susbscribes the function to the topic. Subsequent events
		//		dispached to the topic will create a function call for the
		//		obj.funcName() function.
		// topic: 
		//		a unique, opaque string that names the topic
		// obj:
		//		the scope to locate the function in
		// funcName:
		//		the name of the function to being registered as a listener
		var topic = this.getTopic(topic);
		topic.subscribe(obj, funcName);
	}

	this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
		// summary:
		//		unsubscribes the obj.funcName() from the topic
		// topic: 
		//		a unique, opaque string that names the topic
		// obj:
		//		the scope to locate the function in
		// funcName:
		//		the name of the function to being unregistered as a listener
		var topic = this.getTopic(topic);
		topic.unsubscribe(obj, funcName);
	}

	this.destroy = function(/*String*/topic){
		// summary: 
		//		destroys the topic and unregisters all listeners
		// topic:
		//		a unique, opaque string that names the topic
		this.getTopic(topic).destroy();
		delete this.topics[topic];
	}

	this.publishApply = function(/*String*/topic, /*Array*/args){
		// summary: 
		//		dispatches an event to the topic using the args array as the
		//		source for the call arguments to each listener. This is similar
		//		to JavaScript's built-in Function.apply()
		// topic:
		//		a unique, opaque string that names the topic
		// args:
		//		the arguments to be passed into listeners of the topic
		var topic = this.getTopic(topic);
		topic.sendMessage.apply(topic, args);
	}

	this.publish = function(/*String*/topic, /*Object*/message){
		// summary: 
		//		manually "publish" to the passed topic
		// topic:
		//		a unique, opaque string that names the topic
		// message:
		//		can be an array of parameters (similar to publishApply), or
		//		will be treated as one of many arguments to be passed along in
		//		a "flat" unrolling
		var topic = this.getTopic(topic);
		// if message is an array, we treat it as a set of arguments,
		// otherwise, we just pass on the arguments passed in as-is
		var args = [];
		// could we use concat instead here?
		for(var x=1; x<arguments.length; x++){
			args.push(arguments[x]);
		}
		topic.sendMessage.apply(topic, args);
	}
}

dojo.event.topic.TopicImpl = function(topicName){
	// summary: a class to represent topics

	this.topicName = topicName;

	this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
		// summary:
		//		use dojo.event.connect() to attach the passed listener to the
		//		topic represented by this object
		// listenerObject:
		//		if a string and listenerMethod is ommitted, this is treated as
		//		the name of a function in the global namespace. If
		//		listenerMethod is provided, this is the scope to find/execute
		//		the function in.
		// listenerMethod:
		//		Optional. The function to register.
		var tf = listenerMethod||listenerObject;
		var to = (!listenerMethod) ? dj_global : listenerObject;
		return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint
			srcObj:		this, 
			srcFunc:	"sendMessage", 
			adviceObj:	to,
			adviceFunc: tf
		});
	}

	this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
		// summary:
		//		use dojo.event.disconnect() to attach the passed listener to the
		//		topic represented by this object
		// listenerObject:
		//		if a string and listenerMethod is ommitted, this is treated as
		//		the name of a function in the global namespace. If
		//		listenerMethod is provided, this is the scope to find the
		//		function in.
		// listenerMethod:
		//		Optional. The function to unregister.
		var tf = (!listenerMethod) ? listenerObject : listenerMethod;
		var to = (!listenerMethod) ? null : listenerObject;
		return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint
			srcObj:		this, 
			srcFunc:	"sendMessage", 
			adviceObj:	to,
			adviceFunc: tf
		});
	}

	this._getJoinPoint = function(){
		return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage");
	}

	this.setSquelch = function(/*Boolean*/shouldSquelch){
		// summary: 
		//		determine whether or not exceptions in the calling of a
		//		listener in the chain should stop execution of the chain.
		this._getJoinPoint().squelch = shouldSquelch;
	}

	this.destroy = function(){
		// summary: disconnects all listeners from this topic
		this._getJoinPoint().disconnect();
	}

	this.registerPublisher = function(	/*Object*/publisherObject, 
										/*Function or String*/publisherMethod){
		// summary:
		//		registers the passed function as a publisher on this topic.
		//		Each time the function is called, an event will be published on
		//		this topic.
		// publisherObject:
		//		if a string and listenerMethod is ommitted, this is treated as
		//		the name of a function in the global namespace. If
		//		listenerMethod is provided, this is the scope to find the
		//		function in.
		// publisherMethod:
		//		Optional. The function to register.
		dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
	}

	this.sendMessage = function(message){
		// summary: a stub to be called when a message is sent to the topic.

		// The message has been propagated
	}
}


dojo.provide("dojo.event.browser");


// FIXME: any particular reason this is in the global scope?
dojo._ie_clobber = new function(){
	this.clobberNodes = [];

	function nukeProp(node, prop){
		// try{ node.removeAttribute(prop); 	}catch(e){ /* squelch */ }
		try{ node[prop] = null; 			}catch(e){ /* squelch */ }
		try{ delete node[prop]; 			}catch(e){ /* squelch */ }
		// FIXME: JotLive needs this, but I'm not sure if it's too slow or not
		try{ node.removeAttribute(prop);	}catch(e){ /* squelch */ }
	}

	this.clobber = function(nodeRef){
		var na;
		var tna;
		if(nodeRef){
			tna = nodeRef.all || nodeRef.getElementsByTagName("*");
			na = [nodeRef];
			for(var x=0; x<tna.length; x++){
				// if we're gonna be clobbering the thing, at least make sure
				// we aren't trying to do it twice
				if(tna[x]["__doClobber__"]){
					na.push(tna[x]);
				}
			}
		}else{
			try{ window.onload = null; }catch(e){}
			na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
		}
		tna = null;
		var basis = {};
		for(var i = na.length-1; i>=0; i=i-1){
			var el = na[i];
			try{
				if(el && el["__clobberAttrs__"]){
					for(var j=0; j<el.__clobberAttrs__.length; j++){
						nukeProp(el, el.__clobberAttrs__[j]);
					}
					nukeProp(el, "__clobberAttrs__");
					nukeProp(el, "__doClobber__");
				}
			}catch(e){ /* squelch! */};
		}
		na = null;
	}
}

if(dojo.render.html.ie){
	dojo.addOnUnload(function(){
		dojo._ie_clobber.clobber();
		try{
			if((dojo["widget"])&&(dojo.widget["manager"])){
				dojo.widget.manager.destroyAll();
			}
		}catch(e){}

		// Workaround for IE leak recommended in ticket #1727 by schallm
		if(dojo.widget){
			for(var name in dojo.widget._templateCache){
				if(dojo.widget._templateCache[name].node){
					dojo.dom.destroyNode(dojo.widget._templateCache[name].node);
					dojo.widget._templateCache[name].node = null;
					delete dojo.widget._templateCache[name].node;
				}
			}
		}

		try{ window.onload = null; }catch(e){}
		try{ window.onunload = null; }catch(e){}
		dojo._ie_clobber.clobberNodes = [];
		// CollectGarbage();
	});
}

dojo.event.browser = new function(){

	var clobberIdx = 0;

	this.normalizedEventName = function(/*String*/eventName){
		switch(eventName){
			case "CheckboxStateChange":
			case "DOMAttrModified":
			case "DOMMenuItemActive":
			case "DOMMenuItemInactive":
			case "DOMMouseScroll":
			case "DOMNodeInserted":
			case "DOMNodeRemoved":
			case "RadioStateChange":
				return eventName;
				break;
			default:
				var lcn = eventName.toLowerCase();
				return (lcn.indexOf("on") == 0) ? lcn.substr(2) : lcn;
				break;
		}
	}
	
	this.clean = function(/*DOMNode*/node){
		// summary:
		//		removes native event handlers so that destruction of the node
		//		will not leak memory. On most browsers this is a no-op, but
		//		it's critical for manual node removal on IE.
		// node:
		//		A DOM node. All of it's children will also be cleaned.
		if(dojo.render.html.ie){ 
			dojo._ie_clobber.clobber(node);
		}
	}

	this.addClobberNode = function(/*DOMNode*/node){
		// summary:
		//		register the passed node to support event stripping
		// node:
		//		A DOM node
		if(!dojo.render.html.ie){ return; }
		if(!node["__doClobber__"]){
			node.__doClobber__ = true;
			dojo._ie_clobber.clobberNodes.push(node);
			// this might not be the most efficient thing to do, but it's
			// much less error prone than other approaches which were
			// previously tried and failed
			node.__clobberAttrs__ = [];
		}
	}

	this.addClobberNodeAttrs = function(/*DOMNode*/node, /*Array*/props){
		// summary:
		//		register the passed node to support event stripping
		// node:
		//		A DOM node to stip properties from later
		// props:
		//		A list of propeties to strip from the node
		if(!dojo.render.html.ie){ return; }
		this.addClobberNode(node);
		for(var x=0; x<props.length; x++){
			node.__clobberAttrs__.push(props[x]);
		}
	}

	this.removeListener = function(	/*DOMNode*/ node, 
									/*String*/	evtName, 
									/*Function*/fp, 
									/*Boolean*/	capture){
		// summary:
		//		clobbers the listener from the node
		// evtName:
		//		the name of the handler to remove the function from
		// node:
		//		DOM node to attach the event to
		// fp:
		//		the function to register
		// capture:
		//		Optional. should this listener prevent propigation?
		if(!capture){ var capture = false; }
		evtName = dojo.event.browser.normalizedEventName(evtName);
		if(evtName == "key"){
			if(dojo.render.html.ie){
				this.removeListener(node, "onkeydown", fp, capture);
			}
			evtName = "keypress";
		}
		// FIXME: this is mostly a punt, we aren't actually doing anything on IE
		if(node.removeEventListener){
			node.removeEventListener(evtName, fp, capture);
		}
	}

	this.addListener = function(/*DOMNode*/node, /*String*/evtName, /*Function*/fp, /*Boolean*/capture, /*Boolean*/dontFix){
		// summary:
		//		adds a listener to the node
		// evtName:
		//		the name of the handler to add the listener to can be either of
		//		the form "onclick" or "click"
		// node:
		//		DOM node to attach the event to
		// fp:
		//		the function to register
		// capture:
		//		Optional. Should this listener prevent propigation?
		// dontFix:
		//		Optional. Should we avoid registering a new closure around the
		//		listener to enable fixEvent for dispatch of the registered
		//		function?
		if(!node){ return; } // FIXME: log and/or bail?
		if(!capture){ var capture = false; }
		evtName = dojo.event.browser.normalizedEventName(evtName);
		if(evtName == "key"){
			if(dojo.render.html.ie){
				this.addListener(node, "onkeydown", fp, capture, dontFix);
			}
			evtName = "keypress";
		}

		if(!dontFix){
			// build yet another closure around fp in order to inject fixEvent
			// around the resulting event
			var newfp = function(evt){
				if(!evt){ evt = window.event; }
				var ret = fp(dojo.event.browser.fixEvent(evt, this));
				if(capture){
					dojo.event.browser.stopEvent(evt);
				}
				return ret;
			}
		}else{
			newfp = fp;
		}

		if(node.addEventListener){ 
			node.addEventListener(evtName, newfp, capture);
			return newfp;
		}else{
			evtName = "on"+evtName;
			if(typeof node[evtName] == "function" ){
				var oldEvt = node[evtName];
				node[evtName] = function(e){
					oldEvt(e);
					return newfp(e);
				}
			}else{
				node[evtName]=newfp;
			}
			if(dojo.render.html.ie){
				this.addClobberNodeAttrs(node, [evtName]);
			}
			return newfp;
		}
	}

	this.isEvent = function(/*Object*/obj){
		// summary: 
		//		Tries to determine whether or not the object is a DOM event.

		// FIXME: event detection hack ... could test for additional attributes
		// if necessary
		return (typeof obj != "undefined")&&(obj)&&(typeof Event != "undefined")&&(obj.eventPhase); // Boolean
		// Event does not support instanceof in Opera, otherwise:
		//return (typeof Event != "undefined")&&(obj instanceof Event);
	}

	this.currentEvent = null;
	
	this.callListener = function(/*Function*/listener, /*DOMNode*/curTarget){
		// summary:
		//		calls the specified listener in the context of the passed node
		//		with the current DOM event object as the only parameter
		// listener:
		//		the function to call
		// curTarget:
		//		the Node to call the function in the scope of
		if(typeof listener != 'function'){
			dojo.raise("listener not a function: " + listener);
		}
		dojo.event.browser.currentEvent.currentTarget = curTarget;
		return listener.call(curTarget, dojo.event.browser.currentEvent);
	}

	this._stopPropagation = function(){
		dojo.event.browser.currentEvent.cancelBubble = true; 
	}

	this._preventDefault = function(){
		dojo.event.browser.currentEvent.returnValue = false;
	}

	this.keys = {
		KEY_BACKSPACE: 8,
		KEY_TAB: 9,
		KEY_CLEAR: 12,
		KEY_ENTER: 13,
		KEY_SHIFT: 16,
		KEY_CTRL: 17,
		KEY_ALT: 18,
		KEY_PAUSE: 19,
		KEY_CAPS_LOCK: 20,
		KEY_ESCAPE: 27,
		KEY_SPACE: 32,
		KEY_PAGE_UP: 33,
		KEY_PAGE_DOWN: 34,
		KEY_END: 35,
		KEY_HOME: 36,
		KEY_LEFT_ARROW: 37,
		KEY_UP_ARROW: 38,
		KEY_RIGHT_ARROW: 39,
		KEY_DOWN_ARROW: 40,
		KEY_INSERT: 45,
		KEY_DELETE: 46,
		KEY_HELP: 47,
		KEY_LEFT_WINDOW: 91,
		KEY_RIGHT_WINDOW: 92,
		KEY_SELECT: 93,
		KEY_NUMPAD_0: 96,
		KEY_NUMPAD_1: 97,
		KEY_NUMPAD_2: 98,
		KEY_NUMPAD_3: 99,
		KEY_NUMPAD_4: 100,
		KEY_NUMPAD_5: 101,
		KEY_NUMPAD_6: 102,
		KEY_NUMPAD_7: 103,
		KEY_NUMPAD_8: 104,
		KEY_NUMPAD_9: 105,
		KEY_NUMPAD_MULTIPLY: 106,
		KEY_NUMPAD_PLUS: 107,
		KEY_NUMPAD_ENTER: 108,
		KEY_NUMPAD_MINUS: 109,
		KEY_NUMPAD_PERIOD: 110,
		KEY_NUMPAD_DIVIDE: 111,
		KEY_F1: 112,
		KEY_F2: 113,
		KEY_F3: 114,
		KEY_F4: 115,
		KEY_F5: 116,
		KEY_F6: 117,
		KEY_F7: 118,
		KEY_F8: 119,
		KEY_F9: 120,
		KEY_F10: 121,
		KEY_F11: 122,
		KEY_F12: 123,
		KEY_F13: 124,
		KEY_F14: 125,
		KEY_F15: 126,
		KEY_NUM_LOCK: 144,
		KEY_SCROLL_LOCK: 145
	};

	// reverse lookup
	this.revKeys = [];
	for(var key in this.keys){
		this.revKeys[this.keys[key]] = key;
	}

	this.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
		// summary:
		//		normalizes properties on the event object including event
		//		bubbling methods, keystroke normalization, and x/y positions
		// evt: the native event object
		// sender: the node to treat as "currentTarget"
		if(!evt){
			if(window["event"]){
				evt = window.event;
			}
		}
		
		if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
			evt.keys = this.revKeys;
			// FIXME: how can we eliminate this iteration?
			for(var key in this.keys){
				evt[key] = this.keys[key];
			}
			if(evt["type"] == "keydown" && dojo.render.html.ie){
				switch(evt.keyCode){
					case evt.KEY_SHIFT:
					case evt.KEY_CTRL:
					case evt.KEY_ALT:
					case evt.KEY_CAPS_LOCK:
					case evt.KEY_LEFT_WINDOW:
					case evt.KEY_RIGHT_WINDOW:
					case evt.KEY_SELECT:
					case evt.KEY_NUM_LOCK:
					case evt.KEY_SCROLL_LOCK:
					// I'll get these in keypress after the OS munges them based on numlock
					case evt.KEY_NUMPAD_0:
					case evt.KEY_NUMPAD_1:
					case evt.KEY_NUMPAD_2:
					case evt.KEY_NUMPAD_3:
					case evt.KEY_NUMPAD_4:
					case evt.KEY_NUMPAD_5:
					case evt.KEY_NUMPAD_6:
					case evt.KEY_NUMPAD_7:
					case evt.KEY_NUMPAD_8:
					case evt.KEY_NUMPAD_9:
					case evt.KEY_NUMPAD_PERIOD:
						break; // just ignore the keys that can morph
					case evt.KEY_NUMPAD_MULTIPLY:
					case evt.KEY_NUMPAD_PLUS:
					case evt.KEY_NUMPAD_ENTER:
					case evt.KEY_NUMPAD_MINUS:
					case evt.KEY_NUMPAD_DIVIDE:
						break; // I could handle these but just pick them up in keypress
					case evt.KEY_PAUSE:
					case evt.KEY_TAB:
					case evt.KEY_BACKSPACE:
					case evt.KEY_ENTER:
					case evt.KEY_ESCAPE:
					case evt.KEY_PAGE_UP:
					case evt.KEY_PAGE_DOWN:
					case evt.KEY_END:
					case evt.KEY_HOME:
					case evt.KEY_LEFT_ARROW:
					case evt.KEY_UP_ARROW:
					case evt.KEY_RIGHT_ARROW:
					case evt.KEY_DOWN_ARROW:
					case evt.KEY_INSERT:
					case evt.KEY_DELETE:
					case evt.KEY_F1:
					case evt.KEY_F2:
					case evt.KEY_F3:
					case evt.KEY_F4:
					case evt.KEY_F5:
					case evt.KEY_F6:
					case evt.KEY_F7:
					case evt.KEY_F8:
					case evt.KEY_F9:
					case evt.KEY_F10:
					case evt.KEY_F11:
					case evt.KEY_F12:
					case evt.KEY_F12:
					case evt.KEY_F13:
					case evt.KEY_F14:
					case evt.KEY_F15:
					case evt.KEY_CLEAR:
					case evt.KEY_HELP:
						evt.key = evt.keyCode;
						break;
					default:
						if(evt.ctrlKey || evt.altKey){
							var unifiedCharCode = evt.keyCode;
							// if lower case but keycode is uppercase, convert it
							if(unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){
								unifiedCharCode += 32;
							}
							if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){
								unifiedCharCode += 96; // 001-032 = ctrl+[a-z]
							}
							evt.key = String.fromCharCode(unifiedCharCode);
						}
				}
			} else if(evt["type"] == "keypress"){
				if(dojo.render.html.opera){
					if(evt.which == 0){
						evt.key = evt.keyCode;
					}else if(evt.which > 0){
						switch(evt.which){
							case evt.KEY_SHIFT:
							case evt.KEY_CTRL:
							case evt.KEY_ALT:
							case evt.KEY_CAPS_LOCK:
							case evt.KEY_NUM_LOCK:
							case evt.KEY_SCROLL_LOCK:
								break;
							case evt.KEY_PAUSE:
							case evt.KEY_TAB:
							case evt.KEY_BACKSPACE:
							case evt.KEY_ENTER:
							case evt.KEY_ESCAPE:
								evt.key = evt.which;
								break;
							default:
								var unifiedCharCode = evt.which;
								if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){
									unifiedCharCode += 32;
								}
								evt.key = String.fromCharCode(unifiedCharCode);
						}
					}
				}else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown
					// key combinations were handled in onKeyDown
					if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){
						evt.key = String.fromCharCode(evt.keyCode);
					}
				}else if(dojo.render.html.safari){
					switch(evt.keyCode){
						case 25: evt.key = evt.KEY_TAB; evt.shift = true;break;
						case 63232: evt.key = evt.KEY_UP_ARROW; break;
						case 63233: evt.key = evt.KEY_DOWN_ARROW; break;
						case 63234: evt.key = evt.KEY_LEFT_ARROW; break;
						case 63235: evt.key = evt.KEY_RIGHT_ARROW; break;
						case 63236: evt.key = evt.KEY_F1; break;
						case 63237: evt.key = evt.KEY_F2; break;
						case 63238: evt.key = evt.KEY_F3; break;
						case 63239: evt.key = evt.KEY_F4; break;
						case 63240: evt.key = evt.KEY_F5; break;
						case 63241: evt.key = evt.KEY_F6; break;
						case 63242: evt.key = evt.KEY_F7; break;
						case 63243: evt.key = evt.KEY_F8; break;
						case 63244: evt.key = evt.KEY_F9; break;
						case 63245: evt.key = evt.KEY_F10; break;
						case 63246: evt.key = evt.KEY_F11; break;
						case 63247: evt.key = evt.KEY_F12; break;
						case 63250: evt.key = evt.KEY_PAUSE; break;
						case 63272: evt.key = evt.KEY_DELETE; break;
						case 63273: evt.key = evt.KEY_HOME; break;
						case 63275: evt.key = evt.KEY_END; break;
						case 63276: evt.key = evt.KEY_PAGE_UP; break;
						case 63277: evt.key = evt.KEY_PAGE_DOWN; break;
						case 63302: evt.key = evt.KEY_INSERT; break;
						case 63248://prtscr
						case 63249://scrolllock
						case 63289://numlock
							break;
						default: 
							evt.key = evt.charCode >= evt.KEY_SPACE ? String.fromCharCode(evt.charCode) : evt.keyCode;
					}
				}else{
					evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
				}
			}
		}
		if(dojo.render.html.ie){
			if(!evt.target){ evt.target = evt.srcElement; }
			if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
			if(!evt.layerX){ evt.layerX = evt.offsetX; }
			if(!evt.layerY){ evt.layerY = evt.offsetY; }
			// FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
			// DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used
			// here rather than document.body
			var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
			var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
			if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
			if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
			// mouseover
			if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
			// mouseout
			if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
			this.currentEvent = evt;
			evt.callListener = this.callListener;
			evt.stopPropagation = this._stopPropagation;
			evt.preventDefault = this._preventDefault;
		}
		return evt; // Event
	}

	this.stopEvent = function(/*Event*/evt){
		// summary:
		//		prevents propigation and clobbers the default action of the
		//		passed event
		// evt: Optional for IE. The native event object.
		if(window.event){
			evt.cancelBubble = true;
			evt.returnValue = false;
		}else{
			evt.preventDefault();
			evt.stopPropagation();
		}
	}
}

dojo.kwCompoundRequire({
	common: ["dojo.event.common", "dojo.event.topic"],
	browser: ["dojo.event.browser"],
	dashboard: ["dojo.event.browser"]
});
dojo.provide("dojo.event.*");
dojo.provide("dojo.gfx.*");
dojo.provide("dojo.gfx.color");



// TODO: rewrite the "x2y" methods to take advantage of the parsing
//       abilities of the Color object. Also, beef up the Color
//       object (as possible) to parse most common formats

// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
dojo.gfx.color.Color = function(r, g, b, a) {
	// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
	if(dojo.lang.isArray(r)){
		this.r = r[0];
		this.g = r[1];
		this.b = r[2];
		this.a = r[3]||1.0;
	}else if(dojo.lang.isString(r)){
		var rgb = dojo.gfx.color.extractRGB(r);
		this.r = rgb[0];
		this.g = rgb[1];
		this.b = rgb[2];
		this.a = g||1.0;
	}else if(r instanceof dojo.gfx.color.Color){
		// why does this create a new instance if we were passed one?
		this.r = r.r;
		this.b = r.b;
		this.g = r.g;
		this.a = r.a;
	}else{
		this.r = r;
		this.g = g;
		this.b = b;
		this.a = a;
	}
}

dojo.gfx.color.Color.fromArray = function(arr) {
	return new dojo.gfx.color.Color(arr[0], arr[1], arr[2], arr[3]);
}

dojo.extend(dojo.gfx.color.Color, {
	toRgb: function(includeAlpha) {
		if(includeAlpha) {
			return this.toRgba();
		} else {
			return [this.r, this.g, this.b];
		}
	},
	toRgba: function() {
		return [this.r, this.g, this.b, this.a];
	},
	toHex: function() {
		return dojo.gfx.color.rgb2hex(this.toRgb());
	},
	toCss: function() {
		return "rgb(" + this.toRgb().join() + ")";
	},
	toString: function() {
		return this.toHex(); // decent default?
	},
	blend: function(color, weight){
		var rgb = null;
		if(dojo.lang.isArray(color)){
			rgb = color;
		}else if(color instanceof dojo.gfx.color.Color){
			rgb = color.toRgb();
		}else{
			rgb = new dojo.gfx.color.Color(color).toRgb();
		}
		return dojo.gfx.color.blend(this.toRgb(), rgb, weight);
	}
});

dojo.gfx.color.named = {
	white:      [255,255,255],
	black:      [0,0,0],
	red:        [255,0,0],
	green:	    [0,255,0],
	lime:	    [0,255,0],
	blue:       [0,0,255],
	navy:       [0,0,128],
	gray:       [128,128,128],
	silver:     [192,192,192]
};

dojo.gfx.color.blend = function(a, b, weight){
	// summary: 
	//		blend colors a and b (both as RGB array or hex strings) with weight
	//		from -1 to +1, 0 being a 50/50 blend
	if(typeof a == "string"){
		return dojo.gfx.color.blendHex(a, b, weight);
	}
	if(!weight){
		weight = 0;
	}
	weight = Math.min(Math.max(-1, weight), 1);

	// alex: this interface blows.
	// map -1 to 1 to the range 0 to 1
	weight = ((weight + 1)/2);
	
	var c = [];

	// var stop = (1000*weight);
	for(var x = 0; x < 3; x++){
		c[x] = parseInt( b[x] + ( (a[x] - b[x]) * weight) );
	}
	return c;
}

// very convenient blend that takes and returns hex values
// (will get called automatically by blend when blend gets strings)
dojo.gfx.color.blendHex = function(a, b, weight) {
	return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a), dojo.gfx.color.hex2rgb(b), weight));
}

// get RGB array from css-style color declarations
dojo.gfx.color.extractRGB = function(color) {
	var hex = "0123456789abcdef";
	color = color.toLowerCase();
	if( color.indexOf("rgb") == 0 ) {
		var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
		var ret = matches.splice(1, 3);
		return ret;
	} else {
		var colors = dojo.gfx.color.hex2rgb(color);
		if(colors) {
			return colors;
		} else {
			// named color (how many do we support?)
			return dojo.gfx.color.named[color] || [255, 255, 255];
		}
	}
}

dojo.gfx.color.hex2rgb = function(hex) {
	var hexNum = "0123456789ABCDEF";
	var rgb = new Array(3);
	if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
	hex = hex.toUpperCase();
	if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
		return null;
	}
	if( hex.length == 3 ) {
		rgb[0] = hex.charAt(0) + hex.charAt(0)
		rgb[1] = hex.charAt(1) + hex.charAt(1)
		rgb[2] = hex.charAt(2) + hex.charAt(2);
	} else {
		rgb[0] = hex.substring(0, 2);
		rgb[1] = hex.substring(2, 4);
		rgb[2] = hex.substring(4);
	}
	for(var i = 0; i < rgb.length; i++) {
		rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
	}
	return rgb;
}

dojo.gfx.color.rgb2hex = function(r, g, b) {
	if(dojo.lang.isArray(r)) {
		g = r[1] || 0;
		b = r[2] || 0;
		r = r[0] || 0;
	}
	var ret = dojo.lang.map([r, g, b], function(x) {
		x = new Number(x);
		var s = x.toString(16);
		while(s.length < 2) { s = "0" + s; }
		return s;
	});
	ret.unshift("#");
	return ret.join("");
}

dojo.provide("dojo.lfx.Animation");



/*
	Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
*/
dojo.lfx.Line = function(/*int*/ start, /*int*/ end){
	// summary: dojo.lfx.Line is the object used to generate values
	//			from a start value to an end value
	this.start = start;
	this.end = end;
	if(dojo.lang.isArray(start)){
		/* start: Array
		   end: Array
		   pId: a */
		var diff = [];
		dojo.lang.forEach(this.start, function(s,i){
			diff[i] = this.end[i] - s;
		}, this);
		
		this.getValue = function(/*float*/ n){
			var res = [];
			dojo.lang.forEach(this.start, function(s, i){
				res[i] = (diff[i] * n) + s;
			}, this);
			return res; // Array
		}
	}else{
		var diff = end - start;
			
		this.getValue = function(/*float*/ n){
			//	summary: returns the point on the line
			//	n: a floating point number greater than 0 and less than 1
			return (diff * n) + this.start; // Decimal
		}
	}
}

if((dojo.render.html.khtml)&&(!dojo.render.html.safari)){
	// the cool kids are obviously not using konqueror...
	// found a very wierd bug in floats constants, 1.5 evals as 1
	// seems somebody mixed up ints and floats in 3.5.4 ??
	// FIXME: investigate more and post a KDE bug (Fredrik)
	dojo.lfx.easeDefault = function(/*Decimal?*/ n){
		//	summary: Returns the point for point n on a sin wave.
		return (parseFloat("0.5")+((Math.sin( (n+parseFloat("1.5")) * Math.PI))/2));
	}
}else{
	dojo.lfx.easeDefault = function(/*Decimal?*/ n){
		return (0.5+((Math.sin( (n+1.5) * Math.PI))/2));
	}
}

dojo.lfx.easeIn = function(/*Decimal?*/ n){
	//	summary: returns the point on an easing curve
	//	n: a floating point number greater than 0 and less than 1
	return Math.pow(n, 3);
}

dojo.lfx.easeOut = function(/*Decimal?*/ n){
	//	summary: returns the point on the line
	//	n: a floating point number greater than 0 and less than 1
	return ( 1 - Math.pow(1 - n, 3) );
}

dojo.lfx.easeInOut = function(/*Decimal?*/ n){
	//	summary: returns the point on the line
	//	n: a floating point number greater than 0 and less than 1
	return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
}

dojo.lfx.IAnimation = function(){
	// summary: dojo.lfx.IAnimation is an interface that implements
	//			commonly used functions of animation objects
}
dojo.lang.extend(dojo.lfx.IAnimation, {
	// public properties
	curve: null,
	duration: 1000,
	easing: null,
	repeatCount: 0,
	rate: 10,
	
	// events
	handler: null,
	beforeBegin: null,
	onBegin: null,
	onAnimate: null,
	onEnd: null,
	onPlay: null,
	onPause: null,
	onStop: null,
	
	// public methods
	play: null,
	pause: null,
	stop: null,
	
	connect: function(/*Event*/ evt, /*Object*/ scope, /*Function*/ newFunc){
		// summary: Convenience function.  Quickly connect to an event
		//			of this object and save the old functions connected to it.
		// evt: The name of the event to connect to.
		// scope: the scope in which to run newFunc.
		// newFunc: the function to run when evt is fired.
		if(!newFunc){
			/* scope: Function
			   newFunc: null
			   pId: f */
			newFunc = scope;
			scope = this;
		}
		newFunc = dojo.lang.hitch(scope, newFunc);
		var oldFunc = this[evt]||function(){};
		this[evt] = function(){
			var ret = oldFunc.apply(this, arguments);
			newFunc.apply(this, arguments);
			return ret;
		}
		return this; // dojo.lfx.IAnimation
	},

	fire: function(/*Event*/ evt, /*Array*/ args){
		// summary: Convenience function.  Fire event "evt" and pass it
		//			the arguments specified in "args".
		// evt: The event to fire.
		// args: The arguments to pass to the event.
		if(this[evt]){
			this[evt].apply(this, (args||[]));
		}
		return this; // dojo.lfx.IAnimation
	},
	
	repeat: function(/*int*/ count){
		// summary: Set the repeat count of this object.
		// count: How many times to repeat the animation.
		this.repeatCount = count;
		return this; // dojo.lfx.IAnimation
	},

	// private properties
	_active: false,
	_paused: false
});

dojo.lfx.Animation = function(	/*Object*/ handlers, 
								/*int*/ duration, 
								/*dojo.lfx.Line*/ curve, 
								/*function*/ easing, 
								/*int*/ repeatCount, 
								/*int*/ rate){
	//	summary
	//		a generic animation object that fires callbacks into it's handlers
	//		object at various states
	//	handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
	dojo.lfx.IAnimation.call(this);
	if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
		// no handlers argument:
		rate = repeatCount;
		repeatCount = easing;
		easing = curve;
		curve = duration;
		duration = handlers;
		handlers = null;
	}else if(handlers.getValue||dojo.lang.isArray(handlers)){
		// no handlers or duration:
		rate = easing;
		repeatCount = curve;
		easing = duration;
		curve = handlers;
		duration = null;
		handlers = null;
	}
	if(dojo.lang.isArray(curve)){
		/* curve: Array
		   pId: a */
		this.curve = new dojo.lfx.Line(curve[0], curve[1]);
	}else{
		this.curve = curve;
	}
	if(duration != null && duration > 0){ this.duration = duration; }
	if(repeatCount){ this.repeatCount = repeatCount; }
	if(rate){ this.rate = rate; }
	if(handlers){
		dojo.lang.forEach([
				"handler", "beforeBegin", "onBegin", 
				"onEnd", "onPlay", "onStop", "onAnimate"
			], function(item){
				if(handlers[item]){
					this.connect(item, handlers[item]);
				}
			}, this);
	}
	if(easing && dojo.lang.isFunction(easing)){
		this.easing=easing;
	}
}
dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
dojo.lang.extend(dojo.lfx.Animation, {
	// "private" properties
	_startTime: null,
	_endTime: null,
	_timer: null,
	_percent: 0,
	_startRepeatCount: 0,

	// public methods
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
		// summary: Start the animation.
		// delay: How many milliseconds to delay before starting.
		// gotoStart: If true, starts the animation from the beginning; otherwise,
		//            starts it from its current position.
		if(gotoStart){
			clearTimeout(this._timer);
			this._active = false;
			this._paused = false;
			this._percent = 0;
		}else if(this._active && !this._paused){
			return this; // dojo.lfx.Animation
		}
		
		this.fire("handler", ["beforeBegin"]);
		this.fire("beforeBegin");

		if(delay > 0){
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
			return this; // dojo.lfx.Animation
		}
		
		this._startTime = new Date().valueOf();
		if(this._paused){
			this._startTime -= (this.duration * this._percent / 100);
		}
		this._endTime = this._startTime + this.duration;

		this._active = true;
		this._paused = false;
		
		var step = this._percent / 100;
		var value = this.curve.getValue(step);
		if(this._percent == 0 ){
			if(!this._startRepeatCount){
				this._startRepeatCount = this.repeatCount;
			}
			this.fire("handler", ["begin", value]);
			this.fire("onBegin", [value]);
		}

		this.fire("handler", ["play", value]);
		this.fire("onPlay", [value]);

		this._cycle();
		return this; // dojo.lfx.Animation
	},

	pause: function(){
		// summary: Pauses a running animation.
		clearTimeout(this._timer);
		if(!this._active){ return this; /*dojo.lfx.Animation*/}
		this._paused = true;
		var value = this.curve.getValue(this._percent / 100);
		this.fire("handler", ["pause", value]);
		this.fire("onPause", [value]);
		return this; // dojo.lfx.Animation
	},

	gotoPercent: function(/*Decimal*/ pct, /*bool?*/ andPlay){
		// summary: Sets the progress of the animation.
		// pct: A percentage in decimal notation (between and including 0.0 and 1.0).
		// andPlay: If true, play the animation after setting the progress.
		clearTimeout(this._timer);
		this._active = true;
		this._paused = true;
		this._percent = pct;
		if(andPlay){ this.play(); }
		return this; // dojo.lfx.Animation
	},

	stop: function(/*bool?*/ gotoEnd){
		// summary: Stops a running animation.
		// gotoEnd: If true, the animation will end.
		clearTimeout(this._timer);
		var step = this._percent / 100;
		if(gotoEnd){
			step = 1;
		}
		var value = this.curve.getValue(step);
		this.fire("handler", ["stop", value]);
		this.fire("onStop", [value]);
		this._active = false;
		this._paused = false;
		return this; // dojo.lfx.Animation
	},

	status: function(){
		// summary: Returns a string representation of the status of
		//			the animation.
		if(this._active){
			return this._paused ? "paused" : "playing"; // String
		}else{
			return "stopped"; // String
		}
		return this;
	},

	// "private" methods
	_cycle: function(){
		clearTimeout(this._timer);
		if(this._active){
			var curr = new Date().valueOf();
			var step = (curr - this._startTime) / (this._endTime - this._startTime);

			if(step >= 1){
				step = 1;
				this._percent = 100;
			}else{
				this._percent = step * 100;
			}
			
			// Perform easing
			if((this.easing)&&(dojo.lang.isFunction(this.easing))){
				step = this.easing(step);
			}

			var value = this.curve.getValue(step);
			this.fire("handler", ["animate", value]);
			this.fire("onAnimate", [value]);

			if( step < 1 ){
				this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate);
			}else{
				this._active = false;
				this.fire("handler", ["end"]);
				this.fire("onEnd");

				if(this.repeatCount > 0){
					this.repeatCount--;
					this.play(null, true);
				}else if(this.repeatCount == -1){
					this.play(null, true);
				}else{
					if(this._startRepeatCount){
						this.repeatCount = this._startRepeatCount;
						this._startRepeatCount = 0;
					}
				}
			}
		}
		return this; // dojo.lfx.Animation
	}
});

dojo.lfx.Combine = function(/*dojo.lfx.IAnimation...*/ animations){
	// summary: An animation object to play animations passed to it at the same time.
	dojo.lfx.IAnimation.call(this);
	this._anims = [];
	this._animsEnded = 0;
	
	var anims = arguments;
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
		/* animations: dojo.lfx.IAnimation[]
		   pId: a */
		anims = anims[0];
	}
	
	dojo.lang.forEach(anims, function(anim){
		this._anims.push(anim);
		anim.connect("onEnd", dojo.lang.hitch(this, "_onAnimsEnded"));
	}, this);
}
dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
dojo.lang.extend(dojo.lfx.Combine, {
	// private members
	_animsEnded: 0,
	
	// public methods
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
		// summary: Start the animations.
		// delay: How many milliseconds to delay before starting.
		// gotoStart: If true, starts the animations from the beginning; otherwise,
		//            starts them from their current position.
		if( !this._anims.length ){ return this; /*dojo.lfx.Combine*/}

		this.fire("beforeBegin");

		if(delay > 0){
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
			return this; // dojo.lfx.Combine
		}
		
		if(gotoStart || this._anims[0].percent == 0){
			this.fire("onBegin");
		}
		this.fire("onPlay");
		this._animsCall("play", null, gotoStart);
		return this; // dojo.lfx.Combine
	},
	
	pause: function(){
		// summary: Pauses the running animations.
		this.fire("onPause");
		this._animsCall("pause"); 
		return this; // dojo.lfx.Combine
	},
	
	stop: function(/*bool?*/ gotoEnd){
		// summary: Stops the running animations.
		// gotoEnd: If true, the animations will end.
		this.fire("onStop");
		this._animsCall("stop", gotoEnd);
		return this; // dojo.lfx.Combine
	},
	
	// private methods
	_onAnimsEnded: function(){
		this._animsEnded++;
		if(this._animsEnded >= this._anims.length){
			this.fire("onEnd");
		}
		return this; // dojo.lfx.Combine
	},
	
	_animsCall: function(/*String*/ funcName){
		var args = [];
		if(arguments.length > 1){
			for(var i = 1 ; i < arguments.length ; i++){
				args.push(arguments[i]);
			}
		}
		var _this = this;
		dojo.lang.forEach(this._anims, function(anim){
			anim[funcName](args);
		}, _this);
		return this; // dojo.lfx.Combine
	}
});

dojo.lfx.Chain = function(/*dojo.lfx.IAnimation...*/ animations) {
	// summary: An animation object to play animations passed to it
	//			one after another.
	dojo.lfx.IAnimation.call(this);
	this._anims = [];
	this._currAnim = -1;
	
	var anims = arguments;
	if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
		/* animations: dojo.lfx.IAnimation[]
		   pId: a */
		anims = anims[0];
	}
	
	var _this = this;
	dojo.lang.forEach(anims, function(anim, i, anims_arr){
		this._anims.push(anim);
		if(i < anims_arr.length - 1){
			anim.connect("onEnd", dojo.lang.hitch(this, "_playNext") );
		}else{
			anim.connect("onEnd", dojo.lang.hitch(this, function(){ this.fire("onEnd"); }) );
		}
	}, this);
}
dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
dojo.lang.extend(dojo.lfx.Chain, {
	// private members
	_currAnim: -1,
	
	// public methods
	play: function(/*int?*/ delay, /*bool?*/ gotoStart){
		// summary: Start the animation sequence.
		// delay: How many milliseconds to delay before starting.
		// gotoStart: If true, starts the sequence from the beginning; otherwise,
		//            starts it from its current position.
		if( !this._anims.length ) { return this; /*dojo.lfx.Chain*/}
		if( gotoStart || !this._anims[this._currAnim] ) {
			this._currAnim = 0;
		}

		var currentAnimation = this._anims[this._currAnim];

		this.fire("beforeBegin");
		if(delay > 0){
			setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
			return this; // dojo.lfx.Chain
		}
		
		if(currentAnimation){
			if(this._currAnim == 0){
				this.fire("handler", ["begin", this._currAnim]);
				this.fire("onBegin", [this._currAnim]);
			}
			this.fire("onPlay", [this._currAnim]);
			currentAnimation.play(null, gotoStart);
		}
		return this; // dojo.lfx.Chain
	},
	
	pause: function(){
		// summary: Pauses the running animation sequence.
		if( this._anims[this._currAnim] ) {
			this._anims[this._currAnim].pause();
			this.fire("onPause", [this._currAnim]);
		}
		return this; // dojo.lfx.Chain
	},
	
	playPause: function(){
		// summary: If the animation sequence is playing, pause it; otherwise,
		//			play it.
		if(this._anims.length == 0){ return this; }
		if(this._currAnim == -1){ this._currAnim = 0; }
		var currAnim = this._anims[this._currAnim];
		if( currAnim ) {
			if( !currAnim._active || currAnim._paused ) {
				this.play();
			} else {
				this.pause();
			}
		}
		return this; // dojo.lfx.Chain
	},
	
	stop: function(){
		// summary: Stops the running animations.
		var currAnim = this._anims[this._currAnim];
		if(currAnim){
			currAnim.stop();
			this.fire("onStop", [this._currAnim]);
		}
		return currAnim; // dojo.lfx.IAnimation
	},
	
	// private methods
	_playNext: function(){
		if( this._currAnim == -1 || this._anims.length == 0 ) { return this; }
		this._currAnim++;
		if( this._anims[this._currAnim] ){
			this._anims[this._currAnim].play(null, true);
		}
		return this; // dojo.lfx.Chain
	}
});

dojo.lfx.combine = function(/*dojo.lfx.IAnimation...*/ animations){
	// summary: Convenience function.  Returns a dojo.lfx.Combine created
	//			using the animations passed in.
	var anims = arguments;
	if(dojo.lang.isArray(arguments[0])){
		/* animations: dojo.lfx.IAnimation[]
		   pId: a */
		anims = arguments[0];
	}
	if(anims.length == 1){ return anims[0]; }
	return new dojo.lfx.Combine(anims); // dojo.lfx.Combine
}

dojo.lfx.chain = function(/*dojo.lfx.IAnimation...*/ animations){
	// summary: Convenience function.  Returns a dojo.lfx.Chain created
	//			using the animations passed in.
	var anims = arguments;
	if(dojo.lang.isArray(arguments[0])){
		/* animations: dojo.lfx.IAnimation[]
		   pId: a */
		anims = arguments[0];
	}
	if(anims.length == 1){ return anims[0]; }
	return new dojo.lfx.Chain(anims); // dojo.lfx.Combine
}

dojo.provide("dojo.html.common");



dojo.lang.mixin(dojo.html, dojo.dom);

dojo.html.body = function(){
	dojo.deprecated("dojo.html.body() moved to dojo.body()", "0.5");
	return dojo.body();
}

// FIXME: we are going to assume that we can throw any and every rendering
// engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
// Need to investigate for KHTML and Opera

dojo.html.getEventTarget = function(/* DOMEvent */evt){
	//	summary
	//	Returns the target of an event
	if(!evt) { evt = dojo.global().event || {} };
	var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
	while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
	return t;	//	HTMLElement
}

dojo.html.getViewport = function(){
	//	summary
	//	Returns the dimensions of the viewable area of a browser window
	var _window = dojo.global();
	var _document = dojo.doc();
	var w = 0;
	var h = 0;

	if(dojo.render.html.mozilla){
		// mozilla
		w = _document.documentElement.clientWidth;
		h = _window.innerHeight;
	}else if(!dojo.render.html.opera && _window.innerWidth){
		//in opera9, dojo.body().clientWidth should be used, instead
		//of window.innerWidth/document.documentElement.clientWidth
		//so we have to check whether it is opera
		w = _window.innerWidth;
		h = _window.innerHeight;
	} else if (!dojo.render.html.opera && dojo.exists(_document, "documentElement.clientWidth")){
		// IE6 Strict
		var w2 = _document.documentElement.clientWidth;
		// this lets us account for scrollbars
		if(!w || w2 && w2 < w) {
			w = w2;
		}
		h = _document.documentElement.clientHeight;
	} else if (dojo.body().clientWidth){
		// IE, Opera
		w = dojo.body().clientWidth;
		h = dojo.body().clientHeight;
	}
	return { width: w, height: h };	//	object
}

dojo.html.getScroll = function(){
	//	summary
	//	Returns the scroll position of the document
	var _window = dojo.global();
	var _document = dojo.doc();
	var top = _window.pageYOffset || _document.documentElement.scrollTop || dojo.body().scrollTop || 0;
	var left = _window.pageXOffset || _document.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
	return { 
		top: top, 
		left: left, 
		offset:{ x: left, y: top }	//	note the change, NOT an Array with added properties. 
	};	//	object
}

dojo.html.getParentByType = function(/* HTMLElement */node, /* string */type) {
	//	summary
	//	Returns the first ancestor of node with tagName type.
	var _document = dojo.doc();
	var parent = dojo.byId(node);
	type = type.toLowerCase();
	while((parent)&&(parent.nodeName.toLowerCase()!=type)){
		if(parent==(_document["body"]||_document["documentElement"])){
			return null;
		}
		parent = parent.parentNode;
	}
	return parent;	//	HTMLElement
}

dojo.html.getAttribute = function(/* HTMLElement */node, /* string */attr){
	//	summary
	//	Returns the value of attribute attr from node.
	node = dojo.byId(node);
	// FIXME: need to add support for attr-specific accessors
	if((!node)||(!node.getAttribute)){
		// if(attr !== 'nwType'){
		//	alert("getAttr of '" + attr + "' with bad node"); 
		// }
		return null;
	}
	var ta = typeof attr == 'string' ? attr : new String(attr);

	// first try the approach most likely to succeed
	var v = node.getAttribute(ta.toUpperCase());
	if((v)&&(typeof v == 'string')&&(v!="")){ 
		return v;	//	string 
	}

	// try returning the attributes value, if we couldn't get it as a string
	if(v && v.value){ 
		return v.value;	//	string 
	}

	// this should work on Opera 7, but it's a little on the crashy side
	if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
		return (node.getAttributeNode(ta)).value;	//	string
	}else if(node.getAttribute(ta)){
		return node.getAttribute(ta);	//	string
	}else if(node.getAttribute(ta.toLowerCase())){
		return node.getAttribute(ta.toLowerCase());	//	string
	}
	return null;	//	string
}
	
dojo.html.hasAttribute = function(/* HTMLElement */node, /* string */attr){
	//	summary
	//	Determines whether or not the specified node carries a value for the attribute in question.
	return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false;	//	boolean
}
	
dojo.html.getCursorPosition = function(/* DOMEvent */e){
	//	summary
	//	Returns the mouse position relative to the document (not the viewport).
	//	For example, if you have a document that is 10000px tall,
	//	but your browser window is only 100px tall,
	//	if you scroll to the bottom of the document and call this function it
	//	will return {x: 0, y: 10000}
	//	NOTE: for events delivered via dojo.event.connect() and/or dojoAttachEvent (for widgets),
	//	you can just access evt.pageX and evt.pageY, rather than calling this function.
	e = e || dojo.global().event;
	var cursor = {x:0, y:0};
	if(e.pageX || e.pageY){
		cursor.x = e.pageX;
		cursor.y = e.pageY;
	}else{
		var de = dojo.doc().documentElement;
		var db = dojo.body();
		cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
		cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
	}
	return cursor;	//	object
}

dojo.html.isTag = function(/* HTMLElement */node) {
	//	summary
	//	Like dojo.dom.isTag, except case-insensitive
	node = dojo.byId(node);
	if(node && node.tagName) {
		for (var i=1; i<arguments.length; i++){
			if (node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
				return String(arguments[i]).toLowerCase();	//	string
			}
		}
	}
	return "";	//	string
}

//define dojo.html.createExternalElement for IE to workaround the annoying activation "feature" in new IE
//details: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
if(dojo.render.html.ie && !dojo.render.html.ie70){
	//only define createExternalElement for IE in none https to avoid "mixed content" warning dialog
	if(window.location.href.substr(0,6).toLowerCase() != "https:"){
		(function(){
			// FIXME: this seems not to work correctly on IE 7!!

			//The trick is to define a function in a script.src property:
			// <script src="javascript:'function createExternalElement(){...}'"></script>,
			//which will be treated as an external javascript file in IE
			var xscript = dojo.doc().createElement('script');
			xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
			dojo.doc().getElementsByTagName("head")[0].appendChild(xscript);
		})();
	}
}else{
	//for other browsers, simply use document.createElement
	//is enough
	dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){
		//	summary
		//	Creates an element in the HTML document, here for ActiveX activation workaround.
		return doc.createElement(tag);	//	HTMLElement
	}
}

dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){
	dojo.deprecated("dojo.html." + inFunc,
					"replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5");
	var newArgs = [];
	if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); }
	else { newArgs = args }
	var ret = dojo.html[replFunc].apply(dojo.html, args);
	if(retValue){ return ret[retValue]; }
	else { return ret; }
}

dojo.html.getViewportWidth = function(){
	return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width");
}
dojo.html.getViewportHeight = function(){
	return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height");
}
dojo.html.getViewportSize = function(){
	return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments);
}
dojo.html.getScrollTop = function(){
	return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top");
}
dojo.html.getScrollLeft = function(){
	return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left");
}
dojo.html.getScrollOffset = function(){
	return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset");
}

dojo.provide("dojo.uri.Uri");

dojo.uri = new function() {
	this.dojoUri = function (/*dojo.uri.Uri||String*/uri) {
		// summary: returns a Uri object resolved relative to the dojo root
		return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri);
	}

	this.moduleUri = function(/*String*/module, /*dojo.uri.Uri||String*/uri){
		// summary: returns a Uri object relative to a module
		// description: Examples: dojo.uri.moduleUri("dojo.widget","templates/template.html"), or dojo.uri.moduleUri("acme","images/small.png")
		var loc = dojo.hostenv.getModuleSymbols(module).join('/');
		if(!loc){
			return null;
		}
		if(loc.lastIndexOf("/") != loc.length-1){
			loc += "/";
		}
		
		//If the path is an absolute path (starts with a / or is on another domain/xdomain)
		//then don't add the baseScriptUri.
		var colonIndex = loc.indexOf(":");
		var slashIndex = loc.indexOf("/");
		if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > slashIndex)){
			loc = dojo.hostenv.getBaseScriptUri() + loc;
		}

		return new dojo.uri.Uri(loc,uri);
	}

	this.Uri = function (/*dojo.uri.Uri||String...*/) {
		// summary: Constructor to create an object representing a URI.
		// description: 
		//  Each argument is evaluated in order relative to the next until
		//  a canonical uri is produced. To get an absolute Uri relative
		//  to the current document use
		//      new dojo.uri.Uri(document.baseURI, uri)

		// TODO: support for IPv6, see RFC 2732

		// resolve uri components relative to each other
		var uri = arguments[0];
		for (var i = 1; i < arguments.length; i++) {
			if(!arguments[i]) { continue; }

			// Safari doesn't support this.constructor so we have to be explicit
			var relobj = new dojo.uri.Uri(arguments[i].toString());
			var uriobj = new dojo.uri.Uri(uri.toString());

			if ((relobj.path=="")&&(relobj.scheme==null)&&(relobj.authority==null)&&(relobj.query==null)) {
				if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; }
				relobj = uriobj;
			} else if (relobj.scheme == null) {
				relobj.scheme = uriobj.scheme;

				if (relobj.authority == null) {
					relobj.authority = uriobj.authority;

					if (relobj.path.charAt(0) != "/") {
						var path = uriobj.path.substring(0,
							uriobj.path.lastIndexOf("/") + 1) + relobj.path;

						var segs = path.split("/");
						for (var j = 0; j < segs.length; j++) {
							if (segs[j] == ".") {
								if (j == segs.length - 1) { segs[j] = ""; }
								else { segs.splice(j, 1); j--; }
							} else if (j > 0 && !(j == 1 && segs[0] == "") &&
								segs[j] == ".." && segs[j-1] != "..") {

								if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; }
								else { segs.splice(j - 1, 2); j -= 2; }
							}
						}
						relobj.path = segs.join("/");
					}
				}
			}

			uri = "";
			if (relobj.scheme != null) { uri += relobj.scheme + ":"; }
			if (relobj.authority != null) { uri += "//" + relobj.authority; }
			uri += relobj.path;
			if (relobj.query != null) { uri += "?" + relobj.query; }
			if (relobj.fragment != null) { uri += "#" + relobj.fragment; }
		}

		this.uri = uri.toString();

		// break the uri into its main components
		var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
		var r = this.uri.match(new RegExp(regexp));

		this.scheme = r[2] || (r[1] ? "" : null);
		this.authority = r[4] || (r[3] ? "" : null);
		this.path = r[5]; // can never be undefined
		this.query = r[7] || (r[6] ? "" : null);
		this.fragment  = r[9] || (r[8] ? "" : null);

		if (this.authority != null) {
			// server based naming authority
			regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
			r = this.authority.match(new RegExp(regexp));

			this.user = r[3] || null;
			this.password = r[4] || null;
			this.host = r[5];
			this.port = r[7] || null;
		}

		this.toString = function(){ return this.uri; }
	}
};

dojo.provide("dojo.html.style");



dojo.html.getClass = function(/* HTMLElement */node){
	//	summary
	//	Returns the string value of the list of CSS classes currently assigned directly 
	//	to the node in question. Returns an empty string if no class attribute is found;
	node = dojo.byId(node);
	if(!node){ return ""; }
	var cs = "";
	if(node.className){
		cs = node.className;
	}else if(dojo.html.hasAttribute(node, "class")){
		cs = dojo.html.getAttribute(node, "class");
	}
	return cs.replace(/^\s+|\s+$/g, "");	//	string
}

dojo.html.getClasses = function(/* HTMLElement */node) {
	//	summary
	//	Returns an array of CSS classes currently assigned directly to the node in question. 
	//	Returns an empty array if no classes are found;
	var c = dojo.html.getClass(node);
	return (c == "") ? [] : c.split(/\s+/g);	//	array
}

dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){
	//	summary
	//	Returns whether or not the specified classname is a portion of the
	//	class list currently applied to the node. Does not cover cascaded
	//	styles, only classes directly applied to the node.
	return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node))	//	boolean
}

dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){
	//	summary
	//	Adds the specified class to the beginning of the class list on the
	//	passed node. This gives the specified class the highest precidence
	//	when style cascading is calculated for the node. Returns true or
	//	false; indicating success or failure of the operation, respectively.
	classStr += " " + dojo.html.getClass(node);
	return dojo.html.setClass(node, classStr);	//	boolean
}

dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){
	//	summary
	//	Adds the specified class to the end of the class list on the
	//	passed &node;. Returns &true; or &false; indicating success or failure.
	if (dojo.html.hasClass(node, classStr)) {
	  return false;
	}
	classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,"");
	return dojo.html.setClass(node, classStr);	//	boolean
}

dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){
	//	summary
	//	Clobbers the existing list of classes for the node, replacing it with
	//	the list given in the 2nd argument. Returns true or false
	//	indicating success or failure.
	node = dojo.byId(node);
	var cs = new String(classStr);
	try{
		if(typeof node.className == "string"){
			node.className = cs;
		}else if(node.setAttribute){
			node.setAttribute("class", classStr);
			node.className = cs;
		}else{
			return false;
		}
	}catch(e){
		dojo.debug("dojo.html.setClass() failed", e);
	}
	return true;
}

dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){
	//	summary
	//	Removes the className from the node;. Returns true or false indicating success or failure.
	try{
		if (!allowPartialMatches) {
			var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2");
		} else {
			var newcs = dojo.html.getClass(node).replace(classStr,'');
		}
		dojo.html.setClass(node, newcs);
	}catch(e){
		dojo.debug("dojo.html.removeClass() failed", e);
	}
	return true;	//	boolean
}

dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) {
	//	summary
	//	Replaces 'oldClass' and adds 'newClass' to node
	dojo.html.removeClass(node, oldClass);
	dojo.html.addClass(node, newClass);
}

// Enum type for getElementsByClass classMatchType arg:
dojo.html.classMatchType = {
	ContainsAll : 0, // all of the classes are part of the node's class (default)
	ContainsAny : 1, // any of the classes are part of the node's class
	IsOnly : 2 // only all of the classes are part of the node's class
}


dojo.html.getElementsByClass = function(
	/* string */classStr, 
	/* HTMLElement? */parent, 
	/* string? */nodeType, 
	/* integer? */classMatchType, 
	/* boolean? */useNonXpath
){
	//	summary
	//	Returns an array of nodes for the given classStr, children of a
	//	parent, and optionally of a certain nodeType
	// FIXME: temporarily set to false because of several dojo tickets related
	// to the xpath version not working consistently in firefox.
	useNonXpath = false;
	var _document = dojo.doc();
	parent = dojo.byId(parent) || _document;
	var classes = classStr.split(/\s+/g);
	var nodes = [];
	if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
	var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
	var srtLength = classes.join(" ").length;
	var candidateNodes = [];
	
	if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath
		var xpath = ".//" + (nodeType || "*") + "[contains(";
		if(classMatchType != dojo.html.classMatchType.ContainsAny){
			xpath += "concat(' ',@class,' '), ' " +
			classes.join(" ') and contains(concat(' ',@class,' '), ' ") +
			" ')";
			if (classMatchType == 2) {
				xpath += " and string-length(@class)="+srtLength+"]";
			}else{
				xpath += "]";
			}
		}else{
			xpath += "concat(' ',@class,' '), ' " +
			classes.join(" ') or contains(concat(' ',@class,' '), ' ") +
			" ')]";
		}
		var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
		var result = xpathResult.iterateNext();
		while(result){
			try{
				candidateNodes.push(result);
				result = xpathResult.iterateNext();
			}catch(e){ break; }
		}
		return candidateNodes;	//	NodeList
	}else{
		if(!nodeType){
			nodeType = "*";
		}
		candidateNodes = parent.getElementsByTagName(nodeType);

		var node, i = 0;
		outer:
		while(node = candidateNodes[i++]){
			var nodeClasses = dojo.html.getClasses(node);
			if(nodeClasses.length == 0){ continue outer; }
			var matches = 0;
	
			for(var j = 0; j < nodeClasses.length; j++){
				if(reClass.test(nodeClasses[j])){
					if(classMatchType == dojo.html.classMatchType.ContainsAny){
						nodes.push(node);
						continue outer;
					}else{
						matches++;
					}
				}else{
					if(classMatchType == dojo.html.classMatchType.IsOnly){
						continue outer;
					}
				}
			}
	
			if(matches == classes.length){
				if(	(classMatchType == dojo.html.classMatchType.IsOnly)&&
					(matches == nodeClasses.length)){
					nodes.push(node);
				}else if(classMatchType == dojo.html.classMatchType.ContainsAll){
					nodes.push(node);
				}
			}
		}
		return nodes;	//	NodeList
	}
}
dojo.html.getElementsByClassName = dojo.html.getElementsByClass;

dojo.html.toCamelCase = function(/* string */selector){
	//	summary
	//	Translates a CSS selector string to a camel-cased one.
	var arr = selector.split('-'), cc = arr[0];
	for(var i = 1; i < arr.length; i++) {
		cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
	}
	return cc;	//	string
}

dojo.html.toSelectorCase = function(/* string */selector){
	//	summary
	//	Translates a camel cased string to a selector cased one.
	return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase();	//	string
}

if (dojo.render.html.ie) {
	// IE branch
	dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*String*/value) {
		// summary
		// Get the computed style value for style "property" on "node" (IE).
		node = dojo.byId(node); // FIXME: remove ability to access nodes by id for this time-critical function
		if(!node || !node.currentStyle){return value;}
		// FIXME: standardize on camel-case input to improve speed
		return node.currentStyle[dojo.html.toCamelCase(property)]; // String
	}
	// SJM: getComputedStyle should be abandoned and replaced with the below function.
	// All our supported browsers can return CSS2 compliant CssStyleDeclaration objects
	// which can be queried directly for multiple styles.
	dojo.html.getComputedStyles = function(/*HTMLElement*/node) {
		// summary
		// Get a style object containing computed styles for HTML Element node (IE).
		return node.currentStyle; // CSSStyleDeclaration
	}
} else {
	// non-IE branch
	dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*Any*/value) {
		// summary
		// Get the computed style value for style "property" on "node" (non-IE).
		node = dojo.byId(node);
		if(!node || !node.style){return value;}
		var s = document.defaultView.getComputedStyle(node, null);
		// s may be null on Safari
		return (s&&s[dojo.html.toCamelCase(property)])||''; // String
	}	
	// SJM: getComputedStyle should be abandoned and replaced with the below function.
	// All our supported browsers can return CSS2 compliant CssStyleDeclaration objects
	// which can be queried directly for multiple styles.
	dojo.html.getComputedStyles = function(node) {
		// summary
		// Get a style object containing computed styles for HTML Element node (non-IE).
		return document.defaultView.getComputedStyle(node, null); // CSSStyleDeclaration
	}	
}

dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){
	//	summary
	//	Returns the value of the passed style
	node = dojo.byId(node);
	return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined);	//	string
}

dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){
	//	summary
	//	Returns the computed value of the passed style
	var value = dojo.html.getStyleProperty(node, cssSelector);
	return (value ? value : dojo.html.getComputedStyle(node, cssSelector));	//	string || integer
}

dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){
	//	summary
	//	Set the value of passed style on node
	node = dojo.byId(node);
	if(node && node.style){
		var camelCased = dojo.html.toCamelCase(cssSelector);
		node.style[camelCased] = value;
	}
}

dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) {
	//	summary
	//	Try to set the entire cssText property of the passed target; equiv of setting style attribute.
	try {
	 	target.style.cssText = text;
	} catch (e) {
		target.setAttribute("style", text);
	}
}

dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){
	//	summary
	// work around for opera which doesn't have cssText, and for IE which fails on setAttribute 
	if(!source.style.cssText){ 
		target.setAttribute("style", source.getAttribute("style")); 
	}else{
		target.style.cssText = source.style.cssText; 
	}
	dojo.html.addClass(target, dojo.html.getClass(source));
}

dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
	//	summary
	//	Get the value of passed selector, with the specific units used
	var s = dojo.html.getComputedStyle(node, cssSelector);
	if((!s)||((s == 'auto')&&(autoIsZero))){ 
		return { value: 0, units: 'px' };	//	object 
	}
	// FIXME: is regex inefficient vs. parseInt or some manual test? 
	var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
	if (!match){return dojo.html.getUnitValue.bad;}
	return { value: Number(match[1]), units: match[2].toLowerCase() };	//	object
}
dojo.html.getUnitValue.bad = { value: NaN, units: '' };

if (dojo.render.html.ie) {
	// IE branch
	dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){
		// summary
		//  Extract value in pixels from styleValue (IE version).
		//  If a value cannot be extracted, zero is returned.
		if(!styleValue){return 0;}
		if(styleValue.slice(-2) == 'px'){return parseFloat(styleValue);}
		var pixelValue = 0;
		with(element){
			var sLeft = style.left;
			var rsLeft = runtimeStyle.left;
			runtimeStyle.left = currentStyle.left;
			try {
				style.left = styleValue || 0;
				pixelValue = style.pixelLeft;
				style.left = sLeft;
				runtimeStyle.left = rsLeft;
			}catch(e){
				// FIXME: it's possible for styleValue to be incompatible with
				// style.left. In particular, border width values of 
				// "thick", "medium", or "thin" will provoke an exception.
			}
		}
		return pixelValue; // Number
	}
} else {
	// non-IE branch
	dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){
		// summary
		//  Extract value in pixels from styleValue (non-IE version).
		//  If a value cannot be extracted, zero is returned.
		return (styleValue && (styleValue.slice(-2)=='px') ? parseFloat(styleValue) : 0); // Number
	}
}

dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */styleProperty, /* boolean? */autoIsZero){
	// summary
	//  Get a computed style value, in pixels.
	// node: HTMLElement
	//  Node to interrogate
	// styleProperty: String
	//  Style property to query, in either css-selector or camelCase (property) format.
	// autoIsZero: Boolean
	//  Deprecated. Any value that cannot be converted to pixels is returned as zero.
	// 
	//  summary
	//  Get the value of passed selector in pixels.
	//
	return dojo.html.toPixelValue(node, dojo.html.getComputedStyle(node, styleProperty));
} 

dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){
	//	summary
	//	Attempt to set the value of selector on node as a positive pixel value.
	if(isNaN(value)){return false;}
	node.style[selector] = Math.max(0, value) + 'px'; 
	return true;	//	boolean
}

dojo.html.styleSheet = null;

// FIXME: this is a really basic stub for adding and removing cssRules, but
// it assumes that you know the index of the cssRule that you want to add 
// or remove, making it less than useful.  So we need something that can 
// search for the selector that you you want to remove.
dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) {
	//	summary
	//	Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there.
	if (!dojo.html.styleSheet) {
		if (document.createStyleSheet) { // IE
			dojo.html.styleSheet = document.createStyleSheet();
		} else if (document.styleSheets[0]) { // rest
			// FIXME: should create a new style sheet here
			// fall back on an exsiting style sheet
			dojo.html.styleSheet = document.styleSheets[0];
		} else { 
			return null;	//	integer 
		} // fail
	}

	if (arguments.length < 3) { // index may == 0
		if (dojo.html.styleSheet.cssRules) { // W3
			index = dojo.html.styleSheet.cssRules.length;
		} else if (dojo.html.styleSheet.rules) { // IE
			index = dojo.html.styleSheet.rules.length;
		} else { 
			return null;	//	integer 
		} // fail
	}

	if (dojo.html.styleSheet.insertRule) { // W3
		var rule = selector + " { " + declaration + " }";
		return dojo.html.styleSheet.insertRule(rule, index);	//	integer
	} else if (dojo.html.styleSheet.addRule) { // IE
		return dojo.html.styleSheet.addRule(selector, declaration, index);	//	integer
	} else { 
		return null; // integer
	} // fail
}

dojo.html.removeCssRule = function(/* integer? */index){
	//	summary
	//	Attempt to remove the rule at index.
	if(!dojo.html.styleSheet){
		dojo.debug("no stylesheet defined for removing rules");
		return false;
	}
	if(dojo.render.html.ie){
		if(!index){
			index = dojo.html.styleSheet.rules.length;
			dojo.html.styleSheet.removeRule(index);
		}
	}else if(document.styleSheets[0]){
		if(!index){
			index = dojo.html.styleSheet.cssRules.length;
		}
		dojo.html.styleSheet.deleteRule(index);
	}
	return true;	//	boolean
}

dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM
dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){
	//	summary
	// calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
	if(!URI){ return; }
	if(!doc){ doc = document; }
	var cssStr = dojo.hostenv.getText(URI, false, fail_ok);
	if(cssStr===null){ return; }
	cssStr = dojo.html.fixPathsInCssText(cssStr, URI);

	if(checkDuplicates){
		var idx = -1, node, ent = dojo.html._insertedCssFiles;
		for(var i = 0; i < ent.length; i++){
			if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){
				idx = i; node = ent[i].nodeRef;
				break;
			}
		}
		// make sure we havent deleted our node
		if(node){
			var styles = doc.getElementsByTagName("style");
			for(var i = 0; i < styles.length; i++){
				if(styles[i] == node){
					return;
				}
			}
			// delete this entry
			dojo.html._insertedCssFiles.shift(idx, 1);
		}
	}

	var style = dojo.html.insertCssText(cssStr, doc);
	dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style});

	// insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
	if(style && djConfig.isDebug){
		style.setAttribute("dbgHref", URI);
	}
	return style;	//	HTMLStyleElement
}

dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){
	//	summary
	//	Attempt to insert CSS rules into the document through inserting a style element
	// DomNode Style  = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
	if(!cssStr){ 
		return; //	HTMLStyleElement
	}
	if(!doc){ doc = document; }
	if(URI){// fix paths in cssStr
		cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
	}
	var style = doc.createElement("style");
	style.setAttribute("type", "text/css");
	// IE is b0rken enough to require that we add the element to the doc
	// before changing it's properties
	var head = doc.getElementsByTagName("head")[0];
	if(!head){ // must have a head tag 
		dojo.debug("No head tag in document, aborting styles");
		return;	//	HTMLStyleElement
	}else{
		head.appendChild(style);
	}
	if(style.styleSheet){// IE
		var setFunc = function(){ 
			try{
				style.styleSheet.cssText = cssStr;
			}catch(e){ dojo.debug(e); }
		};
		if(style.styleSheet.disabled){
			setTimeout(setFunc, 10);
		}else{
			setFunc();
		}
	}else{ // w3c
		var cssText = doc.createTextNode(cssStr);
		style.appendChild(cssText);
	}
	return style;	//	HTMLStyleElement
}

dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){
	//	summary
	// usage: cssText comes from dojoroot/src/widget/templates/Foobar.css
	// 	it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/
	if(!cssStr || !URI){ return; }
	var match, str = "", url = "", urlChrs = "[\\t\\s\\w\\(\\)\\/\\.\\\\'\"-:#=&?~]+";
	var regex = new RegExp('url\\(\\s*('+urlChrs+')\\s*\\)');
	var regexProtocol = /(file|https?|ftps?):\/\//;
	regexTrim = new RegExp("^[\\s]*(['\"]?)("+urlChrs+")\\1[\\s]*?$");
	if(dojo.render.html.ie55 || dojo.render.html.ie60){
		var regexIe = new RegExp("AlphaImageLoader\\((.*)src\=['\"]("+urlChrs+")['\"]");
		// TODO: need to decide how to handle relative paths and AlphaImageLoader see #1441
		// current implementation breaks on build with intern_strings
		while(match = regexIe.exec(cssStr)){
			url = match[2].replace(regexTrim, "$2");
			if(!regexProtocol.exec(url)){
				url = (new dojo.uri.Uri(URI, url).toString());
			}
			str += cssStr.substring(0, match.index) + "AlphaImageLoader(" + match[1] + "src='" + url + "'";
			cssStr = cssStr.substr(match.index + match[0].length);
		}
		cssStr = str + cssStr;
		str = "";
	}

	while(match = regex.exec(cssStr)){
		url = match[1].replace(regexTrim, "$2");
		if(!regexProtocol.exec(url)){
			url = (new dojo.uri.Uri(URI, url).toString());
		}
		str += cssStr.substring(0, match.index) + "url(" + url + ")";
		cssStr = cssStr.substr(match.index + match[0].length);
	}
	return str + cssStr;	//	string
}

dojo.html.setActiveStyleSheet = function(/* string */title){
	//	summary
	//	Activate style sheet with specified title.
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
	while (a = els[i++]) {
		if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
			a.disabled = true;
			if (a.getAttribute("title") == title) { a.disabled = false; }
		}
	}
}

dojo.html.getActiveStyleSheet = function(){
	//	summary
	//	return the title of the currently active stylesheet
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
	while (a = els[i++]) {
		if (a.getAttribute("rel").indexOf("style") != -1 
			&& a.getAttribute("title") 
			&& !a.disabled
		){
			return a.getAttribute("title");	//	string 
		}
	}
	return null;	//	string
}

dojo.html.getPreferredStyleSheet = function(){
	//	summary
	//	Return the preferred stylesheet title (i.e. link without alt attribute)
	var i = 0, a, els = dojo.doc().getElementsByTagName("link");
	while (a = els[i++]) {
		if(a.getAttribute("rel").indexOf("style") != -1
			&& a.getAttribute("rel").indexOf("alt") == -1
			&& a.getAttribute("title")
		){ 
			return a.getAttribute("title"); 	//	string
		}
	}
	return null;	//	string
}

dojo.html.applyBrowserClass = function(/* HTMLElement */node){
	//	summary
	//	Applies pre-set class names based on browser & version to the passed node.
	//	Modified version of Morris' CSS hack.
	var drh=dojo.render.html;
	var classes = {
		dj_ie: drh.ie,
		dj_ie55: drh.ie55,
		dj_ie6: drh.ie60,
		dj_ie7: drh.ie70,
		dj_iequirks: drh.ie && drh.quirks,
		dj_opera: drh.opera,
		dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8),
		dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9),
		dj_khtml: drh.khtml,
		dj_safari: drh.safari,
		dj_gecko: drh.mozilla
	}; // no dojo unsupported browsers
	for(var p in classes){
		if(classes[p]){
			dojo.html.addClass(node, p);
		}
	}
};

dojo.provide("dojo.html.display");


dojo.html._toggle = function(node, tester, setter){
	node = dojo.byId(node);
	setter(node, !tester(node));
	return tester(node);
}

dojo.html.show = function(/* HTMLElement */node){
	//	summary
	//	Show the passed element by reverting display property set by dojo.html.hide
	node = dojo.byId(node);
	if(dojo.html.getStyleProperty(node, 'display')=='none'){
		dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||''));
		node.dojoDisplayCache = undefined;	// cannot use delete on a node in IE6
	}
}

dojo.html.hide = function(/* HTMLElement */node){
	//	summary
	//	Hide the passed element by setting display:none
	node = dojo.byId(node);
	if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
		var d = dojo.html.getStyleProperty(node, 'display')
		if(d!='none'){
			node.dojoDisplayCache = d;
		}
	}
	dojo.html.setStyle(node, 'display', 'none');
}

dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){
	//	summary
	// Calls show() if showing is true, hide() otherwise
	dojo.html[(showing ? 'show' : 'hide')](node);
}

dojo.html.isShowing = function(/* HTMLElement */node){
	//	summary
	//	Returns whether the element is displayed or not.
	// FIXME: returns true if node is bad, isHidden would be easier to make correct
	return (dojo.html.getStyleProperty(node, 'display') != 'none');	//	boolean
}

dojo.html.toggleShowing = function(/* HTMLElement */node){
	//	summary
	// Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
	return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing);	//	boolean
}

// Simple mapping of tag names to display values
// FIXME: simplistic 
dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };

dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){
	//	summary
	// Suggest a value for the display property that will show 'node' based on it's tag
	node = dojo.byId(node);
	if(node && node.tagName){
		var tag = node.tagName.toLowerCase();
		return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block');	//	string
	}
}

dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){
	//	summary
	// 	Sets the value of style.display to value of 'display' parameter if it is a string.
	// 	Otherwise, if 'display' is false, set style.display to 'none'.
	// 	Finally, set 'display' to a suggested display value based on the node's tag
	dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none')));
}

dojo.html.isDisplayed = function(/* HTMLElement */node){
	//	summary
	// 	Is true if the the computed display style for node is not 'none'
	// 	FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
	return (dojo.html.getComputedStyle(node, 'display') != 'none');	//	boolean
}

dojo.html.toggleDisplay = function(/* HTMLElement */node){
	//	summary
	// 	Call setDisplay() on node with the complement of isDisplayed(), then
	// 	return the new value of isDisplayed()
	return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay);	//	boolean
}

dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){
	//	summary
	// 	Sets the value of style.visibility to value of 'visibility' parameter if it is a string.
	// 	Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'.
	dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden')));
}

dojo.html.isVisible = function(/* HTMLElement */node){
	//	summary
	// 	Returns true if the the computed visibility style for node is not 'hidden'
	// 	FIXME: returns true if node is bad, isInvisible would be easier to make correct
	return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden');	//	boolean
}

dojo.html.toggleVisibility = function(node){
	//	summary
	// Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible()
	return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility);	//	boolean
}

dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){
	//	summary
	//	Sets the opacity of node in a cross-browser way.
	//	float between 0.0 (transparent) and 1.0 (opaque)
	node = dojo.byId(node);
	var h = dojo.render.html;
	if(!dontFixOpacity){
		if( opacity >= 1.0){
			if(h.ie){
				dojo.html.clearOpacity(node);
				return;
			}else{
				opacity = 0.999999;
			}
		}else if( opacity < 0.0){ opacity = 0; }
	}
	if(h.ie){
		if(node.nodeName.toLowerCase() == "tr"){
			// FIXME: is this too naive? will we get more than we want?
			var tds = node.getElementsByTagName("td");
			for(var x=0; x<tds.length; x++){
				tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
			}
		}
		node.style.filter = "Alpha(Opacity="+opacity*100+")";
	}else if(h.moz){
		node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
		node.style.MozOpacity = opacity;
	}else if(h.safari){
		node.style.opacity = opacity; // 1.3 directly supports "opacity"
		node.style.KhtmlOpacity = opacity;
	}else{
		node.style.opacity = opacity;
	}
}

dojo.html.clearOpacity = function(/* HTMLElement */node){
	//	summary
	//	Clears any opacity setting on the passed element.
	node = dojo.byId(node);
	var ns = node.style;
	var h = dojo.render.html;
	if(h.ie){
		try {
			if( node.filters && node.filters.alpha ){
				ns.filter = ""; // FIXME: may get rid of other filter effects
			}
		} catch(e) {
			/*
			 * IE7 gives error if node.filters not set;
			 * don't know why or how to workaround (other than this)
			 */
		}
	}else if(h.moz){
		ns.opacity = 1;
		ns.MozOpacity = 1;
	}else if(h.safari){
		ns.opacity = 1;
		ns.KhtmlOpacity = 1;
	}else{
		ns.opacity = 1;
	}
}

dojo.html.getOpacity = function(/* HTMLElement */node){
	//	summary
	//	Returns the opacity of the passed element
	node = dojo.byId(node);
	var h = dojo.render.html;
	if(h.ie){
		var opac = (node.filters && node.filters.alpha &&
			typeof node.filters.alpha.opacity == "number"
			? node.filters.alpha.opacity : 100) / 100;
	}else{
		var opac = node.style.opacity || node.style.MozOpacity ||
			node.style.KhtmlOpacity || 1;
	}
	return opac >= 0.999999 ? 1.0 : Number(opac);	//	float
}


dojo.provide("dojo.html.color");




dojo.html.getBackgroundColor = function(/* HTMLElement */node){
	//	summary
	//	returns the background color of the passed node as a 32-bit color (RGBA)
	node = dojo.byId(node);
	var color;
	do{
		color = dojo.html.getStyle(node, "background-color");
		// Safari doesn't say "transparent"
		if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
		if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
		node = node.parentNode;
	}while(node && dojo.lang.inArray(["transparent", ""], color));
	if(color == "transparent"){
		color = [255, 255, 255, 0];
	}else{
		color = dojo.gfx.color.extractRGB(color);
	}
	return color;	//	array
}

dojo.provide("dojo.html.layout");





dojo.html.sumAncestorProperties = function(/* HTMLElement */node, /* string */prop){
	//	summary
	//	Returns the sum of the passed property on all ancestors of node.
	node = dojo.byId(node);
	if(!node){ return 0; } // FIXME: throw an error?
	
	var retVal = 0;
	while(node){
		if(dojo.html.getComputedStyle(node, 'position') == 'fixed'){
			return 0;
		}
		var val = node[prop];
		if(val){
			retVal += val - 0;
			if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
		}
		node = node.parentNode;
	}
	return retVal;	//	integer
}

dojo.html.setStyleAttributes = function(/* HTMLElement */node, /* string */attributes) { 
	//	summary
	//	allows a dev to pass a string similar to what you'd pass in style="", and apply it to a node.
	node = dojo.byId(node);
	var splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";"); 
	for(var i=0; i<splittedAttribs.length; i++){ 
		var nameValue=splittedAttribs[i].split(":"); 
		var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
		var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
		switch(name){
			case "opacity":
				dojo.html.setOpacity(node, value); 
				break; 
			case "content-height":
				dojo.html.setContentBox(node, {height: value}); 
				break; 
			case "content-width":
				dojo.html.setContentBox(node, {width: value}); 
				break; 
			case "outer-height":
				dojo.html.setMarginBox(node, {height: value}); 
				break; 
			case "outer-width":
				dojo.html.setMarginBox(node, {width: value}); 
				break; 
			default:
				node.style[dojo.html.toCamelCase(name)]=value; 
		}
	} 
}

dojo.html.boxSizing = {
	MARGIN_BOX: "margin-box",
	BORDER_BOX: "border-box",
	PADDING_BOX: "padding-box",
	CONTENT_BOX: "content-box"
};

dojo.html.getAbsolutePosition = dojo.html.abs = function(/* HTMLElement */node, /* boolean? */includeScroll, /* string? */boxType){
	//	summary
	//	Gets the absolute position of the passed element based on the document itself.
	node = dojo.byId(node, node.ownerDocument);
	var ret = {
		x: 0,
		y: 0
	};

	var bs = dojo.html.boxSizing;
	if(!boxType) { boxType = bs.CONTENT_BOX; }
	var nativeBoxType = 2; //BORDER box
	var targetBoxType;
	switch(boxType){
		case bs.MARGIN_BOX:
			targetBoxType = 3;
			break;
		case bs.BORDER_BOX:
			targetBoxType = 2;
			break;
		case bs.PADDING_BOX:
		default:
			targetBoxType = 1;
			break;
		case bs.CONTENT_BOX:
			targetBoxType = 0;
			break;
	}

	var h = dojo.render.html;
	var db = document["body"]||document["documentElement"];

	if(h.ie){
		with(node.getBoundingClientRect()){
			ret.x = left-2;
			ret.y = top-2;
		}
	}else if(document.getBoxObjectFor){
		// mozilla
		nativeBoxType = 1; //getBoxObjectFor return padding box coordinate
		try{
			var bo = document.getBoxObjectFor(node);
			ret.x = bo.x - dojo.html.sumAncestorProperties(node, "scrollLeft");
			ret.y = bo.y - dojo.html.sumAncestorProperties(node, "scrollTop");
		}catch(e){
			// squelch
		}
	}else{
		if(node["offsetParent"]){
			var endNode;
			// in Safari, if the node is an absolutely positioned child of
			// the body and the body has a margin the offset of the child
			// and the body contain the body's margins, so we need to end
			// at the body
			if(	(h.safari)&&
				(node.style.getPropertyValue("position") == "absolute")&&
				(node.parentNode == db)){
				endNode = db;
			}else{
				endNode = db.parentNode;
			}

			//TODO: set correct nativeBoxType for safari/konqueror

			if(node.parentNode != db){
				var nd = node;
				if(dojo.render.html.opera){ nd = db; }
				ret.x -= dojo.html.sumAncestorProperties(nd, "scrollLeft");
				ret.y -= dojo.html.sumAncestorProperties(nd, "scrollTop");
			}
			var curnode = node;
			do{
				var n = curnode["offsetLeft"];
				//FIXME: ugly hack to workaround the submenu in 
				//popupmenu2 does not shown up correctly in opera. 
				//Someone have a better workaround?
				if(!h.opera || n>0){
					ret.x += isNaN(n) ? 0 : n;
				}
				var m = curnode["offsetTop"];
				ret.y += isNaN(m) ? 0 : m;
				curnode = curnode.offsetParent;
			}while((curnode != endNode)&&(curnode != null));
		}else if(node["x"]&&node["y"]){
			ret.x += isNaN(node.x) ? 0 : node.x;
			ret.y += isNaN(node.y) ? 0 : node.y;
		}
	}

	// account for document scrolling!
	if(includeScroll){
		var scroll = dojo.html.getScroll();
		ret.y += scroll.top;
		ret.x += scroll.left;
	}

	var extentFuncArray=[dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
	if(nativeBoxType > targetBoxType){
		for(var i=targetBoxType;i<nativeBoxType;++i){
			ret.y += extentFuncArray[i](node, 'top');
			ret.x += extentFuncArray[i](node, 'left');
		}
	}else if(nativeBoxType < targetBoxType){
		for(var i=targetBoxType;i>nativeBoxType;--i){
			ret.y -= extentFuncArray[i-1](node, 'top');
			ret.x -= extentFuncArray[i-1](node, 'left');
		}
	}
	ret.top = ret.y;
	ret.left = ret.x;
	return ret;	//	object
}

dojo.html.isPositionAbsolute = function(/* HTMLElement */node){
	//	summary
	//	Returns true if the element is absolutely positioned.
	return (dojo.html.getComputedStyle(node, 'position') == 'absolute');	//	boolean
}

dojo.html._sumPixelValues = function(/* HTMLElement */node, selectors, autoIsZero){
	var total = 0;
	for(var x=0; x<selectors.length; x++){
		total += dojo.html.getPixelValue(node, selectors[x], autoIsZero);
	}
	return total;
}

dojo.html.getMargin = function(/* HTMLElement */node){
	//	summary
	//	Returns the width and height of the passed node's margin
	return {
		width: dojo.html._sumPixelValues(node, ["margin-left", "margin-right"], (dojo.html.getComputedStyle(node, 'position') == 'absolute')),
		height: dojo.html._sumPixelValues(node, ["margin-top", "margin-bottom"], (dojo.html.getComputedStyle(node, 'position') == 'absolute'))
	};	//	object
}

dojo.html.getBorder = function(/* HTMLElement */node){
	//	summary
	//	Returns the width and height of the passed node's border
	return {
		width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
		height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
	};	//	object
}

dojo.html.getBorderExtent = function(/* HTMLElement */node, /* string */side){
	//	summary
	//	returns the width of the requested border
	return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-' + side + '-width'));	// integer
}

dojo.html.getMarginExtent = function(/* HTMLElement */node, /* string */side){
	//	summary
	//	returns the width of the requested margin
	return dojo.html._sumPixelValues(node, ["margin-" + side], dojo.html.isPositionAbsolute(node));	//	integer
}

dojo.html.getPaddingExtent = function(/* HTMLElement */node, /* string */side){
	//	summary
	//	Returns the width of the requested padding 
	return dojo.html._sumPixelValues(node, ["padding-" + side], true);	//	integer
}

dojo.html.getPadding = function(/* HTMLElement */node){
	//	summary
	//	Returns the width and height of the passed node's padding
	return {
		width: dojo.html._sumPixelValues(node, ["padding-left", "padding-right"], true),
		height: dojo.html._sumPixelValues(node, ["padding-top", "padding-bottom"], true)
	};	//	object
}

dojo.html.getPadBorder = function(/* HTMLElement */node){
	//	summary
	//	Returns the width and height of the passed node's padding and border
	var pad = dojo.html.getPadding(node);
	var border = dojo.html.getBorder(node);
	return { width: pad.width + border.width, height: pad.height + border.height };	//	object
}

dojo.html.getBoxSizing = function(/* HTMLElement */node){
	//	summary
	//	Returns which box model the passed element is working with
	var h = dojo.render.html;
	var bs = dojo.html.boxSizing;
	if(((h.ie)||(h.opera)) && node.nodeName.toLowerCase() != "img"){ 
		var cm = document["compatMode"];
		if((cm == "BackCompat")||(cm == "QuirksMode")){
			return bs.BORDER_BOX; 	//	string
		}else{
			return bs.CONTENT_BOX; 	//	string
		}
	}else{
		if(arguments.length == 0){ node = document.documentElement; }
		var sizing;
		if(!h.ie){
			sizing = dojo.html.getStyle(node, "-moz-box-sizing");
			if(!sizing){ 
				sizing = dojo.html.getStyle(node, "box-sizing");
			}
		}
		return (sizing ? sizing : bs.CONTENT_BOX);	//	string
	}
}

dojo.html.isBorderBox = function(/* HTMLElement */node){
	//	summary
	//	returns whether the passed element is using border box sizing or not.
	return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX);	//	boolean
}

dojo.html.getBorderBox = function(/* HTMLElement */node){
	//	summary
	//	Returns the dimensions of the passed element based on border-box sizing.
	node = dojo.byId(node);
	return { width: node.offsetWidth, height: node.offsetHeight };	//	object
}

dojo.html.getPaddingBox = function(/* HTMLElement */node){
	//	summary
	//	Returns the dimensions of the padding box (see http://www.w3.org/TR/CSS21/box.html)
	var box = dojo.html.getBorderBox(node);
	var border = dojo.html.getBorder(node);
	return {
		width: box.width - border.width,
		height:box.height - border.height
	};	//	object
}

dojo.html.getContentBox = function(/* HTMLElement */node){
	//	summary
	//	Returns the dimensions of the content box (see http://www.w3.org/TR/CSS21/box.html)
	node = dojo.byId(node);
	var padborder = dojo.html.getPadBorder(node);
	return {
		width: node.offsetWidth - padborder.width,
		height: node.offsetHeight - padborder.height
	};	//	object
}

dojo.html.setContentBox = function(/* HTMLElement */node, /* object */args){
	//	summary
	//	Sets the dimensions of the passed node according to content sizing.
	node = dojo.byId(node);
	var width = 0; var height = 0;
	var isbb = dojo.html.isBorderBox(node);
	var padborder = (isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0});
	var ret = {};
	if(typeof args.width != "undefined"){
		width = args.width + padborder.width;
		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
	}
	if(typeof args.height != "undefined"){
		height = args.height + padborder.height;
		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
	}
	return ret;	//	object
}

dojo.html.getMarginBox = function(/* HTMLElement */node){
	//	summary
	//	returns the dimensions of the passed node including any margins.
	var borderbox = dojo.html.getBorderBox(node);
	var margin = dojo.html.getMargin(node);
	return { width: borderbox.width + margin.width, height: borderbox.height + margin.height };	//	object
}

dojo.html.setMarginBox = function(/* HTMLElement */node, /* object */args){
	//	summary
	//	Sets the dimensions of the passed node using margin box calcs.
	node = dojo.byId(node);
	var width = 0; var height = 0;
	var isbb = dojo.html.isBorderBox(node);
	var padborder = (!isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0 });
	var margin = dojo.html.getMargin(node);
	var ret = {};
	if(typeof args.width != "undefined"){
		width = args.width - padborder.width;
		width -= margin.width;
		ret.width = dojo.html.setPositivePixelValue(node, "width", width);
	}
	if(typeof args.height != "undefined"){
		height = args.height - padborder.height;
		height -= margin.height;
		ret.height = dojo.html.setPositivePixelValue(node, "height", height);
	}
	return ret;	//	object
}

dojo.html.getElementBox = function(/* HTMLElement */node, /* string */type){
	//	summary
	//	return dimesions of a node based on the passed box model type.
	var bs = dojo.html.boxSizing;
	switch(type){
		case bs.MARGIN_BOX:
			return dojo.html.getMarginBox(node);	//	object
		case bs.BORDER_BOX:
			return dojo.html.getBorderBox(node);	//	object
		case bs.PADDING_BOX:
			return dojo.html.getPaddingBox(node);	//	object
		case bs.CONTENT_BOX:
		default:
			return dojo.html.getContentBox(node);	//	object
	}
}
// in: coordinate array [x,y,w,h] or dom node
// return: coordinate object
dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function(/* array */coords, /* boolean? */includeScroll, /* string? */boxtype) {
	//	summary
	//	Converts an array of coordinates into an object of named arguments.
	if(coords instanceof Array || typeof coords == "array"){
		dojo.deprecated("dojo.html.toCoordinateArray", "use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead", "0.5");
		// coords is already an array (of format [x,y,w,h]), just return it
		while ( coords.length < 4 ) { coords.push(0); }
		while ( coords.length > 4 ) { coords.pop(); }
		var ret = {
			left: coords[0],
			top: coords[1],
			width: coords[2],
			height: coords[3]
		};
	}else if(!coords.nodeType && !(coords instanceof String || typeof coords == "string") &&
			 ('width' in coords || 'height' in coords || 'left' in coords ||
			  'x' in coords || 'top' in coords || 'y' in coords)){
		// coords is a coordinate object or at least part of one
		var ret = {
			left: coords.left||coords.x||0,
			top: coords.top||coords.y||0,
			width: coords.width||0,
			height: coords.height||0
		};
	}else{
		// coords is an dom object (or dom object id); return it's coordinates
		var node = dojo.byId(coords);
		var pos = dojo.html.abs(node, includeScroll, boxtype);
		var marginbox = dojo.html.getMarginBox(node);
		var ret = {
			left: pos.left,
			top: pos.top,
			width: marginbox.width,
			height: marginbox.height
		};
	}
	ret.x = ret.left;
	ret.y = ret.top;
	return ret;	//	object
}

dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function(node, width){
	return dojo.html._callDeprecated("setMarginBoxWidth", "setMarginBox", arguments, "width");
}
dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function(){
	return dojo.html._callDeprecated("setMarginBoxHeight", "setMarginBox", arguments, "height");
}
dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function(){
	return dojo.html._callDeprecated("getMarginBoxWidth", "getMarginBox", arguments, null, "width");
}
dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function(){
	return dojo.html._callDeprecated("getMarginBoxHeight", "getMarginBox", arguments, null, "height");
}
dojo.html.getTotalOffset = function(node, type, includeScroll){
	return dojo.html._callDeprecated("getTotalOffset", "getAbsolutePosition", arguments, null, type);
}
dojo.html.getAbsoluteX = function(node, includeScroll){
	return dojo.html._callDeprecated("getAbsoluteX", "getAbsolutePosition", arguments, null, "x");
}
dojo.html.getAbsoluteY = function(node, includeScroll){
	return dojo.html._callDeprecated("getAbsoluteY", "getAbsolutePosition", arguments, null, "y");
}
dojo.html.totalOffsetLeft = function(node, includeScroll){
	return dojo.html._callDeprecated("totalOffsetLeft", "getAbsolutePosition", arguments, null, "left");
}
dojo.html.totalOffsetTop = function(node, includeScroll){
	return dojo.html._callDeprecated("totalOffsetTop", "getAbsolutePosition", arguments, null, "top");
}
dojo.html.getMarginWidth = function(node){
	return dojo.html._callDeprecated("getMarginWidth", "getMargin", arguments, null, "width");
}
dojo.html.getMarginHeight = function(node){
	return dojo.html._callDeprecated("getMarginHeight", "getMargin", arguments, null, "height");
}
dojo.html.getBorderWidth = function(node){
	return dojo.html._callDeprecated("getBorderWidth", "getBorder", arguments, null, "width");
}
dojo.html.getBorderHeight = function(node){
	return dojo.html._callDeprecated("getBorderHeight", "getBorder", arguments, null, "height");
}
dojo.html.getPaddingWidth = function(node){
	return dojo.html._callDeprecated("getPaddingWidth", "getPadding", arguments, null, "width");
}
dojo.html.getPaddingHeight = function(node){
	return dojo.html._callDeprecated("getPaddingHeight", "getPadding", arguments, null, "height");
}
dojo.html.getPadBorderWidth = function(node){
	return dojo.html._callDeprecated("getPadBorderWidth", "getPadBorder", arguments, null, "width");
}
dojo.html.getPadBorderHeight = function(node){
	return dojo.html._callDeprecated("getPadBorderHeight", "getPadBorder", arguments, null, "height");
}
dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function(){
	return dojo.html._callDeprecated("getBorderBoxWidth", "getBorderBox", arguments, null, "width");
}
dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function(){
	return dojo.html._callDeprecated("getBorderBoxHeight", "getBorderBox", arguments, null, "height");
}
dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function(){
	return dojo.html._callDeprecated("getContentBoxWidth", "getContentBox", arguments, null, "width");
}
dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function(){
	return dojo.html._callDeprecated("getContentBoxHeight", "getContentBox", arguments, null, "height");
}
dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function(node, width){
	return dojo.html._callDeprecated("setContentBoxWidth", "setContentBox", arguments, "width");
}
dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function(node, height){
	return dojo.html._callDeprecated("setContentBoxHeight", "setContentBox", arguments, "height");
}

dojo.provide("dojo.lfx.html");








dojo.lfx.html._byId = function(nodes){
	if(!nodes){ return []; }
	if(dojo.lang.isArrayLike(nodes)){
		if(!nodes.alreadyChecked){
			var n = [];
			dojo.lang.forEach(nodes, function(node){
				n.push(dojo.byId(node));
			});
			n.alreadyChecked = true;
			return n;
		}else{
			return nodes;
		}
	}else{
		var n = [];
		n.push(dojo.byId(nodes));
		n.alreadyChecked = true;
		return n;
	}
}

dojo.lfx.html.propertyAnimation = function(	/*DOMNode[]*/ nodes, 
											/*Object[]*/ propertyMap, 
											/*int*/ duration,
											/*function*/ easing,
											/*Object*/ handlers){
	// summary: Returns an animation that will transition the properties of "nodes"
	//			depending how they are defined in "propertyMap".
	// nodes: An array of DOMNodes or one DOMNode.
	// propertyMap: { property: String, start: Decimal?, end: Decimal?, units: String? }
	//				An array of objects defining properties to change.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
	nodes = dojo.lfx.html._byId(nodes);

	var targs = {
		"propertyMap": propertyMap,
		"nodes": nodes,
		"duration": duration,
		"easing": easing||dojo.lfx.easeDefault
	};
	
	var setEmUp = function(args){
		if(args.nodes.length==1){
			// FIXME: we're only supporting start-value filling when one node is
			// passed
			
			var pm = args.propertyMap;
			if(!dojo.lang.isArray(args.propertyMap)){
				// it's stupid to have to pack an array with a set of objects
				// when you can just pass in an object list
				var parr = [];
				for(var pname in pm){
					pm[pname].property = pname;
					parr.push(pm[pname]);
				}
				pm = args.propertyMap = parr;
			}
			dojo.lang.forEach(pm, function(prop){
				if(dj_undef("start", prop)){
					if(prop.property != "opacity"){
						prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property));
					}else{
						prop.start = dojo.html.getOpacity(args.nodes[0]);
					}
				}
			});
		}
	}

	var coordsAsInts = function(coords){
		var cints = [];
		dojo.lang.forEach(coords, function(c){ 
			cints.push(Math.round(c));
		});
		return cints;
	}

	var setStyle = function(n, style){
		n = dojo.byId(n);
		if(!n || !n.style){ return; }
		for(var s in style){
			try{
				if(s == "opacity"){
					dojo.html.setOpacity(n, style[s]);
				}else{
						n.style[s] = style[s];
				}
			}catch(e){ dojo.debug(e); }
		}
	}

	var propLine = function(properties){
		this._properties = properties;
		this.diffs = new Array(properties.length);
		dojo.lang.forEach(properties, function(prop, i){
			// calculate the end - start to optimize a bit
			if(dojo.lang.isFunction(prop.start)){
				prop.start = prop.start(prop, i);
			}
			if(dojo.lang.isFunction(prop.end)){
				prop.end = prop.end(prop, i);
			}
			if(dojo.lang.isArray(prop.start)){
				// don't loop through the arrays
				this.diffs[i] = null;
			}else if(prop.start instanceof dojo.gfx.color.Color){
				// save these so we don't have to call toRgb() every getValue() call
				prop.startRgb = prop.start.toRgb();
				prop.endRgb = prop.end.toRgb();
			}else{
				this.diffs[i] = prop.end - prop.start;
			}
		}, this);

		this.getValue = function(n){
			var ret = {};
			dojo.lang.forEach(this._properties, function(prop, i){
				var value = null;
				if(dojo.lang.isArray(prop.start)){
					// FIXME: what to do here?
				}else if(prop.start instanceof dojo.gfx.color.Color){
					value = (prop.units||"rgb") + "(";
					for(var j = 0 ; j < prop.startRgb.length ; j++){
						value += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j < prop.startRgb.length - 1 ? "," : "");
					}
					value += ")";
				}else{
					value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : "");
				}
				ret[dojo.html.toCamelCase(prop.property)] = value;
			}, this);
			return ret;
		}
	}
	
	var anim = new dojo.lfx.Animation({
			beforeBegin: function(){ 
				setEmUp(targs); 
				anim.curve = new propLine(targs.propertyMap);
			},
			onAnimate: function(propValues){
				dojo.lang.forEach(targs.nodes, function(node){
					setStyle(node, propValues);
				});
			}
		},
		targs.duration, 
		null,
		targs.easing
	);
	if(handlers){
		for(var x in handlers){
			if(dojo.lang.isFunction(handlers[x])){
				anim.connect(x, anim, handlers[x]);
			}
		}
	}
	
	return anim; // dojo.lfx.Animation
}

dojo.lfx.html._makeFadeable = function(nodes){
	var makeFade = function(node){
		if(dojo.render.html.ie){
			// only set the zoom if the "tickle" value would be the same as the
			// default
			if( (node.style.zoom.length == 0) &&
				(dojo.html.getStyle(node, "zoom") == "normal") ){
				// make sure the node "hasLayout"
				// NOTE: this has been tested with larger and smaller user-set text
				// sizes and works fine
				node.style.zoom = "1";
				// node.style.zoom = "normal";
			}
			// don't set the width to auto if it didn't already cascade that way.
			// We don't want to f anyones designs
			if(	(node.style.width.length == 0) &&
				(dojo.html.getStyle(node, "width") == "auto") ){
				node.style.width = "auto";
			}
		}
	}
	if(dojo.lang.isArrayLike(nodes)){
		dojo.lang.forEach(nodes, makeFade);
	}else{
		makeFade(nodes);
	}
}

dojo.lfx.html.fade = function(/*DOMNode[]*/ nodes,
							  /*Object*/values,
							  /*int?*/ duration,
							  /*Function?*/ easing,
							  /*Function?*/ callback){
	// summary:Returns an animation that will fade the "nodes" from the start to end values passed.
	// nodes: An array of DOMNodes or one DOMNode.
	// values: { start: Decimal?, end: Decimal? }
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var props = { property: "opacity" };
	if(!dj_undef("start", values)){
		props.start = values.start;
	}else{
		props.start = function(){ return dojo.html.getOpacity(nodes[0]); };
	}

	if(!dj_undef("end", values)){
		props.end = values.end;
	}else{
		dojo.raise("dojo.lfx.html.fade needs an end value");
	}

	var anim = dojo.lfx.propertyAnimation(nodes, [ props ], duration, easing);
	anim.connect("beforeBegin", function(){
		dojo.lfx.html._makeFadeable(nodes);
	});
	if(callback){
		anim.connect("onEnd", function(){ callback(nodes, anim); });
	}

	return anim; // dojo.lfx.Animation
}

dojo.lfx.html.fadeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will fade "nodes" from its current opacity to fully opaque.
	// nodes: An array of DOMNodes or one DOMNode.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	return dojo.lfx.html.fade(nodes, { end: 1 }, duration, easing, callback); // dojo.lfx.Animation
}

dojo.lfx.html.fadeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will fade "nodes" from its current opacity to fully transparent.
	// nodes: An array of DOMNodes or one DOMNode.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.	
	return dojo.lfx.html.fade(nodes, { end: 0 }, duration, easing, callback); // dojo.lfx.Animation
}

dojo.lfx.html.fadeShow = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will fade "nodes" from transparent to opaque and shows
	//			"nodes" at the end if it is hidden.
	// nodes: An array of DOMNodes or one DOMNode.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.	
	nodes=dojo.lfx.html._byId(nodes);
	dojo.lang.forEach(nodes, function(node){
		dojo.html.setOpacity(node, 0.0);
	});

	var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
	anim.connect("beforeBegin", function(){ 
		if(dojo.lang.isArrayLike(nodes)){
			dojo.lang.forEach(nodes, dojo.html.show);
		}else{
			dojo.html.show(nodes);
		}
	});

	return anim; // dojo.lfx.Animation
}

dojo.lfx.html.fadeHide = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will fade "nodes" from its current opacity to opaque and hides
	//			"nodes" at the end.
	// nodes: An array of DOMNodes or one DOMNode.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){
		if(dojo.lang.isArrayLike(nodes)){
			dojo.lang.forEach(nodes, dojo.html.hide);
		}else{
			dojo.html.hide(nodes);
		}
		if(callback){ callback(nodes, anim); }
	});
	
	return anim; // dojo.lfx.Animation
}

dojo.lfx.html.wipeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will show and wipe in "nodes".
	// nodes: An array of DOMNodes or one DOMNode.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	dojo.lang.forEach(nodes, function(node){
		var oprop = {  };	// old properties of node (before we mucked w/them)
		
		// get node height, either it's natural height or it's height specified via style or class attributes
		// (for FF, the node has to be (temporarily) rendered to measure height)
		// TODO: should this offscreen code be part of dojo.html, so that getBorderBox() works on hidden nodes?
		var origTop, origLeft, origPosition;
		with(node.style){
			origTop=top; origLeft=left; origPosition=position;
			top="-9999px"; left="-9999px"; position="absolute";
			display="";
		}
		var nodeHeight = dojo.html.getBorderBox(node).height;
		with(node.style){
			top=origTop; left=origLeft; position=origPosition;
			display="none";
		}

		var anim = dojo.lfx.propertyAnimation(node,
			{	"height": {
					start: 1, // 0 causes IE to display the whole panel
					end: function(){ return nodeHeight; } 
				}
			}, 
			duration, 
			easing);
	
		anim.connect("beforeBegin", function(){
			oprop.overflow = node.style.overflow;
			oprop.height = node.style.height;
			with(node.style){
				overflow = "hidden";
				height = "1px"; // 0 causes IE to display the whole panel
			}
			dojo.html.show(node);
		});
		
		anim.connect("onEnd", function(){ 
			with(node.style){
				overflow = oprop.overflow;
				height = oprop.height;
			}
			if(callback){ callback(node, anim); }
		});
		anims.push(anim);
	});
	
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
}

dojo.lfx.html.wipeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will wipe out and hide "nodes".
	// nodes: An array of DOMNodes or one DOMNode.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];
	
	dojo.lang.forEach(nodes, function(node){
		var oprop = {  };	// old properties of node (before we mucked w/them)
		var anim = dojo.lfx.propertyAnimation(node,
			{	"height": {
					start: function(){ return dojo.html.getContentBox(node).height; },
					end: 1 // 0 causes IE to display the whole panel
				} 
			},
			duration,
			easing,
			{
				"beforeBegin": function(){
					oprop.overflow = node.style.overflow;
					oprop.height = node.style.height;
					with(node.style){
						overflow = "hidden";
					}
					dojo.html.show(node);
				},
				
				"onEnd": function(){ 
					dojo.html.hide(node);
					with(node.style){
						overflow = oprop.overflow;
						height = oprop.height;
					}
					if(callback){ callback(node, anim); }
				}
			}
		);
		anims.push(anim);
	});

	return dojo.lfx.combine(anims); // dojo.lfx.Combine
}

dojo.lfx.html.slideTo = function(/*DOMNode*/ nodes,
								 /*Object*/ coords,
								 /*int?*/ duration,
								 /*Function?*/ easing,
								 /*Function?*/ callback){
	// summary: Returns an animation that will slide "nodes" from its current position to
	//			the position defined in "coords".
	// nodes: An array of DOMNodes or one DOMNode.
	// coords: { top: Decimal?, left: Decimal? }
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];
	var compute = dojo.html.getComputedStyle;
	
	if(dojo.lang.isArray(coords)){
		/* coords: Array
		   pId: a */
		dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5');
		coords = { top: coords[0], left: coords[1] };
	}
	dojo.lang.forEach(nodes, function(node){
		var top = null;
		var left = null;
		
		var init = (function(){
			var innerNode = node;
			return function(){
				var pos = compute(innerNode, 'position');
				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);

				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
					var ret = dojo.html.abs(innerNode, true);
					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
					top = ret.y;
					left = ret.x;
				}
			}
		})();
		init();
		
		var anim = dojo.lfx.propertyAnimation(node,
			{	"top": { start: top, end: (coords.top||0) },
				"left": { start: left, end: (coords.left||0)  }
			},
			duration,
			easing,
			{ "beforeBegin": init }
		);

		if(callback){
			anim.connect("onEnd", function(){ callback(nodes, anim); });
		}

		anims.push(anim);
	});
	
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
}

dojo.lfx.html.slideBy = function(/*DOMNode*/ nodes, /*Object*/ coords, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
	// summary: Returns an animation that will slide "nodes" from its current position
	//			to its current position plus the numbers defined in "coords".
	// nodes: An array of DOMNodes or one DOMNode.
	// coords: { top: Decimal?, left: Decimal? }
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];
	var compute = dojo.html.getComputedStyle;

	if(dojo.lang.isArray(coords)){
		/* coords: Array
		   pId: a */
		dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5');
		coords = { top: coords[0], left: coords[1] };
	}

	dojo.lang.forEach(nodes, function(node){
		var top = null;
		var left = null;
		
		var init = (function(){
			var innerNode = node;
			return function(){
				var pos = compute(innerNode, 'position');
				top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
				left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);

				if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
					var ret = dojo.html.abs(innerNode, true);
					dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
					top = ret.y;
					left = ret.x;
				}
			}
		})();
		init();
		
		var anim = dojo.lfx.propertyAnimation(node,
			{
				"top": { start: top, end: top+(coords.top||0) },
				"left": { start: left, end: left+(coords.left||0) }
			},
			duration,
			easing).connect("beforeBegin", init);

		if(callback){
			anim.connect("onEnd", function(){ callback(nodes, anim); });
		}

		anims.push(anim);
	});

	return dojo.lfx.combine(anims); // dojo.lfx.Combine
}

dojo.lfx.html.explode = function(/*DOMNode*/ start,
								 /*DOMNode*/ endNode,
								 /*int?*/ duration,
								 /*Function?*/ easing,
								 /*Function?*/ callback){
	// summary: Returns an animation that will 
	// start:
	// endNode:
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	var h = dojo.html;
	start = dojo.byId(start);
	endNode = dojo.byId(endNode);
	var startCoords = h.toCoordinateObject(start, true);
	var outline = document.createElement("div");
	h.copyStyle(outline, endNode);
	if(endNode.explodeClassName){ outline.className = endNode.explodeClassName; }
	with(outline.style){
		position = "absolute";
		display = "none";
		// border = "1px solid black";
		var backgroundStyle = h.getStyle(start, "background-color");
		backgroundColor = backgroundStyle ? backgroundStyle.toLowerCase() : "transparent";
		backgroundColor = (backgroundColor == "transparent") ? "rgb(221, 221, 221)" : backgroundColor;
	}
	dojo.body().appendChild(outline);

	with(endNode.style){
		visibility = "hidden";
		display = "block";
	}
	var endCoords = h.toCoordinateObject(endNode, true);
	with(endNode.style){
		display = "none";
		visibility = "visible";
	}

	var props = { opacity: { start: 0.5, end: 1.0 } };
	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
		props[type] = { start: startCoords[type], end: endCoords[type] }
	});
	
	var anim = new dojo.lfx.propertyAnimation(outline, 
		props,
		duration,
		easing,
		{
			"beforeBegin": function(){
				h.setDisplay(outline, "block");
			},
			"onEnd": function(){
				h.setDisplay(endNode, "block");
				outline.parentNode.removeChild(outline);
			}
		}
	);

	if(callback){
		anim.connect("onEnd", function(){ callback(endNode, anim); });
	}
	return anim; // dojo.lfx.Animation
}

dojo.lfx.html.implode = function(/*DOMNode*/ startNode,
								 /*DOMNode*/ end,
								 /*int?*/ duration,
								 /*Function?*/ easing,
								 /*Function?*/ callback){
	// summary: Returns an animation that will 
	// startNode:
	// end:
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	var h = dojo.html;
	startNode = dojo.byId(startNode);
	end = dojo.byId(end);
	var startCoords = dojo.html.toCoordinateObject(startNode, true);
	var endCoords = dojo.html.toCoordinateObject(end, true);

	var outline = document.createElement("div");
	dojo.html.copyStyle(outline, startNode);
	if (startNode.explodeClassName) { outline.className = startNode.explodeClassName; }
	dojo.html.setOpacity(outline, 0.3);
	with(outline.style){
		position = "absolute";
		display = "none";
		backgroundColor = h.getStyle(startNode, "background-color").toLowerCase();
	}
	dojo.body().appendChild(outline);

	var props = { opacity: { start: 1.0, end: 0.5 } };
	dojo.lang.forEach(["height", "width", "top", "left"], function(type){
		props[type] = { start: startCoords[type], end: endCoords[type] }
	});
	
	var anim = new dojo.lfx.propertyAnimation(outline,
		props,
		duration,
		easing,
		{
			"beforeBegin": function(){
				dojo.html.hide(startNode);
				dojo.html.show(outline);
			},
			"onEnd": function(){
				outline.parentNode.removeChild(outline);
			}
		}
	);

	if(callback){
		anim.connect("onEnd", function(){ callback(startNode, anim); });
	}
	return anim; // dojo.lfx.Animation
}

dojo.lfx.html.highlight = function(/*DOMNode[]*/ nodes,
								   /*dojo.gfx.color.Color*/ startColor,
								   /*int?*/ duration,
								   /*Function?*/ easing,
								   /*Function?*/ callback){
	// summary: Returns an animation that will set the background color
	//			of "nodes" to startColor and transition it to "nodes"
	//			original color.
	// startColor: Color to transition from.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	dojo.lang.forEach(nodes, function(node){
		var color = dojo.html.getBackgroundColor(node);
		var bg = dojo.html.getStyle(node, "background-color").toLowerCase();
		var bgImage = dojo.html.getStyle(node, "background-image");
		var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)");
		while(color.length > 3) { color.pop(); }

		var rgb = new dojo.gfx.color.Color(startColor);
		var endRgb = new dojo.gfx.color.Color(color);

		var anim = dojo.lfx.propertyAnimation(node, 
			{ "background-color": { start: rgb, end: endRgb } }, 
			duration, 
			easing,
			{
				"beforeBegin": function(){ 
					if(bgImage){
						node.style.backgroundImage = "none";
					}
					node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")";
				},
				"onEnd": function(){ 
					if(bgImage){
						node.style.backgroundImage = bgImage;
					}
					if(wasTransparent){
						node.style.backgroundColor = "transparent";
					}
					if(callback){
						callback(node, anim);
					}
				}
			}
		);

		anims.push(anim);
	});
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
}

dojo.lfx.html.unhighlight = function(/*DOMNode[]*/ nodes,
									 /*dojo.gfx.color.Color*/ endColor,
									 /*int?*/ duration,
									 /*Function?*/ easing,
									 /*Function?*/ callback){
	// summary: Returns an animation that will transition "nodes" background color
	//			from its current color to "endColor".
	// endColor: Color to transition to.
	// duration: Duration of the animation in milliseconds.
	// easing: An easing function.
	// callback: Function to run at the end of the animation.
	nodes = dojo.lfx.html._byId(nodes);
	var anims = [];

	dojo.lang.forEach(nodes, function(node){
		var color = new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
		var rgb = new dojo.gfx.color.Color(endColor);

		var bgImage = dojo.html.getStyle(node, "background-image");
		
		var anim = dojo.lfx.propertyAnimation(node, 
			{ "background-color": { start: color, end: rgb } },
			duration, 
			easing,
			{
				"beforeBegin": function(){ 
					if(bgImage){
						node.style.backgroundImage = "none";
					}
					node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")";
				},
				"onEnd": function(){ 
					if(callback){
						callback(node, anim);
					}
				}
			}
		);
		anims.push(anim);
	});
	return dojo.lfx.combine(anims); // dojo.lfx.Combine
}

dojo.lang.mixin(dojo.lfx, dojo.lfx.html);

dojo.kwCompoundRequire({
	browser: ["dojo.lfx.html"],
	dashboard: ["dojo.lfx.html"]
});
dojo.provide("dojo.lfx.*");

/*
	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
*/



dojo.provide("dojo.ns");
dojo.ns = {namespaces:{}, failed:{}, loading:{}, loaded:{}, register:function (name, module, resolver, noOverride) {
	if (!noOverride || !this.namespaces[name]) {
		this.namespaces[name] = new dojo.ns.Ns(name, module, resolver);
	}
}, allow:function (name) {
	if (this.failed[name]) {
		return false;
	}
	if ((djConfig.excludeNamespace) && (dojo.lang.inArray(djConfig.excludeNamespace, name))) {
		return false;
	}
	return ((name == this.dojo) || (!djConfig.includeNamespace) || (dojo.lang.inArray(djConfig.includeNamespace, name)));
}, get:function (name) {
	return this.namespaces[name];
}, require:function (name) {
	var ns = this.namespaces[name];
	if ((ns) && (this.loaded[name])) {
		return ns;
	}
	if (!this.allow(name)) {
		return false;
	}
	if (this.loading[name]) {
		dojo.debug("dojo.namespace.require: re-entrant request to load namespace \"" + name + "\" must fail.");
		return false;
	}
	var req = dojo.require;
	this.loading[name] = true;
	try {
		if (name == "dojo") {
			req("dojo.namespaces.dojo");
		} else {
			if (!dojo.hostenv.moduleHasPrefix(name)) {
				dojo.registerModulePath(name, "../" + name);
			}
			req([name, "manifest"].join("."), false, true);
		}
		if (!this.namespaces[name]) {
			this.failed[name] = true;
		}
	}
	finally {
		this.loading[name] = false;
	}
	return this.namespaces[name];
}};
dojo.ns.Ns = function (name, module, resolver) {
	this.name = name;
	this.module = module;
	this.resolver = resolver;
	this._loaded = [];
	this._failed = [];
};
dojo.ns.Ns.prototype.resolve = function (name, domain, omitModuleCheck) {
	if (!this.resolver || djConfig["skipAutoRequire"]) {
		return false;
	}
	var fullName = this.resolver(name, domain);
	if ((fullName) && (!this._loaded[fullName]) && (!this._failed[fullName])) {
		var req = dojo.require;
		req(fullName, false, true);
		if (dojo.hostenv.findModule(fullName, false)) {
			this._loaded[fullName] = true;
		} else {
			if (!omitModuleCheck) {
				dojo.raise("dojo.ns.Ns.resolve: module '" + fullName + "' not found after loading via namespace '" + this.name + "'");
			}
			this._failed[fullName] = true;
		}
	}
	return Boolean(this._loaded[fullName]);
};
dojo.registerNamespace = function (name, module, resolver) {
	dojo.ns.register.apply(dojo.ns, arguments);
};
dojo.registerNamespaceResolver = function (name, resolver) {
	var n = dojo.ns.namespaces[name];
	if (n) {
		n.resolver = resolver;
	}
};
dojo.registerNamespaceManifest = function (module, path, name, widgetModule, resolver) {
	dojo.registerModulePath(name, path);
	dojo.registerNamespace(name, widgetModule, resolver);
};
dojo.registerNamespace("dojo", "dojo.widget");

/*
	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
*/



dojo.provide("dojo.math");
dojo.math.degToRad = function (x) {
	return (x * Math.PI) / 180;
};
dojo.math.radToDeg = function (x) {
	return (x * 180) / Math.PI;
};
dojo.math.factorial = function (n) {
	if (n < 1) {
		return 0;
	}
	var retVal = 1;
	for (var i = 1; i <= n; i++) {
		retVal *= i;
	}
	return retVal;
};
dojo.math.permutations = function (n, k) {
	if (n == 0 || k == 0) {
		return 1;
	}
	return (dojo.math.factorial(n) / dojo.math.factorial(n - k));
};
dojo.math.combinations = function (n, r) {
	if (n == 0 || r == 0) {
		return 1;
	}
	return (dojo.math.factorial(n) / (dojo.math.factorial(n - r) * dojo.math.factorial(r)));
};
dojo.math.bernstein = function (t, n, i) {
	return (dojo.math.combinations(n, i) * Math.pow(t, i) * Math.pow(1 - t, n - i));
};
dojo.math.gaussianRandom = function () {
	var k = 2;
	do {
		var i = 2 * Math.random() - 1;
		var j = 2 * Math.random() - 1;
		k = i * i + j * j;
	} while (k >= 1);
	k = Math.sqrt((-2 * Math.log(k)) / k);
	return i * k;
};
dojo.math.mean = function () {
	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
	var mean = 0;
	for (var i = 0; i < array.length; i++) {
		mean += array[i];
	}
	return mean / array.length;
};
dojo.math.round = function (number, places) {
	if (!places) {
		var shift = 1;
	} else {
		var shift = Math.pow(10, places);
	}
	return Math.round(number * shift) / shift;
};
dojo.math.sd = dojo.math.standardDeviation = function () {
	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
	return Math.sqrt(dojo.math.variance(array));
};
dojo.math.variance = function () {
	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
	var mean = 0, squares = 0;
	for (var i = 0; i < array.length; i++) {
		mean += array[i];
		squares += Math.pow(array[i], 2);
	}
	return (squares / array.length) - Math.pow(mean / array.length, 2);
};
dojo.math.range = function (a, b, step) {
	if (arguments.length < 2) {
		b = a;
		a = 0;
	}
	if (arguments.length < 3) {
		step = 1;
	}
	var range = [];
	if (step > 0) {
		for (var i = a; i < b; i += step) {
			range.push(i);
		}
	} else {
		if (step < 0) {
			for (var i = a; i > b; i += step) {
				range.push(i);
			}
		} else {
			throw new Error("dojo.math.range: step must be non-zero");
		}
	}
	return range;
};

/*
	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
*/



dojo.provide("dojo.math");
dojo.math.degToRad = function (x) {
	return (x * Math.PI) / 180;
};
dojo.math.radToDeg = function (x) {
	return (x * 180) / Math.PI;
};
dojo.math.factorial = function (n) {
	if (n < 1) {
		return 0;
	}
	var retVal = 1;
	for (var i = 1; i <= n; i++) {
		retVal *= i;
	}
	return retVal;
};
dojo.math.permutations = function (n, k) {
	if (n == 0 || k == 0) {
		return 1;
	}
	return (dojo.math.factorial(n) / dojo.math.factorial(n - k));
};
dojo.math.combinations = function (n, r) {
	if (n == 0 || r == 0) {
		return 1;
	}
	return (dojo.math.factorial(n) / (dojo.math.factorial(n - r) * dojo.math.factorial(r)));
};
dojo.math.bernstein = function (t, n, i) {
	return (dojo.math.combinations(n, i) * Math.pow(t, i) * Math.pow(1 - t, n - i));
};
dojo.math.gaussianRandom = function () {
	var k = 2;
	do {
		var i = 2 * Math.random() - 1;
		var j = 2 * Math.random() - 1;
		k = i * i + j * j;
	} while (k >= 1);
	k = Math.sqrt((-2 * Math.log(k)) / k);
	return i * k;
};
dojo.math.mean = function () {
	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
	var mean = 0;
	for (var i = 0; i < array.length; i++) {
		mean += array[i];
	}
	return mean / array.length;
};
dojo.math.round = function (number, places) {
	if (!places) {
		var shift = 1;
	} else {
		var shift = Math.pow(10, places);
	}
	return Math.round(number * shift) / shift;
};
dojo.math.sd = dojo.math.standardDeviation = function () {
	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
	return Math.sqrt(dojo.math.variance(array));
};
dojo.math.variance = function () {
	var array = dojo.lang.isArray(arguments[0]) ? arguments[0] : arguments;
	var mean = 0, squares = 0;
	for (var i = 0; i < array.length; i++) {
		mean += array[i];
		squares += Math.pow(array[i], 2);
	}
	return (squares / array.length) - Math.pow(mean / array.length, 2);
};
dojo.math.range = function (a, b, step) {
	if (arguments.length < 2) {
		b = a;
		a = 0;
	}
	if (arguments.length < 3) {
		step = 1;
	}
	var range = [];
	if (step > 0) {
		for (var i = a; i < b; i += step) {
			range.push(i);
		}
	} else {
		if (step < 0) {
			for (var i = a; i > b; i += step) {
				range.push(i);
			}
		} else {
			throw new Error("dojo.math.range: step must be non-zero");
		}
	}
	return range;
};

/*
	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
*/



dojo.provide("dojo.string.common");
dojo.string.trim = function (str, wh) {
	if (!str.replace) {
		return str;
	}
	if (!str.length) {
		return str;
	}
	var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
	return str.replace(re, "");
};
dojo.string.trimStart = function (str) {
	return dojo.string.trim(str, 1);
};
dojo.string.trimEnd = function (str) {
	return dojo.string.trim(str, -1);
};
dojo.string.repeat = function (str, count, separator) {
	var out = "";
	for (var i = 0; i < count; i++) {
		out += str;
		if (separator && i < count - 1) {
			out += separator;
		}
	}
	return out;
};
dojo.string.pad = function (str, len, c, dir) {
	var out = String(str);
	if (!c) {
		c = "0";
	}
	if (!dir) {
		dir = 1;
	}
	while (out.length < len) {
		if (dir > 0) {
			out = c + out;
		} else {
			out += c;
		}
	}
	return out;
};
dojo.string.padLeft = function (str, len, c) {
	return dojo.string.pad(str, len, c, 1);
};
dojo.string.padRight = function (str, len, c) {
	return dojo.string.pad(str, len, c, -1);
};

/*
	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
*/



dojo.provide("dojo.experimental");
dojo.experimental = function (moduleName, extra) {
	var message = "EXPERIMENTAL: " + moduleName;
	message += " -- Not yet ready for use.  APIs subject to change without notice.";
	if (extra) {
		message += " " + extra;
	}
	dojo.debug(message);
};


/*
	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
*/



dojo.provide("dojo.html.util");
dojo.html.getElementWindow = function (element) {
	return dojo.html.getDocumentWindow(element.ownerDocument);
};
dojo.html.getDocumentWindow = function (doc) {
	if (dojo.render.html.safari && !doc._parentWindow) {
		var fix = function (win) {
			win.document._parentWindow = win;
			for (var i = 0; i < win.frames.length; i++) {
				fix(win.frames[i]);
			}
		};
		fix(window.top);
	}
	if (dojo.render.html.ie && window !== document.parentWindow && !doc._parentWindow) {
		doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
		var win = doc._parentWindow;
		doc._parentWindow = null;
		return win;
	}
	return doc._parentWindow || doc.parentWindow || doc.defaultView;
};
dojo.html.gravity = function (node, e) {
	node = dojo.byId(node);
	var mouse = dojo.html.getCursorPosition(e);
	with (dojo.html) {
		var absolute = getAbsolutePosition(node, true);
		var bb = getBorderBox(node);
		var nodecenterx = absolute.x + (bb.width / 2);
		var nodecentery = absolute.y + (bb.height / 2);
	}
	with (dojo.html.gravity) {
		return ((mouse.x < nodecenterx ? WEST : EAST) | (mouse.y < nodecentery ? NORTH : SOUTH));
	}
};
dojo.html.gravity.NORTH = 1;
dojo.html.gravity.SOUTH = 1 << 1;
dojo.html.gravity.EAST = 1 << 2;
dojo.html.gravity.WEST = 1 << 3;
dojo.html.overElement = function (element, e) {
	element = dojo.byId(element);
	var mouse = dojo.html.getCursorPosition(e);
	var bb = dojo.html.getBorderBox(element);
	var absolute = dojo.html.getAbsolutePosition(element, true, dojo.html.boxSizing.BORDER_BOX);
	var top = absolute.y;
	var bottom = top + bb.height;
	var left = absolute.x;
	var right = left + bb.width;
	return (mouse.x >= left && mouse.x <= right && mouse.y >= top && mouse.y <= bottom);
};
dojo.html.renderedTextContent = function (node) {
	node = dojo.byId(node);
	var result = "";
	if (node == null) {
		return result;
	}
	for (var i = 0; i < node.childNodes.length; i++) {
		switch (node.childNodes[i].nodeType) {
		  case 1:
		  case 5:
			var display = "unknown";
			try {
				display = dojo.html.getStyle(node.childNodes[i], "display");
			}
			catch (E) {
			}
			switch (display) {
			  case "block":
			  case "list-item":
			  case "run-in":
			  case "table":
			  case "table-row-group":
			  case "table-header-group":
			  case "table-footer-group":
			  case "table-row":
			  case "table-column-group":
			  case "table-column":
			  case "table-cell":
			  case "table-caption":
				result += "\n";
				result += dojo.html.renderedTextContent(node.childNodes[i]);
				result += "\n";
				break;
			  case "none":
				break;
			  default:
				if (node.childNodes[i].tagName && node.childNodes[i].tagName.toLowerCase() == "br") {
					result += "\n";
				} else {
					result += dojo.html.renderedTextContent(node.childNodes[i]);
				}
				break;
			}
			break;
		  case 3:
		  case 2:
		  case 4:
			var text = node.childNodes[i].nodeValue;
			var textTransform = "unknown";
			try {
				textTransform = dojo.html.getStyle(node, "text-transform");
			}
			catch (E) {
			}
			switch (textTransform) {
			  case "capitalize":
				var words = text.split(" ");
				for (var i = 0; i < words.length; i++) {
					words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
				}
				text = words.join(" ");
				break;
			  case "uppercase":
				text = text.toUpperCase();
				break;
			  case "lowercase":
				text = text.toLowerCase();
				break;
			  default:
				break;
			}
			switch (textTransform) {
			  case "nowrap":
				break;
			  case "pre-wrap":
				break;
			  case "pre-line":
				break;
			  case "pre":
				break;
			  default:
				text = text.replace(/\s+/, " ");
				if (/\s$/.test(result)) {
					text.replace(/^\s/, "");
				}
				break;
			}
			result += text;
			break;
		  default:
			break;
		}
	}
	return result;
};
dojo.html.createNodesFromText = function (txt, trim) {
	if (trim) {
		txt = txt.replace(/^\s+|\s+$/g, "");
	}
	var tn = dojo.doc().createElement("div");
	tn.style.visibility = "hidden";
	dojo.body().appendChild(tn);
	var tableType = "none";
	if ((/^<t[dh][\s\r\n>]/i).test(txt.replace(/^\s+/))) {
		txt = "<table><tbody><tr>" + txt + "</tr></tbody></table>";
		tableType = "cell";
	} else {
		if ((/^<tr[\s\r\n>]/i).test(txt.replace(/^\s+/))) {
			txt = "<table><tbody>" + txt + "</tbody></table>";
			tableType = "row";
		} else {
			if ((/^<(thead|tbody|tfoot)[\s\r\n>]/i).test(txt.replace(/^\s+/))) {
				txt = "<table>" + txt + "</table>";
				tableType = "section";
			}
		}
	}
	tn.innerHTML = txt;
	if (tn["normalize"]) {
		tn.normalize();
	}
	var parent = null;
	switch (tableType) {
	  case "cell":
		parent = tn.getElementsByTagName("tr")[0];
		break;
	  case "row":
		parent = tn.getElementsByTagName("tbody")[0];
		break;
	  case "section":
		parent = tn.getElementsByTagName("table")[0];
		break;
	  default:
		parent = tn;
		break;
	}
	var nodes = [];
	for (var x = 0; x < parent.childNodes.length; x++) {
		nodes.push(parent.childNodes[x].cloneNode(true));
	}
	tn.style.display = "none";
	dojo.html.destroyNode(tn);
	return nodes;
};
dojo.html.placeOnScreen = function (node, desiredX, desiredY, padding, hasScroll, corners, tryOnly) {
	if (desiredX instanceof Array || typeof desiredX == "array") {
		tryOnly = corners;
		corners = hasScroll;
		hasScroll = padding;
		padding = desiredY;
		desiredY = desiredX[1];
		desiredX = desiredX[0];
	}
	if (corners instanceof String || typeof corners == "string") {
		corners = corners.split(",");
	}
	if (!isNaN(padding)) {
		padding = [Number(padding), Number(padding)];
	} else {
		if (!(padding instanceof Array || typeof padding == "array")) {
			padding = [0, 0];
		}
	}
	var scroll = dojo.html.getScroll().offset;
	var view = dojo.html.getViewport();
	node = dojo.byId(node);
	var oldDisplay = node.style.display;
	node.style.display = "";
	var bb = dojo.html.getBorderBox(node);
	var w = bb.width;
	var h = bb.height;
	node.style.display = oldDisplay;
	if (!(corners instanceof Array || typeof corners == "array")) {
		corners = ["TL"];
	}
	var bestx, besty, bestDistance = Infinity, bestCorner;
	for (var cidex = 0; cidex < corners.length; ++cidex) {
		var corner = corners[cidex];
		var match = true;
		var tryX = desiredX - (corner.charAt(1) == "L" ? 0 : w) + padding[0] * (corner.charAt(1) == "L" ? 1 : -1);
		var tryY = desiredY - (corner.charAt(0) == "T" ? 0 : h) + padding[1] * (corner.charAt(0) == "T" ? 1 : -1);
		if (hasScroll) {
			tryX -= scroll.x;
			tryY -= scroll.y;
		}
		if (tryX < 0) {
			tryX = 0;
			match = false;
		}
		if (tryY < 0) {
			tryY = 0;
			match = false;
		}
		var x = tryX + w;
		if (x > view.width) {
			x = view.width - w;
			match = false;
		} else {
			x = tryX;
		}
		x = Math.max(padding[0], x) + scroll.x;
		var y = tryY + h;
		if (y > view.height) {
			y = view.height - h;
			match = false;
		} else {
			y = tryY;
		}
		y = Math.max(padding[1], y) + scroll.y;
		if (match) {
			bestx = x;
			besty = y;
			bestDistance = 0;
			bestCorner = corner;
			break;
		} else {
			var dist = Math.pow(x - tryX - scroll.x, 2) + Math.pow(y - tryY - scroll.y, 2);
			if (bestDistance > dist) {
				bestDistance = dist;
				bestx = x;
				besty = y;
				bestCorner = corner;
			}
		}
	}
	if (!tryOnly) {
		node.style.left = bestx + "px";
		node.style.top = besty + "px";
	}
	return {left:bestx, top:besty, x:bestx, y:besty, dist:bestDistance, corner:bestCorner};
};
dojo.html.placeOnScreenPoint = function (node, desiredX, desiredY, padding, hasScroll) {
	dojo.deprecated("dojo.html.placeOnScreenPoint", "use dojo.html.placeOnScreen() instead", "0.5");
	return dojo.html.placeOnScreen(node, desiredX, desiredY, padding, hasScroll, ["TL", "TR", "BL", "BR"]);
};
dojo.html.placeOnScreenAroundElement = function (node, aroundNode, padding, aroundType, aroundCorners, tryOnly) {
	var best, bestDistance = Infinity;
	aroundNode = dojo.byId(aroundNode);
	var oldDisplay = aroundNode.style.display;
	aroundNode.style.display = "";
	var mb = dojo.html.getElementBox(aroundNode, aroundType);
	var aroundNodeW = mb.width;
	var aroundNodeH = mb.height;
	var aroundNodePos = dojo.html.getAbsolutePosition(aroundNode, true, aroundType);
	aroundNode.style.display = oldDisplay;
	for (var nodeCorner in aroundCorners) {
		var pos, desiredX, desiredY;
		var corners = aroundCorners[nodeCorner];
		desiredX = aroundNodePos.x + (nodeCorner.charAt(1) == "L" ? 0 : aroundNodeW);
		desiredY = aroundNodePos.y + (nodeCorner.charAt(0) == "T" ? 0 : aroundNodeH);
		pos = dojo.html.placeOnScreen(node, desiredX, desiredY, padding, true, corners, true);
		if (pos.dist == 0) {
			best = pos;
			break;
		} else {
			if (bestDistance > pos.dist) {
				bestDistance = pos.dist;
				best = pos;
			}
		}
	}
	if (!tryOnly) {
		node.style.left = best.left + "px";
		node.style.top = best.top + "px";
	}
	return best;
};
dojo.html.scrollIntoView = function (node) {
	if (!node) {
		return;
	}
	if (dojo.render.html.ie) {
		if (dojo.html.getBorderBox(node.parentNode).height <= node.parentNode.scrollHeight) {
			node.scrollIntoView(false);
		}
	} else {
		if (dojo.render.html.mozilla) {
			node.scrollIntoView(false);
		} else {
			var parent = node.parentNode;
			var parentBottom = parent.scrollTop + dojo.html.getBorderBox(parent).height;
			var nodeBottom = node.offsetTop + dojo.html.getMarginBox(node).height;
			if (parentBottom < nodeBottom) {
				parent.scrollTop += (nodeBottom - parentBottom);
			} else {
				if (parent.scrollTop > node.offsetTop) {
					parent.scrollTop -= (parent.scrollTop - node.offsetTop);
				}
			}
		}
	}
};


/*
	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
*/



dojo.provide("dojo.html.iframe");
dojo.html.iframeContentWindow = function (iframe_el) {
	var win = dojo.html.getDocumentWindow(dojo.html.iframeContentDocument(iframe_el)) || dojo.html.iframeContentDocument(iframe_el).__parent__ || (iframe_el.name && document.frames[iframe_el.name]) || null;
	return win;
};
dojo.html.iframeContentDocument = function (iframe_el) {
	var doc = iframe_el.contentDocument || ((iframe_el.contentWindow) && (iframe_el.contentWindow.document)) || ((iframe_el.name) && (document.frames[iframe_el.name]) && (document.frames[iframe_el.name].document)) || null;
	return doc;
};
dojo.html.BackgroundIframe = function (node) {
	if (dojo.render.html.ie55 || dojo.render.html.ie60) {
		var html = "<iframe src='javascript:false'" + " style='position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;" + "z-index: -1; filter:Alpha(Opacity=\"0\");' " + ">";
		this.iframe = dojo.doc().createElement(html);
		this.iframe.tabIndex = -1;
		if (node) {
			node.appendChild(this.iframe);
			this.domNode = node;
		} else {
			dojo.body().appendChild(this.iframe);
			this.iframe.style.display = "none";
		}
	}
};
dojo.lang.extend(dojo.html.BackgroundIframe, {iframe:null, onResized:function () {
	if (this.iframe && this.domNode && this.domNode.parentNode) {
		var outer = dojo.html.getMarginBox(this.domNode);
		if (outer.width == 0 || outer.height == 0) {
			dojo.lang.setTimeout(this, this.onResized, 100);
			return;
		}
		this.iframe.style.width = outer.width + "px";
		this.iframe.style.height = outer.height + "px";
	}
}, size:function (node) {
	if (!this.iframe) {
		return;
	}
	var coords = dojo.html.toCoordinateObject(node, true, dojo.html.boxSizing.BORDER_BOX);
	with (this.iframe.style) {
		width = coords.width + "px";
		height = coords.height + "px";
		left = coords.left + "px";
		top = coords.top + "px";
	}
}, setZIndex:function (node) {
	if (!this.iframe) {
		return;
	}
	if (dojo.dom.isNode(node)) {
		this.iframe.style.zIndex = dojo.html.getStyle(node, "z-index") - 1;
	} else {
		if (!isNaN(node)) {
			this.iframe.style.zIndex = node;
		}
	}
}, show:function () {
	if (this.iframe) {
		this.iframe.style.display = "block";
	}
}, hide:function () {
	if (this.iframe) {
		this.iframe.style.display = "none";
	}
}, remove:function () {
	if (this.iframe) {
		dojo.html.removeNode(this.iframe, true);
		delete this.iframe;
		this.iframe = null;
	}
}});

/*
	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
*/



dojo.provide("dojo.lang.declare");
dojo.lang.declare = function (className, superclass, init, props) {
	if ((dojo.lang.isFunction(props)) || ((!props) && (!dojo.lang.isFunction(init)))) {
		var temp = props;
		props = init;
		init = temp;
	}
	var mixins = [];
	if (dojo.lang.isArray(superclass)) {
		mixins = superclass;
		superclass = mixins.shift();
	}
	if (!init) {
		init = dojo.evalObjPath(className, false);
		if ((init) && (!dojo.lang.isFunction(init))) {
			init = null;
		}
	}
	var ctor = dojo.lang.declare._makeConstructor();
	var scp = (superclass ? superclass.prototype : null);
	if (scp) {
		scp.prototyping = true;
		ctor.prototype = new superclass();
		scp.prototyping = false;
	}
	ctor.superclass = scp;
	ctor.mixins = mixins;
	for (var i = 0, l = mixins.length; i < l; i++) {
		dojo.lang.extend(ctor, mixins[i].prototype);
	}
	ctor.prototype.initializer = null;
	ctor.prototype.declaredClass = className;
	if (dojo.lang.isArray(props)) {
		dojo.lang.extend.apply(dojo.lang, [ctor].concat(props));
	} else {
		dojo.lang.extend(ctor, (props) || {});
	}
	dojo.lang.extend(ctor, dojo.lang.declare._common);
	ctor.prototype.constructor = ctor;
	ctor.prototype.initializer = (ctor.prototype.initializer) || (init) || (function () {
	});
	var created = dojo.parseObjPath(className, null, true);
	created.obj[created.prop] = ctor;
	return ctor;
};
dojo.lang.declare._makeConstructor = function () {
	return function () {
		var self = this._getPropContext();
		var s = self.constructor.superclass;
		if ((s) && (s.constructor)) {
			if (s.constructor == arguments.callee) {
				this._inherited("constructor", arguments);
			} else {
				this._contextMethod(s, "constructor", arguments);
			}
		}
		var ms = (self.constructor.mixins) || ([]);
		for (var i = 0, m; (m = ms[i]); i++) {
			(((m.prototype) && (m.prototype.initializer)) || (m)).apply(this, arguments);
		}
		if ((!this.prototyping) && (self.initializer)) {
			self.initializer.apply(this, arguments);
		}
	};
};
dojo.lang.declare._common = {_getPropContext:function () {
	return (this.___proto || this);
}, _contextMethod:function (ptype, method, args) {
	var result, stack = this.___proto;
	this.___proto = ptype;
	try {
		result = ptype[method].apply(this, (args || []));
	}
	catch (e) {
		throw e;
	}
	finally {
		this.___proto = stack;
	}
	return result;
}, _inherited:function (prop, args) {
	var p = this._getPropContext();
	do {
		if ((!p.constructor) || (!p.constructor.superclass)) {
			return;
		}
		p = p.constructor.superclass;
	} while (!(prop in p));
	return (dojo.lang.isFunction(p[prop]) ? this._contextMethod(p, prop, args) : p[prop]);
}, inherited:function (prop, args) {
	dojo.deprecated("'inherited' method is dangerous, do not up-call! 'inherited' is slated for removal in 0.5; name your super class (or use superclass property) instead.", "0.5");
	this._inherited(prop, args);
}};
dojo.declare = dojo.lang.declare;

/*
	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
*/



dojo.provide("dojo.widget.Manager");
dojo.widget.manager = new function () {
	this.widgets = [];
	this.widgetIds = [];
	this.topWidgets = {};
	var widgetTypeCtr = {};
	var renderPrefixCache = [];
	this.getUniqueId = function (widgetType) {
		var widgetId;
		do {
			widgetId = widgetType + "_" + (widgetTypeCtr[widgetType] != undefined ? ++widgetTypeCtr[widgetType] : widgetTypeCtr[widgetType] = 0);
		} while (this.getWidgetById(widgetId));
		return widgetId;
	};
	this.add = function (widget) {
		this.widgets.push(widget);
		if (!widget.extraArgs["id"]) {
			widget.extraArgs["id"] = widget.extraArgs["ID"];
		}
		if (widget.widgetId == "") {
			if (widget["id"]) {
				widget.widgetId = widget["id"];
			} else {
				if (widget.extraArgs["id"]) {
					widget.widgetId = widget.extraArgs["id"];
				} else {
					widget.widgetId = this.getUniqueId(widget.ns + "_" + widget.widgetType);
				}
			}
		}
		if (this.widgetIds[widget.widgetId]) {
			dojo.debug("widget ID collision on ID: " + widget.widgetId);
		}
		this.widgetIds[widget.widgetId] = widget;
	};
	this.destroyAll = function () {
		for (var x = this.widgets.length - 1; x >= 0; x--) {
			try {
				this.widgets[x].destroy(true);
				delete this.widgets[x];
			}
			catch (e) {
			}
		}
	};
	this.remove = function (widgetIndex) {
		if (dojo.lang.isNumber(widgetIndex)) {
			var tw = this.widgets[widgetIndex].widgetId;
			delete this.topWidgets[tw];
			delete this.widgetIds[tw];
			this.widgets.splice(widgetIndex, 1);
		} else {
			this.removeById(widgetIndex);
		}
	};
	this.removeById = function (id) {
		if (!dojo.lang.isString(id)) {
			id = id["widgetId"];
			if (!id) {
				dojo.debug("invalid widget or id passed to removeById");
				return;
			}
		}
		for (var i = 0; i < this.widgets.length; i++) {
			if (this.widgets[i].widgetId == id) {
				this.remove(i);
				break;
			}
		}
	};
	this.getWidgetById = function (id) {
		if (dojo.lang.isString(id)) {
			return this.widgetIds[id];
		}
		return id;
	};
	this.getWidgetsByType = function (type) {
		var lt = type.toLowerCase();
		var getType = (type.indexOf(":") < 0 ? function (x) {
			return x.widgetType.toLowerCase();
		} : function (x) {
			return x.getNamespacedType();
		});
		var ret = [];
		dojo.lang.forEach(this.widgets, function (x) {
			if (getType(x) == lt) {
				ret.push(x);
			}
		});
		return ret;
	};
	this.getWidgetsByFilter = function (unaryFunc, onlyOne) {
		var ret = [];
		dojo.lang.every(this.widgets, function (x) {
			if (unaryFunc(x)) {
				ret.push(x);
				if (onlyOne) {
					return false;
				}
			}
			return true;
		});
		return (onlyOne ? ret[0] : ret);
	};
	this.getAllWidgets = function () {
		return this.widgets.concat();
	};
	this.getWidgetByNode = function (node) {
		var w = this.getAllWidgets();
		node = dojo.byId(node);
		for (var i = 0; i < w.length; i++) {
			if (w[i].domNode == node) {
				return w[i];
			}
		}
		return null;
	};
	this.byId = this.getWidgetById;
	this.byType = this.getWidgetsByType;
	this.byFilter = this.getWidgetsByFilter;
	this.byNode = this.getWidgetByNode;
	var knownWidgetImplementations = {};
	var widgetPackages = ["dojo.widget"];
	for (var i = 0; i < widgetPackages.length; i++) {
		widgetPackages[widgetPackages[i]] = true;
	}
	this.registerWidgetPackage = function (pname) {
		if (!widgetPackages[pname]) {
			widgetPackages[pname] = true;
			widgetPackages.push(pname);
		}
	};
	this.getWidgetPackageList = function () {
		return dojo.lang.map(widgetPackages, function (elt) {
			return (elt !== true ? elt : undefined);
		});
	};
	this.getImplementation = function (widgetName, ctorObject, mixins, ns) {
		var impl = this.getImplementationName(widgetName, ns);
		if (impl) {
			var ret = ctorObject ? new impl(ctorObject) : new impl();
			return ret;
		}
	};
	function buildPrefixCache() {
		for (var renderer in dojo.render) {
			if (dojo.render[renderer]["capable"] === true) {
				var prefixes = dojo.render[renderer].prefixes;
				for (var i = 0; i < prefixes.length; i++) {
					renderPrefixCache.push(prefixes[i].toLowerCase());
				}
			}
		}
	}
	var findImplementationInModule = function (lowerCaseWidgetName, module) {
		if (!module) {
			return null;
		}
		for (var i = 0, l = renderPrefixCache.length, widgetModule; i <= l; i++) {
			widgetModule = (i < l ? module[renderPrefixCache[i]] : module);
			if (!widgetModule) {
				continue;
			}
			for (var name in widgetModule) {
				if (name.toLowerCase() == lowerCaseWidgetName) {
					return widgetModule[name];
				}
			}
		}
		return null;
	};
	var findImplementation = function (lowerCaseWidgetName, moduleName) {
		var module = dojo.evalObjPath(moduleName, false);
		return (module ? findImplementationInModule(lowerCaseWidgetName, module) : null);
	};
	this.getImplementationName = function (widgetName, ns) {
		var lowerCaseWidgetName = widgetName.toLowerCase();
		ns = ns || "dojo";
		var imps = knownWidgetImplementations[ns] || (knownWidgetImplementations[ns] = {});
		var impl = imps[lowerCaseWidgetName];
		if (impl) {
			return impl;
		}
		if (!renderPrefixCache.length) {
			buildPrefixCache();
		}
		var nsObj = dojo.ns.get(ns);
		if (!nsObj) {
			dojo.ns.register(ns, ns + ".widget");
			nsObj = dojo.ns.get(ns);
		}
		if (nsObj) {
			nsObj.resolve(widgetName);
		}
		impl = findImplementation(lowerCaseWidgetName, nsObj.module);
		if (impl) {
			return (imps[lowerCaseWidgetName] = impl);
		}
		nsObj = dojo.ns.require(ns);
		if ((nsObj) && (nsObj.resolver)) {
			nsObj.resolve(widgetName);
			impl = findImplementation(lowerCaseWidgetName, nsObj.module);
			if (impl) {
				return (imps[lowerCaseWidgetName] = impl);
			}
		}
		dojo.deprecated("dojo.widget.Manager.getImplementationName", "Could not locate widget implementation for \"" + widgetName + "\" in \"" + nsObj.module + "\" registered to namespace \"" + nsObj.name + "\". " + "Developers must specify correct namespaces for all non-Dojo widgets", "0.5");
		for (var i = 0; i < widgetPackages.length; i++) {
			impl = findImplementation(lowerCaseWidgetName, widgetPackages[i]);
			if (impl) {
				return (imps[lowerCaseWidgetName] = impl);
			}
		}
		throw new Error("Could not locate widget implementation for \"" + widgetName + "\" in \"" + nsObj.module + "\" registered to namespace \"" + nsObj.name + "\"");
	};
	this.resizing = false;
	this.onWindowResized = function () {
		if (this.resizing) {
			return;
		}
		try {
			this.resizing = true;
			for (var id in this.topWidgets) {
				var child = this.topWidgets[id];
				if (child.checkSize) {
					child.checkSize();
				}
			}
		}
		catch (e) {
		}
		finally {
			this.resizing = false;
		}
	};
	if (typeof window != "undefined") {
		dojo.addOnLoad(this, "onWindowResized");
		dojo.event.connect(window, "onresize", this, "onWindowResized");
	}
};
(function () {
	var dw = dojo.widget;
	var dwm = dw.manager;
	var h = dojo.lang.curry(dojo.lang, "hitch", dwm);
	var g = function (oldName, newName) {
		dw[(newName || oldName)] = h(oldName);
	};
	g("add", "addWidget");
	g("destroyAll", "destroyAllWidgets");
	g("remove", "removeWidget");
	g("removeById", "removeWidgetById");
	g("getWidgetById");
	g("getWidgetById", "byId");
	g("getWidgetsByType");
	g("getWidgetsByFilter");
	g("getWidgetsByType", "byType");
	g("getWidgetsByFilter", "byFilter");
	g("getWidgetByNode", "byNode");
	dw.all = function (n) {
		var widgets = dwm.getAllWidgets.apply(dwm, arguments);
		if (arguments.length > 0) {
			return widgets[n];
		}
		return widgets;
	};
	g("registerWidgetPackage");
	g("getImplementation", "getWidgetImplementation");
	g("getImplementationName", "getWidgetImplementationName");
	dw.widgets = dwm.widgets;
	dw.widgetIds = dwm.widgetIds;
	dw.root = dwm.root;
})();



/*
	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
*/



dojo.provide("dojo.widget.Parse");
dojo.widget.Parse = function (fragment) {
	this.propertySetsList = [];
	this.fragment = fragment;
	this.createComponents = function (frag, parentComp) {
		var comps = [];
		var built = false;
		try {
			if (frag && frag.tagName && (frag != frag.nodeRef)) {
				var djTags = dojo.widget.tags;
				var tna = String(frag.tagName).split(";");
				for (var x = 0; x < tna.length; x++) {
					var ltn = tna[x].replace(/^\s+|\s+$/g, "").toLowerCase();
					frag.tagName = ltn;
					var ret;
					if (djTags[ltn]) {
						built = true;
						ret = djTags[ltn](frag, this, parentComp, frag.index);
						comps.push(ret);
					} else {
						if (ltn.indexOf(":") == -1) {
							ltn = "dojo:" + ltn;
						}
						ret = dojo.widget.buildWidgetFromParseTree(ltn, frag, this, parentComp, frag.index);
						if (ret) {
							built = true;
							comps.push(ret);
						}
					}
				}
			}
		}
		catch (e) {
			dojo.debug("dojo.widget.Parse: error:", e);
		}
		if (!built) {
			comps = comps.concat(this.createSubComponents(frag, parentComp));
		}
		return comps;
	};
	this.createSubComponents = function (fragment, parentComp) {
		var frag, comps = [];
		for (var item in fragment) {
			frag = fragment[item];
			if (frag && typeof frag == "object" && (frag != fragment.nodeRef) && (frag != fragment.tagName) && (!dojo.dom.isNode(frag))) {
				comps = comps.concat(this.createComponents(frag, parentComp));
			}
		}
		return comps;
	};
	this.parsePropertySets = function (fragment) {
		return [];
	};
	this.parseProperties = function (fragment) {
		var properties = {};
		for (var item in fragment) {
			if ((fragment[item] == fragment.tagName) || (fragment[item] == fragment.nodeRef)) {
			} else {
				var frag = fragment[item];
				if (frag.tagName && dojo.widget.tags[frag.tagName.toLowerCase()]) {
				} else {
					if (frag[0] && frag[0].value != "" && frag[0].value != null) {
						try {
							if (item.toLowerCase() == "dataprovider") {
								var _this = this;
								this.getDataProvider(_this, frag[0].value);
								properties.dataProvider = this.dataProvider;
							}
							properties[item] = frag[0].value;
							var nestedProperties = this.parseProperties(frag);
							for (var property in nestedProperties) {
								properties[property] = nestedProperties[property];
							}
						}
						catch (e) {
							dojo.debug(e);
						}
					}
				}
				switch (item.toLowerCase()) {
				  case "checked":
				  case "disabled":
					if (typeof properties[item] != "boolean") {
						properties[item] = true;
					}
					break;
				}
			}
		}
		return properties;
	};
	this.getDataProvider = function (objRef, dataUrl) {
		dojo.io.bind({url:dataUrl, load:function (type, evaldObj) {
			if (type == "load") {
				objRef.dataProvider = evaldObj;
			}
		}, mimetype:"text/javascript", sync:true});
	};
	this.getPropertySetById = function (propertySetId) {
		for (var x = 0; x < this.propertySetsList.length; x++) {
			if (propertySetId == this.propertySetsList[x]["id"][0].value) {
				return this.propertySetsList[x];
			}
		}
		return "";
	};
	this.getPropertySetsByType = function (componentType) {
		var propertySets = [];
		for (var x = 0; x < this.propertySetsList.length; x++) {
			var cpl = this.propertySetsList[x];
			var cpcc = cpl.componentClass || cpl.componentType || null;
			var propertySetId = this.propertySetsList[x]["id"][0].value;
			if (cpcc && (propertySetId == cpcc[0].value)) {
				propertySets.push(cpl);
			}
		}
		return propertySets;
	};
	this.getPropertySets = function (fragment) {
		var ppl = "dojo:propertyproviderlist";
		var propertySets = [];
		var tagname = fragment.tagName;
		if (fragment[ppl]) {
			var propertyProviderIds = fragment[ppl].value.split(" ");
			for (var propertySetId in propertyProviderIds) {
				if ((propertySetId.indexOf("..") == -1) && (propertySetId.indexOf("://") == -1)) {
					var propertySet = this.getPropertySetById(propertySetId);
					if (propertySet != "") {
						propertySets.push(propertySet);
					}
				} else {
				}
			}
		}
		return this.getPropertySetsByType(tagname).concat(propertySets);
	};
	this.createComponentFromScript = function (nodeRef, componentName, properties, ns) {
		properties.fastMixIn = true;
		var ltn = (ns || "dojo") + ":" + componentName.toLowerCase();
		if (dojo.widget.tags[ltn]) {
			return [dojo.widget.tags[ltn](properties, this, null, null, properties)];
		}
		return [dojo.widget.buildWidgetFromParseTree(ltn, properties, this, null, null, properties)];
	};
};
dojo.widget._parser_collection = {"dojo":new dojo.widget.Parse()};
dojo.widget.getParser = function (name) {
	if (!name) {
		name = "dojo";
	}
	if (!this._parser_collection[name]) {
		this._parser_collection[name] = new dojo.widget.Parse();
	}
	return this._parser_collection[name];
};
dojo.widget.createWidget = function (name, props, refNode, position) {
	var isNode = false;
	var isNameStr = (typeof name == "string");
	if (isNameStr) {
		var pos = name.indexOf(":");
		var ns = (pos > -1) ? name.substring(0, pos) : "dojo";
		if (pos > -1) {
			name = name.substring(pos + 1);
		}
		var lowerCaseName = name.toLowerCase();
		var namespacedName = ns + ":" + lowerCaseName;
		isNode = (dojo.byId(name) && !dojo.widget.tags[namespacedName]);
	}
	if ((arguments.length == 1) && (isNode || !isNameStr)) {
		var xp = new dojo.xml.Parse();
		var tn = isNode ? dojo.byId(name) : name;
		return dojo.widget.getParser().createComponents(xp.parseElement(tn, null, true))[0];
	}
	function fromScript(placeKeeperNode, name, props, ns) {
		props[namespacedName] = {dojotype:[{value:lowerCaseName}], nodeRef:placeKeeperNode, fastMixIn:true};
		props.ns = ns;
		return dojo.widget.getParser().createComponentFromScript(placeKeeperNode, name, props, ns);
	}
	props = props || {};
	var notRef = false;
	var tn = null;
	var h = dojo.render.html.capable;
	if (h) {
		tn = document.createElement("span");
	}
	if (!refNode) {
		notRef = true;
		refNode = tn;
		if (h) {
			dojo.body().appendChild(refNode);
		}
	} else {
		if (position) {
			dojo.dom.insertAtPosition(tn, refNode, position);
		} else {
			tn = refNode;
		}
	}
	var widgetArray = fromScript(tn, name.toLowerCase(), props, ns);
	if ((!widgetArray) || (!widgetArray[0]) || (typeof widgetArray[0].widgetType == "undefined")) {
		throw new Error("createWidget: Creation of \"" + name + "\" widget failed.");
	}
	try {
		if (notRef && widgetArray[0].domNode.parentNode) {
			widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode);
		}
	}
	catch (e) {
		dojo.debug(e);
	}
	return widgetArray[0];
};





/*
	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
*/



dojo.provide("dojo.a11y");
dojo.a11y = {imgPath:dojo.uri.moduleUri("dojo.widget", "templates/images"), doAccessibleCheck:true, accessible:null, checkAccessible:function () {
	if (this.accessible === null) {
		this.accessible = false;
		if (this.doAccessibleCheck == true) {
			this.accessible = this.testAccessible();
		}
	}
	return this.accessible;
}, testAccessible:function () {
	this.accessible = false;
	if (dojo.render.html.ie || dojo.render.html.mozilla) {
		var div = document.createElement("div");
		//div.style.backgroundImage = "url(\"" + this.imgPath + "/tab_close.gif\")";
		dojo.body().appendChild(div);
		var bkImg = null;
		if (window.getComputedStyle) {
			var cStyle = getComputedStyle(div, "");
			bkImg = cStyle.getPropertyValue("background-image");
		} else {
			bkImg = div.currentStyle.backgroundImage;
		}
		var bUseImgElem = false;
		if (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)")) {
			this.accessible = true;
		}
		dojo.body().removeChild(div);
	}
	return this.accessible;
}, setCheckAccessible:function (bTest) {
	this.doAccessibleCheck = bTest;
}, setAccessibleMode:function () {
	if (this.accessible === null) {
		if (this.checkAccessible()) {
			dojo.render.html.prefixes.unshift("a11y");
		}
	}
	return this.accessible;
}};



/*
	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
*/



dojo.provide("dojo.lfx.toggle");
dojo.lfx.toggle.plain = {show:function (node, duration, easing, callback) {
	dojo.html.show(node);
	if (dojo.lang.isFunction(callback)) {
		callback();
	}
}, hide:function (node, duration, easing, callback) {
	dojo.html.hide(node);
	if (dojo.lang.isFunction(callback)) {
		callback();
	}
}};
dojo.lfx.toggle.fade = {show:function (node, duration, easing, callback) {
	dojo.lfx.fadeShow(node, duration, easing, callback).play();
}, hide:function (node, duration, easing, callback) {
	dojo.lfx.fadeHide(node, duration, easing, callback).play();
}};
dojo.lfx.toggle.wipe = {show:function (node, duration, easing, callback) {
	dojo.lfx.wipeIn(node, duration, easing, callback).play();
}, hide:function (node, duration, easing, callback) {
	dojo.lfx.wipeOut(node, duration, easing, callback).play();
}};
dojo.lfx.toggle.explode = {show:function (node, duration, easing, callback, explodeSrc) {
	dojo.lfx.explode(explodeSrc || {x:0, y:0, width:0, height:0}, node, duration, easing, callback).play();
}, hide:function (node, duration, easing, callback, explodeSrc) {
	dojo.lfx.implode(node, explodeSrc || {x:0, y:0, width:0, height:0}, duration, easing, callback).play();
}};

/*
	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
*/



dojo.provide("dojo.html.selection");
dojo.html.selectionType = {NONE:0, TEXT:1, CONTROL:2};
dojo.html.clearSelection = function () {
	var _window = dojo.global();
	var _document = dojo.doc();
	try {
		if (_window["getSelection"]) {
			if (dojo.render.html.safari) {
				_window.getSelection().collapse();
			} else {
				_window.getSelection().removeAllRanges();
			}
		} else {
			if (_document.selection) {
				if (_document.selection.empty) {
					_document.selection.empty();
				} else {
					if (_document.selection.clear) {
						_document.selection.clear();
					}
				}
			}
		}
		return true;
	}
	catch (e) {
		dojo.debug(e);
		return false;
	}
};
dojo.html.disableSelection = function (element) {
	element = dojo.byId(element) || dojo.body();
	var h = dojo.render.html;
	if (h.mozilla) {
		element.style.MozUserSelect = "none";
	} else {
		if (h.safari) {
			element.style.KhtmlUserSelect = "none";
		} else {
			if (h.ie) {
				element.unselectable = "on";
			} else {
				return false;
			}
		}
	}
	return true;
};
dojo.html.enableSelection = function (element) {
	element = dojo.byId(element) || dojo.body();
	var h = dojo.render.html;
	if (h.mozilla) {
		element.style.MozUserSelect = "";
	} else {
		if (h.safari) {
			element.style.KhtmlUserSelect = "";
		} else {
			if (h.ie) {
				element.unselectable = "off";
			} else {
				return false;
			}
		}
	}
	return true;
};
dojo.html.selectElement = function (element) {
	dojo.deprecated("dojo.html.selectElement", "replaced by dojo.html.selection.selectElementChildren", 0.5);
};
dojo.html.selectInputText = function (element) {
	var _window = dojo.global();
	var _document = dojo.doc();
	element = dojo.byId(element);
	if (_document["selection"] && dojo.body()["createTextRange"]) {
		var range = element.createTextRange();
		range.moveStart("character", 0);
		range.moveEnd("character", element.value.length);
		range.select();
	} else {
		if (_window["getSelection"]) {
			var selection = _window.getSelection();
			element.setSelectionRange(0, element.value.length);
		}
	}
	element.focus();
};
dojo.html.isSelectionCollapsed = function () {
	dojo.deprecated("dojo.html.isSelectionCollapsed", "replaced by dojo.html.selection.isCollapsed", 0.5);
	return dojo.html.selection.isCollapsed();
};
dojo.lang.mixin(dojo.html.selection, {getType:function () {
	if (dojo.doc()["selection"]) {
		return dojo.html.selectionType[dojo.doc().selection.type.toUpperCase()];
	} else {
		var stype = dojo.html.selectionType.TEXT;
		var oSel;
		try {
			oSel = dojo.global().getSelection();
		}
		catch (e) {
		}
		if (oSel && oSel.rangeCount == 1) {
			var oRange = oSel.getRangeAt(0);
			if (oRange.startContainer == oRange.endContainer && (oRange.endOffset - oRange.startOffset) == 1 && oRange.startContainer.nodeType != dojo.dom.TEXT_NODE) {
				stype = dojo.html.selectionType.CONTROL;
			}
		}
		return stype;
	}
}, isCollapsed:function () {
	var _window = dojo.global();
	var _document = dojo.doc();
	if (_document["selection"]) {
		return _document.selection.createRange().text == "";
	} else {
		if (_window["getSelection"]) {
			var selection = _window.getSelection();
			if (dojo.lang.isString(selection)) {
				return selection == "";
			} else {
				return selection.isCollapsed || selection.toString() == "";
			}
		}
	}
}, getSelectedElement:function () {
	if (dojo.html.selection.getType() == dojo.html.selectionType.CONTROL) {
		if (dojo.doc()["selection"]) {
			var range = dojo.doc().selection.createRange();
			if (range && range.item) {
				return dojo.doc().selection.createRange().item(0);
			}
		} else {
			var selection = dojo.global().getSelection();
			return selection.anchorNode.childNodes[selection.anchorOffset];
		}
	}
}, getParentElement:function () {
	if (dojo.html.selection.getType() == dojo.html.selectionType.CONTROL) {
		var p = dojo.html.selection.getSelectedElement();
		if (p) {
			return p.parentNode;
		}
	} else {
		if (dojo.doc()["selection"]) {
			return dojo.doc().selection.createRange().parentElement();
		} else {
			var selection = dojo.global().getSelection();
			if (selection) {
				var node = selection.anchorNode;
				while (node && node.nodeType != dojo.dom.ELEMENT_NODE) {
					node = node.parentNode;
				}
				return node;
			}
		}
	}
}, getSelectedText:function () {
	if (dojo.doc()["selection"]) {
		if (dojo.html.selection.getType() == dojo.html.selectionType.CONTROL) {
			return null;
		}
		return dojo.doc().selection.createRange().text;
	} else {
		var selection = dojo.global().getSelection();
		if (selection) {
			return selection.toString();
		}
	}
}, getSelectedHtml:function () {
	if (dojo.doc()["selection"]) {
		if (dojo.html.selection.getType() == dojo.html.selectionType.CONTROL) {
			return null;
		}
		return dojo.doc().selection.createRange().htmlText;
	} else {
		var selection = dojo.global().getSelection();
		if (selection && selection.rangeCount) {
			var frag = selection.getRangeAt(0).cloneContents();
			var div = document.createElement("div");
			div.appendChild(frag);
			return div.innerHTML;
		}
		return null;
	}
}, hasAncestorElement:function (tagName) {
	return (dojo.html.selection.getAncestorElement.apply(this, arguments) != null);
}, getAncestorElement:function (tagName) {
	var node = dojo.html.selection.getSelectedElement() || dojo.html.selection.getParentElement();
	while (node) {
		if (dojo.html.selection.isTag(node, arguments).length > 0) {
			return node;
		}
		node = node.parentNode;
	}
	return null;
}, isTag:function (node, tags) {
	if (node && node.tagName) {
		for (var i = 0; i < tags.length; i++) {
			if (node.tagName.toLowerCase() == String(tags[i]).toLowerCase()) {
				return String(tags[i]).toLowerCase();
			}
		}
	}
	return "";
}, selectElement:function (element) {
	var _window = dojo.global();
	var _document = dojo.doc();
	element = dojo.byId(element);
	if (_document.selection && dojo.body().createTextRange) {
		try {
			var range = dojo.body().createControlRange();
			range.addElement(element);
			range.select();
		}
		catch (e) {
			dojo.html.selection.selectElementChildren(element);
		}
	} else {
		if (_window["getSelection"]) {
			var selection = _window.getSelection();
			if (selection["removeAllRanges"]) {
				var range = _document.createRange();
				range.selectNode(element);
				selection.removeAllRanges();
				selection.addRange(range);
			}
		}
	}
}, selectElementChildren:function (element) {
	var _window = dojo.global();
	var _document = dojo.doc();
	element = dojo.byId(element);
	if (_document.selection && dojo.body().createTextRange) {
		var range = dojo.body().createTextRange();
		range.moveToElementText(element);
		range.select();
	} else {
		if (_window["getSelection"]) {
			var selection = _window.getSelection();
			if (selection["setBaseAndExtent"]) {
				selection.setBaseAndExtent(element, 0, element, element.innerText.length - 1);
			} else {
				if (selection["selectAllChildren"]) {
					selection.selectAllChildren(element);
				}
			}
		}
	}
}, getBookmark:function () {
	var bookmark;
	var _document = dojo.doc();
	if (_document["selection"]) {
		var range = _document.selection.createRange();
		bookmark = range.getBookmark();
	} else {
		var selection;
		try {
			selection = dojo.global().getSelection();
		}
		catch (e) {
		}
		if (selection) {
			var range = selection.getRangeAt(0);
			bookmark = range.cloneRange();
		} else {
			dojo.debug("No idea how to store the current selection for this browser!");
		}
	}
	return bookmark;
}, moveToBookmark:function (bookmark) {
	var _document = dojo.doc();
	if (_document["selection"]) {
		var range = _document.selection.createRange();
		range.moveToBookmark(bookmark);
		range.select();
	} else {
		var selection;
		try {
			selection = dojo.global().getSelection();
		}
		catch (e) {
		}
		if (selection && selection["removeAllRanges"]) {
			selection.removeAllRanges();
			selection.addRange(bookmark);
		} else {
			dojo.debug("No idea how to restore selection for this browser!");
		}
	}
}, collapse:function (beginning) {
	if (dojo.global()["getSelection"]) {
		var selection = dojo.global().getSelection();
		if (selection.removeAllRanges) {
			if (beginning) {
				selection.collapseToStart();
			} else {
				selection.collapseToEnd();
			}
		} else {
			dojo.global().getSelection().collapse(beginning);
		}
	} else {
		if (dojo.doc().selection) {
			var range = dojo.doc().selection.createRange();
			range.collapse(beginning);
			range.select();
		}
	}
}, remove:function () {
	if (dojo.doc().selection) {
		var selection = dojo.doc().selection;
		if (selection.type.toUpperCase() != "NONE") {
			selection.clear();
		}
		return selection;
	} else {
		var selection = dojo.global().getSelection();
		for (var i = 0; i < selection.rangeCount; i++) {
			selection.getRangeAt(i).deleteContents();
		}
		return selection;
	}
}});

/*
	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
*/



dojo.provide("dojo.gfx.color.hsl");
dojo.lang.extend(dojo.gfx.color.Color, {toHsl:function () {
	return dojo.gfx.color.rgb2hsl(this.toRgb());
}});
dojo.gfx.color.rgb2hsl = function (r, g, b) {
	if (dojo.lang.isArray(r)) {
		b = r[2] || 0;
		g = r[1] || 0;
		r = r[0] || 0;
	}
	r /= 255;
	g /= 255;
	b /= 255;
	var h = null;
	var s = null;
	var l = null;
	var min = Math.min(r, g, b);
	var max = Math.max(r, g, b);
	var delta = max - min;
	l = (min + max) / 2;
	s = 0;
	if ((l > 0) && (l < 1)) {
		s = delta / ((l < 0.5) ? (2 * l) : (2 - 2 * l));
	}
	h = 0;
	if (delta > 0) {
		if ((max == r) && (max != g)) {
			h += (g - b) / delta;
		}
		if ((max == g) && (max != b)) {
			h += (2 + (b - r) / delta);
		}
		if ((max == b) && (max != r)) {
			h += (4 + (r - g) / delta);
		}
		h *= 60;
	}
	h = (h == 0) ? 360 : Math.ceil((h / 360) * 255);
	s = Math.ceil(s * 255);
	l = Math.ceil(l * 255);
	return [h, s, l];
};
dojo.gfx.color.hsl2rgb = function (h, s, l) {
	if (dojo.lang.isArray(h)) {
		l = h[2] || 0;
		s = h[1] || 0;
		h = h[0] || 0;
	}
	h = (h / 255) * 360;
	if (h == 360) {
		h = 0;
	}
	s = s / 255;
	l = l / 255;
	while (h < 0) {
		h += 360;
	}
	while (h > 360) {
		h -= 360;
	}
	var r, g, b;
	if (h < 120) {
		r = (120 - h) / 60;
		g = h / 60;
		b = 0;
	} else {
		if (h < 240) {
			r = 0;
			g = (240 - h) / 60;
			b = (h - 120) / 60;
		} else {
			r = (h - 240) / 60;
			g = 0;
			b = (360 - h) / 60;
		}
	}
	r = Math.min(r, 1);
	g = Math.min(g, 1);
	b = Math.min(b, 1);
	r = 2 * s * r + (1 - s);
	g = 2 * s * g + (1 - s);
	b = 2 * s * b + (1 - s);
	if (l < 0.5) {
		r = l * r;
		g = l * g;
		b = l * b;
	} else {
		r = (1 - l) * r + 2 * l - 1;
		g = (1 - l) * g + 2 * l - 1;
		b = (1 - l) * b + 2 * l - 1;
	}
	r = Math.ceil(r * 255);
	g = Math.ceil(g * 255);
	b = Math.ceil(b * 255);
	return [r, g, b];
};
dojo.gfx.color.hsl2hex = function (h, s, l) {
	var rgb = dojo.gfx.color.hsl2rgb(h, s, l);
	return dojo.gfx.color.rgb2hex(rgb[0], rgb[1], rgb[2]);
};
dojo.gfx.color.hex2hsl = function (hex) {
	var rgb = dojo.gfx.color.hex2rgb(hex);
	return dojo.gfx.color.rgb2hsl(rgb[0], rgb[1], rgb[2]);
};

/*
	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
*/



dojo.provide("dojo.charting.Axis");
dojo.charting.Axis = function (label, scale, labels) {
	var id = "dojo-charting-axis-" + dojo.charting.Axis.count++;
	this.getId = function () {
		return id;
	};
	this.setId = function (key) {
		id = key;
	};
	this.scale = scale || "linear";
	this.label = label || "";
	this.showLabel = true;
	this.showLabels = true;
	this.showLines = false;
	this.showTicks = false;
	this.range = {upper:100, lower:0};
	this.origin = "min";
	this._origin = null;
	this.labels = labels || [];
	this._labels = [];
	this.nodes = {main:null, axis:null, label:null, labels:null, lines:null, ticks:null};
	this._rerender = false;
};
dojo.charting.Axis.count = 0;
dojo.extend(dojo.charting.Axis, {getCoord:function (val, plotArea, plot) {
	val = parseFloat(val, 10);
	var area = plotArea.getArea();
	if (plot.axisX == this) {
		var offset = 0 - this.range.lower;
		var min = this.range.lower + offset;
		var max = this.range.upper + offset;
		val += offset;
		return (val * ((area.right - area.left) / max)) + area.left;
	} else {
		var max = this.range.upper;
		var min = this.range.lower;
		var offset = 0;
		if (min < 0) {
			offset += Math.abs(min);
		}
		max += offset;
		min += offset;
		val += offset;
		var pmin = area.bottom;
		var pmax = area.top;
		return (((pmin - pmax) / (max - min)) * (max - val)) + pmax;
	}
}, initializeOrigin:function (drawAgainst, plane) {
	if (this._origin == null) {
		this._origin = this.origin;
	}
	if (isNaN(this._origin)) {
		if (this._origin.toLowerCase() == "max") {
			this.origin = drawAgainst.range[(plane == "y") ? "upper" : "lower"];
		} else {
			if (this._origin.toLowerCase() == "min") {
				this.origin = drawAgainst.range[(plane == "y") ? "lower" : "upper"];
			} else {
				this.origin = 0;
			}
		}
	}
}, initializeLabels:function () {
	this._labels = [];
	if (this.labels.length == 0) {
		this.showLabels = false;
		this.showLines = false;
		this.showTicks = false;
	} else {
		if (this.labels[0].label && this.labels[0].value != null) {
			for (var i = 0; i < this.labels.length; i++) {
				this._labels.push(this.labels[i]);
			}
		} else {
			if (!isNaN(this.labels[0])) {
				for (var i = 0; i < this.labels.length; i++) {
					this._labels.push({label:this.labels[i], value:this.labels[i]});
				}
			} else {
				var a = [];
				for (var i = 0; i < this.labels.length; i++) {
					a.push(this.labels[i]);
				}
				var s = a.shift();
				this._labels.push({label:s, value:this.range.lower});
				if (a.length > 0) {
					var s = a.pop();
					this._labels.push({label:s, value:this.range.upper});
				}
				if (a.length > 0) {
					var range = this.range.upper - this.range.lower;
					var step = range / (this.labels.length - 1);
					for (var i = 1; i <= a.length; i++) {
						this._labels.push({label:a[i - 1], value:this.range.lower + (step * i)});
					}
				}
			}
		}
	}
}, initialize:function (plotArea, plot, drawAgainst, plane) {
	this.destroy();
	this.initializeOrigin(drawAgainst, plane);
	this.initializeLabels();
	var node = this.render(plotArea, plot, drawAgainst, plane);
	return node;
}, destroy:function () {
	for (var p in this.nodes) {
		while (this.nodes[p] && this.nodes[p].childNodes.length > 0) {
			this.nodes[p].removeChild(this.nodes[p].childNodes[0]);
		}
		if (this.nodes[p] && this.nodes[p].parentNode) {
			this.nodes[p].parentNode.removeChild(this.nodes[p]);
		}
		this.nodes[p] = null;
	}
}});

/*
	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
*/



dojo.provide("dojo.charting.vml.Axis");
if (dojo.render.vml.capable) {
	dojo.extend(dojo.charting.Axis, {renderLines:function (plotArea, plot, plane) {
		if (this.nodes.lines) {
			while (this.nodes.lines.childNodes.length > 0) {
				this.nodes.lines.removeChild(this.nodes.lines.childNodes[0]);
			}
			if (this.nodes.lines.parentNode) {
				this.nodes.lines.parentNode.removeChild(this.nodes.lines);
				this.nodes.lines = null;
			}
		}
		var area = plotArea.getArea();
		var g = this.nodes.lines = document.createElement("div");
		g.setAttribute("id", this.getId() + "-lines");
		for (var i = 0; i < this._labels.length; i++) {
			if (this._labels[i].value == this.origin) {
				continue;
			}
			var v = this.getCoord(this._labels[i].value, plotArea, plot);
			var l = document.createElement("v:line");
			var str = document.createElement("v:stroke");
			str.dashstyle = "dot";
			l.appendChild(str);
			l.setAttribute("strokecolor", "#666");
			l.setAttribute("strokeweight", "1px");
			var s = l.style;
			s.position = "absolute";
			s.top = "0px";
			s.left = "0px";
			s.antialias = "false";
			if (plane == "x") {
				l.setAttribute("from", v + "px," + area.top + "px");
				l.setAttribute("to", v + "px," + area.bottom + "px");
			} else {
				if (plane == "y") {
					l.setAttribute("from", area.left + "px," + v + "px");
					l.setAttribute("to", area.right + "px," + v + "px");
				}
			}
			g.appendChild(l);
		}
		return g;
	}, renderTicks:function (plotArea, plot, plane, coord) {
		if (this.nodes.ticks) {
			while (this.nodes.ticks.childNodes.length > 0) {
				this.nodes.ticks.removeChild(this.nodes.ticks.childNodes[0]);
			}
			if (this.nodes.ticks.parentNode) {
				this.nodes.ticks.parentNode.removeChild(this.nodes.ticks);
				this.nodes.ticks = null;
			}
		}
		var g = this.nodes.ticks = document.createElement("div");
		g.setAttribute("id", this.getId() + "-ticks");
		for (var i = 0; i < this._labels.length; i++) {
			var v = this.getCoord(this._labels[i].value, plotArea, plot);
			var l = document.createElement("v:line");
			l.setAttribute("strokecolor", "#000");
			l.setAttribute("strokeweight", "1px");
			var s = l.style;
			s.position = "absolute";
			s.top = "0px";
			s.left = "0px";
			s.antialias = "false";
			if (plane == "x") {
				l.setAttribute("from", v + "px," + coord + "px");
				l.setAttribute("to", v + "px," + (coord + 3) + "px");
			} else {
				if (plane == "y") {
					l.setAttribute("from", (coord - 2) + "px," + v + "px");
					l.setAttribute("to", (coord + 2) + "px," + v + "px");
				}
			}
			g.appendChild(l);
		}
		return g;
	}, renderLabels:function (plotArea, plot, plane, coord, textSize, anchor) {
		function createLabel(label, x, y, textSize, anchor) {
			var text = document.createElement("div");
			var s = text.style;
			text.innerHTML = label;
			s.fontSize = textSize + "px";
			s.fontFamily = "sans-serif";
			s.position = "absolute";
			s.top = y + "px";
			if (anchor == "center") {
				s.left = x + "px";
				s.textAlign = "center";
			} else {
				if (anchor == "left") {
					s.left = x + "px";
					s.textAlign = "left";
				} else {
					if (anchor == "right") {
						s.right = x + "px";
						s.textAlign = "right";
					}
				}
			}
			return text;
		}
		if (this.nodes.labels) {
			while (this.nodes.labels.childNodes.length > 0) {
				this.nodes.labels.removeChild(this.nodes.labels.childNodes[0]);
			}
			if (this.nodes.labels.parentNode) {
				this.nodes.labels.parentNode.removeChild(this.nodes.labels);
				this.nodes.labels = null;
			}
		}
		var g = this.nodes.labels = document.createElement("div");
		g.setAttribute("id", this.getId() + "-labels");
		for (var i = 0; i < this._labels.length; i++) {
			var v = this.getCoord(this._labels[i].value, plotArea, plot);
			if (plane == "x") {
				var node = createLabel(this._labels[i].label, v, coord, textSize, anchor);
				document.body.appendChild(node);
				node.style.left = v - (node.offsetWidth / 2) + "px";
				g.appendChild(node);
			} else {
				if (plane == "y") {
					var node = createLabel(this._labels[i].label, coord, v, textSize, anchor);
					document.body.appendChild(node);
					node.style.top = v - (node.offsetHeight / 2) + "px";
					g.appendChild(node);
				}
			}
		}
		return g;
	}, render:function (plotArea, plot, drawAgainst, plane) {
		if (!this._rerender && this.nodes.main) {
			return this.nodes.main;
		}
		this._rerender = false;
		var area = plotArea.getArea();
		var stroke = 1;
		var style = "stroke:#000;stroke-width:" + stroke + "px;";
		var textSize = 10;
		var coord = drawAgainst.getCoord(this.origin, plotArea, plot);
		var g = this.nodes.main = document.createElement("div");
		g.setAttribute("id", this.getId());
		var line = this.nodes.axis = document.createElement("v:line");
		line.setAttribute("strokecolor", "#000");
		line.setAttribute("strokeweight", stroke + "px");
		var s = line.style;
		s.position = "absolute";
		s.top = "0px";
		s.left = "0px";
		s.antialias = "false";
		if (plane == "x") {
			line.setAttribute("from", area.left + "px," + coord + "px");
			line.setAttribute("to", area.right + "px," + coord + "px");
			var y = coord + Math.floor(textSize / 2);
			if (this.showLines) {
				g.appendChild(this.renderLines(plotArea, plot, plane, y));
			}
			if (this.showTicks) {
				g.appendChild(this.renderTicks(plotArea, plot, plane, coord));
			}
			if (this.showLabels) {
				g.appendChild(this.renderLabels(plotArea, plot, plane, y, textSize, "center"));
			}
			if (this.showLabel && this.label) {
				var x = plotArea.size.width / 2;
				var y = coord + Math.round(textSize * 1.5);
				var text = document.createElement("div");
				var s = text.style;
				text.innerHTML = this.label;
				s.fontSize = (textSize + 2) + "px";
				s.fontFamily = "sans-serif";
				s.fontWeight = "bold";
				s.position = "absolute";
				s.top = y + "px";
				s.left = x + "px";
				s.textAlign = "center";
				document.body.appendChild(text);
				text.style.left = x - (text.offsetWidth / 2) + "px";
				g.appendChild(text);
			}
		} else {
			line.setAttribute("from", coord + "px," + area.top + "px");
			line.setAttribute("to", coord + "px," + area.bottom + "px");
			var isMax = this.origin == drawAgainst.range.upper;
			var x = coord + 4;
			var anchor = "left";
			if (!isMax) {
				x = area.right - coord + textSize + 4;
				anchor = "right";
				if (coord == area.left) {
					x += (textSize * 2) - (textSize / 2);
				}
			}
			if (this.showLines) {
				g.appendChild(this.renderLines(plotArea, plot, plane, x));
			}
			if (this.showTicks) {
				g.appendChild(this.renderTicks(plotArea, plot, plane, coord));
			}
			if (this.showLabels) {
				g.appendChild(this.renderLabels(plotArea, plot, plane, x, textSize, anchor));
			}
			if (this.showLabel && this.label) {
				x += (textSize * 2) - 2;
				var y = plotArea.size.height / 2;
				var text = document.createElement("div");
				var s = text.style;
				text.innerHTML = this.label;
				s.fontSize = (textSize + 2) + "px";
				s.fontFamily = "sans-serif";
				s.fontWeight = "bold";
				s.position = "absolute";
				s.height = plotArea.size.height + "px";
				s.writingMode = "tb-rl";
				s.textAlign = "center";
				s[anchor] = x + "px";
				document.body.appendChild(text);
				s.top = y - (text.offsetHeight / 2) + "px";
				g.appendChild(text);
			}
		}
		g.appendChild(line);
		return g;
	}});
}

/*
	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
*/



dojo.provide("dojo.charting.Plotters");

/*
	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
*/



dojo.provide("dojo.charting.vml.Plotters");
if (dojo.render.vml.capable) {
	dojo.mixin(dojo.charting.Plotters, {_group:function (plotarea) {
		var group = document.createElement("div");
		group.style.position = "absolute";
		group.style.top = "0px";
		group.style.left = "0px";
		group.style.width = plotarea.size.width + "px";
		group.style.height = plotarea.size.height + "px";
		return group;
	}, Bar:function (plotarea, plot, kwArgs, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = plot.series.length;
		var data = [];
		for (var i = 0; i < n; i++) {
			var tmp = plot.series[i].data.evaluate(kwArgs);
			data.push(tmp);
		}
		var space = 8;
		var nPoints = data[0].length;
		if (nPoints == 0) {
			return group;
		}
		var width = ((area.right - area.left) - (space * (nPoints - 1))) / nPoints;
		var barWidth = Math.round(width / n);
		var yOrigin = plot.axisY.getCoord(plot.axisX.origin, plotarea, plot);
		for (var i = 0; i < nPoints; i++) {
			var xStart = area.left + (width * i) + (space * i);
			for (var j = 0; j < n; j++) {
				var value = data[j][i].y;
				var yA = yOrigin;
				var x = xStart + (barWidth * j);
				var y = plot.axisY.getCoord(value, plotarea, plot);
				var h = Math.abs(yA - y);
				if (value < plot.axisX.origin) {
					yA = y;
					y = yOrigin;
				}
				var bar = document.createElement("v:rect");
				bar.style.position = "absolute";
				bar.style.top = y + 1 + "px";
				bar.style.left = x + "px";
				bar.style.width = barWidth + "px";
				bar.style.height = h + "px";
				bar.setAttribute("fillColor", data[j][i].series.color);
				bar.setAttribute("stroked", "false");
				bar.style.antialias = "false";
				var fill = document.createElement("v:fill");
				fill.setAttribute("opacity", "0.6");
				bar.appendChild(fill);
				if (applyTo) {
					applyTo(bar, data[j][i].src);
				}
				group.appendChild(bar);
			}
		}
		return group;
	}, HorizontalBar:function (plotarea, plot, kwArgs, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = plot.series.length;
		var data = [];
		for (var i = 0; i < n; i++) {
			var tmp = plot.series[i].data.evaluate(kwArgs);
			data.push(tmp);
		}
		var space = 6;
		var nPoints = data[0].length;
		if (nPoints == 0) {
			return group;
		}
		var h = ((area.bottom - area.top) - (space * (nPoints - 1))) / nPoints;
		var barH = h / n;
		var xOrigin = plot.axisX.getCoord(0, plotarea, plot);
		for (var i = 0; i < nPoints; i++) {
			var yStart = area.top + (h * i) + (space * i);
			for (var j = 0; j < n; j++) {
				var value = data[j][i].y;
				var y = yStart + (barH * j);
				var xA = xOrigin;
				var x = plot.axisX.getCoord(value, plotarea, plot);
				var w = Math.abs(x - xA);
				if (value > 0) {
					x = xOrigin;
				}
				var bar = document.createElement("v:rect");
				bar.style.position = "absolute";
				bar.style.top = y + 1 + "px";
				bar.style.left = xA + "px";
				bar.style.width = w + "px";
				bar.style.height = barH + "px";
				bar.setAttribute("fillColor", data[j][i].series.color);
				bar.setAttribute("stroked", "false");
				bar.style.antialias = "false";
				var fill = document.createElement("v:fill");
				fill.setAttribute("opacity", "0.6");
				bar.appendChild(fill);
				if (applyTo) {
					applyTo(bar, data[j][i].src);
				}
				group.appendChild(bar);
			}
		}
		var space = 4;
		var n = plot.series.length;
		var h = ((area.bottom - area.top) - (space * (n - 1))) / n;
		var xOrigin = plot.axisX.getCoord(0, plotarea, plot);
		for (var i = 0; i < n; i++) {
			var series = plot.series[i];
			var data = series.data.evaluate(kwArgs);
			var y = area.top + (h * i) + (space * i);
			var value = data[data.length - 1].y;
			var xA = xOrigin;
			var x = plot.axisX.getCoord(value, plotarea, plot);
			var w = Math.abs(xA - x);
			if (value > 0) {
				xA = x;
				x = xOrigin;
			}
		}
		return group;
	}, Gantt:function (plotarea, plot, kwArgs, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = plot.series.length;
		var data = [];
		for (var i = 0; i < n; i++) {
			var tmp = plot.series[i].data.evaluate(kwArgs);
			data.push(tmp);
		}
		var space = 2;
		var nPoints = data[0].length;
		if (nPoints == 0) {
			return group;
		}
		var h = ((area.bottom - area.top) - (space * (nPoints - 1))) / nPoints;
		var barH = h / n;
		for (var i = 0; i < nPoints; i++) {
			var yStart = area.top + (h * i) + (space * i);
			for (var j = 0; j < n; j++) {
				var high = data[j][i].high;
				var low = data[j][i].low;
				if (low > high) {
					var t = high;
					high = low;
					low = t;
				}
				var x = plot.axisX.getCoord(low, plotarea, plot);
				var w = plot.axisX.getCoord(high, plotarea, plot) - x;
				var y = yStart + (barH * j);
				var bar = document.createElement("v:rect");
				bar.style.position = "absolute";
				bar.style.top = y + 1 + "px";
				bar.style.left = x + "px";
				bar.style.width = w + "px";
				bar.style.height = barH + "px";
				bar.setAttribute("fillColor", data[j][i].series.color);
				bar.setAttribute("stroked", "false");
				bar.style.antialias = "false";
				var fill = document.createElement("v:fill");
				fill.setAttribute("opacity", "0.6");
				bar.appendChild(fill);
				if (applyTo) {
					applyTo(bar, data[j][i].src);
				}
				group.appendChild(bar);
			}
		}
		return group;
	}, StackedArea:function (plotarea, plot, kwArgs, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = plot.series.length;
		var data = [];
		var totals = [];
		for (var i = 0; i < n; i++) {
			var tmp = plot.series[i].data.evaluate(kwArgs);
			for (var j = 0; j < tmp.length; j++) {
				if (i == 0) {
					totals.push(tmp[j].y);
				} else {
					totals[j] += tmp[j].y;
				}
				tmp[j].y = totals[j];
			}
			data.push(tmp);
		}
		for (var i = n - 1; i >= 0; i--) {
			var path = document.createElement("v:shape");
			path.setAttribute("strokeweight", "1px");
			path.setAttribute("strokecolor", data[i][0].series.color);
			path.setAttribute("fillcolor", data[i][0].series.color);
			path.setAttribute("coordsize", (area.right - area.left) + "," + (area.bottom - area.top));
			path.style.position = "absolute";
			path.style.top = "0px";
			path.style.left = "0px";
			path.style.width = area.right - area.left + "px";
			path.style.height = area.bottom - area.top + "px";
			var stroke = document.createElement("v:stroke");
			stroke.setAttribute("opacity", "0.8");
			path.appendChild(stroke);
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.4");
			path.appendChild(fill);
			var cmd = [];
			var r = 3;
			for (var j = 0; j < data[i].length; j++) {
				var values = data[i];
				var x = Math.round(plot.axisX.getCoord(values[j].x, plotarea, plot));
				var y = Math.round(plot.axisY.getCoord(values[j].y, plotarea, plot));
				if (j == 0) {
					cmd.push("m");
					cmd.push(x + "," + y);
				} else {
					cmd.push("l");
					cmd.push(x + "," + y);
				}
				var c = document.createElement("v:oval");
				c.setAttribute("strokeweight", "1px");
				c.setAttribute("strokecolor", values[j].series.color);
				c.setAttribute("fillcolor", values[j].series.color);
				var str = document.createElement("v:stroke");
				str.setAttribute("opacity", "0.8");
				c.appendChild(str);
				str = document.createElement("v:fill");
				str.setAttribute("opacity", "0.6");
				c.appendChild(str);
				var s = c.style;
				s.position = "absolute";
				s.top = (y - r) + "px";
				s.left = (x - r) + "px";
				s.width = (r * 2) + "px";
				s.height = (r * 2) + "px";
				group.appendChild(c);
				if (applyTo) {
					applyTo(c, data[j].src);
				}
			}
			if (i == 0) {
				cmd.push("l");
				cmd.push(x + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
				cmd.push("l");
				cmd.push(Math.round(plot.axisX.getCoord(data[0][0].x, plotarea, plot)) + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
			} else {
				var values = data[i - 1];
				cmd.push("l");
				cmd.push(x + "," + Math.round(plot.axisY.getCoord(values[values.length - 1].y, plotarea, plot)));
				for (var j = values.length - 2; j >= 0; j--) {
					var x = Math.round(plot.axisX.getCoord(values[j].x, plotarea, plot));
					var y = Math.round(plot.axisY.getCoord(values[j].y, plotarea, plot));
					cmd.push("l");
					cmd.push(x + "," + y);
				}
			}
			path.setAttribute("path", cmd.join(" ") + " x e");
			group.appendChild(path);
		}
		return group;
	}, StackedCurvedArea:function (plotarea, plot, kwArgs, applyTo) {
		var tension = 3;
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = plot.series.length;
		var data = [];
		var totals = [];
		for (var i = 0; i < n; i++) {
			var tmp = plot.series[i].data.evaluate(kwArgs);
			for (var j = 0; j < tmp.length; j++) {
				if (i == 0) {
					totals.push(tmp[j].y);
				} else {
					totals[j] += tmp[j].y;
				}
				tmp[j].y = totals[j];
			}
			data.push(tmp);
		}
		for (var i = n - 1; i >= 0; i--) {
			var path = document.createElement("v:shape");
			path.setAttribute("strokeweight", "1px");
			path.setAttribute("strokecolor", data[i][0].series.color);
			path.setAttribute("fillcolor", data[i][0].series.color);
			path.setAttribute("coordsize", (area.right - area.left) + "," + (area.bottom - area.top));
			path.style.position = "absolute";
			path.style.top = "0px";
			path.style.left = "0px";
			path.style.width = area.right - area.left + "px";
			path.style.height = area.bottom - area.top + "px";
			var stroke = document.createElement("v:stroke");
			stroke.setAttribute("opacity", "0.8");
			path.appendChild(stroke);
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.4");
			path.appendChild(fill);
			var cmd = [];
			var r = 3;
			for (var j = 0; j < data[i].length; j++) {
				var values = data[i];
				var x = Math.round(plot.axisX.getCoord(values[j].x, plotarea, plot));
				var y = Math.round(plot.axisY.getCoord(values[j].y, plotarea, plot));
				if (j == 0) {
					cmd.push("m");
					cmd.push(x + "," + y);
				} else {
					var lastx = Math.round(plot.axisX.getCoord(values[j - 1].x, plotarea, plot));
					var lasty = Math.round(plot.axisY.getCoord(values[j - 1].y, plotarea, plot));
					var dx = x - lastx;
					var dy = y - lasty;
					cmd.push("c");
					var cx = Math.round((x - (tension - 1) * (dx / tension)));
					cmd.push(cx + "," + lasty);
					cx = Math.round((x - (dx / tension)));
					cmd.push(cx + "," + y);
					cmd.push(x + "," + y);
				}
				var c = document.createElement("v:oval");
				c.setAttribute("strokeweight", "1px");
				c.setAttribute("strokecolor", values[j].series.color);
				c.setAttribute("fillcolor", values[j].series.color);
				var str = document.createElement("v:stroke");
				str.setAttribute("opacity", "0.8");
				c.appendChild(str);
				str = document.createElement("v:fill");
				str.setAttribute("opacity", "0.6");
				c.appendChild(str);
				var s = c.style;
				s.position = "absolute";
				s.top = (y - r) + "px";
				s.left = (x - r) + "px";
				s.width = (r * 2) + "px";
				s.height = (r * 2) + "px";
				group.appendChild(c);
				if (applyTo) {
					applyTo(c, data[j].src);
				}
			}
			if (i == 0) {
				cmd.push("l");
				cmd.push(x + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
				cmd.push("l");
				cmd.push(Math.round(plot.axisX.getCoord(data[0][0].x, plotarea, plot)) + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
			} else {
				var values = data[i - 1];
				cmd.push("l");
				cmd.push(x + "," + Math.round(plot.axisY.getCoord(values[values.length - 1].y, plotarea, plot)));
				for (var j = values.length - 2; j >= 0; j--) {
					var x = Math.round(plot.axisX.getCoord(values[j].x, plotarea, plot));
					var y = Math.round(plot.axisY.getCoord(values[j].y, plotarea, plot));
					var lastx = Math.round(plot.axisX.getCoord(values[j + 1].x, plotarea, plot));
					var lasty = Math.round(plot.axisY.getCoord(values[j + 1].y, plotarea, plot));
					var dx = x - lastx;
					var dy = y - lasty;
					cmd.push("c");
					var cx = Math.round((x - (tension - 1) * (dx / tension)));
					cmd.push(cx + "," + lasty);
					cx = Math.round((x - (dx / tension)));
					cmd.push(cx + "," + y);
					cmd.push(x + "," + y);
				}
			}
			path.setAttribute("path", cmd.join(" ") + " x e");
			group.appendChild(path);
		}
		return group;
	}, DataBar:function (data, plotarea, plot, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = data.length;
		var w = (area.right - area.left) / (plot.axisX.range.upper - plot.axisX.range.lower);
		var yOrigin = plot.axisY.getCoord(plot.axisX.origin, plotarea, plot);
		for (var i = 0; i < n; i++) {
			var value = data[i].y;
			var yA = yOrigin;
			var x = plot.axisX.getCoord(data[i].x, plotarea, plot) - (w / 2) + 1;
			var y = plot.axisY.getCoord(value, plotarea, plot);
			var h = Math.abs(yA - y);
			if (value < plot.axisX.origin) {
				yA = y;
				y = yOrigin;
			}
			var bar = document.createElement("v:rect");
			bar.style.position = "absolute";
			bar.style.top = y + 1 + "px";
			bar.style.left = x + "px";
			bar.style.width = w + "px";
			bar.style.height = h + "px";
			bar.setAttribute("fillColor", data[i].series.color);
			bar.setAttribute("stroked", "false");
			bar.style.antialias = "false";
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.6");
			bar.appendChild(fill);
			if (applyTo) {
				applyTo(bar, data[i].src);
			}
			group.appendChild(bar);
		}
		return group;
	}, Line:function (data, plotarea, plot, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		if (data.length == 0) {
			return group;
		}
		var path = document.createElement("v:shape");
		path.setAttribute("strokeweight", "2px");
		path.setAttribute("strokecolor", data[0].series.color);
		path.setAttribute("fillcolor", "none");
		path.setAttribute("filled", "false");
		path.setAttribute("coordsize", (area.right - area.left) + "," + (area.bottom - area.top));
		path.style.position = "absolute";
		path.style.top = "0px";
		path.style.left = "0px";
		path.style.width = area.right - area.left + "px";
		path.style.height = area.bottom - area.top + "px";
		var stroke = document.createElement("v:stroke");
		stroke.setAttribute("opacity", "0.8");
		path.appendChild(stroke);
		var cmd = [];
		var r = 3;
		for (var i = 0; i < data.length; i++) {
			var x = Math.round(plot.axisX.getCoord(data[i].x, plotarea, plot));
			var y = Math.round(plot.axisY.getCoord(data[i].y, plotarea, plot));
			if (i == 0) {
				cmd.push("m");
				cmd.push(x + "," + y);
			} else {
				cmd.push("l");
				cmd.push(x + "," + y);
			}
			var c = document.createElement("v:oval");
			c.setAttribute("strokeweight", "1px");
			c.setAttribute("strokecolor", data[i].series.color);
			c.setAttribute("fillcolor", data[i].series.color);
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.8");
			c.appendChild(str);
			str = document.createElement("v:fill");
			str.setAttribute("opacity", "0.6");
			c.appendChild(str);
			var s = c.style;
			s.position = "absolute";
			s.top = (y - r) + "px";
			s.left = (x - r) + "px";
			s.width = (r * 2) + "px";
			s.height = (r * 2) + "px";
			group.appendChild(c);
			if (applyTo) {
				applyTo(c, data[i].src);
			}
		}
		path.setAttribute("path", cmd.join(" ") + " e");
		group.appendChild(path);
		return group;
	}, CurvedLine:function (data, plotarea, plot, applyTo) {
		var tension = 3;
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		if (data.length == 0) {
			return group;
		}
		var path = document.createElement("v:shape");
		path.setAttribute("strokeweight", "2px");
		path.setAttribute("strokecolor", data[0].series.color);
		path.setAttribute("fillcolor", "none");
		path.setAttribute("filled", "false");
		path.setAttribute("coordsize", (area.right - area.left) + "," + (area.bottom - area.top));
		path.style.position = "absolute";
		path.style.top = "0px";
		path.style.left = "0px";
		path.style.width = area.right - area.left + "px";
		path.style.height = area.bottom - area.top + "px";
		var stroke = document.createElement("v:stroke");
		stroke.setAttribute("opacity", "0.8");
		path.appendChild(stroke);
		var cmd = [];
		var r = 3;
		for (var i = 0; i < data.length; i++) {
			var x = Math.round(plot.axisX.getCoord(data[i].x, plotarea, plot));
			var y = Math.round(plot.axisY.getCoord(data[i].y, plotarea, plot));
			if (i == 0) {
				cmd.push("m");
				cmd.push(x + "," + y);
			} else {
				var lastx = Math.round(plot.axisX.getCoord(data[i - 1].x, plotarea, plot));
				var lasty = Math.round(plot.axisY.getCoord(data[i - 1].y, plotarea, plot));
				var dx = x - lastx;
				var dy = y - lasty;
				cmd.push("c");
				var cx = Math.round((x - (tension - 1) * (dx / tension)));
				cmd.push(cx + "," + lasty);
				cx = Math.round((x - (dx / tension)));
				cmd.push(cx + "," + y);
				cmd.push(x + "," + y);
			}
			var c = document.createElement("v:oval");
			c.setAttribute("strokeweight", "1px");
			c.setAttribute("strokecolor", data[i].series.color);
			c.setAttribute("fillcolor", data[i].series.color);
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.8");
			c.appendChild(str);
			str = document.createElement("v:fill");
			str.setAttribute("opacity", "0.6");
			c.appendChild(str);
			var s = c.style;
			s.position = "absolute";
			s.top = (y - r) + "px";
			s.left = (x - r) + "px";
			s.width = (r * 2) + "px";
			s.height = (r * 2) + "px";
			group.appendChild(c);
			if (applyTo) {
				applyTo(c, data[i].src);
			}
		}
		path.setAttribute("path", cmd.join(" ") + " e");
		group.appendChild(path);
		return group;
	}, Area:function (data, plotarea, plot, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		if (data.length == 0) {
			return group;
		}
		var path = document.createElement("v:shape");
		path.setAttribute("strokeweight", "1px");
		path.setAttribute("strokecolor", data[0].series.color);
		path.setAttribute("fillcolor", data[0].series.color);
		path.setAttribute("coordsize", (area.right - area.left) + "," + (area.bottom - area.top));
		path.style.position = "absolute";
		path.style.top = "0px";
		path.style.left = "0px";
		path.style.width = area.right - area.left + "px";
		path.style.height = area.bottom - area.top + "px";
		var stroke = document.createElement("v:stroke");
		stroke.setAttribute("opacity", "0.8");
		path.appendChild(stroke);
		var fill = document.createElement("v:fill");
		fill.setAttribute("opacity", "0.4");
		path.appendChild(fill);
		var cmd = [];
		var r = 3;
		for (var i = 0; i < data.length; i++) {
			var x = Math.round(plot.axisX.getCoord(data[i].x, plotarea, plot));
			var y = Math.round(plot.axisY.getCoord(data[i].y, plotarea, plot));
			if (i == 0) {
				cmd.push("m");
				cmd.push(x + "," + y);
			} else {
				cmd.push("l");
				cmd.push(x + "," + y);
			}
			var c = document.createElement("v:oval");
			c.setAttribute("strokeweight", "1px");
			c.setAttribute("strokecolor", data[i].series.color);
			c.setAttribute("fillcolor", data[i].series.color);
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.8");
			c.appendChild(str);
			str = document.createElement("v:fill");
			str.setAttribute("opacity", "0.6");
			c.appendChild(str);
			var s = c.style;
			s.position = "absolute";
			s.top = (y - r) + "px";
			s.left = (x - r) + "px";
			s.width = (r * 2) + "px";
			s.height = (r * 2) + "px";
			group.appendChild(c);
			if (applyTo) {
				applyTo(c, data[i].src);
			}
		}
		cmd.push("l");
		cmd.push(x + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
		cmd.push("l");
		cmd.push(Math.round(plot.axisX.getCoord(data[0].x, plotarea, plot)) + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
		path.setAttribute("path", cmd.join(" ") + " x e");
		group.appendChild(path);
		return group;
	}, CurvedArea:function (data, plotarea, plot, applyTo) {
		var tension = 3;
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		if (data.length == 0) {
			return group;
		}
		var path = document.createElement("v:shape");
		path.setAttribute("strokeweight", "1px");
		path.setAttribute("strokecolor", data[0].series.color);
		path.setAttribute("fillcolor", data[0].series.color);
		path.setAttribute("coordsize", (area.right - area.left) + "," + (area.bottom - area.top));
		path.style.position = "absolute";
		path.style.top = "0px";
		path.style.left = "0px";
		path.style.width = area.right - area.left + "px";
		path.style.height = area.bottom - area.top + "px";
		var stroke = document.createElement("v:stroke");
		stroke.setAttribute("opacity", "0.8");
		path.appendChild(stroke);
		var fill = document.createElement("v:fill");
		fill.setAttribute("opacity", "0.4");
		path.appendChild(fill);
		var cmd = [];
		var r = 3;
		for (var i = 0; i < data.length; i++) {
			var x = Math.round(plot.axisX.getCoord(data[i].x, plotarea, plot));
			var y = Math.round(plot.axisY.getCoord(data[i].y, plotarea, plot));
			if (i == 0) {
				cmd.push("m");
				cmd.push(x + "," + y);
			} else {
				var lastx = Math.round(plot.axisX.getCoord(data[i - 1].x, plotarea, plot));
				var lasty = Math.round(plot.axisY.getCoord(data[i - 1].y, plotarea, plot));
				var dx = x - lastx;
				var dy = y - lasty;
				cmd.push("c");
				var cx = Math.round((x - (tension - 1) * (dx / tension)));
				cmd.push(cx + "," + lasty);
				cx = Math.round((x - (dx / tension)));
				cmd.push(cx + "," + y);
				cmd.push(x + "," + y);
			}
			var c = document.createElement("v:oval");
			c.setAttribute("strokeweight", "1px");
			c.setAttribute("strokecolor", data[i].series.color);
			c.setAttribute("fillcolor", data[i].series.color);
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.8");
			c.appendChild(str);
			str = document.createElement("v:fill");
			str.setAttribute("opacity", "0.6");
			c.appendChild(str);
			var s = c.style;
			s.position = "absolute";
			s.top = (y - r) + "px";
			s.left = (x - r) + "px";
			s.width = (r * 2) + "px";
			s.height = (r * 2) + "px";
			group.appendChild(c);
			if (applyTo) {
				applyTo(c, data[i].src);
			}
		}
		cmd.push("l");
		cmd.push(x + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
		cmd.push("l");
		cmd.push(Math.round(plot.axisX.getCoord(data[0].x, plotarea, plot)) + "," + Math.round(plot.axisY.getCoord(plot.axisX.origin, plotarea, plot)));
		path.setAttribute("path", cmd.join(" ") + " x e");
		group.appendChild(path);
		return group;
	}, HighLow:function (data, plotarea, plot, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = data.length;
		var part = ((area.right - area.left) / (plot.axisX.range.upper - plot.axisX.range.lower)) / 4;
		var w = part * 2;
		for (var i = 0; i < n; i++) {
			var high = data[i].high;
			var low = data[i].low;
			if (low > high) {
				var t = low;
				low = high;
				high = t;
			}
			var x = plot.axisX.getCoord(data[i].x, plotarea, plot) - (w / 2);
			var y = plot.axisY.getCoord(high, plotarea, plot);
			var h = plot.axisY.getCoord(low, plotarea, plot) - y;
			var bar = document.createElement("v:rect");
			bar.style.position = "absolute";
			bar.style.top = y + 1 + "px";
			bar.style.left = x + "px";
			bar.style.width = w + "px";
			bar.style.height = h + "px";
			bar.setAttribute("fillColor", data[i].series.color);
			bar.setAttribute("stroked", "false");
			bar.style.antialias = "false";
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.6");
			bar.appendChild(fill);
			if (applyTo) {
				applyTo(bar, data[i].src);
			}
			group.appendChild(bar);
		}
		return group;
	}, HighLowClose:function (data, plotarea, plot, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = data.length;
		var part = ((area.right - area.left) / (plot.axisX.range.upper - plot.axisX.range.lower)) / 4;
		var w = part * 2;
		for (var i = 0; i < n; i++) {
			var high = data[i].high;
			var low = data[i].low;
			if (low > high) {
				var t = low;
				low = high;
				high = t;
			}
			var c = data[i].close;
			var x = plot.axisX.getCoord(data[i].x, plotarea, plot) - (w / 2);
			var y = plot.axisY.getCoord(high, plotarea, plot);
			var h = plot.axisY.getCoord(low, plotarea, plot) - y;
			var close = plot.axisY.getCoord(c, plotarea, plot);
			var g = document.createElement("div");
			var bar = document.createElement("v:rect");
			bar.style.position = "absolute";
			bar.style.top = y + 1 + "px";
			bar.style.left = x + "px";
			bar.style.width = w + "px";
			bar.style.height = h + "px";
			bar.setAttribute("fillColor", data[i].series.color);
			bar.setAttribute("stroked", "false");
			bar.style.antialias = "false";
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.6");
			bar.appendChild(fill);
			g.appendChild(bar);
			var line = document.createElement("v:line");
			line.setAttribute("strokecolor", data[i].series.color);
			line.setAttribute("strokeweight", "1px");
			line.setAttribute("from", x + "px," + close + "px");
			line.setAttribute("to", (x + w + (part * 2) - 2) + "px," + close + "px");
			var s = line.style;
			s.position = "absolute";
			s.top = "0px";
			s.left = "0px";
			s.antialias = "false";
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.6");
			line.appendChild(str);
			g.appendChild(line);
			if (applyTo) {
				applyTo(g, data[i].src);
			}
			group.appendChild(g);
		}
		return group;
	}, HighLowOpenClose:function (data, plotarea, plot, applyTo) {
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		var n = data.length;
		var part = ((area.right - area.left) / (plot.axisX.range.upper - plot.axisX.range.lower)) / 4;
		var w = part * 2;
		for (var i = 0; i < n; i++) {
			var high = data[i].high;
			var low = data[i].low;
			if (low > high) {
				var t = low;
				low = high;
				high = t;
			}
			var o = data[i].open;
			var c = data[i].close;
			var x = plot.axisX.getCoord(data[i].x, plotarea, plot) - (w / 2);
			var y = plot.axisY.getCoord(high, plotarea, plot);
			var h = plot.axisY.getCoord(low, plotarea, plot) - y;
			var open = plot.axisY.getCoord(o, plotarea, plot);
			var close = plot.axisY.getCoord(c, plotarea, plot);
			var g = document.createElement("div");
			var bar = document.createElement("v:rect");
			bar.style.position = "absolute";
			bar.style.top = y + 1 + "px";
			bar.style.left = x + "px";
			bar.style.width = w + "px";
			bar.style.height = h + "px";
			bar.setAttribute("fillColor", data[i].series.color);
			bar.setAttribute("stroked", "false");
			bar.style.antialias = "false";
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.6");
			bar.appendChild(fill);
			g.appendChild(bar);
			var line = document.createElement("v:line");
			line.setAttribute("strokecolor", data[i].series.color);
			line.setAttribute("strokeweight", "1px");
			line.setAttribute("from", (x - (part * 2)) + "px," + open + "px");
			line.setAttribute("to", (x + w - 2) + "px," + open + "px");
			var s = line.style;
			s.position = "absolute";
			s.top = "0px";
			s.left = "0px";
			s.antialias = "false";
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.6");
			line.appendChild(str);
			g.appendChild(line);
			var line = document.createElement("v:line");
			line.setAttribute("strokecolor", data[i].series.color);
			line.setAttribute("strokeweight", "1px");
			line.setAttribute("from", x + "px," + close + "px");
			line.setAttribute("to", (x + w + (part * 2) - 2) + "px," + close + "px");
			var s = line.style;
			s.position = "absolute";
			s.top = "0px";
			s.left = "0px";
			s.antialias = "false";
			var str = document.createElement("v:stroke");
			str.setAttribute("opacity", "0.6");
			line.appendChild(str);
			g.appendChild(line);
			if (applyTo) {
				applyTo(g, data[i].src);
			}
			group.appendChild(g);
		}
		return group;
	}, Scatter:function (data, plotarea, plot, applyTo) {
		var r = 6;
		var mod = r / 2;
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		for (var i = 0; i < data.length; i++) {
			var x = Math.round(plot.axisX.getCoord(data[i].x, plotarea, plot));
			var y = Math.round(plot.axisY.getCoord(data[i].y, plotarea, plot));
			var point = document.createElement("v:rect");
			point.setAttribute("strokecolor", data[i].series.color);
			point.setAttribute("fillcolor", data[i].series.color);
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.6");
			point.appendChild(fill);
			var s = point.style;
			s.position = "absolute";
			s.rotation = "45";
			s.top = (y - mod) + "px";
			s.left = (x - mod) + "px";
			s.width = r + "px";
			s.height = r + "px";
			group.appendChild(point);
			if (applyTo) {
				applyTo(point, data[i].src);
			}
		}
		return group;
	}, Bubble:function (data, plotarea, plot, applyTo) {
		var sizeFactor = 1;
		var area = plotarea.getArea();
		var group = dojo.charting.Plotters._group(plotarea);
		for (var i = 0; i < data.length; i++) {
			var x = Math.round(plot.axisX.getCoord(data[i].x, plotarea, plot));
			var y = Math.round(plot.axisY.getCoord(data[i].y, plotarea, plot));
			if (i == 0) {
				var raw = data[i].size;
				var dy = plot.axisY.getCoord(data[i].y + raw, plotarea, plot) - y;
				sizeFactor = dy / raw;
			}
			if (sizeFactor < 1) {
				sizeFactor = 1;
			}
			var r = (data[i].size / 2) * sizeFactor;
			var point = document.createElement("v:oval");
			point.setAttribute("strokecolor", data[i].series.color);
			point.setAttribute("fillcolor", data[i].series.color);
			var fill = document.createElement("v:fill");
			fill.setAttribute("opacity", "0.6");
			point.appendChild(fill);
			var s = point.style;
			s.position = "absolute";
			s.rotation = "45";
			s.top = (y - r) + "px";
			s.left = (x - r) + "px";
			s.width = (r * 2) + "px";
			s.height = (r * 2) + "px";
			group.appendChild(point);
			if (applyTo) {
				applyTo(point, data[i].src);
			}
		}
		return group;
	}});
	dojo.charting.Plotters["Default"] = dojo.charting.Plotters.Line;
}

/*
	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
*/



dojo.provide("dojo.html.metrics");
dojo.html.getScrollbar = function () {
	var scroll = document.createElement("div");
	scroll.style.width = "100px";
	scroll.style.height = "100px";
	scroll.style.overflow = "scroll";
	scroll.style.position = "absolute";
	scroll.style.top = "-300px";
	scroll.style.left = "0px";
	var test = document.createElement("div");
	test.style.width = "400px";
	test.style.height = "400px";
	scroll.appendChild(test);
	dojo.body().appendChild(scroll);
	var width = scroll.offsetWidth - scroll.clientWidth;
	dojo.body().removeChild(scroll);
	scroll.removeChild(test);
	scroll = test = null;
	return {width:width};
};
dojo.html.getFontMeasurements = function () {
	var heights = {"1em":0, "1ex":0, "100%":0, "12pt":0, "16px":0, "xx-small":0, "x-small":0, "small":0, "medium":0, "large":0, "x-large":0, "xx-large":0};
	if (dojo.render.html.ie) {
		document.documentElement.style.fontSize = "100%";
	}
	var div = document.createElement("div");
	div.style.position = "absolute";
	div.style.left = "-100px";
	div.style.top = "0";
	div.style.width = "30px";
	div.style.height = "1000em";
	div.style.border = "0";
	div.style.margin = "0";
	div.style.padding = "0";
	div.style.outline = "0";
	div.style.lineHeight = "1";
	div.style.overflow = "hidden";
	dojo.body().appendChild(div);
	for (var p in heights) {
		div.style.fontSize = p;
		heights[p] = Math.round(div.offsetHeight * 12 / 16) * 16 / 12 / 1000;
	}
	dojo.body().removeChild(div);
	div = null;
	return heights;
};
dojo.html._fontMeasurements = null;
dojo.html.getCachedFontMeasurements = function (recalculate) {
	if (recalculate || !dojo.html._fontMeasurements) {
		dojo.html._fontMeasurements = dojo.html.getFontMeasurements();
	}
	return dojo.html._fontMeasurements;
};
dojo.html.measureFragment = function (node, html, boxType) {
	var clone = node.cloneNode(true);
	clone.innerHTML = html;
	node.parentNode.appendChild(clone);
	var ret = dojo.html.getElementBox(clone, boxType);
	node.parentNode.removeChild(clone);
	clone = null;
	return ret;
};
dojo.html.getFittedFragment = function (node, html) {
	function cl(node) {
		var element = document.createElement(node.tagName);
		element.id = node.id + "-clone";
		element.className = node.className;
		for (var j = 0; j < node.attributes.length; j++) {
			if (node.attributes[j].specified) {
				if (node.attributes[j].nodeName.toLowerCase() != "style" && node.attributes[j].nodeName.toLowerCase() != "edited" && node.attributes[j].nodeName.toLowerCase() != "contenteditable" && node.attributes[j].nodeName.toLowerCase() != "id" && node.attributes[j].nodeName.toLowerCase() != "class") {
					element.setAttribute(node.attributes[j].nodeName.toLowerCase(), node.attributes[j].nodeValue);
				}
			}
		}
		return element;
	}
	var height = dojo.html.getFontMeasurements()["16px"];
	var n = cl(node);
	n.style.width = dojo.html.getBorderBox(node).width + "px";
	n.style.height = (height + 4) + "px";
	node.parentNode.appendChild(n);
	var rem = dojo.html.fitToElement(n, html);
	var ret = n.innerHTML;
	n.parentNode.removeChild(n);
	return ret;
};
dojo.html.fitToElement = function (node, html) {
	function cl(node) {
		var element = document.createElement(node.tagName);
		element.id = node.id + "-clone";
		element.className = node.className;
		for (var j = 0; j < node.attributes.length; j++) {
			if (node.attributes[j].specified) {
				if (node.attributes[j].nodeName.toLowerCase() != "style" && node.attributes[j].nodeName.toLowerCase() != "edited" && node.attributes[j].nodeName.toLowerCase() != "contenteditable" && node.attributes[j].nodeName.toLowerCase() != "id" && node.attributes[j].nodeName.toLowerCase() != "class") {
					element.setAttribute(node.attributes[j].nodeName.toLowerCase(), node.attributes[j].nodeValue);
				}
			}
		}
		return element;
	}
	var clone = cl(node);
	node.parentNode.appendChild(clone);
	var t = dojo.html.getBorderBox(node);
	clone.style.width = t.width + "px";
	var singletons = ["br", "img", "hr", "input", "!--"];
	var chop = ["<BR>", "<br>", "<br/>", "<br />", "<p></p>", "<P></P>"];
	var openTags = [];
	var str = html;
	var i = 0;
	var limit = str.length;
	var add = 0;
	var doLoop = true;
	clone.innerHTML = str;
	while (doLoop) {
		add = Math.round((limit - i) / 2);
		if (add <= 1) {
			doLoop = false;
		}
		i += add;
		clone.innerHTML = str.substr(0, i);
		if (clone.offsetHeight > t.height) {
			limit = i;
			i -= add;
		}
	}
	if (str.substr(0, i) != str) {
		var lastSpace = str.substr(0, i).lastIndexOf(" ");
		var lastNewLine = str.substr(0, i).lastIndexOf("\n");
		var lastGreater = str.substr(0, i).lastIndexOf(">");
		var lastLess = str.substr(0, i).lastIndexOf("<");
		if (lastLess <= lastGreater && lastNewLine == i - 1) {
			i = i;
		} else {
			if (lastSpace != -1 && lastSpace > lastGreater && lastGreater > lastLess) {
				i = lastSpace + 1;
			} else {
				if (lastLess > lastGreater) {
					i = lastLess;
				} else {
					if (lastGreater != -1) {
						i = lastGreater + 1;
					}
				}
			}
		}
	}
	str = str.substr(0, i);
	var ret = html.substr(str.length);
	var doPush = true;
	var tags = str.split("<");
	tags.shift();
	for (var j = 0; j < tags.length; j++) {
		tags[j] = tags[j].split(">")[0];
		if (tags[j].charAt(tags[j].length - 1) == "/") {
			continue;
		}
		if (tags[j].charAt(0) != "/") {
			for (var k = 0; k < singletons.length; k++) {
				if (tags[j].split(" ")[0].toLowerCase() == singletons[k]) {
					doPush = false;
				}
			}
			if (doPush) {
				openTags.push(tags[j]);
			}
			doPush = true;
		} else {
			openTags.pop();
		}
	}
	for (var j = 0; j < chop.length; j++) {
		if (ret.charAt(0) == "\n") {
			ret = ret.substr(1);
		}
		while (ret.indexOf(chop[j]) == 0) {
			ret = ret.substr(chop[j].length);
		}
	}
	for (var j = openTags.length - 1; j >= 0; j--) {
		if (str.lastIndexOf(openTags[j]) == (str.length - openTags[j].length - 1)) {
			str = str.substring(0, str.lastIndexOf(openTags[j]));
		} else {
			str += "</" + openTags[j] + ">";
		}
		if (ret.length > 0) {
			ret = "<" + openTags[j] + ">" + ret;
		}
	}
	for (var j = 0; j < chop.length; j++) {
		if (ret.charAt(0) == "\n") {
			ret = ret.substr(1);
		}
		while (ret.indexOf(chop[j]) == 0) {
			ret = ret.substr(chop[j].length);
		}
	}
	node.innerHTML = str;
	clone.parentNode.removeChild(clone);
	clone = null;
	return ret;
};

/*
	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
*/



dojo.provide("dojo.AdapterRegistry");
dojo.AdapterRegistry = function (returnWrappers) {
	this.pairs = [];
	this.returnWrappers = returnWrappers || false;
};
dojo.lang.extend(dojo.AdapterRegistry, {register:function (name, check, wrap, directReturn, override) {
	var type = (override) ? "unshift" : "push";
	this.pairs[type]([name, check, wrap, directReturn]);
}, match:function () {
	for (var i = 0; i < this.pairs.length; i++) {
		var pair = this.pairs[i];
		if (pair[1].apply(this, arguments)) {
			if ((pair[3]) || (this.returnWrappers)) {
				return pair[2];
			} else {
				return pair[2].apply(this, arguments);
			}
		}
	}
	throw new Error("No match found");
}, unregister:function (name) {
	for (var i = 0; i < this.pairs.length; i++) {
		var pair = this.pairs[i];
		if (pair[0] == name) {
			this.pairs.splice(i, 1);
			return true;
		}
	}
	return false;
}});


/*
	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
*/



dojo.provide("dojo.collections.Store");
dojo.collections.Store = function (jsonArray) {
	var data = [];
	var items = {};
	this.keyField = "Id";
	this.get = function () {
		return data;
	};
	this.getByKey = function (key) {
		return items[key];
	};
	this.getByIndex = function (idx) {
		return data[idx];
	};
	this.getIndexOf = function (key) {
		for (var i = 0; i < data.length; i++) {
			if (data[i].key == key) {
				return i;
			}
		}
		return -1;
	};
	this.getData = function () {
		var arr = [];
		for (var i = 0; i < data.length; i++) {
			arr.push(data[i].src);
		}
		return arr;
	};
	this.getDataByKey = function (key) {
		if (items[key] != null) {
			return items[key].src;
		}
		return null;
	};
	this.getIndexOfData = function (obj) {
		for (var i = 0; i < data.length; i++) {
			if (data[i].src == obj) {
				return i;
			}
		}
		return -1;
	};
	this.getDataByIndex = function (idx) {
		if (data[idx]) {
			return data[idx].src;
		}
		return null;
	};
	this.update = function (obj, fieldPath, val, bDontFire) {
		var parts = fieldPath.split("."), i = 0, o = obj, field;
		if (parts.length > 1) {
			field = parts.pop();
			do {
				if (parts[i].indexOf("()") > -1) {
					var temp = parts[i++].split("()")[0];
					if (!o[temp]) {
						dojo.raise("dojo.collections.Store.getField(obj, '" + field + "'): '" + temp + "' is not a property of the passed object.");
					} else {
						o = o[temp]();
					}
				} else {
					o = o[parts[i++]];
				}
			} while (i < parts.length && o != null);
		} else {
			field = parts[0];
		}
		obj[field] = val;
		if (!bDontFire) {
			this.onUpdateField(obj, fieldPath, val);
		}
	};
	this.forEach = function (fn) {
		if (Array.forEach) {
			Array.forEach(data, fn, this);
		} else {
			for (var i = 0; i < data.length; i++) {
				fn.call(this, data[i]);
			}
		}
	};
	this.forEachData = function (fn) {
		if (Array.forEach) {
			Array.forEach(this.getData(), fn, this);
		} else {
			var a = this.getData();
			for (var i = 0; i < a.length; i++) {
				fn.call(this, a[i]);
			}
		}
	};
	this.setData = function (arr, bDontFire) {
		data = [];
		for (var i = 0; i < arr.length; i++) {
			var o = {key:arr[i][this.keyField], src:arr[i]};
			data.push(o);
			items[o.key] = o;
		}
		if (!bDontFire) {
			this.onSetData();
		}
	};
	this.clearData = function (bDontFire) {
		data = [];
		items = {};
		if (!bDontFire) {
			this.onClearData();
		}
	};
	this.addData = function (obj, key, bDontFire) {
		var k = key || obj[this.keyField];
		if (items[k] != null) {
			var o = items[k];
			o.src = obj;
		} else {
			var o = {key:k, src:obj};
			data.push(o);
			items[o.key] = o;
		}
		if (!bDontFire) {
			this.onAddData(o);
		}
	};
	this.addDataRange = function (arr, bDontFire) {
		var objects = [];
		for (var i = 0; i < arr.length; i++) {
			var k = arr[i][this.keyField];
			if (items[k] != null) {
				var o = items[k];
				o.src = arr[i];
			} else {
				var o = {key:k, src:arr[i]};
				data.push(o);
				items[k] = o;
			}
			objects.push(o);
		}
		if (!bDontFire) {
			this.onAddDataRange(objects);
		}
	};
	this.addDataByIndex = function (obj, idx, key, bDontFire) {
		var k = key || obj[this.keyField];
		if (items[k] != null) {
			var i = this.getIndexOf(k);
			var o = data.splice(i, 1);
			o.src = obj;
		} else {
			var o = {key:k, src:obj};
			items[k] = o;
		}
		data.splice(idx, 0, o);
		if (!bDontFire) {
			this.onAddData(o);
		}
	};
	this.addDataRangeByIndex = function (arr, idx, bDontFire) {
		var objects = [];
		for (var i = 0; i < arr.length; i++) {
			var k = arr[i][this.keyField];
			if (items[k] != null) {
				var j = this.getIndexOf(k);
				var o = data.splice(j, 1);
				o.src = arr[i];
			} else {
				var o = {key:k, src:arr[i]};
				items[k] = o;
			}
			objects.push(o);
		}
		data.splice(idx, 0, objects);
		if (!bDontFire) {
			this.onAddDataRange(objects);
		}
	};
	this.removeData = function (obj, bDontFire) {
		var idx = -1;
		var o = null;
		for (var i = 0; i < data.length; i++) {
			if (data[i].src == obj) {
				idx = i;
				o = data[i];
				break;
			}
		}
		if (!bDontFire) {
			this.onRemoveData(o);
		}
		if (idx > -1) {
			data.splice(idx, 1);
			delete items[o.key];
		}
	};
	this.removeDataRange = function (idx, range, bDontFire) {
		var ret = data.splice(idx, range);
		for (var i = 0; i < ret.length; i++) {
			delete items[ret[i].key];
		}
		if (!bDontFire) {
			this.onRemoveDataRange(ret);
		}
		return ret;
	};
	this.removeDataByKey = function (key, bDontFire) {
		this.removeData(this.getDataByKey(key), bDontFire);
	};
	this.removeDataByIndex = function (idx, bDontFire) {
		this.removeData(this.getDataByIndex(idx), bDontFire);
	};
	if (jsonArray && jsonArray.length && jsonArray[0]) {
		this.setData(jsonArray, true);
	}
};
dojo.extend(dojo.collections.Store, {getField:function (obj, field) {
	var parts = field.split("."), i = 0, o = obj;
	do {
		if (parts[i].indexOf("()") > -1) {
			var temp = parts[i++].split("()")[0];
			if (!o[temp]) {
				dojo.raise("dojo.collections.Store.getField(obj, '" + field + "'): '" + temp + "' is not a property of the passed object.");
			} else {
				o = o[temp]();
			}
		} else {
			o = o[parts[i++]];
		}
	} while (i < parts.length && o != null);
	if (i < parts.length) {
		dojo.raise("dojo.collections.Store.getField(obj, '" + field + "'): '" + field + "' is not a property of the passed object.");
	}
	return o;
}, getFromHtml:function (meta, body, fnMod) {
	var rows = body.rows;
	var ctor = function (row) {
		var obj = {};
		for (var i = 0; i < meta.length; i++) {
			var o = obj;
			var data = row.cells[i].innerHTML;
			var p = meta[i].getField();
			if (p.indexOf(".") > -1) {
				p = p.split(".");
				while (p.length > 1) {
					var pr = p.shift();
					o[pr] = {};
					o = o[pr];
				}
				p = p[0];
			}
			var type = meta[i].getType();
			if (type == String) {
				o[p] = data;
			} else {
				if (data) {
					o[p] = new type(data);
				} else {
					o[p] = new type();
				}
			}
		}
		return obj;
	};
	var arr = [];
	for (var i = 0; i < rows.length; i++) {
		var o = ctor(rows[i]);
		if (fnMod) {
			fnMod(o, rows[i]);
		}
		arr.push(o);
	}
	return arr;
}, onSetData:function () {
}, onClearData:function () {
}, onAddData:function (obj) {
}, onAddDataRange:function (arr) {
}, onRemoveData:function (obj) {
}, onRemoveDataRange:function (arr) {
}, onUpdateField:function (obj, field, val) {
}});



/*
	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
*/



dojo.provide("dojo.widget.Widget");
dojo.declare("dojo.widget.Widget", null, function () {
	this.children = [];
	this.extraArgs = {};
}, {parent:null, isTopLevel:false, disabled:false, isContainer:false, widgetId:"", widgetType:"Widget", ns:"dojo", getNamespacedType:function () {
	return (this.ns ? this.ns + ":" + this.widgetType : this.widgetType).toLowerCase();
}, toString:function () {
	return "[Widget " + this.getNamespacedType() + ", " + (this.widgetId || "NO ID") + "]";
}, repr:function () {
	return this.toString();
}, enable:function () {
	this.disabled = false;
}, disable:function () {
	this.disabled = true;
}, onResized:function () {
	this.notifyChildrenOfResize();
}, notifyChildrenOfResize:function () {
	for (var i = 0; i < this.children.length; i++) {
		var child = this.children[i];
		if (child.onResized) {
			child.onResized();
		}
	}
}, create:function (args, fragment, parent, ns) {
	if (ns) {
		this.ns = ns;
	}
	this.satisfyPropertySets(args, fragment, parent);
	this.mixInProperties(args, fragment, parent);
	this.postMixInProperties(args, fragment, parent);
	dojo.widget.manager.add(this);
	this.buildRendering(args, fragment, parent);
	this.initialize(args, fragment, parent);
	this.postInitialize(args, fragment, parent);
	this.postCreate(args, fragment, parent);
	return this;
}, destroy:function (finalize) {
	if (this.parent) {
		this.parent.removeChild(this);
	}
	this.destroyChildren();
	this.uninitialize();
	this.destroyRendering(finalize);
	dojo.widget.manager.removeById(this.widgetId);
}, destroyChildren:function () {
	var widget;
	var i = 0;
	while (this.children.length > i) {
		widget = this.children[i];
		if (widget instanceof dojo.widget.Widget) {
			this.removeChild(widget);
			widget.destroy();
			continue;
		}
		i++;
	}
}, getChildrenOfType:function (type, recurse) {
	var ret = [];
	var isFunc = dojo.lang.isFunction(type);
	if (!isFunc) {
		type = type.toLowerCase();
	}
	for (var x = 0; x < this.children.length; x++) {
		if (isFunc) {
			if (this.children[x] instanceof type) {
				ret.push(this.children[x]);
			}
		} else {
			if (this.children[x].widgetType.toLowerCase() == type) {
				ret.push(this.children[x]);
			}
		}
		if (recurse) {
			ret = ret.concat(this.children[x].getChildrenOfType(type, recurse));
		}
	}
	return ret;
}, getDescendants:function () {
	var result = [];
	var stack = [this];
	var elem;
	while ((elem = stack.pop())) {
		result.push(elem);
		if (elem.children) {
			dojo.lang.forEach(elem.children, function (elem) {
				stack.push(elem);
			});
		}
	}
	return result;
}, isFirstChild:function () {
	return this === this.parent.children[0];
}, isLastChild:function () {
	return this === this.parent.children[this.parent.children.length - 1];
}, satisfyPropertySets:function (args) {
	return args;
}, mixInProperties:function (args, frag) {
	if ((args["fastMixIn"]) || (frag["fastMixIn"])) {
		for (var x in args) {
			this[x] = args[x];
		}
		return;
	}
	var undef;
	var lcArgs = dojo.widget.lcArgsCache[this.widgetType];
	if (lcArgs == null) {
		lcArgs = {};
		for (var y in this) {
			lcArgs[((new String(y)).toLowerCase())] = y;
		}
		dojo.widget.lcArgsCache[this.widgetType] = lcArgs;
	}
	var visited = {};
	for (var x in args) {
		if (!this[x]) {
			var y = lcArgs[(new String(x)).toLowerCase()];
			if (y) {
				args[y] = args[x];
				x = y;
			}
		}
		if (visited[x]) {
			continue;
		}
		visited[x] = true;
		if ((typeof this[x]) != (typeof undef)) {
			if (typeof args[x] != "string") {
				this[x] = args[x];
			} else {
				if (dojo.lang.isString(this[x])) {
					this[x] = args[x];
				} else {
					if (dojo.lang.isNumber(this[x])) {
						this[x] = new Number(args[x]);
					} else {
						if (dojo.lang.isBoolean(this[x])) {
							this[x] = (args[x].toLowerCase() == "false") ? false : true;
						} else {
							if (dojo.lang.isFunction(this[x])) {
								if (args[x].search(/[^\w\.]+/i) == -1) {
									this[x] = dojo.evalObjPath(args[x], false);
								} else {
									var tn = dojo.lang.nameAnonFunc(new Function(args[x]), this);
									dojo.event.kwConnect({srcObj:this, srcFunc:x, adviceObj:this, adviceFunc:tn});
								}
							} else {
								if (dojo.lang.isArray(this[x])) {
									this[x] = args[x].split(";");
								} else {
									if (this[x] instanceof Date) {
										this[x] = new Date(Number(args[x]));
									} else {
										if (typeof this[x] == "object") {
											if (this[x] instanceof dojo.uri.Uri) {
												this[x] = dojo.uri.dojoUri(args[x]);
											} else {
												var pairs = args[x].split(";");
												for (var y = 0; y < pairs.length; y++) {
													var si = pairs[y].indexOf(":");
													if ((si != -1) && (pairs[y].length > si)) {
														this[x][pairs[y].substr(0, si).replace(/^\s+|\s+$/g, "")] = pairs[y].substr(si + 1);
													}
												}
											}
										} else {
											this[x] = args[x];
										}
									}
								}
							}
						}
					}
				}
			}
		} else {
			this.extraArgs[x.toLowerCase()] = args[x];
		}
	}
}, postMixInProperties:function (args, frag, parent) {
}, initialize:function (args, frag, parent) {
	return false;
}, postInitialize:function (args, frag, parent) {
	return false;
}, postCreate:function (args, frag, parent) {
	return false;
}, uninitialize:function () {
	return false;
}, buildRendering:function (args, frag, parent) {
	dojo.unimplemented("dojo.widget.Widget.buildRendering, on " + this.toString() + ", ");
	return false;
}, destroyRendering:function () {
	dojo.unimplemented("dojo.widget.Widget.destroyRendering");
	return false;
}, addedTo:function (parent) {
}, addChild:function (child) {
	dojo.unimplemented("dojo.widget.Widget.addChild");
	return false;
}, removeChild:function (widget) {
	for (var x = 0; x < this.children.length; x++) {
		if (this.children[x] === widget) {
			this.children.splice(x, 1);
			widget.parent = null;
			break;
		}
	}
	return widget;
}, getPreviousSibling:function () {
	var idx = this.getParentIndex();
	if (idx <= 0) {
		return null;
	}
	return this.parent.children[idx - 1];
}, getSiblings:function () {
	return this.parent.children;
}, getParentIndex:function () {
	return dojo.lang.indexOf(this.parent.children, this, true);
}, getNextSibling:function () {
	var idx = this.getParentIndex();
	if (idx == this.parent.children.length - 1) {
		return null;
	}
	if (idx < 0) {
		return null;
	}
	return this.parent.children[idx + 1];
}});
dojo.widget.lcArgsCache = {};
dojo.widget.tags = {};
dojo.widget.tags.addParseTreeHandler = function (type) {
	dojo.deprecated("addParseTreeHandler", ". ParseTreeHandlers are now reserved for components. Any unfiltered DojoML tag without a ParseTreeHandler is assumed to be a widget", "0.5");
};
dojo.widget.tags["dojo:propertyset"] = function (fragment, widgetParser, parentComp) {
	var properties = widgetParser.parseProperties(fragment["dojo:propertyset"]);
};
dojo.widget.tags["dojo:connect"] = function (fragment, widgetParser, parentComp) {
	var properties = widgetParser.parseProperties(fragment["dojo:connect"]);
};
dojo.widget.buildWidgetFromParseTree = function (type, frag, parser, parentComp, insertionIndex, localProps) {
	dojo.a11y.setAccessibleMode();
	var stype = type.split(":");
	stype = (stype.length == 2) ? stype[1] : type;
	var localProperties = localProps || parser.parseProperties(frag[frag["ns"] + ":" + stype]);
	var twidget = dojo.widget.manager.getImplementation(stype, null, null, frag["ns"]);
	if (!twidget) {
		throw new Error("cannot find \"" + type + "\" widget");
	} else {
		if (!twidget.create) {
			throw new Error("\"" + type + "\" widget object has no \"create\" method and does not appear to implement *Widget");
		}
	}
	localProperties["dojoinsertionindex"] = insertionIndex;
	var ret = twidget.create(localProperties, frag, parentComp, frag["ns"]);
	return ret;
};
dojo.widget.defineWidget = function (widgetClass, renderer, superclasses, init, props) {
	if (dojo.lang.isString(arguments[3])) {
		dojo.widget._defineWidget(arguments[0], arguments[3], arguments[1], arguments[4], arguments[2]);
	} else {
		var args = [arguments[0]], p = 3;
		if (dojo.lang.isString(arguments[1])) {
			args.push(arguments[1], arguments[2]);
		} else {
			args.push("", arguments[1]);
			p = 2;
		}
		if (dojo.lang.isFunction(arguments[p])) {
			args.push(arguments[p], arguments[p + 1]);
		} else {
			args.push(null, arguments[p]);
		}
		dojo.widget._defineWidget.apply(this, args);
	}
};
dojo.widget.defineWidget.renderers = "html|svg|vml";
dojo.widget._defineWidget = function (widgetClass, renderer, superclasses, init, props) {
	var module = widgetClass.split(".");
	var type = module.pop();
	var regx = "\\.(" + (renderer ? renderer + "|" : "") + dojo.widget.defineWidget.renderers + ")\\.";
	var r = widgetClass.search(new RegExp(regx));
	module = (r < 0 ? module.join(".") : widgetClass.substr(0, r));
	dojo.widget.manager.registerWidgetPackage(module);
	var pos = module.indexOf(".");
	var nsName = (pos > -1) ? module.substring(0, pos) : module;
	props = (props) || {};
	props.widgetType = type;
	if ((!init) && (props["classConstructor"])) {
		init = props.classConstructor;
		delete props.classConstructor;
	}
	dojo.declare(widgetClass, superclasses, init, props);
};

/*
	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
*/



dojo.provide("dojo.xml.Parse");
dojo.xml.Parse = function () {
	var isIE = ((dojo.render.html.capable) && (dojo.render.html.ie));
	function getTagName(node) {
		try {
			return node.tagName.toLowerCase();
		}
		catch (e) {
			return "";
		}
	}
	function getDojoTagName(node) {
		var tagName = getTagName(node);
		if (!tagName) {
			return "";
		}
		if ((dojo.widget) && (dojo.widget.tags[tagName])) {
			return tagName;
		}
		var p = tagName.indexOf(":");
		if (p >= 0) {
			return tagName;
		}
		if (tagName.substr(0, 5) == "dojo:") {
			return tagName;
		}
		if (dojo.render.html.capable && dojo.render.html.ie && node.scopeName != "HTML") {
			return node.scopeName.toLowerCase() + ":" + tagName;
		}
		if (tagName.substr(0, 4) == "dojo") {
			return "dojo:" + tagName.substring(4);
		}
		var djt = node.getAttribute("dojoType") || node.getAttribute("dojotype");
		if (djt) {
			if (djt.indexOf(":") < 0) {
				djt = "dojo:" + djt;
			}
			return djt.toLowerCase();
		}
		djt = node.getAttributeNS && node.getAttributeNS(dojo.dom.dojoml, "type");
		if (djt) {
			return "dojo:" + djt.toLowerCase();
		}
		try {
			djt = node.getAttribute("dojo:type");
		}
		catch (e) {
		}
		if (djt) {
			return "dojo:" + djt.toLowerCase();
		}
		if ((dj_global["djConfig"]) && (!djConfig["ignoreClassNames"])) {
			var classes = node.className || node.getAttribute("class");
			if ((classes) && (classes.indexOf) && (classes.indexOf("dojo-") != -1)) {
				var aclasses = classes.split(" ");
				for (var x = 0, c = aclasses.length; x < c; x++) {
					if (aclasses[x].slice(0, 5) == "dojo-") {
						return "dojo:" + aclasses[x].substr(5).toLowerCase();
					}
				}
			}
		}
		return "";
	}
	this.parseElement = function (node, hasParentNodeSet, optimizeForDojoML, thisIdx) {
		var tagName = getTagName(node);
		if (isIE && tagName.indexOf("/") == 0) {
			return null;
		}
		try {
			var attr = node.getAttribute("parseWidgets");
			if (attr && attr.toLowerCase() == "false") {
				return {};
			}
		}
		catch (e) {
		}
		var process = true;
		if (optimizeForDojoML) {
			var dojoTagName = getDojoTagName(node);
			tagName = dojoTagName || tagName;
			process = Boolean(dojoTagName);
		}
		var parsedNodeSet = {};
		parsedNodeSet[tagName] = [];
		var pos = tagName.indexOf(":");
		if (pos > 0) {
			var ns = tagName.substring(0, pos);
			parsedNodeSet["ns"] = ns;
			if ((dojo.ns) && (!dojo.ns.allow(ns))) {
				process = false;
			}
		}
		if (process) {
			var attributeSet = this.parseAttributes(node);
			for (var attr in attributeSet) {
				if ((!parsedNodeSet[tagName][attr]) || (typeof parsedNodeSet[tagName][attr] != "array")) {
					parsedNodeSet[tagName][attr] = [];
				}
				parsedNodeSet[tagName][attr].push(attributeSet[attr]);
			}
			parsedNodeSet[tagName].nodeRef = node;
			parsedNodeSet.tagName = tagName;
			parsedNodeSet.index = thisIdx || 0;
		}
		var count = 0;
		for (var i = 0; i < node.childNodes.length; i++) {
			var tcn = node.childNodes.item(i);
			switch (tcn.nodeType) {
			  case dojo.dom.ELEMENT_NODE:
				var ctn = getDojoTagName(tcn) || getTagName(tcn);
				if (!parsedNodeSet[ctn]) {
					parsedNodeSet[ctn] = [];
				}
				parsedNodeSet[ctn].push(this.parseElement(tcn, true, optimizeForDojoML, count));
				if ((tcn.childNodes.length == 1) && (tcn.childNodes.item(0).nodeType == dojo.dom.TEXT_NODE)) {
					parsedNodeSet[ctn][parsedNodeSet[ctn].length - 1].value = tcn.childNodes.item(0).nodeValue;
				}
				count++;
				break;
			  case dojo.dom.TEXT_NODE:
				if (node.childNodes.length == 1) {
					parsedNodeSet[tagName].push({value:node.childNodes.item(0).nodeValue});
				}
				break;
			  default:
				break;
			}
		}
		return parsedNodeSet;
	};
	this.parseAttributes = function (node) {
		var parsedAttributeSet = {};
		var atts = node.attributes;
		var attnode, i = 0;
		while ((attnode = atts[i++])) {
			if (isIE) {
				if (!attnode) {
					continue;
				}
				if ((typeof attnode == "object") && (typeof attnode.nodeValue == "undefined") || (attnode.nodeValue == null) || (attnode.nodeValue == "")) {
					continue;
				}
			}
			var nn = attnode.nodeName.split(":");
			nn = (nn.length == 2) ? nn[1] : attnode.nodeName;
			parsedAttributeSet[nn] = {value:attnode.nodeValue};
		}
		return parsedAttributeSet;
	};
};

/*
	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
*/



dojo.provide("dojo.widget.html.layout");
dojo.widget.html.layout = function (container, children, layoutPriority) {
	dojo.html.addClass(container, "dojoLayoutContainer");
	children = dojo.lang.filter(children, function (child, idx) {
		child.idx = idx;
		return dojo.lang.inArray(["top", "bottom", "left", "right", "client", "flood"], child.layoutAlign);
	});
	if (layoutPriority && layoutPriority != "none") {
		var rank = function (child) {
			switch (child.layoutAlign) {
			  case "flood":
				return 1;
			  case "left":
			  case "right":
				return (layoutPriority == "left-right") ? 2 : 3;
			  case "top":
			  case "bottom":
				return (layoutPriority == "left-right") ? 3 : 2;
			  default:
				return 4;
			}
		};
		children.sort(function (a, b) {
			return (rank(a) - rank(b)) || (a.idx - b.idx);
		});
	}
	var f = {top:dojo.html.getPixelValue(container, "padding-top", true), left:dojo.html.getPixelValue(container, "padding-left", true)};
	dojo.lang.mixin(f, dojo.html.getContentBox(container));
	dojo.lang.forEach(children, function (child) {
		var elm = child.domNode;
		var pos = child.layoutAlign;
		with (elm.style) {
			left = f.left + "px";
			top = f.top + "px";
			bottom = "auto";
			right = "auto";
		}
		dojo.html.addClass(elm, "dojoAlign" + dojo.string.capitalize(pos));
		if ((pos == "top") || (pos == "bottom")) {
			dojo.html.setMarginBox(elm, {width:f.width});
			var h = dojo.html.getMarginBox(elm).height;
			f.height -= h;
			if (pos == "top") {
				f.top += h;
			} else {
				elm.style.top = f.top + f.height + "px";
			}
			if (child.onResized) {
				child.onResized();
			}
		} else {
			if (pos == "left" || pos == "right") {
				var w = dojo.html.getMarginBox(elm).width;
				if (child.resizeTo) {
					child.resizeTo(w, f.height);
				} else {
					dojo.html.setMarginBox(elm, {width:w, height:f.height});
				}
				f.width -= w;
				if (pos == "left") {
					f.left += w;
				} else {
					elm.style.left = f.left + f.width + "px";
				}
			} else {
				if (pos == "flood" || pos == "client") {
					if (child.resizeTo) {
						child.resizeTo(f.width, f.height);
					} else {
						dojo.html.setMarginBox(elm, {width:f.width, height:f.height});
					}
				}
			}
		}
	});
};
dojo.html.insertCssText(".dojoLayoutContainer{ position: relative; display: block; overflow: hidden; }\n" + "body .dojoAlignTop, body .dojoAlignBottom, body .dojoAlignLeft, body .dojoAlignRight { position: absolute; overflow: hidden; }\n" + "body .dojoAlignClient { position: absolute }\n" + ".dojoAlignClient { overflow: auto; }\n");


/*
	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
*/



dojo.provide("dojo.gfx.common");
dojo.lang.mixin(dojo.gfx, {defaultPath:{type:"path", path:""}, defaultPolyline:{type:"polyline", points:[]}, defaultRect:{type:"rect", x:0, y:0, width:100, height:100, r:0}, defaultEllipse:{type:"ellipse", cx:0, cy:0, rx:200, ry:100}, defaultCircle:{type:"circle", cx:0, cy:0, r:100}, defaultLine:{type:"line", x1:0, y1:0, x2:100, y2:100}, defaultImage:{type:"image", width:0, height:0, src:""}, defaultStroke:{color:"black", width:1, cap:"butt", join:4}, defaultLinearGradient:{type:"linear", x1:0, y1:0, x2:100, y2:100, colors:[{offset:0, color:"black"}, {offset:1, color:"white"}]}, defaultRadialGradient:{type:"radial", cx:0, cy:0, r:100, colors:[{offset:0, color:"black"}, {offset:1, color:"white"}]}, defaultPattern:{type:"pattern", x:0, y:0, width:0, height:0, src:""}, normalizeColor:function (color) {
	return (color instanceof dojo.gfx.color.Color) ? color : new dojo.gfx.color.Color(color);
}, normalizeParameters:function (existed, update) {
	if (update) {
		var empty = {};
		for (var x in existed) {
			if (x in update && !(x in empty)) {
				existed[x] = update[x];
			}
		}
	}
	return existed;
}, makeParameters:function (defaults, update) {
	if (!update) {
		return dojo.lang.shallowCopy(defaults, true);
	}
	var result = {};
	for (var i in defaults) {
		if (!(i in result)) {
			result[i] = dojo.lang.shallowCopy((i in update) ? update[i] : defaults[i], true);
		}
	}
	return result;
}, formatNumber:function (x, addSpace) {
	var val = x.toString();
	if (val.indexOf("e") >= 0) {
		val = x.toFixed(4);
	} else {
		var point = val.indexOf(".");
		if (point >= 0 && val.length - point > 5) {
			val = x.toFixed(4);
		}
	}
	if (x < 0) {
		return val;
	}
	return addSpace ? " " + val : val;
}, pathRegExp:/([A-Za-z]+)|(\d+(\.\d+)?)|(\.\d+)|(-\d+(\.\d+)?)|(-\.\d+)/g});
dojo.declare("dojo.gfx.Surface", null, {initializer:function () {
	this.rawNode = null;
}, getEventSource:function () {
	return this.rawNode;
}});
dojo.declare("dojo.gfx.Point", null, {});
dojo.declare("dojo.gfx.Rectangle", null, {});

/*
	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
*/



dojo.provide("dojo.widget.DomWidget");
dojo.widget._cssFiles = {};
dojo.widget._cssStrings = {};
dojo.widget._templateCache = {};
dojo.widget.defaultStrings = {dojoRoot:dojo.hostenv.getBaseScriptUri(), dojoWidgetModuleUri:dojo.uri.moduleUri("dojo.widget"), baseScriptUri:dojo.hostenv.getBaseScriptUri()};
dojo.widget.fillFromTemplateCache = function (obj, templatePath, templateString, avoidCache) {
	var tpath = templatePath || obj.templatePath;
	var tmplts = dojo.widget._templateCache;
	if (!tpath && !obj["widgetType"]) {
		do {
			var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++;
		} while (tmplts[dummyName]);
		obj.widgetType = dummyName;
	}
	var wt = tpath ? tpath.toString() : obj.widgetType;
	var ts = tmplts[wt];
	if (!ts) {
		tmplts[wt] = {"string":null, "node":null};
		if (avoidCache) {
			ts = {};
		} else {
			ts = tmplts[wt];
		}
	}
	if ((!obj.templateString) && (!avoidCache)) {
		obj.templateString = templateString || ts["string"];
	}
	if (obj.templateString) {
		obj.templateString = this._sanitizeTemplateString(obj.templateString);
	}
	if ((!obj.templateNode) && (!avoidCache)) {
		obj.templateNode = ts["node"];
	}
	if ((!obj.templateNode) && (!obj.templateString) && (tpath)) {
		var tstring = this._sanitizeTemplateString(dojo.hostenv.getText(tpath));
		obj.templateString = tstring;
		if (!avoidCache) {
			tmplts[wt]["string"] = tstring;
		}
	}
	if ((!ts["string"]) && (!avoidCache)) {
		ts.string = obj.templateString;
	}
};
dojo.widget._sanitizeTemplateString = function (tString) {
	if (tString) {
		tString = tString.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
		var matches = tString.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
		if (matches) {
			tString = matches[1];
		}
	} else {
		tString = "";
	}
	return tString;
};
dojo.widget._templateCache.dummyCount = 0;
dojo.widget.attachProperties = ["dojoAttachPoint", "id"];
dojo.widget.eventAttachProperty = "dojoAttachEvent";
dojo.widget.onBuildProperty = "dojoOnBuild";
dojo.widget.waiNames = ["waiRole", "waiState"];
dojo.widget.wai = {waiRole:{name:"waiRole", "namespace":"http://www.w3.org/TR/xhtml2", alias:"x2", prefix:"wairole:"}, waiState:{name:"waiState", "namespace":"http://www.w3.org/2005/07/aaa", alias:"aaa", prefix:""}, setAttr:function (node, ns, attr, value) {
	if (dojo.render.html.ie) {
		node.setAttribute(this[ns].alias + ":" + attr, this[ns].prefix + value);
	} else {
		node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix + value);
	}
}, getAttr:function (node, ns, attr) {
	if (dojo.render.html.ie) {
		return node.getAttribute(this[ns].alias + ":" + attr);
	} else {
		return node.getAttributeNS(this[ns]["namespace"], attr);
	}
}, removeAttr:function (node, ns, attr) {
	var success = true;
	if (dojo.render.html.ie) {
		success = node.removeAttribute(this[ns].alias + ":" + attr);
	} else {
		node.removeAttributeNS(this[ns]["namespace"], attr);
	}
	return success;
}};
dojo.widget.attachTemplateNodes = function (rootNode, targetObj, events) {
	var elementNodeType = dojo.dom.ELEMENT_NODE;
	function trim(str) {
		return str.replace(/^\s+|\s+$/g, "");
	}
	if (!rootNode) {
		rootNode = targetObj.domNode;
	}
	if (rootNode.nodeType != elementNodeType) {
		return;
	}
	var nodes = rootNode.all || rootNode.getElementsByTagName("*");
	var _this = targetObj;
	for (var x = -1; x < nodes.length; x++) {
		var baseNode = (x == -1) ? rootNode : nodes[x];
		var attachPoint = [];
		if (!targetObj.widgetsInTemplate || !baseNode.getAttribute("dojoType")) {
			for (var y = 0; y < this.attachProperties.length; y++) {
				var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
				if (tmpAttachPoint) {
					attachPoint = tmpAttachPoint.split(";");
					for (var z = 0; z < attachPoint.length; z++) {
						if (dojo.lang.isArray(targetObj[attachPoint[z]])) {
							targetObj[attachPoint[z]].push(baseNode);
						} else {
							targetObj[attachPoint[z]] = baseNode;
						}
					}
					break;
				}
			}
			var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
			if (attachEvent) {
				var evts = attachEvent.split(";");
				for (var y = 0; y < evts.length; y++) {
					if ((!evts[y]) || (!evts[y].length)) {
						continue;
					}
					var thisFunc = null;
					var tevt = trim(evts[y]);
					if (evts[y].indexOf(":") >= 0) {
						var funcNameArr = tevt.split(":");
						tevt = trim(funcNameArr[0]);
						thisFunc = trim(funcNameArr[1]);
					}
					if (!thisFunc) {
						thisFunc = tevt;
					}
					var tf = function () {
						var ntf = new String(thisFunc);
						return function (evt) {
							if (_this[ntf]) {
								_this[ntf](dojo.event.browser.fixEvent(evt, this));
							}
						};
					}();
					dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
				}
			}
			for (var y = 0; y < events.length; y++) {
				var evtVal = baseNode.getAttribute(events[y]);
				if ((evtVal) && (evtVal.length)) {
					var thisFunc = null;
					var domEvt = events[y].substr(4);
					thisFunc = trim(evtVal);
					var funcs = [thisFunc];
					if (thisFunc.indexOf(";") >= 0) {
						funcs = dojo.lang.map(thisFunc.split(";"), trim);
					}
					for (var z = 0; z < funcs.length; z++) {
						if (!funcs[z].length) {
							continue;
						}
						var tf = function () {
							var ntf = new String(funcs[z]);
							return function (evt) {
								if (_this[ntf]) {
									_this[ntf](dojo.event.browser.fixEvent(evt, this));
								}
							};
						}();
						dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
					}
				}
			}
		}
		var tmpltPoint = baseNode.getAttribute(this.templateProperty);
		if (tmpltPoint) {
			targetObj[tmpltPoint] = baseNode;
		}
		dojo.lang.forEach(dojo.widget.waiNames, function (name) {
			var wai = dojo.widget.wai[name];
			var val = baseNode.getAttribute(wai.name);
			if (val) {
				if (val.indexOf("-") == -1) {
					dojo.widget.wai.setAttr(baseNode, wai.name, "role", val);
				} else {
					var statePair = val.split("-");
					dojo.widget.wai.setAttr(baseNode, wai.name, statePair[0], statePair[1]);
				}
			}
		}, this);
		var onBuild = baseNode.getAttribute(this.onBuildProperty);
		if (onBuild) {
			eval("var node = baseNode; var widget = targetObj; " + onBuild);
		}
	}
};
dojo.widget.getDojoEventsFromStr = function (str) {
	var re = /(dojoOn([a-z]+)(\s?))=/gi;
	var evts = str ? str.match(re) || [] : [];
	var ret = [];
	var lem = {};
	for (var x = 0; x < evts.length; x++) {
		if (evts[x].length < 1) {
			continue;
		}
		var cm = evts[x].replace(/\s/, "");
		cm = (cm.slice(0, cm.length - 1));
		if (!lem[cm]) {
			lem[cm] = true;
			ret.push(cm);
		}
	}
	return ret;
};
dojo.declare("dojo.widget.DomWidget", dojo.widget.Widget, function () {
	if ((arguments.length > 0) && (typeof arguments[0] == "object")) {
		this.create(arguments[0]);
	}
}, {templateNode:null, templateString:null, templateCssString:null, preventClobber:false, domNode:null, containerNode:null, widgetsInTemplate:false, addChild:function (widget, overrideContainerNode, pos, ref, insertIndex) {
	if (!this.isContainer) {
		dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
		return null;
	} else {
		if (insertIndex == undefined) {
			insertIndex = this.children.length;
		}
		this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
		this.registerChild(widget, insertIndex);
	}
	return widget;
}, addWidgetAsDirectChild:function (widget, overrideContainerNode, pos, ref, insertIndex) {
	if ((!this.containerNode) && (!overrideContainerNode)) {
		this.containerNode = this.domNode;
	}
	var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
	if (!pos) {
		pos = "after";
	}
	if (!ref) {
		if (!cn) {
			cn = dojo.body();
		}
		ref = cn.lastChild;
	}
	if (!insertIndex) {
		insertIndex = 0;
	}
	widget.domNode.setAttribute("dojoinsertionindex", insertIndex);
	if (!ref) {
		cn.appendChild(widget.domNode);
	} else {
		if (pos == "insertAtIndex") {
			dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
		} else {
			if ((pos == "after") && (ref === cn.lastChild)) {
				cn.appendChild(widget.domNode);
			} else {
				dojo.dom.insertAtPosition(widget.domNode, cn, pos);
			}
		}
	}
}, registerChild:function (widget, insertionIndex) {
	widget.dojoInsertionIndex = insertionIndex;
	var idx = -1;
	for (var i = 0; i < this.children.length; i++) {
		if (this.children[i].dojoInsertionIndex <= insertionIndex) {
			idx = i;
		}
	}
	this.children.splice(idx + 1, 0, widget);
	widget.parent = this;
	widget.addedTo(this, idx + 1);
	delete dojo.widget.manager.topWidgets[widget.widgetId];
}, removeChild:function (widget) {
	dojo.dom.removeNode(widget.domNode);
	return dojo.widget.DomWidget.superclass.removeChild.call(this, widget);
}, getFragNodeRef:function (frag) {
	if (!frag) {
		return null;
	}
	if (!frag[this.getNamespacedType()]) {
		dojo.raise("Error: no frag for widget type " + this.getNamespacedType() + ", id " + this.widgetId + " (maybe a widget has set it's type incorrectly)");
	}
	return frag[this.getNamespacedType()]["nodeRef"];
}, postInitialize:function (args, frag, parentComp) {
	var sourceNodeRef = this.getFragNodeRef(frag);
	if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)) {
		parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "", args["dojoinsertionindex"], sourceNodeRef);
	} else {
		if (sourceNodeRef) {
			if (this.domNode && (this.domNode !== sourceNodeRef)) {
				this._sourceNodeRef = dojo.dom.replaceNode(sourceNodeRef, this.domNode);
			}
		}
	}
	if (parentComp) {
		parentComp.registerChild(this, args.dojoinsertionindex);
	} else {
		dojo.widget.manager.topWidgets[this.widgetId] = this;
	}
	if (this.widgetsInTemplate) {
		var parser = new dojo.xml.Parse();
		var subContainerNode;
		var subnodes = this.domNode.getElementsByTagName("*");
		for (var i = 0; i < subnodes.length; i++) {
			if (subnodes[i].getAttribute("dojoAttachPoint") == "subContainerWidget") {
				subContainerNode = subnodes[i];
			}
			if (subnodes[i].getAttribute("dojoType")) {
				subnodes[i].setAttribute("isSubWidget", true);
			}
		}
		if (this.isContainer && !this.containerNode) {
			if (subContainerNode) {
				var src = this.getFragNodeRef(frag);
				if (src) {
					dojo.dom.moveChildren(src, subContainerNode);
					frag["dojoDontFollow"] = true;
				}
			} else {
				dojo.debug("No subContainerWidget node can be found in template file for widget " + this);
			}
		}
		var templatefrag = parser.parseElement(this.domNode, null, true);
		dojo.widget.getParser().createSubComponents(templatefrag, this);
		var subwidgets = [];
		var stack = [this];
		var w;
		while ((w = stack.pop())) {
			for (var i = 0; i < w.children.length; i++) {
				var cwidget = w.children[i];
				if (cwidget._processedSubWidgets || !cwidget.extraArgs["issubwidget"]) {
					continue;
				}
				subwidgets.push(cwidget);
				if (cwidget.isContainer) {
					stack.push(cwidget);
				}
			}
		}
		for (var i = 0; i < subwidgets.length; i++) {
			var widget = subwidgets[i];
			if (widget._processedSubWidgets) {
				dojo.debug("This should not happen: widget._processedSubWidgets is already true!");
				return;
			}
			widget._processedSubWidgets = true;
			if (widget.extraArgs["dojoattachevent"]) {
				var evts = widget.extraArgs["dojoattachevent"].split(";");
				for (var j = 0; j < evts.length; j++) {
					var thisFunc = null;
					var tevt = dojo.string.trim(evts[j]);
					if (tevt.indexOf(":") >= 0) {
						var funcNameArr = tevt.split(":");
						tevt = dojo.string.trim(funcNameArr[0]);
						thisFunc = dojo.string.trim(funcNameArr[1]);
					}
					if (!thisFunc) {
						thisFunc = tevt;
					}
					if (dojo.lang.isFunction(widget[tevt])) {
						dojo.event.kwConnect({srcObj:widget, srcFunc:tevt, targetObj:this, targetFunc:thisFunc});
					} else {
						alert(tevt + " is not a function in widget " + widget);
					}
				}
			}
			if (widget.extraArgs["dojoattachpoint"]) {
				this[widget.extraArgs["dojoattachpoint"]] = widget;
			}
		}
	}
	if (this.isContainer && !frag["dojoDontFollow"]) {
		dojo.widget.getParser().createSubComponents(frag, this);
	}
}, buildRendering:function (args, frag) {
	var ts = dojo.widget._templateCache[this.widgetType];
	if (args["templatecsspath"]) {
		args["templateCssPath"] = args["templatecsspath"];
	}
	var cpath = args["templateCssPath"] || this.templateCssPath;
	if (cpath && !dojo.widget._cssFiles[cpath.toString()]) {
		if ((!this.templateCssString) && (cpath)) {
			this.templateCssString = dojo.hostenv.getText(cpath);
			this.templateCssPath = null;
		}
		dojo.widget._cssFiles[cpath.toString()] = true;
	}
	if ((this["templateCssString"]) && (!dojo.widget._cssStrings[this.templateCssString])) {
		dojo.html.insertCssText(this.templateCssString, null, cpath);
		dojo.widget._cssStrings[this.templateCssString] = true;
	}
	if ((!this.preventClobber) && ((this.templatePath) || (this.templateNode) || ((this["templateString"]) && (this.templateString.length)) || ((typeof ts != "undefined") && ((ts["string"]) || (ts["node"]))))) {
		this.buildFromTemplate(args, frag);
	} else {
		this.domNode = this.getFragNodeRef(frag);
	}
	this.fillInTemplate(args, frag);
}, buildFromTemplate:function (args, frag) {
	var avoidCache = false;
	if (args["templatepath"]) {
		args["templatePath"] = args["templatepath"];
	}
	dojo.widget.fillFromTemplateCache(this, args["templatePath"], null, avoidCache);
	var ts = dojo.widget._templateCache[this.templatePath ? this.templatePath.toString() : this.widgetType];
	if ((ts) && (!avoidCache)) {
		if (!this.templateString.length) {
			this.templateString = ts["string"];
		}
		if (!this.templateNode) {
			this.templateNode = ts["node"];
		}
	}
	var matches = false;
	var node = null;
	var tstr = this.templateString;
	if ((!this.templateNode) && (this.templateString)) {
		matches = this.templateString.match(/\$\{([^\}]+)\}/g);
		if (matches) {
			var hash = this.strings || {};
			for (var key in dojo.widget.defaultStrings) {
				if (dojo.lang.isUndefined(hash[key])) {
					hash[key] = dojo.widget.defaultStrings[key];
				}
			}
			for (var i = 0; i < matches.length; i++) {
				var key = matches[i];
				key = key.substring(2, key.length - 1);
				var kval = (key.substring(0, 5) == "this.") ? dojo.lang.getObjPathValue(key.substring(5), this) : hash[key];
				var value;
				if ((kval) || (dojo.lang.isString(kval))) {
					value = new String((dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval);
					while (value.indexOf("\"") > -1) {
						value = value.replace("\"", "&quot;");
					}
					tstr = tstr.replace(matches[i], value);
				}
			}
		} else {
			this.templateNode = this.createNodesFromText(this.templateString, true)[0];
			if (!avoidCache) {
				ts.node = this.templateNode;
			}
		}
	}
	if ((!this.templateNode) && (!matches)) {
		dojo.debug("DomWidget.buildFromTemplate: could not create template");
		return false;
	} else {
		if (!matches) {
			node = this.templateNode.cloneNode(true);
			if (!node) {
				return false;
			}
		} else {
			node = this.createNodesFromText(tstr, true)[0];
		}
	}
	this.domNode = node;
	this.attachTemplateNodes();
	if (this.isContainer && this.containerNode) {
		var src = this.getFragNodeRef(frag);
		if (src) {
			dojo.dom.moveChildren(src, this.containerNode);
		}
	}
}, attachTemplateNodes:function (baseNode, targetObj) {
	if (!baseNode) {
		baseNode = this.domNode;
	}
	if (!targetObj) {
		targetObj = this;
	}
	return dojo.widget.attachTemplateNodes(baseNode, targetObj, dojo.widget.getDojoEventsFromStr(this.templateString));
}, fillInTemplate:function () {
}, destroyRendering:function () {
	try {
		dojo.dom.destroyNode(this.domNode);
		delete this.domNode;
	}
	catch (e) {
	}
	if (this._sourceNodeRef) {
		try {
			dojo.dom.destroyNode(this._sourceNodeRef);
		}
		catch (e) {
		}
	}
}, createNodesFromText:function () {
	dojo.unimplemented("dojo.widget.DomWidget.createNodesFromText");
}});

/*
	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
*/



dojo.provide("dojo.charting.Series");
dojo.charting.Series = function (kwArgs) {
	var args = kwArgs || {length:1};
	this.dataSource = args.dataSource || null;
	this.bindings = {};
	this.color = args.color;
	this.label = args.label;
	if (args.bindings) {
		for (var p in args.bindings) {
			this.addBinding(p, args.bindings[p]);
		}
	}
};
dojo.extend(dojo.charting.Series, {bind:function (src, bindings) {
	this.dataSource = src;
	this.bindings = bindings;
}, addBinding:function (name, binding) {
	this.bindings[name] = binding;
}, evaluate:function (kwArgs) {
	var ret = [];
	var a = this.dataSource.getData();
	var l = a.length;
	var start = 0;
	var end = l;
	if (kwArgs) {
		if (kwArgs.between) {
			for (var i = 0; i < l; i++) {
				var fld = this.dataSource.getField(a[i], kwArgs.between.field);
				if (fld >= kwArgs.between.low && fld <= kwArgs.between.high) {
					var o = {src:a[i], series:this};
					for (var p in this.bindings) {
						o[p] = this.dataSource.getField(a[i], this.bindings[p]);
					}
					ret.push(o);
				}
			}
		} else {
			if (kwArgs.from || kwArgs.length) {
				if (kwArgs.from) {
					start = Math.max(kwArgs.from, 0);
					if (kwArgs.to) {
						end = Math.min(kwArgs.to, end);
					}
				} else {
					if (kwArgs.length < 0) {
						start = Math.max((end + length), 0);
					} else {
						end = Math.min((start + length), end);
					}
				}
				for (var i = start; i < end; i++) {
					var o = {src:a[i], series:this};
					for (var p in this.bindings) {
						o[p] = this.dataSource.getField(a[i], this.bindings[p]);
					}
					ret.push(o);
				}
			}
		}
	} else {
		for (var i = start; i < end; i++) {
			var o = {src:a[i], series:this};
			for (var p in this.bindings) {
				o[p] = this.dataSource.getField(a[i], this.bindings[p]);
			}
			ret.push(o);
		}
	}
	if (ret.length > 0 && typeof (ret[0].x) != "undefined") {
		ret.sort(function (a, b) {
			if (a.x > b.x) {
				return 1;
			}
			if (a.x < b.x) {
				return -1;
			}
			return 0;
		});
	}
	return ret;
}, trends:{createRange:function (values, len) {
	var idx = values.length - 1;
	var length = (len || values.length);
	return {"index":idx, "length":length, "start":Math.max(idx - length, 0)};
}, mean:function (values, len) {
	var range = this.createRange(values, len);
	if (range.index < 0) {
		return 0;
	}
	var total = 0;
	var count = 0;
	for (var i = range.index; i >= range.start; i--) {
		total += values[i].y;
		count++;
	}
	total /= Math.max(count, 1);
	return total;
}, variance:function (values, len) {
	var range = this.createRange(values, len);
	if (range.index < 0) {
		return 0;
	}
	var total = 0;
	var square = 0;
	var count = 0;
	for (var i = range.index; i >= range.start; i--) {
		total += values[i].y;
		square += Math.pow(values[i].y, 2);
		count++;
	}
	return (square / count) - Math.pow(total / count, 2);
}, standardDeviation:function (values, len) {
	return Math.sqrt(this.getVariance(values, len));
}, max:function (values, len) {
	var range = this.createRange(values, len);
	if (range.index < 0) {
		return 0;
	}
	var max = Number.MIN_VALUE;
	for (var i = range.index; i >= range.start; i--) {
		max = Math.max(values[i].y, max);
	}
	return max;
}, min:function (values, len) {
	var range = this.createRange(values, len);
	if (range.index < 0) {
		return 0;
	}
	var min = Number.MAX_VALUE;
	for (var i = range.index; i >= range.start; i--) {
		min = Math.min(values[i].y, min);
	}
	return min;
}, median:function (values, len) {
	var range = this.createRange(values, len);
	if (range.index < 0) {
		return 0;
	}
	var a = [];
	for (var i = range.index; i >= range.start; i--) {
		var b = false;
		for (var j = 0; j < a.length; j++) {
			if (values[i].y == a[j]) {
				b = true;
				break;
			}
		}
		if (!b) {
			a.push(values[i].y);
		}
	}
	a.sort();
	if (a.length > 0) {
		return a[Math.ceil(a.length / 2)];
	}
	return 0;
}, mode:function (values, len) {
	var range = this.createRange(values, len);
	if (range.index < 0) {
		return 0;
	}
	var o = {};
	var ret = 0;
	var median = Number.MIN_VALUE;
	for (var i = range.index; i >= range.start; i--) {
		if (!o[values[i].y]) {
			o[values[i].y] = 1;
		} else {
			o[values[i].y]++;
		}
	}
	for (var p in o) {
		if (median < o[p]) {
			median = o[p];
			ret = p;
		}
	}
	return ret;
}}});

/*
	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
*/



dojo.provide("dojo.gfx.shape");
dojo.declare("dojo.gfx.Shape", null, {initializer:function () {
	this.rawNode = null;
	this.shape = null;
	this.matrix = null;
	this.fillStyle = null;
	this.strokeStyle = null;
	this.bbox = null;
	this.parent = null;
	this.parentMatrix = null;
}, getNode:function () {
	return this.rawNode;
}, getShape:function () {
	return this.shape;
}, getTransform:function () {
	return this.matrix;
}, getFill:function () {
	return this.fillStyle;
}, getStroke:function () {
	return this.strokeStyle;
}, getParent:function () {
	return this.parent;
}, getBoundingBox:function () {
	return this.bbox;
}, getEventSource:function () {
	return this.rawNode;
}, setShape:function (shape) {
	return this;
}, setFill:function (fill) {
	return this;
}, setStroke:function (stroke) {
	return this;
}, moveToFront:function () {
	return this;
}, moveToBack:function () {
	return this;
}, setTransform:function (matrix) {
	this.matrix = dojo.gfx.matrix.clone(matrix ? dojo.gfx.matrix.normalize(matrix) : dojo.gfx.identity, true);
	return this._applyTransform();
}, applyRightTransform:function (matrix) {
	return matrix ? this.setTransform([this.matrix, matrix]) : this;
}, applyLeftTransform:function (matrix) {
	return matrix ? this.setTransform([matrix, this.matrix]) : this;
}, applyTransform:function (matrix) {
	return matrix ? this.setTransform([this.matrix, matrix]) : this;
}, remove:function (silently) {
	if (this.parent) {
		this.parent.remove(this, silently);
	}
	return this;
}, _setParent:function (parent, matrix) {
	this.parent = parent;
	return this._updateParentMatrix(matrix);
}, _updateParentMatrix:function (matrix) {
	this.parentMatrix = matrix ? dojo.gfx.matrix.clone(matrix) : null;
	return this._applyTransform();
}, _getRealMatrix:function () {
	return this.parentMatrix ? new dojo.gfx.matrix.Matrix2D([this.parentMatrix, this.matrix]) : this.matrix;
}});
dojo.declare("dojo.gfx.shape.VirtualGroup", dojo.gfx.Shape, {initializer:function () {
	this.children = [];
}, add:function (shape) {
	var oldParent = shape.getParent();
	if (oldParent) {
		oldParent.remove(shape, true);
	}
	this.children.push(shape);
	return shape._setParent(this, this._getRealMatrix());
}, remove:function (shape, silently) {
	for (var i = 0; i < this.children.length; ++i) {
		if (this.children[i] == shape) {
			if (silently) {
			} else {
				shape._setParent(null, null);
			}
			this.children.splice(i, 1);
			break;
		}
	}
	return this;
}, _applyTransform:function () {
	var matrix = this._getRealMatrix();
	for (var i = 0; i < this.children.length; ++i) {
		this.children[i]._updateParentMatrix(matrix);
	}
	return this;
}});
dojo.declare("dojo.gfx.shape.Rect", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultRect, true);
	this.attach(rawNode);
}, getBoundingBox:function () {
	return this.shape;
}});
dojo.declare("dojo.gfx.shape.Ellipse", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultEllipse, true);
	this.attach(rawNode);
}, getBoundingBox:function () {
	if (!this.bbox) {
		var shape = this.shape;
		this.bbox = {x:shape.cx - shape.rx, y:shape.cy - shape.ry, width:2 * shape.rx, height:2 * shape.ry};
	}
	return this.bbox;
}});
dojo.declare("dojo.gfx.shape.Circle", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultCircle, true);
	this.attach(rawNode);
}, getBoundingBox:function () {
	if (!this.bbox) {
		var shape = this.shape;
		this.bbox = {x:shape.cx - shape.r, y:shape.cy - shape.r, width:2 * shape.r, height:2 * shape.r};
	}
	return this.bbox;
}});
dojo.declare("dojo.gfx.shape.Line", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultLine, true);
	this.attach(rawNode);
}, getBoundingBox:function () {
	if (!this.bbox) {
		var shape = this.shape;
		this.bbox = {x:Math.min(shape.x1, shape.x2), y:Math.min(shape.y1, shape.y2), width:Math.abs(shape.x2 - shape.x1), height:Math.abs(shape.y2 - shape.y1)};
	}
	return this.bbox;
}});
dojo.declare("dojo.gfx.shape.Polyline", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultPolyline, true);
	this.attach(rawNode);
}, getBoundingBox:function () {
	if (!this.bbox && this.shape.points.length) {
		var p = this.shape.points;
		var l = p.length;
		var t = p[0];
		var bbox = {l:t.x, t:t.y, r:t.x, b:t.y};
		for (var i = 1; i < l; ++i) {
			t = p[i];
			if (bbox.l > t.x) {
				bbox.l = t.x;
			}
			if (bbox.r < t.x) {
				bbox.r = t.x;
			}
			if (bbox.t > t.y) {
				bbox.t = t.y;
			}
			if (bbox.b < t.y) {
				bbox.b = t.y;
			}
		}
		this.bbox = {x:bbox.l, y:bbox.t, width:bbox.r - bbox.l, height:bbox.b - bbox.t};
	}
	return this.bbox;
}});
dojo.declare("dojo.gfx.shape.Image", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultImage, true);
	this.attach(rawNode);
}, getBoundingBox:function () {
	if (!this.bbox) {
		var shape = this.shape;
		this.bbox = {x:0, y:0, width:shape.width, height:shape.height};
	}
	return this.bbox;
}});

/*
	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
*/



dojo.provide("dojo.widget.HtmlWidget");
dojo.declare("dojo.widget.HtmlWidget", dojo.widget.DomWidget, {templateCssPath:null, templatePath:null, lang:"", toggle:"plain", toggleDuration:150, initialize:function (args, frag) {
}, postMixInProperties:function (args, frag) {
	if (this.lang === "") {
		this.lang = null;
	}
	this.toggleObj = dojo.lfx.toggle[this.toggle.toLowerCase()] || dojo.lfx.toggle.plain;
}, createNodesFromText:function (txt, wrap) {
	return dojo.html.createNodesFromText(txt, wrap);
}, destroyRendering:function (finalize) {
	try {
		if (this.bgIframe) {
			this.bgIframe.remove();
			delete this.bgIframe;
		}
		if (!finalize && this.domNode) {
			dojo.event.browser.clean(this.domNode);
		}
		dojo.widget.HtmlWidget.superclass.destroyRendering.call(this);
	}
	catch (e) {
	}
}, isShowing:function () {
	return dojo.html.isShowing(this.domNode);
}, toggleShowing:function () {
	if (this.isShowing()) {
		this.hide();
	} else {
		this.show();
	}
}, show:function () {
	if (this.isShowing()) {
		return;
	}
	this.animationInProgress = true;
	this.toggleObj.show(this.domNode, this.toggleDuration, null, dojo.lang.hitch(this, this.onShow), this.explodeSrc);
}, onShow:function () {
	this.animationInProgress = false;
	this.checkSize();
}, hide:function () {
	if (!this.isShowing()) {
		return;
	}
	this.animationInProgress = true;
	this.toggleObj.hide(this.domNode, this.toggleDuration, null, dojo.lang.hitch(this, this.onHide), this.explodeSrc);
}, onHide:function () {
	this.animationInProgress = false;
}, _isResized:function (w, h) {
	if (!this.isShowing()) {
		return false;
	}
	var wh = dojo.html.getMarginBox(this.domNode);
	var width = w || wh.width;
	var height = h || wh.height;
	if (this.width == width && this.height == height) {
		return false;
	}
	this.width = width;
	this.height = height;
	return true;
}, checkSize:function () {
	if (!this._isResized()) {
		return;
	}
	this.onResized();
}, resizeTo:function (w, h) {
	dojo.html.setMarginBox(this.domNode, {width:w, height:h});
	if (this.isShowing()) {
		this.onResized();
	}
}, resizeSoon:function () {
	if (this.isShowing()) {
		dojo.lang.setTimeout(this, this.onResized, 0);
	}
}, onResized:function () {
	dojo.lang.forEach(this.children, function (child) {
		if (child.checkSize) {
			child.checkSize();
		}
	});
}});

/*
	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
*/



dojo.provide("dojo.charting.Plot");
dojo.charting.RenderPlotSeries = {Singly:"single", Grouped:"grouped"};
dojo.charting.Plot = function (xaxis, yaxis, series) {
	var id = "dojo-charting-plot-" + dojo.charting.Plot.count++;
	this.getId = function () {
		return id;
	};
	this.setId = function (key) {
		id = key;
	};
	this.axisX = null;
	this.axisY = null;
	this.series = [];
	this.dataNode = null;
	this.renderType = dojo.charting.RenderPlotSeries.Singly;
	if (xaxis) {
		this.setAxis(xaxis, "x");
	}
	if (yaxis) {
		this.setAxis(yaxis, "y");
	}
	if (series) {
		for (var i = 0; i < series.length; i++) {
			this.addSeries(series[i]);
		}
	}
};
dojo.charting.Plot.count = 0;
dojo.extend(dojo.charting.Plot, {addSeries:function (series, plotter) {
	if (series.plotter) {
		this.series.push(series);
	} else {
		this.series.push({data:series, plotter:plotter || dojo.charting.Plotters["Default"]});
	}
}, setAxis:function (axis, which) {
	if (which.toLowerCase() == "x") {
		this.axisX = axis;
	} else {
		if (which.toLowerCase() == "y") {
			this.axisY = axis;
		}
	}
}, getRanges:function () {
	var xmin, xmax, ymin, ymax;
	xmin = ymin = Number.MAX_VALUE;
	xmax = ymax = Number.MIN_VALUE;
	for (var i = 0; i < this.series.length; i++) {
		var values = this.series[i].data.evaluate();
		for (var j = 0; j < values.length; j++) {
			var comp = values[j];
			xmin = Math.min(comp.x, xmin);
			ymin = Math.min(comp.y, ymin);
			xmax = Math.max(comp.x, xmax);
			ymax = Math.max(comp.y, ymax);
		}
	}
	return {x:{upper:xmax, lower:xmin}, y:{upper:ymax, lower:ymin}, toString:function () {
		return "[ x:" + xmax + " - " + xmin + ", y:" + ymax + " - " + ymin + "]";
	}};
}, destroy:function () {
	var node = this.dataNode;
	while (node && node.childNodes && node.childNodes.length > 0) {
		node.removeChild(node.childNodes[0]);
	}
	this.dataNode = null;
}});

/*
	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
*/



dojo.provide("dojo.gfx.path");
dojo.declare("dojo.gfx.path.Path", dojo.gfx.Shape, {initializer:function (rawNode) {
	this.shape = dojo.lang.shallowCopy(dojo.gfx.defaultPath, true);
	this.segments = [];
	this.absolute = true;
	this.last = {};
	this.attach(rawNode);
}, setAbsoluteMode:function (mode) {
	this.absolute = typeof (mode) == "string" ? (mode == "absolute") : mode;
	return this;
}, getAbsoluteMode:function () {
	return this.absolute;
}, getBoundingBox:function () {
	return "l" in this.bbox ? {x:this.bbox.l, y:this.bbox.t, width:this.bbox.r - this.bbox.l, height:this.bbox.b - this.bbox.t} : null;
}, getLastPosition:function () {
	return "x" in this.last ? this.last : null;
}, _updateBBox:function (x, y) {
	if ("l" in this.bbox) {
		if (this.bbox.l > x) {
			this.bbox.l = x;
		}
		if (this.bbox.r < x) {
			this.bbox.r = x;
		}
		if (this.bbox.t > y) {
			this.bbox.t = y;
		}
		if (this.bbox.b < y) {
			this.bbox.b = y;
		}
	} else {
		this.bbox = {l:x, b:y, r:x, t:y};
	}
}, _updateWithSegment:function (segment) {
	var n = segment.args;
	var l = n.length;
	switch (segment.action) {
	  case "M":
	  case "L":
	  case "C":
	  case "S":
	  case "Q":
	  case "T":
		for (var i = 0; i < l; i += 2) {
			this._updateBBox(this.bbox, n[i], n[i + 1]);
		}
		this.last.x = n[l - 2];
		this.last.y = n[l - 1];
		this.absolute = true;
		break;
	  case "H":
		for (var i = 0; i < l; ++i) {
			this._updateBBox(this.bbox, n[i], this.last.y);
		}
		this.last.x = n[l - 1];
		this.absolute = true;
		break;
	  case "V":
		for (var i = 0; i < l; ++i) {
			this._updateBBox(this.bbox, this.last.x, n[i]);
		}
		this.last.y = n[l - 1];
		this.absolute = true;
		break;
	  case "m":
		var start = 0;
		if (!("x" in this.last)) {
			this._updateBBox(this.bbox, this.last.x = n[0], this.last.y = n[1]);
			start = 2;
		}
		for (var i = start; i < l; i += 2) {
			this._updateBBox(this.bbox, this.last.x += n[i], this.last.y += n[i + 1]);
		}
		this.absolute = false;
		break;
	  case "l":
	  case "t":
		for (var i = 0; i < l; i += 2) {
			this._updateBBox(this.bbox, this.last.x += n[i], this.last.y += n[i + 1]);
		}
		this.absolute = false;
		break;
	  case "h":
		for (var i = 0; i < l; ++i) {
			this._updateBBox(this.bbox, this.last.x += n[i], this.last.y);
		}
		this.absolute = false;
		break;
	  case "v":
		for (var i = 0; i < l; ++i) {
			this._updateBBox(this.bbox, this.last.x, this.last.y += n[i]);
		}
		this.absolute = false;
		break;
	  case "c":
		for (var i = 0; i < l; i += 6) {
			this._updateBBox(this.bbox, this.last.x + n[i], this.last.y + n[i + 1]);
			this._updateBBox(this.bbox, this.last.x + n[i + 2], this.last.y + n[i + 3]);
			this._updateBBox(this.bbox, this.last.x += n[i + 4], this.last.y += n[i + 5]);
		}
		this.absolute = false;
		break;
	  case "s":
	  case "q":
		for (var i = 0; i < l; i += 4) {
			this._updateBBox(this.bbox, this.last.x + n[i], this.last.y + n[i + 1]);
			this._updateBBox(this.bbox, this.last.x += n[i + 2], this.last.y += n[i + 3]);
		}
		this.absolute = false;
		break;
	  case "A":
		for (var i = 0; i < l; i += 7) {
			this._updateBBox(this.bbox, n[i + 5], n[i + 6]);
		}
		this.last.x = n[l - 2];
		this.last.y = n[l - 1];
		this.absolute = true;
		break;
	  case "a":
		for (var i = 0; i < l; i += 7) {
			this._updateBBox(this.bbox, this.last.x += n[i + 5], this.last.y += n[i + 6]);
		}
		this.absolute = false;
		break;
	}
	var path = [segment.action];
	for (var i = 0; i < l; ++i) {
		path.push(dojo.gfx.formatNumber(n[i], true));
	}
	if (typeof (this.shape.path) == "string") {
		this.shape.path += path.join("");
	} else {
		this.shape.path = this.shape.path.concat(path);
	}
}, _validSegments:{m:2, l:2, h:1, v:1, c:6, s:4, q:4, t:2, a:7, z:0}, _pushSegment:function (action, args) {
	var group = this._validSegments[action.toLowerCase()];
	if (typeof (group) == "number") {
		if (group) {
			if (args.length >= group) {
				var segment = {action:action, args:args.slice(0, args.length - args.length % group)};
				this.segments.push(segment);
				this._updateWithSegment(segment);
			}
		} else {
			var segment = {action:action, args:[]};
			this.segments.push(segment);
			this._updateWithSegment(segment);
		}
	}
}, _collectArgs:function (array, args) {
	for (var i = 0; i < args.length; ++i) {
		var t = args[i];
		if (typeof (t) == "boolean") {
			array.push(t ? 1 : 0);
		} else {
			if (typeof (t) == "number") {
				array.push(t);
			} else {
				if (t instanceof Array) {
					this._collectArgs(array, t);
				} else {
					if ("x" in t && "y" in t) {
						array.push(t.x);
						array.push(t.y);
					}
				}
			}
		}
	}
}, moveTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "M" : "m", args);
	return this;
}, lineTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "L" : "l", args);
	return this;
}, hLineTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "H" : "h", args);
	return this;
}, vLineTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "V" : "v", args);
	return this;
}, curveTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "C" : "c", args);
	return this;
}, smoothCurveTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "S" : "s", args);
	return this;
}, qCurveTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "Q" : "q", args);
	return this;
}, qSmoothCurveTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	this._pushSegment(this.absolute ? "T" : "t", args);
	return this;
}, arcTo:function () {
	var args = [];
	this._collectArgs(args, arguments);
	for (var i = 2; i < args.length; i += 7) {
		args[i] = -args[i];
	}
	this._pushSegment(this.absolute ? "A" : "a", args);
	return this;
}, closePath:function () {
	this._pushSegment("Z", []);
	return this;
}, _setPath:function (path) {
	var p = path.match(dojo.gfx.pathRegExp);
	this.segments = [];
	this.absolute = true;
	this.bbox = {};
	this.last = {};
	if (!p) {
		return;
	}
	var action = "";
	var args = [];
	for (var i = 0; i < p.length; ++i) {
		var t = p[i];
		var x = parseFloat(t);
		if (isNaN(x)) {
			if (action) {
				this._pushSegment(action, args);
			}
			args = [];
			action = t;
		} else {
			args.push(x);
		}
	}
	this._pushSegment(action, args);
}, setShape:function (newShape) {
	this.shape = dojo.gfx.makeParameters(this.shape, typeof (newShape) == "string" ? {path:newShape} : newShape);
	var path = this.shape.path;
	this.shape.path = [];
	this._setPath(path);
	this.shape.path = this.shape.path.join("");
	return this;
}, _2PI:Math.PI * 2});

/*
	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
*/



dojo.provide("dojo.widget.ContentPane");
dojo.widget.defineWidget("dojo.widget.ContentPane", dojo.widget.HtmlWidget, function () {
	this._styleNodes = [];
	this._onLoadStack = [];
	this._onUnloadStack = [];
	this._callOnUnload = false;
	this._ioBindObj;
	this.scriptScope;
	this.bindArgs = {};
}, {isContainer:true, adjustPaths:true, href:"", method: "get", postData: {}, extractContent:true, parseContent:true, cacheContent:true, preload:false, refreshOnShow:false, handler:"", executeScripts:false, scriptSeparation:true, loadingMessage:"Cargando...", isLoaded:false, postCreate:function (args, frag, parentComp) {
	if (this.handler !== "") {
		this.setHandler(this.handler);
	}
	if (this.isShowing() || this.preload) {
		this.loadContents();
	}
}, show:function () {
	if (this.refreshOnShow) {
		this.refresh();
	} else {
		this.loadContents();
	}
	dojo.widget.ContentPane.superclass.show.call(this);
}, refresh:function () {
	this.isLoaded = false;
	this.loadContents();
}, loadContents:function () {
	if (this.isLoaded) {
		return;
	}
	if (dojo.lang.isFunction(this.handler)) {
		this._runHandler();
	} else {
		if (this.href != "") {
			this._downloadExternalContent(this.href, this.cacheContent && !this.refreshOnShow);
		}
	}
}, setUrl:function (url) {
	this.href = url;
	this.isLoaded = false;
	if (this.preload || this.isShowing()) {
		this.loadContents();
	}
}, abort:function () {
	var bind = this._ioBindObj;
	if (!bind || !bind.abort) {
		return;
	}
	bind.abort();
	delete this._ioBindObj;
}, _downloadExternalContent:function (url, useCache) {
	this.abort();
	this._handleDefaults(this.loadingMessage, "onDownloadStart");
	var self = this;	
	this._ioBindObj = dojo.io.bind(this._cacheSetting({url:url, method: this.method, content: this.postData, mimetype:"text/html", handler:function (type, data, xhr) {
		delete self._ioBindObj;
		if (type == "load") {
			self.onDownloadEnd.call(self, url, data);
		} else {
			var e = {responseText:xhr.responseText, status:xhr.status, statusText:xhr.statusText, responseHeaders:xhr.getAllResponseHeaders(), text:"Error loading '" + url + "' (" + xhr.status + " " + xhr.statusText + ")"};
			self._handleDefaults.call(self, e, "onDownloadError");
			self.onLoad();
		}
	}}, useCache));
}, _cacheSetting:function (bindObj, useCache) {
	for (var x in this.bindArgs) {
		if (dojo.lang.isUndefined(bindObj[x])) {
			bindObj[x] = this.bindArgs[x];
		}
	}
	if (dojo.lang.isUndefined(bindObj.useCache)) {
		bindObj.useCache = useCache;
	}
	if (dojo.lang.isUndefined(bindObj.preventCache)) {
		bindObj.preventCache = !useCache;
	}
	if (dojo.lang.isUndefined(bindObj.mimetype)) {
		bindObj.mimetype = "text/html";
	}
	return bindObj;
}, onLoad:function (e) {
	this._runStack("_onLoadStack");
	this.isLoaded = true;
}, onUnLoad:function (e) {
	dojo.deprecated(this.widgetType + ".onUnLoad, use .onUnload (lowercased load)", 0.5);
}, onUnload:function (e) {
	this._runStack("_onUnloadStack");
	delete this.scriptScope;
	if (this.onUnLoad !== dojo.widget.ContentPane.prototype.onUnLoad) {
		this.onUnLoad.apply(this, arguments);
	}
}, _runStack:function (stName) {
	var st = this[stName];
	var err = "";
	var scope = this.scriptScope || window;
	for (var i = 0; i < st.length; i++) {
		try {
			st[i].call(scope);
		}
		catch (e) {
			err += "\n" + st[i] + " failed: " + e.description;
		}
	}
	this[stName] = [];
	if (err.length) {
		var name = (stName == "_onLoadStack") ? "addOnLoad" : "addOnUnLoad";
		this._handleDefaults(name + " failure\n " + err, "onExecError", "debug");
	}
}, addOnLoad:function (obj, func) {
	this._pushOnStack(this._onLoadStack, obj, func);
}, addOnUnload:function (obj, func) {
	this._pushOnStack(this._onUnloadStack, obj, func);
}, addOnUnLoad:function () {
	dojo.deprecated(this.widgetType + ".addOnUnLoad, use addOnUnload instead. (lowercased Load)", 0.5);
	this.addOnUnload.apply(this, arguments);
}, _pushOnStack:function (stack, obj, func) {
	if (typeof func == "undefined") {
		stack.push(obj);
	} else {
		stack.push(function () {
			obj[func]();
		});
	}
}, destroy:function () {
	this.onUnload();
	dojo.widget.ContentPane.superclass.destroy.call(this);
}, onExecError:function (e) {
}, onContentError:function (e) {
}, onDownloadError:function (e) {
}, onDownloadStart:function (e) {
}, onDownloadEnd:function (url, data) {
	data = this.splitAndFixPaths(data, url);
	this.setContent(data);
}, _handleDefaults:function (e, handler, messType) {
	if (!handler) {
		handler = "onContentError";
	}
	if (dojo.lang.isString(e)) {
		e = {text:e};
	}
	if (!e.text) {
		e.text = e.toString();
	}
	e.toString = function () {
		return this.text;
	};
	if (typeof e.returnValue != "boolean") {
		e.returnValue = true;
	}
	if (typeof e.preventDefault != "function") {
		e.preventDefault = function () {
			this.returnValue = false;
		};
	}
	this[handler](e);
	if (e.returnValue) {
		switch (messType) {
		  case true:
		  case "alert":
			alert(e.toString());
			break;
		  case "debug":
			dojo.debug(e.toString());
			break;
		  default:
			if (this._callOnUnload) {
				this.onUnload();
			}
			this._callOnUnload = false;
			if (arguments.callee._loopStop) {
				dojo.debug(e.toString());
			} else {
				arguments.callee._loopStop = true;
				this._setContent(e.toString());
			}
		}
	}
	arguments.callee._loopStop = false;
}, splitAndFixPaths:function (s, url) {
	var titles = [], scripts = [], tmp = [];
	var match = [], requires = [], attr = [], styles = [];
	var str = "", path = "", fix = "", tagFix = "", tag = "", origPath = "";
	if (!url) {
		url = "./";
	}
	if (s) {
		var regex = /<title[^>]*>([\s\S]*?)<\/title>/i;
		while (match = regex.exec(s)) {
			titles.push(match[1]);
			s = s.substring(0, match.index) + s.substr(match.index + match[0].length);
		}
		if (this.adjustPaths) {
			var regexFindTag = /<[a-z][a-z0-9]*[^>]*\s(?:(?:src|href|style)=[^>])+[^>]*>/i;
			var regexFindAttr = /\s(src|href|style)=(['"]?)([\w()\[\]\/.,\\'"-:;#=&?\s@]+?)\2/i;
			var regexProtocols = /^(?:[#]|(?:(?:https?|ftps?|file|javascript|mailto|news):))/;
			while (tag = regexFindTag.exec(s)) {
				str += s.substring(0, tag.index);
				s = s.substring((tag.index + tag[0].length), s.length);
				tag = tag[0];
				tagFix = "";
				while (attr = regexFindAttr.exec(tag)) {
					path = "";
					origPath = attr[3];
					switch (attr[1].toLowerCase()) {
					  case "src":
					  case "href":
						if (regexProtocols.exec(origPath)) {
							path = origPath;
						} else {
							path = (new dojo.uri.Uri(url, origPath).toString());
						}
						break;
					  case "style":
						path = dojo.html.fixPathsInCssText(origPath, url);
						break;
					  default:
						path = origPath;
					}
					fix = " " + attr[1] + "=" + attr[2] + path + attr[2];
					tagFix += tag.substring(0, attr.index) + fix;
					tag = tag.substring((attr.index + attr[0].length), tag.length);
				}
				str += tagFix + tag;
			}
			s = str + s;
		}
		regex = /(?:<(style)[^>]*>([\s\S]*?)<\/style>|<link ([^>]*rel=['"]?stylesheet['"]?[^>]*)>)/i;
		while (match = regex.exec(s)) {
			if (match[1] && match[1].toLowerCase() == "style") {
				styles.push(dojo.html.fixPathsInCssText(match[2], url));
			} else {
				if (attr = match[3].match(/href=(['"]?)([^'">]*)\1/i)) {
					styles.push({path:attr[2]});
				}
			}
			s = s.substring(0, match.index) + s.substr(match.index + match[0].length);
		}
		var regex = /<script([^>]*)>([\s\S]*?)<\/script>/i;
		var regexSrc = /src=(['"]?)([^"']*)\1/i;
		var regexDojoJs = /.*(\bdojo\b\.js(?:\.uncompressed\.js)?)$/;
		var regexInvalid = /(?:var )?\bdjConfig\b(?:[\s]*=[\s]*\{[^}]+\}|\.[\w]*[\s]*=[\s]*[^;\n]*)?;?|dojo\.hostenv\.writeIncludes\(\s*\);?/g;
		var regexRequires = /dojo\.(?:(?:require(?:After)?(?:If)?)|(?:widget\.(?:manager\.)?registerWidgetPackage)|(?:(?:hostenv\.)?setModulePrefix|registerModulePath)|defineNamespace)\((['"]).*?\1\)\s*;?/;
		while (match = regex.exec(s)) {
			if (this.executeScripts && match[1]) {
				if (attr = regexSrc.exec(match[1])) {
					if (regexDojoJs.exec(attr[2])) {
						dojo.debug("Security note! inhibit:" + attr[2] + " from  being loaded again.");
					} else {
						scripts.push({path:attr[2]});
					}
				}
			}
			if (match[2]) {
				var sc = match[2].replace(regexInvalid, "");
				if (!sc) {
					continue;
				}
				while (tmp = regexRequires.exec(sc)) {
					requires.push(tmp[0]);
					sc = sc.substring(0, tmp.index) + sc.substr(tmp.index + tmp[0].length);
				}
				if (this.executeScripts) {
					scripts.push(sc);
				}
			}
			s = s.substr(0, match.index) + s.substr(match.index + match[0].length);
		}
		if (this.extractContent) {
			match = s.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
			if (match) {
				s = match[1];
			}
		}
		if (this.executeScripts && this.scriptSeparation) {
			var regex = /(<[a-zA-Z][a-zA-Z0-9]*\s[^>]*?\S=)((['"])[^>]*scriptScope[^>]*>)/;
			var regexAttr = /([\s'";:\(])scriptScope(.*)/;
			str = "";
			while (tag = regex.exec(s)) {
				tmp = ((tag[3] == "'") ? "\"" : "'");
				fix = "";
				str += s.substring(0, tag.index) + tag[1];
				while (attr = regexAttr.exec(tag[2])) {
					tag[2] = tag[2].substring(0, attr.index) + attr[1] + "dojo.widget.byId(" + tmp + this.widgetId + tmp + ").scriptScope" + attr[2];
				}
				str += tag[2];
				s = s.substr(tag.index + tag[0].length);
			}
			s = str + s;
		}
	}
	return {"xml":s, "styles":styles, "titles":titles, "requires":requires, "scripts":scripts, "url":url};
}, _setContent:function (cont) {
	this.destroyChildren();
	for (var i = 0; i < this._styleNodes.length; i++) {
		if (this._styleNodes[i] && this._styleNodes[i].parentNode) {
			this._styleNodes[i].parentNode.removeChild(this._styleNodes[i]);
		}
	}
	this._styleNodes = [];
	try {
		var node = this.containerNode || this.domNode;
		while (node.firstChild) {
			dojo.html.destroyNode(node.firstChild);
		}
		if (typeof cont != "string") {
			node.appendChild(cont);
		} else {
			node.innerHTML = cont;
		}
	}
	catch (e) {
		e.text = "Couldn't load content:" + e.description;
		this._handleDefaults(e, "onContentError");
	}
}, setContent:function (data) {
	this.abort();
	if (this._callOnUnload) {
		this.onUnload();
	}
	this._callOnUnload = true;
	if (!data || dojo.html.isNode(data)) {
		this._setContent(data);
		this.onResized();
		this.onLoad();
	} else {
		if (typeof data.xml != "string") {
			this.href = "";
			data = this.splitAndFixPaths(data);
		}
		this._setContent(data.xml);
		for (var i = 0; i < data.styles.length; i++) {
			if (data.styles[i].path) {
				this._styleNodes.push(dojo.html.insertCssFile(data.styles[i].path, dojo.doc(), false, true));
			} else {
				this._styleNodes.push(dojo.html.insertCssText(data.styles[i]));
			}
		}
		if (this.parseContent) {
			for (var i = 0; i < data.requires.length; i++) {
				try {
					eval(data.requires[i]);
				}
				catch (e) {
					e.text = "ContentPane: error in package loading calls, " + (e.description || e);
					this._handleDefaults(e, "onContentError", "debug");
				}
			}
		}
		var _self = this;
		function asyncParse() {
			if (_self.executeScripts) {
				_self._executeScripts(data.scripts);
			}
			if (_self.parseContent) {
				var node = _self.containerNode || _self.domNode;
				var parser = new dojo.xml.Parse();
				var frag = parser.parseElement(node, null, true);
				dojo.widget.getParser().createSubComponents(frag, _self);
			}
			_self.onResized();
			_self.onLoad();
		}
		if (dojo.hostenv.isXDomain && data.requires.length) {
			dojo.addOnLoad(asyncParse);
		} else {
			asyncParse();
		}
	}
}, setHandler:function (handler) {
	var fcn = dojo.lang.isFunction(handler) ? handler : window[handler];
	if (!dojo.lang.isFunction(fcn)) {
		this._handleDefaults("Unable to set handler, '" + handler + "' not a function.", "onExecError", true);
		return;
	}
	this.handler = function () {
		return fcn.apply(this, arguments);
	};
}, _runHandler:function () {
	var ret = true;
	if (dojo.lang.isFunction(this.handler)) {
		this.handler(this, this.domNode);
		ret = false;
	}
	this.onLoad();
	return ret;
}, _executeScripts:function (scripts) {
	var self = this;
	var tmp = "", code = "";
	for (var i = 0; i < scripts.length; i++) {
		if (scripts[i].path) {
			dojo.io.bind(this._cacheSetting({"url":scripts[i].path, "load":function (type, scriptStr) {
				dojo.lang.hitch(self, tmp = ";" + scriptStr);
			}, "error":function (type, error) {
				error.text = type + " downloading remote script";
				self._handleDefaults.call(self, error, "onExecError", "debug");
			}, "mimetype":"text/plain", "sync":true}, this.cacheContent));
			code += tmp;
		} else {
			code += scripts[i];
		}
	}
	try {
		if (this.scriptSeparation) {
			delete this.scriptScope;
			this.scriptScope = new (new Function("_container_", code + "; return this;"))(self);
		} else {
			var djg = dojo.global();
			if (djg.execScript) {
				djg.execScript(code);
			} else {
				var djd = dojo.doc();
				var sc = djd.createElement("script");
				sc.appendChild(djd.createTextNode(code));
				(this.containerNode || this.domNode).appendChild(sc);
			}
		}
	}
	catch (e) {
		e.text = "Error running scripts from content:\n" + e.description;
		this._handleDefaults(e, "onExecError", "debug");
	}
}});

/*
	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
*/



dojo.provide("dojo.widget.PageContainer");
dojo.widget.defineWidget("dojo.widget.PageContainer", dojo.widget.HtmlWidget, {isContainer:true, doLayout:true, templateString:"<div dojoAttachPoint='containerNode'></div>", selectedChild:"", fillInTemplate:function (args, frag) {
	var source = this.getFragNodeRef(frag);
	dojo.html.copyStyle(this.domNode, source);
	dojo.widget.PageContainer.superclass.fillInTemplate.apply(this, arguments);
}, postCreate:function (args, frag) {
	if (this.children.length) {
		dojo.lang.forEach(this.children, this._setupChild, this);
		var initialChild;
		if (this.selectedChild) {
			this.selectChild(this.selectedChild);
		} else {
			for (var i = 0; i < this.children.length; i++) {
				if (this.children[i].selected) {
					this.selectChild(this.children[i]);
					break;
				}
			}
			if (!this.selectedChildWidget) {
				this.selectChild(this.children[0]);
			}
		}
	}
}, addChild:function (child) {
	dojo.widget.PageContainer.superclass.addChild.apply(this, arguments);
	this._setupChild(child);
	this.onResized();
	if (!this.selectedChildWidget) {
		this.selectChild(child);
	}
}, _setupChild:function (page) {
	page.hide();
	page.domNode.style.position = "relative";
	dojo.event.topic.publish(this.widgetId + "-addChild", page);
}, removeChild:function (page) {
	dojo.widget.PageContainer.superclass.removeChild.apply(this, arguments);
	if (this._beingDestroyed) {
		return;
	}
	dojo.event.topic.publish(this.widgetId + "-removeChild", page);
	this.onResized();
	if (this.selectedChildWidget === page) {
		this.selectedChildWidget = undefined;
		if (this.children.length > 0) {
			this.selectChild(this.children[0], true);
		}
	}
}, selectChild:function (page, callingWidget) {
	page = dojo.widget.byId(page);
	this.correspondingPageButton = callingWidget;
	if (this.selectedChildWidget) {
		this._hideChild(this.selectedChildWidget);
	}
	this.selectedChildWidget = page;
	this.selectedChild = page.widgetId;
	this._showChild(page);
	page.isFirstChild = (page == this.children[0]);
	page.isLastChild = (page == this.children[this.children.length - 1]);
	dojo.event.topic.publish(this.widgetId + "-selectChild", page);
}, forward:function () {
	var index = dojo.lang.find(this.children, this.selectedChildWidget);
	this.selectChild(this.children[index + 1]);
}, back:function () {
	var index = dojo.lang.find(this.children, this.selectedChildWidget);
	this.selectChild(this.children[index - 1]);
}, onResized:function () {
	if (this.doLayout && this.selectedChildWidget) {
		with (this.selectedChildWidget.domNode.style) {
			top = dojo.html.getPixelValue(this.containerNode, "padding-top", true);
			left = dojo.html.getPixelValue(this.containerNode, "padding-left", true);
		}
		var content = dojo.html.getContentBox(this.containerNode);
		this.selectedChildWidget.resizeTo(content.width, content.height);
	}
}, _showChild:function (page) {
	if (this.doLayout) {
		var content = dojo.html.getContentBox(this.containerNode);
		page.resizeTo(content.width, content.height);
	}
	page.selected = true;
	page.show();
}, _hideChild:function (page) {
	page.selected = false;
	page.hide();
}, closeChild:function (page) {
	var remove = page.onClose(this, page);
	if (remove) {
		this.removeChild(page);
		page.destroy();
	}
}, destroy:function () {
	this._beingDestroyed = true;
	dojo.event.topic.destroy(this.widgetId + "-addChild");
	dojo.event.topic.destroy(this.widgetId + "-removeChild");
	dojo.event.topic.destroy(this.widgetId + "-selectChild");
	dojo.widget.PageContainer.superclass.destroy.apply(this, arguments);
}});
dojo.widget.defineWidget("dojo.widget.PageController", dojo.widget.HtmlWidget, {templateString:"<span wairole='tablist' dojoAttachEvent='onKey'></span>", isContainer:true, containerId:"", buttonWidget:"PageButton", "class":"dojoPageController", fillInTemplate:function () {
	dojo.html.addClass(this.domNode, this["class"]);
	dojo.widget.wai.setAttr(this.domNode, "waiRole", "role", "tablist");
}, postCreate:function () {
	this.pane2button = {};
	var container = dojo.widget.byId(this.containerId);
	if (container) {
		dojo.lang.forEach(container.children, this.onAddChild, this);
	}
	dojo.event.topic.subscribe(this.containerId + "-addChild", this, "onAddChild");
	dojo.event.topic.subscribe(this.containerId + "-removeChild", this, "onRemoveChild");
	dojo.event.topic.subscribe(this.containerId + "-selectChild", this, "onSelectChild");
}, destroy:function () {
	dojo.event.topic.unsubscribe(this.containerId + "-addChild", this, "onAddChild");
	dojo.event.topic.unsubscribe(this.containerId + "-removeChild", this, "onRemoveChild");
	dojo.event.topic.unsubscribe(this.containerId + "-selectChild", this, "onSelectChild");
	dojo.widget.PageController.superclass.destroy.apply(this, arguments);
}, onAddChild:function (page) {
	var button = dojo.widget.createWidget(this.buttonWidget, {label:page.label, closeButton:page.closable});
	this.addChild(button);
	this.domNode.appendChild(button.domNode);
	this.pane2button[page] = button;
	page.controlButton = button;
	var _this = this;
	dojo.event.connect(button, "onClick", function () {
		_this.onButtonClick(page);
	});
	dojo.event.connect(button, "onCloseButtonClick", function () {
		_this.onCloseButtonClick(page);
	});
}, onRemoveChild:function (page) {
	if (this._currentChild == page) {
		this._currentChild = null;
	}
	var button = this.pane2button[page];
	if (button) {
		button.destroy();
	}
	this.pane2button[page] = null;
}, onSelectChild:function (page) {
	if (this._currentChild) {
		var oldButton = this.pane2button[this._currentChild];
		oldButton.clearSelected();
	}
	var newButton = this.pane2button[page];
	newButton.setSelected();
	this._currentChild = page;
}, onButtonClick:function (page) {
	var container = dojo.widget.byId(this.containerId);
	container.selectChild(page, false, this);
}, onCloseButtonClick:function (page) {
	var container = dojo.widget.byId(this.containerId);
	container.closeChild(page);
}, onKey:function (evt) {
	if ((evt.keyCode == evt.KEY_RIGHT_ARROW) || (evt.keyCode == evt.KEY_LEFT_ARROW)) {
		var current = 0;
		var next = null;
		var current = dojo.lang.find(this.children, this.pane2button[this._currentChild]);
		if (evt.keyCode == evt.KEY_RIGHT_ARROW) {
			next = this.children[(current + 1) % this.children.length];
		} else {
			next = this.children[(current + (this.children.length - 1)) % this.children.length];
		}
		dojo.event.browser.stopEvent(evt);
		next.onClick();
	}
}});
dojo.widget.defineWidget("dojo.widget.PageButton", dojo.widget.HtmlWidget, {templateString:"<span class='item'>" + "<span dojoAttachEvent='onClick' dojoAttachPoint='titleNode' class='selectButton'>${this.label}</span>" + "<span dojoAttachEvent='onClick:onCloseButtonClick' class='closeButton'>[X]</span>" + "</span>", label:"foo", closeButton:false, onClick:function () {
	this.focus();
}, onCloseButtonMouseOver:function () {
	dojo.html.addClass(this.closeButtonNode, "closeHover");
}, onCloseButtonMouseOut:function () {
	dojo.html.removeClass(this.closeButtonNode, "closeHover");
}, onCloseButtonClick:function (evt) {
}, setSelected:function () {
	dojo.html.addClass(this.domNode, "current");
	this.titleNode.setAttribute("tabIndex", "0");
}, clearSelected:function () {
	dojo.html.removeClass(this.domNode, "current");
	this.titleNode.setAttribute("tabIndex", "-1");
}, focus:function () {
	if (this.titleNode.focus) {
		this.titleNode.focus();
	}
}});
dojo.lang.extend(dojo.widget.Widget, {label:"", selected:false, closable:false, onClose:function () {
	return true;
}});

/*
	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
*/



dojo.provide("dojo.charting.PlotArea");
dojo.charting.PlotArea = function () {
	var id = "dojo-charting-plotarea-" + dojo.charting.PlotArea.count++;
	this.getId = function () {
		return id;
	};
	this.setId = function (key) {
		id = key;
	};
	this.areaType = "standard";
	this.plots = [];
	this.size = {width:600, height:400};
	this.padding = {top:10, right:10, bottom:20, left:20};
	this.nodes = {main:null, area:null, background:null, axes:null, plots:null};
	this._color = {h:140, s:120, l:120, step:27};
};
dojo.charting.PlotArea.count = 0;
dojo.extend(dojo.charting.PlotArea, {nextColor:function () {
	var rgb = dojo.gfx.color.hsl2rgb(this._color.h, this._color.s, this._color.l);
	this._color.h = (this._color.h + this._color.step) % 360;
	while (this._color.h < 140) {
		this._color.h += this._color.step;
	}
	return dojo.gfx.color.rgb2hex(rgb[0], rgb[1], rgb[2]);
}, getArea:function () {
	return {left:this.padding.left, right:this.size.width - this.padding.right, top:this.padding.top, bottom:this.size.height - this.padding.bottom, toString:function () {
		var a = [this.top, this.right, this.bottom, this.left];
		return "[" + a.join() + "]";
	}};
}, getAxes:function () {
	var axes = {};
	for (var i = 0; i < this.plots.length; i++) {
		var plot = this.plots[i];
		axes[plot.axisX.getId()] = {axis:plot.axisX, drawAgainst:plot.axisY, plot:plot, plane:"x"};
		axes[plot.axisY.getId()] = {axis:plot.axisY, drawAgainst:plot.axisX, plot:plot, plane:"y"};
	}
	return axes;
}, getLegendInfo:function () {
	var a = [];
	for (var i = 0; i < this.plots.length; i++) {
		for (var j = 0; j < this.plots[i].series.length; j++) {
			var data = this.plots[i].series[j].data;
			a.push({label:data.label, color:data.color});
		}
	}
	return a;
}, setAxesRanges:function () {
	var ranges = {};
	var axes = {};
	for (var i = 0; i < this.plots.length; i++) {
		var plot = this.plots[i];
		var ranges = plot.getRanges();
		var x = ranges.x;
		var y = ranges.y;
		var ax, ay;
		if (!axes[plot.axisX.getId()]) {
			axes[plot.axisX.getId()] = plot.axisX;
			ranges[plot.axisX.getId()] = {upper:x.upper, lower:x.lower};
		}
		ax = ranges[plot.axisX.getId()];
		ax.upper = Math.max(ax.upper, x.upper);
		ax.lower = Math.min(ax.lower, x.lower);
		if (!axes[plot.axisY.getId()]) {
			axes[plot.axisY.getId()] = plot.axisY;
			ranges[plot.axisY.getId()] = {upper:y.upper, lower:y.lower};
		}
		ay = ranges[plot.axisY.getId()];
		ay.upper = Math.max(ay.upper, y.upper);
		ay.lower = Math.min(ay.lower, y.lower);
	}
	for (var p in axes) {
		axes[p].range = ranges[p];
	}
}, render:function (kwArgs, applyToData) {
	if (!this.nodes.main || !this.nodes.area || !this.nodes.background || !this.nodes.plots || !this.nodes.axes) {
		this.initialize();
	}
	this.resize();
	for (var i = 0; i < this.plots.length; i++) {
		var plot = this.plots[i];
		if (plot.dataNode) {
			this.nodes.plots.removeChild(plot.dataNode);
		}
		var target = this.initializePlot(plot);
		switch (plot.renderType) {
		  case dojo.charting.RenderPlotSeries.Grouped:
			if (plot.series[0]) {
				target.appendChild(plot.series[0].plotter(this, plot, kwArgs, applyToData));
			}
			break;
		  case dojo.charting.RenderPlotSeries.Singly:
		  default:
			for (var j = 0; j < plot.series.length; j++) {
				var series = plot.series[j];
				var data = series.data.evaluate(kwArgs);
				target.appendChild(series.plotter(data, this, plot, applyToData));
			}
		}
		this.nodes.plots.appendChild(target);
	}
}, destroy:function () {
	for (var i = 0; i < this.plots.length; i++) {
		this.plots[i].destroy();
	}
	for (var p in this.nodes) {
		var node = this.nodes[p];
		if (!node) {
			continue;
		}
		if (!node.childNodes) {
			continue;
		}
		while (node.childNodes.length > 0) {
			node.removeChild(node.childNodes[0]);
		}
		this.nodes[p] = null;
	}
}});



/*
	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
*/



dojo.provide("dojo.charting.vml.PlotArea");
if (dojo.render.vml.capable) {
	dojo.extend(dojo.charting.PlotArea, {resize:function () {
		var a = this.getArea();
		this.nodes.area.style.width = this.size.width + "px";
		this.nodes.area.style.height = this.size.height + "px";
		this.nodes.background.style.width = this.size.width + "px";
		this.nodes.background.style.height = this.size.height + "px";
		this.nodes.plots.width = this.size.width + "px";
		this.nodes.plots.height = this.size.height + "px";
		this.nodes.plots.style.clip = "rect(" + a.top + " " + a.right + " " + a.bottom + " " + a.left + ")";
		if (this.nodes.axes) {
			this.nodes.area.removeChild(this.nodes.axes);
		}
		var axes = this.nodes.axes = document.createElement("div");
		axes.id = this.getId() + "-axes";
		this.nodes.area.appendChild(axes);
		var ax = this.getAxes();
		for (var p in ax) {
			var obj = ax[p];
			axes.appendChild(obj.axis.initialize(this, obj.plot, obj.drawAgainst, obj.plane));
		}
	}, initializePlot:function (plot) {
		plot.destroy();
		plot.dataNode = document.createElement("div");
		plot.dataNode.id = plot.getId();
		return plot.dataNode;
	}, initialize:function () {
		this.destroy();
		var main = this.nodes.main = document.createElement("div");
		var area = this.nodes.area = document.createElement("div");
		area.id = this.getId();
		area.style.position = "absolute";
		main.appendChild(area);
		var bg = this.nodes.background = document.createElement("div");
		bg.id = this.getId() + "-background";
		bg.style.position = "absolute";
		bg.style.top = "0px";
		bg.style.left = "0px";
		bg.style.backgroundColor = "#fff";
		area.appendChild(bg);
		var a = this.getArea();
		var plots = this.nodes.plots = document.createElement("div");
		plots.id = this.getId() + "-plots";
		plots.style.position = "absolute";
		plots.style.top = "0px";
		plots.style.left = "0px";
		area.appendChild(plots);
		for (var i = 0; i < this.plots.length; i++) {
			plots.appendChild(this.initializePlot(this.plots[i]));
		}
		this.resize();
		return main;
	}});
}

/*
	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
*/



dojo.provide("dojo.widget.Dialog");
dojo.declare("dojo.widget.ModalDialogBase", null, {isContainer:true, focusElement:"", bgColor:"black", bgOpacity:0.4, followScroll:true, closeOnBackgroundClick:false, trapTabs:function (e) {
	if (e.target == this.tabStartOuter) {
		if (this._fromTrap) {
			this.tabStart.focus();
			this._fromTrap = false;
		} else {
			this._fromTrap = true;
			this.tabEnd.focus();
		}
	} else {
		if (e.target == this.tabStart) {
			if (this._fromTrap) {
				this._fromTrap = false;
			} else {
				this._fromTrap = true;
				this.tabEnd.focus();
			}
		} else {
			if (e.target == this.tabEndOuter) {
				if (this._fromTrap) {
					this.tabEnd.focus();
					this._fromTrap = false;
				} else {
					this._fromTrap = true;
					this.tabStart.focus();
				}
			} else {
				if (e.target == this.tabEnd) {
					if (this._fromTrap) {
						this._fromTrap = false;
					} else {
						this._fromTrap = true;
						this.tabStart.focus();
					}
				}
			}
		}
	}
}, clearTrap:function (e) {
	var _this = this;
	setTimeout(function () {
		_this._fromTrap = false;
	}, 100);
}, postCreate:function () {
	with (this.domNode.style) {
		position = "absolute";
		zIndex = 999;
		display = "none";
		overflow = "visible";
	}
	var b = dojo.body();
	b.appendChild(this.domNode);
	this.bg = document.createElement("div");
	this.bg.className = "dialogUnderlay";
	with (this.bg.style) {
		position = "absolute";
		left = top = "0px";
		zIndex = 998;
		display = "none";
	}
	b.appendChild(this.bg);
	this.setBackgroundColor(this.bgColor);
	this.bgIframe = new dojo.html.BackgroundIframe();
	if (this.bgIframe.iframe) {
		with (this.bgIframe.iframe.style) {
			position = "absolute";
			left = top = "0px";
			zIndex = 90;
			display = "none";
		}
	}
	if (this.closeOnBackgroundClick) {
		dojo.event.kwConnect({srcObj:this.bg, srcFunc:"onclick", adviceObj:this, adviceFunc:"onBackgroundClick", once:true});
	}
}, uninitialize:function () {
	this.bgIframe.remove();
	dojo.html.removeNode(this.bg, true);
}, setBackgroundColor:function (color) {
	if (arguments.length >= 3) {
		color = new dojo.gfx.color.Color(arguments[0], arguments[1], arguments[2]);
	} else {
		color = new dojo.gfx.color.Color(color);
	}
	this.bg.style.backgroundColor = color.toString();
	return this.bgColor = color;
}, setBackgroundOpacity:function (op) {
	if (arguments.length == 0) {
		op = this.bgOpacity;
	}
	dojo.html.setOpacity(this.bg, op);
	try {
		this.bgOpacity = dojo.html.getOpacity(this.bg);
	}
	catch (e) {
		this.bgOpacity = op;
	}
	return this.bgOpacity;
}, _sizeBackground:function () {
	if (this.bgOpacity > 0) {
		var viewport = dojo.html.getViewport();
		var h = viewport.height;
		var w = viewport.width;
		with (this.bg.style) {
			width = w + "px";
			height = h + "px";
		}
		var scroll_offset = dojo.html.getScroll().offset;
		this.bg.style.top = scroll_offset.y + "px";
		this.bg.style.left = scroll_offset.x + "px";
		var viewport = dojo.html.getViewport();
		if (viewport.width != w) {
			this.bg.style.width = viewport.width + "px";
		}
		if (viewport.height != h) {
			this.bg.style.height = viewport.height + "px";
		}
	}
	this.bgIframe.size(this.bg);
}, _showBackground:function () {
	if (this.bgOpacity > 0) {
		this.bg.style.display = "block";
	}
	if (this.bgIframe.iframe) {
		this.bgIframe.iframe.style.display = "block";
	}
}, placeModalDialog:function () {
	var scroll_offset = dojo.html.getScroll().offset;
	var viewport_size = dojo.html.getViewport();
	var mb;
	if (this.isShowing()) {
		mb = dojo.html.getMarginBox(this.domNode);
	} else {
		dojo.html.setVisibility(this.domNode, false);
		dojo.html.show(this.domNode);
		mb = dojo.html.getMarginBox(this.domNode);
		dojo.html.hide(this.domNode);
		dojo.html.setVisibility(this.domNode, true);
	}
	var x = scroll_offset.x + (viewport_size.width - mb.width) / 2;
	var y = scroll_offset.y + (viewport_size.height - mb.height) / 2;
	with (this.domNode.style) {
		left = x + "px";
		top = y + "px";
	}
}, _onKey:function (evt) {
	if (evt.key) {
		var node = evt.target;
		while (node != null) {
			if (node == this.domNode) {
				return;
			}
			node = node.parentNode;
		}
		if (evt.key != evt.KEY_TAB) {
			dojo.event.browser.stopEvent(evt);
		} else {
			if (!dojo.render.html.opera) {
				try {
					this.tabStart.focus();
				}
				catch (e) {
				}
			}
		}
	}
}, showModalDialog:function () {
	if (this.followScroll && !this._scrollConnected) {
		this._scrollConnected = true;
		dojo.event.connect(window, "onscroll", this, "_onScroll");
	}
	dojo.event.connect(document.documentElement, "onkey", this, "_onKey");
	this.placeModalDialog();
	this.setBackgroundOpacity();
	this._sizeBackground();
	this._showBackground();
	this._fromTrap = true;
	setTimeout(dojo.lang.hitch(this, function () {
		try {
			this.tabStart.focus();
		}
		catch (e) {
		}
	}), 50);
}, hideModalDialog:function () {
	if (this.focusElement) {
		dojo.byId(this.focusElement).focus();
		dojo.byId(this.focusElement).blur();
	}
	this.bg.style.display = "none";
	this.bg.style.width = this.bg.style.height = "1px";
	if (this.bgIframe.iframe) {
		this.bgIframe.iframe.style.display = "none";
	}
	dojo.event.disconnect(document.documentElement, "onkey", this, "_onKey");
	if (this._scrollConnected) {
		this._scrollConnected = false;
		dojo.event.disconnect(window, "onscroll", this, "_onScroll");
	}
}, _onScroll:function () {
	var scroll_offset = dojo.html.getScroll().offset;
	this.bg.style.top = scroll_offset.y + "px";
	this.bg.style.left = scroll_offset.x + "px";
	this.placeModalDialog();
}, checkSize:function () {
	if (this.isShowing()) {
		this._sizeBackground();
		this.placeModalDialog();
		this.onResized();
	}
}, onBackgroundClick:function () {
	if (this.lifetime - this.timeRemaining >= this.blockDuration) {
		return;
	}
	this.hide();
}});
dojo.widget.defineWidget("dojo.widget.Dialog", [dojo.widget.ContentPane, dojo.widget.ModalDialogBase], {templateString:"<div id=\"${this.widgetId}\" class=\"dojoDialog\" dojoattachpoint=\"wrapper\">\n\t<span dojoattachpoint=\"tabStartOuter\" dojoonfocus=\"trapTabs\" dojoonblur=\"clearTrap\"\ttabindex=\"0\"></span>\n\t<span dojoattachpoint=\"tabStart\" dojoonfocus=\"trapTabs\" dojoonblur=\"clearTrap\" tabindex=\"0\"></span>\n\t<div dojoattachpoint=\"containerNode\" style=\"position: relative; z-index: 2;\"></div>\n\t<span dojoattachpoint=\"tabEnd\" dojoonfocus=\"trapTabs\" dojoonblur=\"clearTrap\" tabindex=\"0\"></span>\n\t<span dojoattachpoint=\"tabEndOuter\" dojoonfocus=\"trapTabs\" dojoonblur=\"clearTrap\" tabindex=\"0\"></span>\n</div>\n", blockDuration:0, lifetime:0, closeNode:"", postMixInProperties:function () {
	dojo.widget.Dialog.superclass.postMixInProperties.apply(this, arguments);
	if (this.closeNode) {
		this.setCloseControl(this.closeNode);
	}
}, postCreate:function () {
	dojo.widget.Dialog.superclass.postCreate.apply(this, arguments);
	dojo.widget.ModalDialogBase.prototype.postCreate.apply(this, arguments);
}, show:function () {
	if (this.lifetime) {
		this.timeRemaining = this.lifetime;
		if (this.timerNode) {
			this.timerNode.innerHTML = Math.ceil(this.timeRemaining / 1000);
		}
		if (this.blockDuration && this.closeNode) {
			if (this.lifetime > this.blockDuration) {
				this.closeNode.style.visibility = "hidden";
			} else {
				this.closeNode.style.display = "none";
			}
		}
		if (this.timer) {
			clearInterval(this.timer);
		}
		this.timer = setInterval(dojo.lang.hitch(this, "_onTick"), 100);
	}
	this.showModalDialog();
	dojo.widget.Dialog.superclass.show.call(this);
}, onLoad:function () {
	this.placeModalDialog();
	dojo.widget.Dialog.superclass.onLoad.call(this);
}, fillInTemplate:function () {
}, hide:function () {
	this.hideModalDialog();
	dojo.widget.Dialog.superclass.hide.call(this);
	if (this.timer) {
		clearInterval(this.timer);
	}
}, setTimerNode:function (node) {
	this.timerNode = node;
}, setCloseControl:function (node) {
	this.closeNode = dojo.byId(node);
	dojo.event.connect(this.closeNode, "onclick", this, "hide");
}, setShowControl:function (node) {
	node = dojo.byId(node);
	dojo.event.connect(node, "onclick", this, "show");
}, _onTick:function () {
	if (this.timer) {
		this.timeRemaining -= 100;
		if (this.lifetime - this.timeRemaining >= this.blockDuration) {
			if (this.closeNode) {
				this.closeNode.style.visibility = "visible";
			}
		}
		if (!this.timeRemaining) {
			clearInterval(this.timer);
			this.hide();
		} else {
			if (this.timerNode) {
				this.timerNode.innerHTML = Math.ceil(this.timeRemaining / 1000);
			}
		}
	}
}});

/*
	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
*/



dojo.provide("dojo.widget.TabContainer");
dojo.widget.defineWidget("dojo.widget.TabContainer", dojo.widget.PageContainer, {labelPosition:"top", closeButton:"none", templateString:null, templateString:"<div id=\"${this.widgetId}\" class=\"dojoTabContainer\">\n\t<div dojoAttachPoint=\"tablistNode\"></div>\n\t<div class=\"dojoTabPaneWrapper\" dojoAttachPoint=\"containerNode\" dojoAttachEvent=\"onKey\" waiRole=\"tabpanel\"></div>\n</div>\n", templateCssString:".dojoTabContainer {\n\tposition : relative;\n}\n\n.dojoTabPaneWrapper {\n\tborder : 1px solid #b2aa83;\n\t_zoom: 1; /* force IE6 layout mode so top border doesnt disappear */\n\tdisplay: block;\n\tclear: both;\n\toverflow: hidden;\n}\n\n.dojoTabLabels-top {\n\tposition : relative;\n\ttop : 0px;\n\tleft : 0px;\n\toverflow : visible;\n\tmargin-bottom : -1px;\n\twidth : 100%;\n\tz-index: 2;\t/* so the bottom of the tab label will cover up the border of dojoTabPaneWrapper */\n}\n\n.dojoTabNoLayout.dojoTabLabels-top .dojoTab {\n\tmargin-bottom: -1px;\n\t_margin-bottom: 0px; /* IE filter so top border lines up correctly */\n}\n\n.dojoTab {\n\tposition : relative;\n\tfloat : left;\n\tpadding-left : 9px;\n\tborder-bottom : 1px solid #b2aa83;\n\tcursor: pointer;\n\twhite-space: nowrap;\n\tz-index: 3;\n}\n\n.dojoTab div {\n\tdisplay : block;\n\tpadding : 4px 15px 4px 6px;\n\tbackground : url(images/tab_top_right.gif) no-repeat right top;\n\tcolor : #333;\n\tfont-size : 90%;\n}\n\n.dojoTab .close {\n\tdisplay : inline-block;\n\theight : 12px;\n\twidth : 12px;\n\tpadding : 0 12px 0 0;\n\tmargin : 0 -10px 0 10px;\n\tcursor : default;\n\tfont-size: small;\n}\n\n.dojoTab .closeImage {\n\tbackground : url(images/tab_close.gif) no-repeat right top;\n}\n\n.dojoTab .closeHover {\n\tbackground-image : url(images/tab_close_h.gif);\n}\n\n.dojoTab.current {\n\tpadding-bottom : 1px;\n\tborder-bottom : 0;\n\tbackground-position : 0 -150px;\n}\n\n.dojoTab.current div {\n\tpadding-bottom : 5px;\n\tmargin-bottom : -1px;\n\tbackground-position : 100% -150px;\n}\n\n/* bottom tabs */\n\n.dojoTabLabels-bottom {\n\tposition : relative;\n\tbottom : 0px;\n\tleft : 0px;\n\toverflow : visible;\n\tmargin-top : -1px;\n\twidth : 100%;\n\tz-index: 2;\n}\n\n.dojoTabNoLayout.dojoTabLabels-bottom {\n\tposition : relative;\n}\n\n.dojoTabLabels-bottom .dojoTab {\n\tborder-top :  1px solid #b2aa83;\n\tborder-bottom : 0;\n\tbackground : url(images/tab_bot_left.gif) no-repeat left bottom;\n}\n\n.dojoTabLabels-bottom .dojoTab div {\n\tbackground : url(images/tab_bot_right.gif) no-repeat right bottom;\n}\n\n.dojoTabLabels-bottom .dojoTab.current {\n\tborder-top : 0;\n\tbackground : url(images/tab_bot_left_curr.gif) no-repeat left bottom;\n}\n\n.dojoTabLabels-bottom .dojoTab.current div {\n\tpadding-top : 4px;\n\tbackground : url(images/tab_bot_right_curr.gif) no-repeat right bottom;\n}\n\n/* right-h tabs */\n\n.dojoTabLabels-right-h {\n\toverflow : visible;\n\tmargin-left : -1px;\n\tz-index: 2;\n}\n\n.dojoTabLabels-right-h .dojoTab {\n\tpadding-left : 0;\n\tborder-left :  1px solid #b2aa83;\n\tborder-bottom : 0;\n\tbackground : url(images/tab_bot_right.gif) no-repeat right bottom;\n\tfloat : none;\n}\n\n.dojoTabLabels-right-h .dojoTab div {\n\tpadding : 4px 15px 4px 15px;\n}\n\n.dojoTabLabels-right-h .dojoTab.current {\n\tborder-left :  0;\n\tborder-bottom :  1px solid #b2aa83;\n}\n\n/* left-h tabs */\n\n.dojoTabLabels-left-h {\n\toverflow : visible;\n\tmargin-right : -1px;\n\tz-index: 2;\n}\n\n.dojoTabLabels-left-h .dojoTab {\n\tborder-right :  1px solid #b2aa83;\n\tborder-bottom : 0;\n\tfloat : none;\n\tbackground : url(images/tab_top_left.gif) no-repeat left top;\n}\n\n.dojoTabLabels-left-h .dojoTab.current {\n\tborder-right : 0;\n\tborder-bottom :  1px solid #b2aa83;\n\tpadding-bottom : 0;\n\tbackground : url(images/tab_top_left.gif) no-repeat 0 -150px;\n}\n\n.dojoTabLabels-left-h .dojoTab div {\n\tbackground : 0;\n\tborder-bottom :  1px solid blue;\n}\n", templateCssPath:dojo.uri.moduleUri("dojo.widget", "templates/TabContainer.css"), selectedTab:"", postMixInProperties:function () {
	if (this.selectedTab) {
		dojo.deprecated("selectedTab deprecated, use selectedChild instead, will be removed in", "0.5");
		this.selectedChild = this.selectedTab;
	}
	if (this.closeButton != "none") {
		dojo.deprecated("closeButton deprecated, use closable='true' on each child instead, will be removed in", "0.5");
	}
	dojo.widget.TabContainer.superclass.postMixInProperties.apply(this, arguments);
}, fillInTemplate:function () {
	this.tablist = dojo.widget.createWidget("TabController", {id:this.widgetId + "_tablist", labelPosition:this.labelPosition, doLayout:this.doLayout, containerId:this.widgetId}, this.tablistNode);
	dojo.widget.TabContainer.superclass.fillInTemplate.apply(this, arguments);
}, postCreate:function (args, frag) {
	dojo.widget.TabContainer.superclass.postCreate.apply(this, arguments);
	this.onResized();
}, _setupChild:function (tab) {
	if (this.closeButton == "tab" || this.closeButton == "pane") {
		tab.closable = true;
	}
	dojo.html.addClass(tab.domNode, "dojoTabPane");
	dojo.widget.TabContainer.superclass._setupChild.apply(this, arguments);
}, onResized:function () {
	if (!this.doLayout) {
		return;
	}
	var labelAlign = this.labelPosition.replace(/-h/, "");
	var children = [{domNode:this.tablist.domNode, layoutAlign:labelAlign}, {domNode:this.containerNode, layoutAlign:"client"}];
	dojo.widget.html.layout(this.domNode, children);
	if (this.selectedChildWidget) {
		var containerSize = dojo.html.getContentBox(this.containerNode);
		this.selectedChildWidget.resizeTo(containerSize.width, containerSize.height);
	}
}, selectTab:function (tab, callingWidget) {
	dojo.deprecated("use selectChild() rather than selectTab(), selectTab() will be removed in", "0.5");
	this.selectChild(tab, callingWidget);
}, onKey:function (e) {
	if (e.keyCode == e.KEY_UP_ARROW && e.ctrlKey) {
		var button = this.correspondingTabButton || this.selectedTabWidget.tabButton;
		button.focus();
		dojo.event.browser.stopEvent(e);
	} else {
		if (e.keyCode == e.KEY_DELETE && e.altKey) {
			if (this.selectedChildWidget.closable) {
				this.closeChild(this.selectedChildWidget);
				dojo.event.browser.stopEvent(e);
			}
		}
	}
}, destroy:function () {
	this.tablist.destroy();
	dojo.widget.TabContainer.superclass.destroy.apply(this, arguments);
}});
dojo.widget.defineWidget("dojo.widget.TabController", dojo.widget.PageController, {templateString:"<div wairole='tablist' dojoAttachEvent='onKey'></div>", labelPosition:"top", doLayout:true, "class":"", buttonWidget:"TabButton", postMixInProperties:function () {
	if (!this["class"]) {
		this["class"] = "dojoTabLabels-" + this.labelPosition + (this.doLayout ? "" : " dojoTabNoLayout");
	}
	dojo.widget.TabController.superclass.postMixInProperties.apply(this, arguments);
}});
dojo.widget.defineWidget("dojo.widget.TabButton", dojo.widget.PageButton, {templateString:"<div class='dojoTab' dojoAttachEvent='onClick'>" + "<div dojoAttachPoint='innerDiv'>" + "<span dojoAttachPoint='titleNode' tabIndex='-1' waiRole='tab'>${this.label}</span>" + "<span dojoAttachPoint='closeButtonNode' class='close closeImage' style='${this.closeButtonStyle}'" + "	dojoAttachEvent='onMouseOver:onCloseButtonMouseOver; onMouseOut:onCloseButtonMouseOut; onClick:onCloseButtonClick'></span>" + "</div>" + "</div>", postMixInProperties:function () {
	this.closeButtonStyle = this.closeButton ? "" : "display: none";
	dojo.widget.TabButton.superclass.postMixInProperties.apply(this, arguments);
}, fillInTemplate:function () {
	dojo.html.disableSelection(this.titleNode);
	dojo.widget.TabButton.superclass.fillInTemplate.apply(this, arguments);
}, onCloseButtonClick:function (evt) {
	evt.stopPropagation();
	dojo.widget.TabButton.superclass.onCloseButtonClick.apply(this, arguments);
}});
dojo.widget.defineWidget("dojo.widget.a11y.TabButton", dojo.widget.TabButton, {imgPath:dojo.uri.moduleUri("dojo.widget", "templates/images/tab_close.gif"), templateString:"<div class='dojoTab' dojoAttachEvent='onClick;onKey'>" + "<div dojoAttachPoint='innerDiv'>" + "<span dojoAttachPoint='titleNode' tabIndex='-1' waiRole='tab'>${this.label}</span>" + "<img class='close' src='${this.imgPath}' alt='[x]' style='${this.closeButtonStyle}'" + "	dojoAttachEvent='onClick:onCloseButtonClick'>" + "</div>" + "</div>"});

/*
	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
*/



dojo.provide("dojo.charting.Chart");
dojo.charting.Chart = function (node, title, description) {
	this.node = node || null;
	this.title = title || "Chart";
	this.description = description || "";
	this.plotAreas = [];
};
dojo.extend(dojo.charting.Chart, {addPlotArea:function (obj, doRender) {
	if (obj.x != null && obj.left == null) {
		obj.left = obj.x;
	}
	if (obj.y != null && obj.top == null) {
		obj.top = obj.y;
	}
	this.plotAreas.push(obj);
	if (doRender) {
		this.render();
	}
}, onInitialize:function (chart) {
}, onRender:function (chart) {
}, onDestroy:function (chart) {
}, initialize:function () {
	if (!this.node) {
		dojo.raise("dojo.charting.Chart.initialize: there must be a root node defined for the Chart.");
	}
	this.destroy();
	this.render();
	this.onInitialize(this);
}, render:function () {
	if (this.node.style.position != "absolute") {
		this.node.style.position = "relative";
	}
	for (var i = 0; i < this.plotAreas.length; i++) {
		var area = this.plotAreas[i].plotArea;
		var node = area.initialize();
		node.style.position = "absolute";
		node.style.top = this.plotAreas[i].top + "px";
		node.style.left = this.plotAreas[i].left + "px";
		this.node.appendChild(node);
		area.render();
	}
}, destroy:function () {
	for (var i = 0; i < this.plotAreas.length; i++) {
		this.plotAreas[i].plotArea.destroy();
	}
	while (this.node && this.node.childNodes && this.node.childNodes.length > 0) {
		this.node.removeChild(this.node.childNodes[0]);
	}
}});

/*
	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
*/



dojo.provide("dojo.gfx.matrix");
dojo.gfx.matrix.Matrix2D = function (arg) {
	if (arg) {
		if (arg instanceof Array) {
			if (arg.length > 0) {
				var m = dojo.gfx.matrix.normalize(arg[0]);
				for (var i = 1; i < arg.length; ++i) {
					var l = m;
					var r = dojo.gfx.matrix.normalize(arg[i]);
					m = new dojo.gfx.matrix.Matrix2D();
					m.xx = l.xx * r.xx + l.xy * r.yx;
					m.xy = l.xx * r.xy + l.xy * r.yy;
					m.yx = l.yx * r.xx + l.yy * r.yx;
					m.yy = l.yx * r.xy + l.yy * r.yy;
					m.dx = l.xx * r.dx + l.xy * r.dy + l.dx;
					m.dy = l.yx * r.dx + l.yy * r.dy + l.dy;
				}
				dojo.mixin(this, m);
			}
		} else {
			dojo.mixin(this, arg);
		}
	}
};
dojo.extend(dojo.gfx.matrix.Matrix2D, {xx:1, xy:0, yx:0, yy:1, dx:0, dy:0});
dojo.mixin(dojo.gfx.matrix, {identity:new dojo.gfx.matrix.Matrix2D(), flipX:new dojo.gfx.matrix.Matrix2D({xx:-1}), flipY:new dojo.gfx.matrix.Matrix2D({yy:-1}), flipXY:new dojo.gfx.matrix.Matrix2D({xx:-1, yy:-1}), translate:function (a, b) {
	if (arguments.length > 1) {
		return new dojo.gfx.matrix.Matrix2D({dx:a, dy:b});
	}
	return new dojo.gfx.matrix.Matrix2D({dx:a.x, dy:a.y});
}, scale:function (a, b) {
	if (arguments.length > 1) {
		return new dojo.gfx.matrix.Matrix2D({xx:a, yy:b});
	}
	if (typeof a == "number") {
		return new dojo.gfx.matrix.Matrix2D({xx:a, yy:a});
	}
	return new dojo.gfx.matrix.Matrix2D({xx:a.x, yy:a.y});
}, rotate:function (angle) {
	var c = Math.cos(angle);
	var s = Math.sin(angle);
	return new dojo.gfx.matrix.Matrix2D({xx:c, xy:s, yx:-s, yy:c});
}, rotateg:function (degree) {
	return dojo.gfx.matrix.rotate(dojo.math.degToRad(degree));
}, skewX:function (angle) {
	return new dojo.gfx.matrix.Matrix2D({xy:Math.tan(angle)});
}, skewXg:function (degree) {
	return dojo.gfx.matrix.skewX(dojo.math.degToRad(degree));
}, skewY:function (angle) {
	return new dojo.gfx.matrix.Matrix2D({yx:-Math.tan(angle)});
}, skewYg:function (degree) {
	return dojo.gfx.matrix.skewY(dojo.math.degToRad(degree));
}, normalize:function (matrix) {
	return (matrix instanceof dojo.gfx.matrix.Matrix2D) ? matrix : new dojo.gfx.matrix.Matrix2D(matrix);
}, clone:function (matrix) {
	var obj = new dojo.gfx.matrix.Matrix2D();
	for (var i in matrix) {
		if (typeof (matrix[i]) == "number" && typeof (obj[i]) == "number" && obj[i] != matrix[i]) {
			obj[i] = matrix[i];
		}
	}
	return obj;
}, invert:function (matrix) {
	var m = dojo.gfx.matrix.normalize(matrix);
	var D = m.xx * m.yy - m.xy * m.yx;
	var M = new dojo.gfx.matrix.Matrix2D({xx:m.yy / D, xy:-m.xy / D, yx:-m.yx / D, yy:m.xx / D, dx:(m.yx * m.dy - m.yy * m.dx) / D, dy:(m.xy * m.dx - m.xx * m.dy) / D});
	return M;
}, _multiplyPoint:function (m, x, y) {
	return {x:m.xx * x + m.xy * y + m.dx, y:m.yx * x + m.yy * y + m.dy};
}, multiplyPoint:function (matrix, a, b) {
	var m = dojo.gfx.matrix.normalize(matrix);
	if (typeof a == "number" && typeof b == "number") {
		return dojo.gfx.matrix._multiplyPoint(m, a, b);
	}
	return dojo.gfx.matrix._multiplyPoint(m, a.x, a.y);
}, multiply:function (matrix) {
	var m = dojo.gfx.matrix.normalize(matrix);
	for (var i = 1; i < arguments.length; ++i) {
		var l = m;
		var r = dojo.gfx.matrix.normalize(arguments[i]);
		m = new dojo.gfx.matrix.Matrix2D();
		m.xx = l.xx * r.xx + l.xy * r.yx;
		m.xy = l.xx * r.xy + l.xy * r.yy;
		m.yx = l.yx * r.xx + l.yy * r.yx;
		m.yy = l.yx * r.xy + l.yy * r.yy;
		m.dx = l.xx * r.dx + l.xy * r.dy + l.dx;
		m.dy = l.yx * r.dx + l.yy * r.dy + l.dy;
	}
	return m;
}, _sandwich:function (m, x, y) {
	return dojo.gfx.matrix.multiply(dojo.gfx.matrix.translate(x, y), m, dojo.gfx.matrix.translate(-x, -y));
}, scaleAt:function (a, b, c, d) {
	switch (arguments.length) {
	  case 4:
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.scale(a, b), c, d);
	  case 3:
		if (typeof c == "number") {
			return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.scale(a), b, c);
		}
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.scale(a, b), c.x, c.y);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.scale(a), b.x, b.y);
}, rotateAt:function (angle, a, b) {
	if (arguments.length > 2) {
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.rotate(angle), a, b);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.rotate(angle), a.x, a.y);
}, rotategAt:function (degree, a, b) {
	if (arguments.length > 2) {
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.rotateg(degree), a, b);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.rotateg(degree), a.x, a.y);
}, skewXAt:function (angle, a, b) {
	if (arguments.length > 2) {
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewX(angle), a, b);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewX(angle), a.x, a.y);
}, skewXgAt:function (degree, a, b) {
	if (arguments.length > 2) {
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewXg(degree), a, b);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewXg(degree), a.x, a.y);
}, skewYAt:function (angle, a, b) {
	if (arguments.length > 2) {
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewY(angle), a, b);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewY(angle), a.x, a.y);
}, skewYgAt:function (degree, a, b) {
	if (arguments.length > 2) {
		return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewYg(degree), a, b);
	}
	return dojo.gfx.matrix._sandwich(dojo.gfx.matrix.skewYg(degree), a.x, a.y);
}});

/*
	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
*/



dojo.provide("dojo.math.curves");
dojo.math.curves = {Line:function (start, end) {
	this.start = start;
	this.end = end;
	this.dimensions = start.length;
	for (var i = 0; i < start.length; i++) {
		start[i] = Number(start[i]);
	}
	for (var i = 0; i < end.length; i++) {
		end[i] = Number(end[i]);
	}
	this.getValue = function (n) {
		var retVal = new Array(this.dimensions);
		for (var i = 0; i < this.dimensions; i++) {
			retVal[i] = ((this.end[i] - this.start[i]) * n) + this.start[i];
		}
		return retVal;
	};
	return this;
}, Bezier:function (pnts) {
	this.getValue = function (step) {
		if (step >= 1) {
			return this.p[this.p.length - 1];
		}
		if (step <= 0) {
			return this.p[0];
		}
		var retVal = new Array(this.p[0].length);
		for (var k = 0; j < this.p[0].length; k++) {
			retVal[k] = 0;
		}
		for (var j = 0; j < this.p[0].length; j++) {
			var C = 0;
			var D = 0;
			for (var i = 0; i < this.p.length; i++) {
				C += this.p[i][j] * this.p[this.p.length - 1][0] * dojo.math.bernstein(step, this.p.length, i);
			}
			for (var l = 0; l < this.p.length; l++) {
				D += this.p[this.p.length - 1][0] * dojo.math.bernstein(step, this.p.length, l);
			}
			retVal[j] = C / D;
		}
		return retVal;
	};
	this.p = pnts;
	return this;
}, CatmullRom:function (pnts, c) {
	this.getValue = function (step) {
		var percent = step * (this.p.length - 1);
		var node = Math.floor(percent);
		var progress = percent - node;
		var i0 = node - 1;
		if (i0 < 0) {
			i0 = 0;
		}
		var i = node;
		var i1 = node + 1;
		if (i1 >= this.p.length) {
			i1 = this.p.length - 1;
		}
		var i2 = node + 2;
		if (i2 >= this.p.length) {
			i2 = this.p.length - 1;
		}
		var u = progress;
		var u2 = progress * progress;
		var u3 = progress * progress * progress;
		var retVal = new Array(this.p[0].length);
		for (var k = 0; k < this.p[0].length; k++) {
			var x1 = (-this.c * this.p[i0][k]) + ((2 - this.c) * this.p[i][k]) + ((this.c - 2) * this.p[i1][k]) + (this.c * this.p[i2][k]);
			var x2 = (2 * this.c * this.p[i0][k]) + ((this.c - 3) * this.p[i][k]) + ((3 - 2 * this.c) * this.p[i1][k]) + (-this.c * this.p[i2][k]);
			var x3 = (-this.c * this.p[i0][k]) + (this.c * this.p[i1][k]);
			var x4 = this.p[i][k];
			retVal[k] = x1 * u3 + x2 * u2 + x3 * u + x4;
		}
		return retVal;
	};
	if (!c) {
		this.c = 0.7;
	} else {
		this.c = c;
	}
	this.p = pnts;
	return this;
}, Arc:function (start, end, ccw) {
	var center = dojo.math.points.midpoint(start, end);
	var sides = dojo.math.points.translate(dojo.math.points.invert(center), start);
	var rad = Math.sqrt(Math.pow(sides[0], 2) + Math.pow(sides[1], 2));
	var theta = dojo.math.radToDeg(Math.atan(sides[1] / sides[0]));
	if (sides[0] < 0) {
		theta -= 90;
	} else {
		theta += 90;
	}
	dojo.math.curves.CenteredArc.call(this, center, rad, theta, theta + (ccw ? -180 : 180));
}, CenteredArc:function (center, radius, start, end) {
	this.center = center;
	this.radius = radius;
	this.start = start || 0;
	this.end = end;
	this.getValue = function (n) {
		var retVal = new Array(2);
		var theta = dojo.math.degToRad(this.start + ((this.end - this.start) * n));
		retVal[0] = this.center[0] + this.radius * Math.sin(theta);
		retVal[1] = this.center[1] - this.radius * Math.cos(theta);
		return retVal;
	};
	return this;
}, Circle:function (center, radius) {
	dojo.math.curves.CenteredArc.call(this, center, radius, 0, 360);
	return this;
}, Path:function () {
	var curves = [];
	var weights = [];
	var ranges = [];
	var totalWeight = 0;
	this.add = function (curve, weight) {
		if (weight < 0) {
			dojo.raise("dojo.math.curves.Path.add: weight cannot be less than 0");
		}
		curves.push(curve);
		weights.push(weight);
		totalWeight += weight;
		computeRanges();
	};
	this.remove = function (curve) {
		for (var i = 0; i < curves.length; i++) {
			if (curves[i] == curve) {
				curves.splice(i, 1);
				totalWeight -= weights.splice(i, 1)[0];
				break;
			}
		}
		computeRanges();
	};
	this.removeAll = function () {
		curves = [];
		weights = [];
		totalWeight = 0;
	};
	this.getValue = function (n) {
		var found = false, value = 0;
		for (var i = 0; i < ranges.length; i++) {
			var r = ranges[i];
			if (n >= r[0] && n < r[1]) {
				var subN = (n - r[0]) / r[2];
				value = curves[i].getValue(subN);
				found = true;
				break;
			}
		}
		if (!found) {
			value = curves[curves.length - 1].getValue(1);
		}
		for (var j = 0; j < i; j++) {
			value = dojo.math.points.translate(value, curves[j].getValue(1));
		}
		return value;
	};
	function computeRanges() {
		var start = 0;
		for (var i = 0; i < weights.length; i++) {
			var end = start + weights[i] / totalWeight;
			var len = end - start;
			ranges[i] = [start, end, len];
			start = end;
		}
	}
	return this;
}};

/*
	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
*/



dojo.provide("dojo.gfx.vml");
dojo.experimental("dojo.gfx.vml");
dojo.gfx.vml.xmlns = "urn:schemas-microsoft-com:vml";
dojo.gfx.vml._parseFloat = function (str) {
	return str.match(/^\d+f$/i) ? parseInt(str) / 65536 : parseFloat(str);
};
dojo.gfx.vml.cm_in_pt = 72 / 2.54;
dojo.gfx.vml.mm_in_pt = 7.2 / 2.54;
dojo.gfx.vml.px_in_pt = function () {
	return dojo.html.getCachedFontMeasurements()["12pt"] / 12;
};
dojo.gfx.vml.pt2px = function (len) {
	return len * this.px_in_pt();
};
dojo.gfx.vml.px2pt = function (len) {
	return len / this.px_in_pt();
};
dojo.gfx.vml.normalizedLength = function (len) {
	if (len.length == 0) {
		return 0;
	}
	if (len.length > 2) {
		var px_in_pt = this.px_in_pt();
		var val = parseFloat(len);
		switch (len.slice(-2)) {
		  case "px":
			return val;
		  case "pt":
			return val * px_in_pt;
		  case "in":
			return val * 72 * px_in_pt;
		  case "pc":
			return val * 12 * px_in_pt;
		  case "mm":
			return val / this.mm_in_pt * px_in_pt;
		  case "cm":
			return val / this.cm_in_pt * px_in_pt;
		}
	}
	return parseFloat(len);
};
dojo.lang.extend(dojo.gfx.Shape, {setFill:function (fill) {
	if (!fill) {
		this.fillStyle = null;
		this.rawNode.filled = false;
		return this;
	}
	if (typeof (fill) == "object" && "type" in fill) {
		switch (fill.type) {
		  case "linear":
			var f = dojo.gfx.makeParameters(dojo.gfx.defaultLinearGradient, fill);
			this.fillStyle = f;
			var s = "";
			for (var i = 0; i < f.colors.length; ++i) {
				f.colors[i].color = dojo.gfx.normalizeColor(f.colors[i].color);
				s += f.colors[i].offset.toFixed(8) + " " + f.colors[i].color.toHex() + ";";
			}
			var fo = this.rawNode.fill;
			fo.colors.value = s;
			fo.method = "sigma";
			fo.type = "gradient";
			fo.angle = (dojo.math.radToDeg(Math.atan2(f.x2 - f.x1, f.y2 - f.y1)) + 180) % 360;
			fo.on = true;
			break;
		  case "radial":
			var f = dojo.gfx.makeParameters(dojo.gfx.defaultRadialGradient, fill);
			this.fillStyle = f;
			var w = parseFloat(this.rawNode.style.width);
			var h = parseFloat(this.rawNode.style.height);
			var c = isNaN(w) ? 1 : 2 * f.r / w;
			var i = f.colors.length - 1;
			f.colors[i].color = dojo.gfx.normalizeColor(f.colors[i].color);
			var s = "0 " + f.colors[i].color.toHex();
			for (; i >= 0; --i) {
				f.colors[i].color = dojo.gfx.normalizeColor(f.colors[i].color);
				s += (1 - c * f.colors[i].offset).toFixed(8) + " " + f.colors[i].color.toHex() + ";";
			}
			var fo = this.rawNode.fill;
			fo.colors.value = s;
			fo.method = "sigma";
			fo.type = "gradientradial";
			if (isNaN(w) || isNaN(h)) {
				fo.focusposition = "0.5 0.5";
			} else {
				fo.focusposition = (f.cx / w).toFixed(8) + " " + (f.cy / h).toFixed(8);
			}
			fo.focussize = "0 0";
			fo.on = true;
			break;
		  case "pattern":
			var f = dojo.gfx.makeParameters(dojo.gfx.defaultPattern, fill);
			this.fillStyle = f;
			var fo = this.rawNode.fill;
			fo.type = "tile";
			fo.src = f.src;
			if (f.width && f.height) {
				fo.size.x = dojo.gfx.vml.px2pt(f.width);
				fo.size.y = dojo.gfx.vml.px2pt(f.height);
			}
			fo.alignShape = false;
			fo.position.x = 0;
			fo.position.y = 0;
			fo.origin.x = f.width ? f.x / f.width : 0;
			fo.origin.y = f.height ? f.y / f.height : 0;
			fo.on = true;
			break;
		}
		this.rawNode.fill.opacity = 1;
		return this;
	}
	this.fillStyle = dojo.gfx.normalizeColor(fill);
	this.rawNode.fillcolor = this.fillStyle.toHex();
	this.rawNode.fill.opacity = this.fillStyle.a;
	this.rawNode.filled = true;
	return this;
}, setStroke:function (stroke) {
	if (!stroke) {
		this.strokeStyle = null;
		this.rawNode.stroked = false;
		return this;
	}
	this.strokeStyle = dojo.gfx.makeParameters(dojo.gfx.defaultStroke, stroke);
	this.strokeStyle.color = dojo.gfx.normalizeColor(this.strokeStyle.color);
	var s = this.strokeStyle;
	this.rawNode.stroked = true;
	this.rawNode.strokecolor = s.color.toCss();
	this.rawNode.strokeweight = s.width + "px";
	if (this.rawNode.stroke) {
		this.rawNode.stroke.opacity = s.color.a;
		this.rawNode.stroke.endcap = this._translate(this._capMap, s.cap);
		if (typeof (s.join) == "number") {
			this.rawNode.stroke.joinstyle = "miter";
			this.rawNode.stroke.miterlimit = s.join;
		} else {
			this.rawNode.stroke.joinstyle = s.join;
		}
	}
	return this;
}, _capMap:{butt:"flat"}, _capMapReversed:{flat:"butt"}, _translate:function (dict, value) {
	return (value in dict) ? dict[value] : value;
}, _applyTransform:function () {
	var matrix = this._getRealMatrix();
	if (!matrix) {
		return this;
	}
	var skew = this.rawNode.skew;
	if (typeof (skew) == "undefined") {
		for (var i = 0; i < this.rawNode.childNodes.length; ++i) {
			if (this.rawNode.childNodes[i].tagName == "skew") {
				skew = this.rawNode.childNodes[i];
				break;
			}
		}
	}
	if (skew) {
		skew.on = false;
		var mt = matrix.xx.toFixed(8) + " " + matrix.xy.toFixed(8) + " " + matrix.yx.toFixed(8) + " " + matrix.yy.toFixed(8) + " 0 0";
		var offset = Math.floor(matrix.dx).toFixed() + "px " + Math.floor(matrix.dy).toFixed() + "px";
		var l = parseFloat(this.rawNode.style.left);
		var t = parseFloat(this.rawNode.style.top);
		var w = parseFloat(this.rawNode.style.width);
		var h = parseFloat(this.rawNode.style.height);
		if (isNaN(l)) {
			l = 0;
		}
		if (isNaN(t)) {
			t = 0;
		}
		if (isNaN(w)) {
			w = 1;
		}
		if (isNaN(h)) {
			h = 1;
		}
		var origin = (-l / w - 0.5).toFixed(8) + " " + (-t / h - 0.5).toFixed(8);
		skew.matrix = mt;
		skew.origin = origin;
		skew.offset = offset;
		skew.on = true;
	}
	return this;
}, setRawNode:function (rawNode) {
	rawNode.stroked = false;
	rawNode.filled = false;
	this.rawNode = rawNode;
}, attachFill:function (rawNode) {
	var fillStyle = null;
	var fo = rawNode.fill;
	if (rawNode) {
		if (fo.on && fo.type == "gradient") {
			var fillStyle = dojo.lang.shallowCopy(dojo.gfx.defaultLinearGradient, true);
			var rad = dojo.math.degToRad(fo.angle);
			fillStyle.x2 = Math.cos(rad);
			fillStyle.y2 = Math.sin(rad);
			fillStyle.colors = [];
			var stops = fo.colors.value.split(";");
			for (var i = 0; i < stops.length; ++i) {
				var t = stops[i].match(/\S+/g);
				if (!t || t.length != 2) {
					continue;
				}
				fillStyle.colors.push({offset:dojo.gfx.vml._parseFloat(t[0]), color:new dojo.gfx.color.Color(t[1])});
			}
		} else {
			if (fo.on && fo.type == "gradientradial") {
				var fillStyle = dojo.lang.shallowCopy(dojo.gfx.defaultRadialGradient, true);
				var w = parseFloat(rawNode.style.width);
				var h = parseFloat(rawNode.style.height);
				fillStyle.cx = isNaN(w) ? 0 : fo.focusposition.x * w;
				fillStyle.cy = isNaN(h) ? 0 : fo.focusposition.y * h;
				fillStyle.r = isNaN(w) ? 1 : w / 2;
				fillStyle.colors = [];
				var stops = fo.colors.value.split(";");
				for (var i = stops.length - 1; i >= 0; --i) {
					var t = stops[i].match(/\S+/g);
					if (!t || t.length != 2) {
						continue;
					}
					fillStyle.colors.push({offset:dojo.gfx.vml._parseFloat(t[0]), color:new dojo.gfx.color.Color(t[1])});
				}
			} else {
				if (fo.on && fo.type == "tile") {
					var fillStyle = dojo.lang.shallowCopy(dojo.gfx.defaultPattern, true);
					fillStyle.width = dojo.gfx.vml.pt2px(fo.size.x);
					fillStyle.height = dojo.gfx.vml.pt2px(fo.size.y);
					fillStyle.x = fo.origin.x * fillStyle.width;
					fillStyle.y = fo.origin.y * fillStyle.height;
					fillStyle.src = fo.src;
				} else {
					if (fo.on && rawNode.fillcolor) {
						fillStyle = new dojo.gfx.color.Color(rawNode.fillcolor + "");
						fillStyle.a = fo.opacity;
					}
				}
			}
		}
	}
	return fillStyle;
}, attachStroke:function (rawNode) {
	var strokeStyle = dojo.lang.shallowCopy(dojo.gfx.defaultStroke, true);
	if (rawNode && rawNode.stroked) {
		strokeStyle.color = new dojo.gfx.color.Color(rawNode.strokecolor.value);
		dojo.debug("We are expecting an .75pt here, instead of strokeweight = " + rawNode.strokeweight);
		strokeStyle.width = dojo.gfx.vml.normalizedLength(rawNode.strokeweight + "");
		strokeStyle.color.a = rawNode.stroke.opacity;
		strokeStyle.cap = this._translate(this._capMapReversed, rawNode.stroke.endcap);
		strokeStyle.join = rawNode.stroke.joinstyle == "miter" ? rawNode.stroke.miterlimit : rawNode.stroke.joinstyle;
	} else {
		return null;
	}
	return strokeStyle;
}, attachTransform:function (rawNode) {
	var matrix = {};
	if (rawNode) {
		var s = rawNode.skew;
		matrix.xx = s.matrix.xtox;
		matrix.xy = s.matrix.ytox;
		matrix.yx = s.matrix.xtoy;
		matrix.yy = s.matrix.ytoy;
		matrix.dx = dojo.gfx.vml.pt2px(s.offset.x);
		matrix.dy = dojo.gfx.vml.pt2px(s.offset.y);
	}
	return dojo.gfx.matrix.normalize(matrix);
}, attach:function (rawNode) {
	if (rawNode) {
		this.rawNode = rawNode;
		this.shape = this.attachShape(rawNode);
		this.fillStyle = this.attachFill(rawNode);
		this.strokeStyle = this.attachStroke(rawNode);
		this.matrix = this.attachTransform(rawNode);
	}
}});
dojo.declare("dojo.gfx.Group", dojo.gfx.shape.VirtualGroup, {add:function (shape) {
	if (this != shape.getParent()) {
		this.rawNode.appendChild(shape.rawNode);
		dojo.gfx.Group.superclass.add.apply(this, arguments);
	}
	return this;
}, remove:function (shape, silently) {
	if (this == shape.getParent()) {
		if (this.rawNode == shape.rawNode.parentNode) {
			this.rawNode.removeChild(shape.rawNode);
		}
		dojo.gfx.Group.superclass.remove.apply(this, arguments);
	}
	return this;
}, attach:function (rawNode) {
	if (rawNode) {
		this.rawNode = rawNode;
		this.shape = null;
		this.fillStyle = null;
		this.strokeStyle = null;
		this.matrix = null;
	}
}});
dojo.gfx.Group.nodeType = "group";
var zIndex = {moveToFront:function () {
	this.rawNode.parentNode.appendChild(this.rawNode);
	return this;
}, moveToBack:function () {
	this.rawNode.parentNode.insertBefore(this.rawNode, this.rawNode.parentNode.firstChild);
	return this;
}};
dojo.lang.extend(dojo.gfx.Shape, zIndex);
dojo.lang.extend(dojo.gfx.Group, zIndex);
delete zIndex;
dojo.declare("dojo.gfx.Rect", dojo.gfx.shape.Rect, {attachShape:function (rawNode) {
	var arcsize = rawNode.outerHTML.match(/arcsize = \"(\d*\.?\d+[%f]?)\"/)[1];
	arcsize = (arcsize.indexOf("%") >= 0) ? parseFloat(arcsize) / 100 : dojo.gfx.vml._parseFloat(arcsize);
	var style = rawNode.style;
	var width = parseFloat(style.width);
	var height = parseFloat(style.height);
	var o = dojo.gfx.makeParameters(dojo.gfx.defaultRect, {x:parseInt(style.left), y:parseInt(style.top), width:width, height:height, r:Math.min(width, height) * arcsize});
	return o;
}, setShape:function (newShape) {
	var shape = this.shape = dojo.gfx.makeParameters(this.shape, newShape);
	this.bbox = null;
	var style = this.rawNode.style;
	style.left = shape.x.toFixed();
	style.top = shape.y.toFixed();
	style.width = (typeof (shape.width) == "string" && shape.width.indexOf("%") >= 0) ? shape.width : shape.width.toFixed();
	style.height = (typeof (shape.width) == "string" && shape.height.indexOf("%") >= 0) ? shape.height : shape.height.toFixed();
	var r = Math.min(1, (shape.r / Math.min(parseFloat(shape.width), parseFloat(shape.height)))).toFixed(8);
	var parent = this.rawNode.parentNode;
	var before = null;
	if (parent) {
		if (parent.lastChild != this.rawNode) {
			for (var i = 0; i < parent.childNodes.length; ++i) {
				if (parent.childNodes[i] == this.rawNode) {
					before = parent.childNodes[i + 1];
					break;
				}
			}
		}
		parent.removeChild(this.rawNode);
	}
	this.rawNode.arcsize = r;
	if (parent) {
		if (before) {
			parent.insertBefore(this.rawNode, before);
		} else {
			parent.appendChild(this.rawNode);
		}
	}
	return this.setTransform(this.matrix);
}});
dojo.gfx.Rect.nodeType = "roundrect";
dojo.declare("dojo.gfx.Ellipse", dojo.gfx.shape.Ellipse, {attachShape:function (rawNode) {
	var style = this.rawNode.style;
	var rx = parseInt(style.width) / 2;
	var ry = parseInt(style.height) / 2;
	var o = dojo.gfx.makeParameters(dojo.gfx.defaultEllipse, {cx:parseInt(style.left) + rx, cy:parseInt(style.top) + ry, rx:rx, ry:ry});
	return o;
}, setShape:function (newShape) {
	var shape = this.shape = dojo.gfx.makeParameters(this.shape, newShape);
	this.bbox = null;
	var style = this.rawNode.style;
	style.left = (shape.cx - shape.rx).toFixed();
	style.top = (shape.cy - shape.ry).toFixed();
	style.width = (shape.rx * 2).toFixed();
	style.height = (shape.ry * 2).toFixed();
	return this.setTransform(this.matrix);
}});
dojo.gfx.Ellipse.nodeType = "oval";
dojo.declare("dojo.gfx.Circle", dojo.gfx.shape.Circle, {attachShape:function (rawNode) {
	var style = this.rawNode.style;
	var r = parseInt(style.width) / 2;
	var o = dojo.gfx.makeParameters(dojo.gfx.defaultCircle, {cx:parseInt(style.left) + r, cy:parseInt(style.top) + r, r:r});
	return o;
}, setShape:function (newShape) {
	var shape = this.shape = dojo.gfx.makeParameters(this.shape, newShape);
	this.bbox = null;
	var style = this.rawNode.style;
	style.left = (shape.cx - shape.r).toFixed();
	style.top = (shape.cy - shape.r).toFixed();
	style.width = (shape.r * 2).toFixed();
	style.height = (shape.r * 2).toFixed();
	return this;
}});
dojo.gfx.Circle.nodeType = "oval";
dojo.declare("dojo.gfx.Line", dojo.gfx.shape.Line, function (rawNode) {
	if (rawNode) {
		rawNode.setAttribute("dojoGfxType", "line");
	}
}, {attachShape:function (rawNode) {
	var p = rawNode.path.v.match(dojo.gfx.pathRegExp);
	var shape = {};
	do {
		if (p.length < 7 || p[0] != "m" || p[3] != "l" || p[6] != "e") {
			break;
		}
		shape.x1 = parseInt(p[1]);
		shape.y1 = parseInt(p[2]);
		shape.x2 = parseInt(p[4]);
		shape.y2 = parseInt(p[5]);
	} while (false);
	return dojo.gfx.makeParameters(dojo.gfx.defaultLine, shape);
}, setShape:function (newShape) {
	var shape = this.shape = dojo.gfx.makeParameters(this.shape, newShape);
	this.bbox = null;
	this.rawNode.path.v = "m" + shape.x1.toFixed() + " " + shape.y1.toFixed() + "l" + shape.x2.toFixed() + " " + shape.y2.toFixed() + "e";
	return this.setTransform(this.matrix);
}});
dojo.gfx.Line.nodeType = "shape";
dojo.declare("dojo.gfx.Polyline", dojo.gfx.shape.Polyline, function (rawNode) {
	if (rawNode) {
		rawNode.setAttribute("dojoGfxType", "polyline");
	}
}, {attachShape:function (rawNode) {
	var shape = dojo.lang.shallowCopy(dojo.gfx.defaultPolyline, true);
	var p = rawNode.path.v.match(dojo.gfx.pathRegExp);
	do {
		if (p.length < 3 || p[0] != "m") {
			break;
		}
		var x = parseInt(p[0]);
		var y = parseInt(p[1]);
		if (isNaN(x) || isNaN(y)) {
			break;
		}
		shape.points.push({x:x, y:y});
		if (p.length < 6 || p[3] != "l") {
			break;
		}
		for (var i = 4; i < p.length; i += 2) {
			x = parseInt(p[i]);
			y = parseInt(p[i + 1]);
			if (isNaN(x) || isNaN(y)) {
				break;
			}
			shape.points.push({x:x, y:y});
		}
	} while (false);
	return shape;
}, setShape:function (points, closed) {
	if (points && points instanceof Array) {
		this.shape = dojo.gfx.makeParameters(this.shape, {points:points});
		if (closed && this.shape.points.length) {
			this.shape.points.push(this.shape.points[0]);
		}
	} else {
		this.shape = dojo.gfx.makeParameters(this.shape, points);
	}
	this.bbox = null;
	var attr = [];
	var p = this.shape.points;
	if (p.length > 0) {
		attr.push("m");
		attr.push(p[0].x.toFixed());
		attr.push(p[0].y.toFixed());
		if (p.length > 1) {
			attr.push("l");
			for (var i = 1; i < p.length; ++i) {
				attr.push(p[i].x.toFixed());
				attr.push(p[i].y.toFixed());
			}
		}
	}
	attr.push("e");
	this.rawNode.path.v = attr.join(" ");
	return this.setTransform(this.matrix);
}});
dojo.gfx.Polyline.nodeType = "shape";
dojo.declare("dojo.gfx.Image", dojo.gfx.shape.Image, {getEventSource:function () {
	return this.rawNode ? this.rawNode.firstChild : null;
}, attachShape:function (rawNode) {
	var shape = dojo.lang.shallowCopy(dojo.gfx.defaultImage, true);
	shape.src = rawNode.firstChild.src;
	return shape;
}, setShape:function (newShape) {
	var shape = this.shape = dojo.gfx.makeParameters(this.shape, newShape);
	this.bbox = null;
	var firstChild = this.rawNode.firstChild;
	firstChild.src = shape.src;
	if (shape.width || shape.height) {
		firstChild.style.width = shape.width;
		firstChild.style.height = shape.height;
	}
	return this.setTransform(this.matrix);
}, setStroke:function () {
	return this;
}, setFill:function () {
	return this;
}, attachStroke:function (rawNode) {
	return null;
}, attachFill:function (rawNode) {
	return null;
}, attachTransform:function (rawNode) {
	var matrix = {};
	if (rawNode) {
		var m = rawNode.filters["DXImageTransform.Microsoft.Matrix"];
		matrix.xx = m.M11;
		matrix.xy = m.M12;
		matrix.yx = m.M21;
		matrix.yy = m.M22;
		matrix.dx = m.Dx;
		matrix.dy = m.Dy;
	}
	return dojo.gfx.matrix.normalize(matrix);
}, _applyTransform:function () {
	var matrix = this._getRealMatrix();
	if (!matrix) {
		return this;
	}
	with (this.rawNode.filters["DXImageTransform.Microsoft.Matrix"]) {
		M11 = matrix.xx;
		M12 = matrix.xy;
		M21 = matrix.yx;
		M22 = matrix.yy;
		Dx = matrix.dx;
		Dy = matrix.dy;
	}
	return this;
}});
dojo.gfx.Image.nodeType = "image";
dojo.gfx.path._calcArc = function (alpha) {
	var cosa = Math.cos(alpha);
	var sina = Math.sin(alpha);
	var p2 = {x:cosa + (4 / 3) * (1 - cosa), y:sina - (4 / 3) * cosa * (1 - cosa) / sina};
	return {s:{x:cosa, y:sina}, c1:p2, c2:{x:p2.x, y:-p2.y}, e:{x:cosa, y:-sina}};
};
dojo.declare("dojo.gfx.Path", dojo.gfx.path.Path, function (rawNode) {
	if (rawNode) {
		rawNode.setAttribute("dojoGfxType", "path");
	}
	this.vmlPath = "";
	this.lastControl = {};
}, {_updateWithSegment:function (segment) {
	var last = dojo.lang.shallowCopy(this.last);
	dojo.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
	var path = this[this.renderers[segment.action]](segment, last);
	if (typeof (this.vmlPath) == "string") {
		this.vmlPath += path.join("");
	} else {
		this.vmlPath = this.vmlPath.concat(path);
	}
	if (typeof (this.vmlPath) == "string") {
		this.rawNode.path.v = this.vmlPath + " e";
	}
}, attachShape:function (rawNode) {
	var shape = dojo.lang.shallowCopy(dojo.gfx.defaultPath, true);
	var p = rawNode.path.v.match(dojo.gfx.pathRegExp);
	var t = [], skip = false;
	for (var i = 0; i < p.length; ++p) {
		var s = p[i];
		if (s in this._pathVmlToSvgMap) {
			skip = false;
			t.push(this._pathVmlToSvgMap[s]);
		} else {
			if (!skip) {
				var n = parseInt(s);
				if (isNaN(n)) {
					skip = true;
				} else {
					t.push(n);
				}
			}
		}
	}
	if (t.length) {
		shape.path = t.join(" ");
	}
	return shape;
}, setShape:function (newShape) {
	this.vmlPath = [];
	this.lastControl = {};
	dojo.gfx.Path.superclass.setShape.apply(this, arguments);
	this.vmlPath = this.vmlPath.join("");
	this.rawNode.path.v = this.vmlPath + " e";
	return this;
}, _pathVmlToSvgMap:{m:"M", l:"L", t:"m", r:"l", c:"C", v:"c", qb:"Q", x:"z", e:""}, renderers:{M:"_moveToA", m:"_moveToR", L:"_lineToA", l:"_lineToR", H:"_hLineToA", h:"_hLineToR", V:"_vLineToA", v:"_vLineToR", C:"_curveToA", c:"_curveToR", S:"_smoothCurveToA", s:"_smoothCurveToR", Q:"_qCurveToA", q:"_qCurveToR", T:"_qSmoothCurveToA", t:"_qSmoothCurveToR", A:"_arcTo", a:"_arcTo", Z:"_closePath", z:"_closePath"}, _addArgs:function (path, args, from, upto) {
	if (typeof (upto) == "undefined") {
		upto = args.length;
	}
	if (typeof (from) == "undefined") {
		from = 0;
	}
	for (var i = from; i < upto; ++i) {
		path.push(" ");
		path.push(args[i].toFixed());
	}
}, _addArgsAdjusted:function (path, last, args, from, upto) {
	if (typeof (upto) == "undefined") {
		upto = args.length;
	}
	if (typeof (from) == "undefined") {
		from = 0;
	}
	for (var i = from; i < upto; i += 2) {
		path.push(" ");
		path.push((last.x + args[i]).toFixed());
		path.push(" ");
		path.push((last.y + args[i + 1]).toFixed());
	}
}, _moveToA:function (segment) {
	var p = [" m"];
	var n = segment.args;
	var l = n.length;
	if (l == 2) {
		this._addArgs(p, n);
	} else {
		this._addArgs(p, n, 0, 2);
		p.push(" l");
		this._addArgs(p, n, 2);
	}
	this.lastControl = {};
	return p;
}, _moveToR:function (segment, last) {
	var p = ["x" in last ? " t" : " m"];
	var n = segment.args;
	var l = n.length;
	if (l == 2) {
		this._addArgs(p, n);
	} else {
		this._addArgs(p, n, 0, 2);
		p.push(" r");
		this._addArgs(p, n, 2);
	}
	this.lastControl = {};
	return p;
}, _lineToA:function (segment) {
	var p = [" l"];
	this._addArgs(p, segment.args);
	this.lastControl = {};
	return p;
}, _lineToR:function (segment) {
	var p = [" r"];
	this._addArgs(p, segment.args);
	this.lastControl = {};
	return p;
}, _hLineToA:function (segment, last) {
	var p = [" l"];
	var n = segment.args;
	var l = n.length;
	var y = " " + last.y.toFixed();
	for (var i = 0; i < l; ++i) {
		p.push(" ");
		p.push(n[i].toFixed());
		p.push(y);
	}
	this.lastControl = {};
	return p;
}, _hLineToR:function (segment) {
	var p = [" r"];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; ++i) {
		p.push(" ");
		p.push(n[i].toFixed());
		p.push(" 0");
	}
	this.lastControl = {};
	return p;
}, _vLineToA:function (segment, last) {
	var p = [" l"];
	var n = segment.args;
	var l = n.length;
	var x = " " + last.x.toFixed();
	for (var i = 0; i < l; ++i) {
		p.push(x);
		p.push(" ");
		p.push(n[i].toFixed());
	}
	this.lastControl = {};
	return p;
}, _vLineToR:function (segment) {
	var p = [" r"];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; ++i) {
		p.push(" 0 ");
		p.push(n[i].toFixed());
	}
	this.lastControl = {};
	return p;
}, _curveToA:function (segment) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 6) {
		p.push(" c");
		this._addArgs(p, n, i, i + 6);
	}
	this.lastControl = {x:n[l - 4], y:n[l - 3], type:"C"};
	return p;
}, _curveToR:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 6) {
		p.push(" v");
		this._addArgs(p, n, i, i + 6);
		this.lastControl = {x:last.x + n[i + 2], y:last.y + n[i + 3]};
		last.x += n[i + 4];
		last.y += n[i + 5];
	}
	this.lastControl.type = "C";
	return p;
}, _smoothCurveToA:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 4) {
		p.push(" c");
		if (this.lastControl.type == "C") {
			this._addArgs(p, [2 * last.x - this.lastControl.x, 2 * last.y - this.lastControl.y]);
		} else {
			this._addArgs(p, [last.x, last.y]);
		}
		this._addArgs(p, n, i, i + 4);
	}
	this.lastControl = {x:n[l - 4], y:n[l - 3], type:"C"};
	return p;
}, _smoothCurveToR:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 4) {
		p.push(" v");
		if (this.lastControl.type == "C") {
			this._addArgs(p, [last.x - this.lastControl.x, last.y - this.lastControl.y]);
		} else {
			this._addArgs(p, [0, 0]);
		}
		this._addArgs(p, n, i, i + 4);
		this.lastControl = {x:last.x + n[i], y:last.y + n[i + 1]};
		last.x += n[i + 2];
		last.y += n[i + 3];
	}
	this.lastControl.type = "C";
	return p;
}, _qCurveToA:function (segment) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 4) {
		p.push(" qb");
		this._addArgs(p, n, i, i + 4);
	}
	this.lastControl = {x:n[l - 4], y:n[l - 3], type:"Q"};
	return p;
}, _qCurveToR:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 4) {
		p.push(" qb");
		this._addArgsAdjusted(p, last, n, i, i + 4);
		this.lastControl = {x:last.x + n[i], y:last.y + n[i + 1]};
		last.x += n[i + 2];
		last.y += n[i + 3];
	}
	this.lastControl.type = "Q";
	return p;
}, _qSmoothCurveToA:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 2) {
		p.push(" qb");
		if (this.lastControl.type == "Q") {
			this._addArgs(p, [this.lastControl.x = 2 * last.x - this.lastControl.x, this.lastControl.y = 2 * last.y - this.lastControl.y]);
		} else {
			this._addArgs(p, [this.lastControl.x = last.x, this.lastControl.y = last.y]);
		}
		this._addArgs(p, n, i, i + 2);
	}
	this.lastControl.type = "Q";
	return p;
}, _qSmoothCurveToR:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	for (var i = 0; i < l; i += 2) {
		p.push(" qb");
		if (this.lastControl.type == "Q") {
			this._addArgs(p, [this.lastControl.x = 2 * last.x - this.lastControl.x, this.lastControl.y = 2 * last.y - this.lastControl.y]);
		} else {
			this._addArgs(p, [this.lastControl.x = last.x, this.lastControl.y = last.y]);
		}
		this._addArgsAdjusted(p, last, n, i, i + 2);
	}
	this.lastControl.type = "Q";
	return p;
}, _PI4:Math.PI / 4, _curvePI4:dojo.gfx.path._calcArc(Math.PI / 8), _calcArcTo:function (path, last, rx, ry, xRotg, large, cw, x, y) {
	var m = dojo.gfx.matrix;
	var xRot = -dojo.math.degToRad(xRotg);
	var rx2 = rx * rx;
	var ry2 = ry * ry;
	var pa = m.multiplyPoint(m.rotate(-xRot), {x:(last.x - x) / 2, y:(last.y - y) / 2});
	var pax2 = pa.x * pa.x;
	var pay2 = pa.y * pa.y;
	var c1 = Math.sqrt((rx2 * ry2 - rx2 * pay2 - ry2 * pax2) / (rx2 * pay2 + ry2 * pax2));
	var ca = {x:c1 * rx * pa.y / ry, y:-c1 * ry * pa.x / rx};
	if (large == cw) {
		ca = {x:-ca.x, y:-ca.y};
	}
	var c = m.multiplyPoint([m.translate((last.x + x) / 2, (last.y + y) / 2), m.rotate(xRot)], ca);
	var startAngle = Math.atan2(c.y - last.y, last.x - c.x) - xRot;
	var endAngle = Math.atan2(c.y - y, x - c.x) - xRot;
	var theta = cw ? startAngle - endAngle : endAngle - startAngle;
	if (theta < 0) {
		theta += this._2PI;
	} else {
		if (theta > this._2PI) {
			theta = this._2PI;
		}
	}
	var elliptic_transform = m.normalize([m.translate(c.x, c.y), m.rotate(xRot), m.scale(rx, ry)]);
	var alpha = this._PI4 / 2;
	var curve = this._curvePI4;
	var step = cw ? -alpha : alpha;
	for (var angle = theta; angle > 0; angle -= this._PI4) {
		if (angle < this._PI4) {
			alpha = angle / 2;
			curve = dojo.gfx.path._calcArc(alpha);
			step = cw ? -alpha : alpha;
		}
		var c1, c2, e;
		var M = m.normalize([elliptic_transform, m.rotate(startAngle + step)]);
		if (cw) {
			c1 = m.multiplyPoint(M, curve.c2);
			c2 = m.multiplyPoint(M, curve.c1);
			e = m.multiplyPoint(M, curve.s);
		} else {
			c1 = m.multiplyPoint(M, curve.c1);
			c2 = m.multiplyPoint(M, curve.c2);
			e = m.multiplyPoint(M, curve.e);
		}
		path.push(" c");
		this._addArgs(path, [c1.x, c1.y, c2.x, c2.y, e.x, e.y]);
		startAngle += 2 * step;
	}
}, _arcTo:function (segment, last) {
	var p = [];
	var n = segment.args;
	var l = n.length;
	var relative = segment.action == "a";
	for (var i = 0; i < l; i += 7) {
		var x1 = n[i + 5];
		var y1 = n[i + 6];
		if (relative) {
			x1 += last.x;
			y1 += last.y;
		}
		this._calcArcTo(p, last, n[i], n[i + 1], n[i + 2], n[i + 3] ? 1 : 0, n[i + 4] ? 1 : 0, x1, y1);
		last = {x:x1, y:y1};
	}
	this.lastControl = {};
	return p;
}, _closePath:function () {
	this.lastControl = {};
	return ["x"];
}});
dojo.gfx.Path.nodeType = "shape";
dojo.gfx._creators = {createPath:function (path) {
	return this.createObject(dojo.gfx.Path, path, true);
}, createRect:function (rect) {
	return this.createObject(dojo.gfx.Rect, rect);
}, createCircle:function (circle) {
	return this.createObject(dojo.gfx.Circle, circle);
}, createEllipse:function (ellipse) {
	return this.createObject(dojo.gfx.Ellipse, ellipse);
}, createLine:function (line) {
	return this.createObject(dojo.gfx.Line, line, true);
}, createPolyline:function (points) {
	return this.createObject(dojo.gfx.Polyline, points, true);
}, createImage:function (image) {
	if (!this.rawNode) {
		return null;
	}
	var shape = new dojo.gfx.Image();
	var node = document.createElement("div");
	node.style.position = "relative";
	node.style.width = this.rawNode.style.width;
	node.style.height = this.rawNode.style.height;
	node.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11=1, M12=0, M21=0, M22=1, Dx=0, Dy=0)";
	var img = document.createElement("img");
	node.appendChild(img);
	shape.setRawNode(node);
	this.rawNode.appendChild(node);
	shape.setShape(image);
	this.add(shape);
	return shape;
}, createGroup:function () {
	return this.createObject(dojo.gfx.Group, null, true);
}, createObject:function (shapeType, rawShape, overrideSize) {
	if (!this.rawNode) {
		return null;
	}
	var shape = new shapeType();
	var node = document.createElement("v:" + shapeType.nodeType);
	shape.setRawNode(node);
	this.rawNode.appendChild(node);
	if (overrideSize) {
		this._overrideSize(node);
	}
	shape.setShape(rawShape);
	this.add(shape);
	return shape;
}, _overrideSize:function (node) {
	node.style.width = this.rawNode.style.width;
	node.style.height = this.rawNode.style.height;
	node.coordsize = parseFloat(node.style.width) + " " + parseFloat(node.style.height);
}};
dojo.lang.extend(dojo.gfx.Group, dojo.gfx._creators);
dojo.lang.extend(dojo.gfx.Surface, dojo.gfx._creators);
delete dojo.gfx._creators;
dojo.gfx.attachNode = function (node) {
	if (!node) {
		return null;
	}
	var s = null;
	switch (node.tagName.toLowerCase()) {
	  case dojo.gfx.Rect.nodeType:
		s = new dojo.gfx.Rect();
		break;
	  case dojo.gfx.Ellipse.nodeType:
		s = (node.style.width == node.style.height) ? new dojo.gfx.Circle() : new dojo.gfx.Ellipse();
		break;
	  case dojo.gfx.Path.nodeType:
		switch (node.getAttribute("dojoGfxType")) {
		  case "line":
			s = new dojo.gfx.Line();
			break;
		  case "polyline":
			s = new dojo.gfx.Polyline();
			break;
		  case "path":
			s = new dojo.gfx.Path();
			break;
		}
		break;
	  case dojo.gfx.Image.nodeType:
		s = new dojo.gfx.Image();
		break;
	  default:
		dojo.debug("FATAL ERROR! tagName = " + node.tagName);
	}
	s.attach(node);
	return s;
};
dojo.lang.extend(dojo.gfx.Surface, {setDimensions:function (width, height) {
	if (!this.rawNode) {
		return this;
	}
	this.rawNode.style.width = width;
	this.rawNode.style.height = height;
	this.rawNode.coordsize = width + " " + height;
	return this;
}, getDimensions:function () {
	return this.rawNode ? {width:this.rawNode.style.width, height:this.rawNode.style.height} : null;
}, add:function (shape) {
	var oldParent = shape.getParent();
	if (this != oldParent) {
		this.rawNode.appendChild(shape.rawNode);
		if (oldParent) {
			oldParent.remove(shape, true);
		}
		shape._setParent(this, null);
	}
	return this;
}, remove:function (shape, silently) {
	if (this == shape.getParent()) {
		if (this.rawNode == shape.rawNode.parentNode) {
			this.rawNode.removeChild(shape.rawNode);
		}
		shape._setParent(null, null);
	}
	return this;
}});
dojo.gfx.createSurface = function (parentNode, width, height) {
	var s = new dojo.gfx.Surface();
	s.rawNode = document.createElement("v:group");
	s.rawNode.style.width = width ? width : "100%";
	s.rawNode.style.height = height ? height : "100%";
	s.rawNode.coordsize = (width && height) ? (parseFloat(width) + " " + parseFloat(height)) : "100% 100%";
	s.rawNode.coordorigin = "0 0";
	dojo.byId(parentNode).appendChild(s.rawNode);
	return s;
};
dojo.gfx.attachSurface = function (node) {
	var s = new dojo.gfx.Surface();
	s.rawNode = node;
	return s;
};

/*
	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
*/



dojo.provide("dojo.json");
dojo.json = {jsonRegistry:new dojo.AdapterRegistry(), register:function (name, check, wrap, override) {
	dojo.json.jsonRegistry.register(name, check, wrap, override);
}, evalJson:function (json) {
	try {
		return eval("(" + json + ")");
	}
	catch (e) {
		dojo.debug(e);
		return json;
	}
}, serialize:function (o) {
	var objtype = typeof (o);
	if (objtype == "undefined") {
		return "undefined";
	} else {
		if ((objtype == "number") || (objtype == "boolean")) {
			return o + "";
		} else {
			if (o === null) {
				return "null";
			}
		}
	}
	if (objtype == "string") {
		return dojo.string.escapeString(o);
	}
	var me = arguments.callee;
	var newObj;
	if (typeof (o.__json__) == "function") {
		newObj = o.__json__();
		if (o !== newObj) {
			return me(newObj);
		}
	}
	if (typeof (o.json) == "function") {
		newObj = o.json();
		if (o !== newObj) {
			return me(newObj);
		}
	}
	if (objtype != "function" && typeof (o.length) == "number") {
		var res = [];
		for (var i = 0; i < o.length; i++) {
			var val = me(o[i]);
			if (typeof (val) != "string") {
				val = "undefined";
			}
			res.push(val);
		}
		return "[" + res.join(",") + "]";
	}
	try {
		window.o = o;
		newObj = dojo.json.jsonRegistry.match(o);
		return me(newObj);
	}
	catch (e) {
	}
	if (objtype == "function") {
		return null;
	}
	res = [];
	for (var k in o) {
		var useKey;
		if (typeof (k) == "number") {
			useKey = "\"" + k + "\"";
		} else {
			if (typeof (k) == "string") {
				useKey = dojo.string.escapeString(k);
			} else {
				continue;
			}
		}
		val = me(o[k]);
		if (typeof (val) != "string") {
			continue;
		}
		res.push(useKey + ":" + val);
	}
	return "{" + res.join(",") + "}";
}};



dojo.provide("dojo.widget.PopupContainer");
dojo.declare("dojo.widget.PopupContainerBase", null, function () {
	this.queueOnAnimationFinish = [];
}, {isShowingNow:false, currentSubpopup:null, beginZIndex:1000, parentPopup:null, parent:null, popupIndex:0, aroundBox:dojo.html.boxSizing.BORDER_BOX, openedForWindow:null, processKey:function (evt) {
	return false;
}, applyPopupBasicStyle:function () {
	with (this.domNode.style) {
		display = "none";
		position = "absolute";
	}
}, aboutToShow:function () {
}, open:function (x, y, parent, explodeSrc, orient, padding) {
	if (this.isShowingNow) {
		return;
	}
	if (this.animationInProgress) {
		this.queueOnAnimationFinish.push(this.open, arguments);
		return;
	}
	this.aboutToShow();
	var around = false, node, aroundOrient;
	if (typeof x == "object") {
		node = x;
		aroundOrient = explodeSrc;
		explodeSrc = parent;
		parent = y;
		around = true;
	}
	this.parent = parent;
	dojo.body().appendChild(this.domNode);
	explodeSrc = explodeSrc || parent["domNode"] || [];
	var parentPopup = null;
	this.isTopLevel = true;
	while (parent) {
		if (parent !== this && (parent.setOpenedSubpopup != undefined && parent.applyPopupBasicStyle != undefined)) {
			parentPopup = parent;
			this.isTopLevel = false;
			parentPopup.setOpenedSubpopup(this);
			break;
		}
		parent = parent.parent;
	}
	this.parentPopup = parentPopup;
	this.popupIndex = parentPopup ? parentPopup.popupIndex + 1 : 1;
	if (this.isTopLevel) {
		var button = dojo.html.isNode(explodeSrc) ? explodeSrc : null;
		dojo.widget.PopupManager.opened(this, button);
	}
	if (this.isTopLevel && !dojo.withGlobal(this.openedForWindow || dojo.global(), dojo.html.selection.isCollapsed)) {
		this._bookmark = dojo.withGlobal(this.openedForWindow || dojo.global(), dojo.html.selection.getBookmark);
	} else {
		this._bookmark = null;
	}
	if (explodeSrc instanceof Array) {
		explodeSrc = {left:explodeSrc[0], top:explodeSrc[1], width:0, height:0};
	}
	with (this.domNode.style) {
		display = "";
		zIndex = this.beginZIndex + this.popupIndex;
	}
	if (around) {
		this.move(node, padding, aroundOrient);
	} else {
		this.move(x, y, padding, orient);
	}
	this.domNode.style.display = "none";
	this.explodeSrc = explodeSrc;
	this.show();
	this.isShowingNow = true;
}, move:function (x, y, padding, orient) {
	var around = (typeof x == "object");
	if (around) {
		var aroundOrient = padding;
		var node = x;
		padding = y;
		if (!aroundOrient) {
			aroundOrient = {"BL":"TL", "TL":"BL"};
		}
		dojo.html.placeOnScreenAroundElement(this.domNode, node, padding, this.aroundBox, aroundOrient);
	} else {
		if (!orient) {
			orient = "TL,TR,BL,BR";
		}
		dojo.html.placeOnScreen(this.domNode, x, y, padding, true, orient);
	}
}, close:function (force) {
	if (force) {
		this.domNode.style.display = "none";
	}
	if (this.animationInProgress) {
		this.queueOnAnimationFinish.push(this.close, []);
		return;
	}
	this.closeSubpopup(force);
	this.hide();
	if (this.bgIframe) {
		this.bgIframe.hide();
		this.bgIframe.size({left:0, top:0, width:0, height:0});
	}
	if (this.isTopLevel) {
		dojo.widget.PopupManager.closed(this);
	}
	this.isShowingNow = false;
	if (this.parent) {
		setTimeout(dojo.lang.hitch(this, function () {
			try {
				if (this.parent["focus"]) {
					this.parent.focus();
				} else {
					this.parent.domNode.focus();
				}
			}
			catch (e) {
				dojo.debug("No idea how to focus to parent", e);
			}
		}), 10);
	}
	if (this._bookmark && dojo.withGlobal(this.openedForWindow || dojo.global(), dojo.html.selection.isCollapsed)) {
		if (this.openedForWindow) {
			this.openedForWindow.focus();
		}
		try {
			dojo.withGlobal(this.openedForWindow || dojo.global(), "moveToBookmark", dojo.html.selection, [this._bookmark]);
		}
		catch (e) {
		}
	}
	this._bookmark = null;
}, closeAll:function (force) {
	if (this.parentPopup) {
		this.parentPopup.closeAll(force);
	} else {
		this.close(force);
	}
}, setOpenedSubpopup:function (popup) {
	this.currentSubpopup = popup;
}, closeSubpopup:function (force) {
	if (this.currentSubpopup == null) {
		return;
	}
	this.currentSubpopup.close(force);
	this.currentSubpopup = null;
}, onShow:function () {
	dojo.widget.PopupContainer.superclass.onShow.apply(this, arguments);
	this.openedSize = {w:this.domNode.style.width, h:this.domNode.style.height};
	if (dojo.render.html.ie) {
		if (!this.bgIframe) {
			this.bgIframe = new dojo.html.BackgroundIframe();
			this.bgIframe.setZIndex(this.domNode);
		}
		this.bgIframe.size(this.domNode);
		this.bgIframe.show();
	}
	this.processQueue();
}, processQueue:function () {
	if (!this.queueOnAnimationFinish.length) {
		return;
	}
	var func = this.queueOnAnimationFinish.shift();
	var args = this.queueOnAnimationFinish.shift();
	func.apply(this, args);
}, onHide:function () {
	dojo.widget.HtmlWidget.prototype.onHide.call(this);
	if (this.openedSize) {
		with (this.domNode.style) {
			width = this.openedSize.w;
			height = this.openedSize.h;
		}
	}
	this.processQueue();
}});
dojo.widget.defineWidget("dojo.widget.PopupContainer", [dojo.widget.HtmlWidget, dojo.widget.PopupContainerBase], {isContainer:true, fillInTemplate:function () {
	this.applyPopupBasicStyle();
	dojo.widget.PopupContainer.superclass.fillInTemplate.apply(this, arguments);
}});
dojo.widget.PopupManager = new function () {
	this.currentMenu = null;
	this.currentButton = null;
	this.currentFocusMenu = null;
	this.focusNode = null;
	this.registeredWindows = [];
	this.registerWin = function (win) {
		if (!win.__PopupManagerRegistered) {
			dojo.event.connect(win.document, "onmousedown", this, "onClick");
			dojo.event.connect(win, "onscroll", this, "onClick");
			dojo.event.connect(win.document, "onkey", this, "onKey");
			win.__PopupManagerRegistered = true;
			this.registeredWindows.push(win);
		}
	};
	this.registerAllWindows = function (targetWindow) {
		if (!targetWindow) {
			targetWindow = dojo.html.getDocumentWindow(window.top && window.top.document || window.document);
		}
		this.registerWin(targetWindow);
		for (var i = 0; i < targetWindow.frames.length; i++) {
			try {
				var win = dojo.html.getDocumentWindow(targetWindow.frames[i].document);
				if (win) {
					this.registerAllWindows(win);
				}
			}
			catch (e) {
			}
		}
	};
	this.unRegisterWin = function (win) {
		try {
			if (win.__PopupManagerRegistered) {
				dojo.event.disconnect(win.document, "onmousedown", this, "onClick");
				dojo.event.disconnect(win, "onscroll", this, "onClick");
				dojo.event.disconnect(win.document, "onkey", this, "onKey");
				win.__PopupManagerRegistered = false;
			}
		} catch(e) {};
		
	};
	this.unRegisterAllWindows = function () {
		for (var i = 0; i < this.registeredWindows.length; ++i) {
			this.unRegisterWin(this.registeredWindows[i]);
		}
		this.registeredWindows = [];
	};
	dojo.addOnLoad(this, "registerAllWindows");
	dojo.addOnUnload(this, "unRegisterAllWindows");
	this.closed = function (menu) {
		if (this.currentMenu == menu) {
			this.currentMenu = null;
			this.currentButton = null;
			this.currentFocusMenu = null;
		}
	};
	this.opened = function (menu, button) {
		if (menu == this.currentMenu) {
			return;
		}
		if (this.currentMenu) {
			this.currentMenu.close();
		}
		this.currentMenu = menu;
		this.currentFocusMenu = menu;
		this.currentButton = button;
	};
	this.setFocusedMenu = function (menu) {
		this.currentFocusMenu = menu;
	};
	this.onKey = function (e) {
		if (!e.key) {
			return;
		}
		if (!this.currentMenu || !this.currentMenu.isShowingNow) {
			return;
		}
		var m = this.currentFocusMenu;
		while (m) {
			if (m.processKey(e)) {
				e.preventDefault();
				e.stopPropagation();
				break;
			}
			m = m.parentPopup || m.parentMenu;
		}
	}, this.onClick = function (e) {
		if (!this.currentMenu) {
			return;
		}
		var scrolloffset = dojo.html.getScroll().offset;
		var m = this.currentMenu;
		while (m) {
			if (dojo.html.overElement(m.domNode, e) || dojo.html.isDescendantOf(e.target, m.domNode)) {
				return;
			}
			m = m.currentSubpopup;
		}
		if (this.currentButton && dojo.html.overElement(this.currentButton, e)) {
			return;
		}
		this.currentMenu.closeAll(true);
	};
};


/*
	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
*/



dojo.provide("dojo.widget.Menu2");
dojo.declare("dojo.widget.MenuBase", null, function () {
	this.eventNames = {open:""};
}, {isContainer:true, isMenu:true, eventNaming:"default", templateCssString:"", templateCssPath:"/dojo/src/widget/templates/Menu2.css", submenuDelay:500, initialize:function (args, frag) {
	if (this.eventNaming == "default") {
		for (var eventName in this.eventNames) {
			this.eventNames[eventName] = this.widgetId + "/" + eventName;
		}
	}
}, _moveToNext:function (evt) {
	this._highlightOption(1);
	return true;
}, _moveToPrevious:function (evt) {
	this._highlightOption(-1);
	return true;
}, _moveToParentMenu:function (evt) {
	if (this._highlighted_option && this.parentMenu) {
		if (evt._menu2UpKeyProcessed) {
			return true;
		} else {
			this._highlighted_option.onUnhover();
			this.closeSubmenu();
			evt._menu2UpKeyProcessed = true;
		}
	}
	return false;
}, _moveToChildMenu:function (evt) {
	if (this._highlighted_option && this._highlighted_option.submenuId) {
		this._highlighted_option._onClick(true);
		return true;
	}
	return false;
}, _selectCurrentItem:function (evt) {
	if (this._highlighted_option) {
		this._highlighted_option._onClick();
		return true;
	}
	return false;
}, processKey:function (evt) {
	if (evt.ctrlKey || evt.altKey || !evt.key) {
		return false;
	}
	var rval = false;
	switch (evt.key) {
	  case evt.KEY_DOWN_ARROW:
		rval = this._moveToNext(evt);
		break;
	  case evt.KEY_UP_ARROW:
		rval = this._moveToPrevious(evt);
		break;
	  case evt.KEY_RIGHT_ARROW:
		rval = this._moveToChildMenu(evt);
		break;
	  case evt.KEY_LEFT_ARROW:
		rval = this._moveToParentMenu(evt);
		break;
	  case " ":
	  case evt.KEY_ENTER:
		if (rval = this._selectCurrentItem(evt)) {
			break;
		}
	  case evt.KEY_ESCAPE:
	  case evt.KEY_TAB:
		this.close(true);
		rval = true;
		break;
	}
	return rval;
}, _findValidItem:function (dir, curItem) {
	if (curItem) {
		curItem = dir > 0 ? curItem.getNextSibling() : curItem.getPreviousSibling();
	}
	for (var i = 0; i < this.children.length; ++i) {
		if (!curItem) {
			curItem = dir > 0 ? this.children[0] : this.children[this.children.length - 1];
		}
		if (curItem.onHover && curItem.isShowing()) {
			return curItem;
		}
		curItem = dir > 0 ? curItem.getNextSibling() : curItem.getPreviousSibling();
	}
}, _highlightOption:function (dir) {
	var item;
	if ((!this._highlighted_option)) {
		item = this._findValidItem(dir);
	} else {
		item = this._findValidItem(dir, this._highlighted_option);
	}
	if (item) {
		if (this._highlighted_option) {
			this._highlighted_option.onUnhover();
		}
		item.onHover();
		dojo.html.scrollIntoView(item.domNode);
		try {
			var node = dojo.html.getElementsByClass("dojoMenuItem2Label", item.domNode)[0];
			node.focus();
		}
		catch (e) {
		}
	}
}, onItemClick:function (item) {
}, closeSubmenu:function (force) {
	if (this.currentSubmenu == null) {
		return;
	}
	this.currentSubmenu.close(force);
	this.currentSubmenu = null;
	this.currentSubmenuTrigger.is_open = false;
	this.currentSubmenuTrigger._closedSubmenu(force);
	this.currentSubmenuTrigger = null;
}});
dojo.widget.defineWidget("dojo.widget.PopupMenu2", [dojo.widget.HtmlWidget, dojo.widget.PopupContainerBase, dojo.widget.MenuBase], function () {
	this.targetNodeIds = [];
}, {templateString:"<table class=\"dojoPopupMenu2\" border=0 cellspacing=0 cellpadding=0 style=\"display: none; position: absolute;\">" + "<tbody dojoAttachPoint=\"containerNode\"></tbody>" + "</table>", submenuOverlap:5, contextMenuForWindow:false, parentMenu:null, postCreate:function () {
	if (this.contextMenuForWindow) {
		var doc = dojo.body();
		this.bindDomNode(doc);
	} else {
		if (this.targetNodeIds.length > 0) {
			dojo.lang.forEach(this.targetNodeIds, this.bindDomNode, this);
		}
	}
	this._subscribeSubitemsOnOpen();
}, _subscribeSubitemsOnOpen:function () {
	var subItems = this.getChildrenOfType(dojo.widget.MenuItem2);
	for (var i = 0; i < subItems.length; i++) {
		dojo.event.topic.subscribe(this.eventNames.open, subItems[i], "menuOpen");
	}
}, getTopOpenEvent:function () {
	var menu = this;
	while (menu.parentMenu) {
		menu = menu.parentMenu;
	}
	return menu.openEvent;
}, bindDomNode:function (node) {
	node = dojo.byId(node);
	var win = dojo.html.getElementWindow(node);
	if (dojo.html.isTag(node, "iframe") == "iframe") {
		win = dojo.html.iframeContentWindow(node);
		node = dojo.withGlobal(win, dojo.body);
	}
	dojo.widget.Menu2.OperaAndKonqFixer.fixNode(node);
	dojo.event.kwConnect({srcObj:node, srcFunc:"oncontextmenu", targetObj:this, targetFunc:"onOpen", once:true});
	if (dojo.render.html.moz && win.document.designMode.toLowerCase() == "on") {
		dojo.event.browser.addListener(node, "contextmenu", dojo.lang.hitch(this, "onOpen"));
	}
	dojo.widget.PopupManager.registerWin(win);
}, unBindDomNode:function (nodeName) {
	var node = dojo.byId(nodeName);
	dojo.event.kwDisconnect({srcObj:node, srcFunc:"oncontextmenu", targetObj:this, targetFunc:"onOpen", once:true});
	dojo.widget.Menu2.OperaAndKonqFixer.cleanNode(node);
}, _openAsSubmenu:function (parent, explodeSrc, orient) {
	if (this.isShowingNow) {
		return;
	}
	this.parentMenu = parent;
	this.open(explodeSrc, parent, explodeSrc, orient);
}, close:function (force) {
	if (this.animationInProgress) {
		dojo.widget.PopupContainerBase.prototype.close.call(this, force);
		return;
	}
	if (this._highlighted_option) {
		this._highlighted_option.onUnhover();
	}
	dojo.widget.PopupContainerBase.prototype.close.call(this, force);
	this.parentMenu = null;
}, closeAll:function (force) {
	if (this.parentMenu) {
		this.parentMenu.closeAll(force);
	} else {
		this.close(force);
	}
}, _openSubmenu:function (submenu, from_item) {
	submenu._openAsSubmenu(this, from_item.arrow, {"TR":"TL", "TL":"TR"});
	this.currentSubmenu = submenu;
	this.currentSubmenuTrigger = from_item;
	this.currentSubmenuTrigger.is_open = true;
}, focus:function () {
	if (this.currentSubmenuTrigger) {
		if (this.currentSubmenuTrigger.caption) {
			try {
				this.currentSubmenuTrigger.caption.focus();
			}
			catch (e) {
			}
		} else {
			try {
				this.currentSubmenuTrigger.domNode.focus();
			}
			catch (e) {
			}
		}
	}
}, onOpen:function (e) {
	this.openEvent = e;
	if (e["target"]) {
		this.openedForWindow = dojo.html.getElementWindow(e.target);
	} else {
		this.openedForWindow = null;
	}
	var x = e.pageX, y = e.pageY;
	var win = dojo.html.getElementWindow(e.target);
	var iframe = win._frameElement || win.frameElement;
	/*if (iframe) {
		var cood = dojo.html.abs(iframe, true);
		x += cood.x - dojo.withGlobal(win, dojo.html.getScroll).left;
		y += cood.y - dojo.withGlobal(win, dojo.html.getScroll).top;
	}*/
	this.open(x, y, null, [x, y]);
	dojo.event.browser.stopEvent(e);
}});
dojo.widget.defineWidget("dojo.widget.MenuItem2", dojo.widget.HtmlWidget, function () {
	this.eventNames = {engage:""};
}, {templateString:"<tr class=\"dojoMenuItem2\" dojoAttachEvent=\"onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick; onKey:onKey;\">" + "<td><div class=\"${this.iconClass}\" style=\"${this.iconStyle}\"></div></td>" + "<td tabIndex=\"-1\" class=\"dojoMenuItem2Label\" dojoAttachPoint=\"caption\">${this.caption}</td>" + "<td class=\"dojoMenuItem2Accel\">${this.accelKey}</td>" + "<td><div class=\"dojoMenuItem2Submenu\" style=\"display:${this.arrowDisplay};\" dojoAttachPoint=\"arrow\"></div></td>" + "</tr>", is_hovering:false, hover_timer:null, is_open:false, topPosition:0, caption:"Untitled", accelKey:"", iconSrc:"", disabledClass:"dojoMenuItem2Disabled", iconClass:"dojoMenuItem2Icon", submenuId:"", eventNaming:"default", highlightClass:"dojoMenuItem2Hover", postMixInProperties:function () {
	this.iconStyle = "";
	if (this.iconSrc) {
		if ((this.iconSrc.toLowerCase().substring(this.iconSrc.length - 4) == ".png") && (dojo.render.html.ie55 || dojo.render.html.ie60)) {
			this.iconStyle = "filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.iconSrc + "', sizingMethod='image')";
		} else {
			this.iconStyle = "background-image: url(" + this.iconSrc + ")";
		}
	}
	this.arrowDisplay = this.submenuId ? "block" : "none";
	dojo.widget.MenuItem2.superclass.postMixInProperties.apply(this, arguments);
}, fillInTemplate:function () {
	dojo.html.disableSelection(this.domNode);
	if (this.disabled) {
		this.setDisabled(true);
	}
	if (this.eventNaming == "default") {
		for (var eventName in this.eventNames) {
			this.eventNames[eventName] = this.widgetId + "/" + eventName;
		}
	}
}, onHover:function () {
	this.onUnhover();
	if (this.is_hovering) {
		return;
	}
	if (this.is_open) {
		return;
	}
	if (this.parent._highlighted_option) {
		this.parent._highlighted_option.onUnhover();
	}
	this.parent.closeSubmenu();
	this.parent._highlighted_option = this;
	dojo.widget.PopupManager.setFocusedMenu(this.parent);
	this._highlightItem();
	if (this.is_hovering) {
		this._stopSubmenuTimer();
	}
	this.is_hovering = true;
	this._startSubmenuTimer();
}, onUnhover:function () {
	if (!this.is_open) {
		this._unhighlightItem();
	}
	this.is_hovering = false;
	this.parent._highlighted_option = null;
	if (this.parent.parentMenu) {
		dojo.widget.PopupManager.setFocusedMenu(this.parent.parentMenu);
	}
	this._stopSubmenuTimer();
}, _onClick:function (focus) {
	var displayingSubMenu = false;
	if (this.disabled) {
		return false;
	}
	if (this.submenuId) {
		if (!this.is_open) {
			this._stopSubmenuTimer();
			this._openSubmenu();
		}
		displayingSubMenu = true;
	} else {
		this.onUnhover();
		this.parent.closeAll(true);
	}
	this.onClick();
	dojo.event.topic.publish(this.eventNames.engage, this);
	if (displayingSubMenu && focus) {
		dojo.widget.getWidgetById(this.submenuId)._highlightOption(1);
	}
	return;
}, onClick:function () {
	this.parent.onItemClick(this);
}, _highlightItem:function () {
	dojo.html.addClass(this.domNode, this.highlightClass);
}, _unhighlightItem:function () {
	dojo.html.removeClass(this.domNode, this.highlightClass);
}, _startSubmenuTimer:function () {
	this._stopSubmenuTimer();
	if (this.disabled) {
		return;
	}
	var self = this;
	var closure = function () {
		return function () {
			self._openSubmenu();
		};
	}();
	this.hover_timer = dojo.lang.setTimeout(closure, this.parent.submenuDelay);
}, _stopSubmenuTimer:function () {
	if (this.hover_timer) {
		dojo.lang.clearTimeout(this.hover_timer);
		this.hover_timer = null;
	}
}, _openSubmenu:function () {
	if (this.disabled) {
		return;
	}
	this.parent.closeSubmenu();
	var submenu = dojo.widget.getWidgetById(this.submenuId);
	if (submenu) {
		this.parent._openSubmenu(submenu, this);
	}
}, _closedSubmenu:function () {
	this.onUnhover();
}, setDisabled:function (value) {
	this.disabled = value;
	if (this.disabled) {
		dojo.html.addClass(this.domNode, this.disabledClass);
	} else {
		dojo.html.removeClass(this.domNode, this.disabledClass);
	}
}, enable:function () {
	this.setDisabled(false);
}, disable:function () {
	this.setDisabled(true);
}, menuOpen:function (message) {
}});
dojo.widget.defineWidget("dojo.widget.MenuSeparator2", dojo.widget.HtmlWidget, {templateString:"<tr class=\"dojoMenuSeparator2\"><td colspan=4>" + "<div class=\"dojoMenuSeparator2Top\"></div>" + "<div class=\"dojoMenuSeparator2Bottom\"></div>" + "</td></tr>", postCreate:function () {
	dojo.html.disableSelection(this.domNode);
}});
dojo.widget.defineWidget("dojo.widget.MenuBar2", [dojo.widget.HtmlWidget, dojo.widget.MenuBase], {menuOverlap:2, templateString:"<div class=\"dojoMenuBar2\" dojoAttachPoint=\"containerNode\" tabIndex=\"0\"></div>", close:function (force) {
	if (this._highlighted_option) {
		this._highlighted_option.onUnhover();
	}
	this.closeSubmenu(force);
}, closeAll:function (force) {
	this.close(force);
}, processKey:function (evt) {
	if (evt.ctrlKey || evt.altKey) {
		return false;
	}
	var rval = false;
	switch (evt.key) {
	  case evt.KEY_DOWN_ARROW:
		rval = this._moveToChildMenu(evt);
		break;
	  case evt.KEY_UP_ARROW:
		rval = this._moveToParentMenu(evt);
		break;
	  case evt.KEY_RIGHT_ARROW:
		rval = this._moveToNext(evt);
		break;
	  case evt.KEY_LEFT_ARROW:
		rval = this._moveToPrevious(evt);
		break;
	  default:
		rval = dojo.widget.MenuBar2.superclass.processKey.apply(this, arguments);
		break;
	}
	return rval;
}, postCreate:function () {
	dojo.widget.MenuBar2.superclass.postCreate.apply(this, arguments);
	this.isShowingNow = true;
}, _openSubmenu:function (submenu, from_item) {
	submenu._openAsSubmenu(this, from_item.domNode, {"BL":"TL", "TL":"BL"});
	this.currentSubmenu = submenu;
	this.currentSubmenuTrigger = from_item;
	this.currentSubmenuTrigger.is_open = true;
}});
dojo.widget.defineWidget("dojo.widget.MenuBarItem2", dojo.widget.MenuItem2, {templateString:"<span class=\"dojoMenuItem2\" dojoAttachEvent=\"onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;\">${this.caption}</span>"});
dojo.widget.Menu2.OperaAndKonqFixer = new function () {
	var implement = true;
	var delfunc = false;
	if (!dojo.lang.isFunction(dojo.doc().oncontextmenu)) {
		dojo.doc().oncontextmenu = function () {
			implement = false;
			delfunc = true;
		};
	}
	if (dojo.doc().createEvent) {
		try {
			var e = dojo.doc().createEvent("MouseEvents");
			e.initMouseEvent("contextmenu", 1, 1, dojo.global(), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, null);
			dojo.doc().dispatchEvent(e);
		}
		catch (e) {
		}
	} else {
		implement = false;
	}
	if (delfunc) {
		delete dojo.doc().oncontextmenu;
	}
	this.fixNode = function (node) {
		if (implement) {
			if (!dojo.lang.isFunction(node.oncontextmenu)) {
				node.oncontextmenu = function (e) {
				};
			}
			if (dojo.render.html.opera) {
				node._menufixer_opera = function (e) {
					if (e.ctrlKey) {
						this.oncontextmenu(e);
					}
				};
				dojo.event.connect(node, "onclick", node, "_menufixer_opera");
			} else {
				node._menufixer_konq = function (e) {
					if (e.button == 2) {
						e.preventDefault();
						this.oncontextmenu(e);
					}
				};
				dojo.event.connect(node, "onmousedown", node, "_menufixer_konq");
			}
		}
	};
	this.cleanNode = function (node) {
		if (implement) {
			if (node._menufixer_opera) {
				dojo.event.disconnect(node, "onclick", node, "_menufixer_opera");
				delete node._menufixer_opera;
			} else {
				if (node._menufixer_konq) {
					dojo.event.disconnect(node, "onmousedown", node, "_menufixer_konq");
					delete node._menufixer_konq;
				}
			}
			if (node.oncontextmenu) {
				delete node.oncontextmenu;
			}
		}
	};
};

