/*
	Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
	Available via Academic Free License >= 2.1 OR the modified BSD license.
	see: http://dojotoolkit.org/license for details
*/


// a host environment specifically built for Mozilla extensions, but derived
// from the browser host environment
if(typeof window != 'undefined'){
	dojo.isBrowser = true;
	dojo._name = "browser";


	// FIXME: PORTME
	//	http://developer.mozilla.org/en/mozIJSSubScriptLoader


	// attempt to figure out the path to dojo if it isn't set in the config
	(function(){
		var d = dojo;
		// this is a scope protection closure. We set browser versions and grab
		// the URL we were loaded from here.

		// FIXME: need to probably use a different reference to "document" to get the hosting XUL environment

		d.baseUrl = d.config.baseUrl;

		// fill in the rendering support information in dojo.render.*
		var n = navigator;
		var dua = n.userAgent;
		var dav = n.appVersion;
		var tv = parseFloat(dav);

		d.isMozilla = d.isMoz = tv;
		if(d.isMoz){
			d.isFF = parseFloat(dua.split("Firefox/")[1]) || undefined;
		}

		// FIXME
		d.isQuirks = document.compatMode == "BackCompat";

		// FIXME
		// TODO: is the HTML LANG attribute relevant?
		d.locale = dojo.config.locale || n.language.toLowerCase();

		d._xhrObj = function(){
			return new XMLHttpRequest();
		}

		// monkey-patch _loadUri to handle file://, chrome://, and resource:// url's
		var oldLoadUri = d._loadUri;
		d._loadUri = function(uri, cb){
			var handleLocal = ["file:", "chrome:", "resource:"].some(function(prefix){
				return String(uri).indexOf(prefix) == 0;
			});
			if(handleLocal){
				// see:
				//		http://developer.mozilla.org/en/mozIJSSubScriptLoader
				var l = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
					.getService(Components.interfaces.mozIJSSubScriptLoader);
				var value = l.loadSubScript(uri, d.global)
				if(cb){ cb(value); }
				return true;
			}else{
				// otherwise, call the pre-existing version
				return oldLoadUri.apply(d, arguments);
			}
		}

		// FIXME: PORTME
		d._isDocumentOk = function(http){
			var stat = http.status || 0;
			return (stat >= 200 && stat < 300) || 	// Boolean
				stat == 304 || 						// allow any 2XX response code
				stat == 1223 || 						// get it out of the cache
				(!stat && (location.protocol=="file:" || location.protocol=="chrome:") );
		}

		// FIXME: PORTME
		// var owloc = window.location+"";
		// var base = document.getElementsByTagName("base");
		// var hasBase = (base && base.length > 0);
		var hasBase = false;

		d._getText = function(/*URI*/ uri, /*Boolean*/ 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.
			// fail_ok:
			//		Default false. If fail_ok and loading fails, return null
			//		instead of throwing.
			// returns: The response text. null is returned when there is a
			//		failure and failure is okay (an exception otherwise)

			// alert("_getText: " + uri);

			// NOTE: must be declared before scope switches ie. this._xhrObj()
			var http = d._xhrObj();

			if(!hasBase && dojo._Url){
				uri = (new dojo._Url(uri)).toString();
			}
			if(d.config.cacheBust){
				//Make sure we have a string before string methods are used on uri
				uri += "";
				uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,"");
			}
			var handleLocal = ["file:", "chrome:", "resource:"].some(function(prefix){
				return String(uri).indexOf(prefix) == 0;
			});
			if(handleLocal){
				// see:
				//		http://forums.mozillazine.org/viewtopic.php?p=921150#921150
				var ioService = Components.classes["@mozilla.org/network/io-service;1"]
					.getService(Components.interfaces.nsIIOService);
				var scriptableStream=Components
					.classes["@mozilla.org/scriptableinputstream;1"]
					.getService(Components.interfaces.nsIScriptableInputStream);

				var channel = ioService.newChannel(uri, null, null);
				var input = channel.open();
				scriptableStream.init(input);
				var str = scriptableStream.read(input.available());
				scriptableStream.close();
				input.close();
				return str;
			}else{
				http.open('GET', uri, false);
				try{
					http.send(null);
					// alert(http);
					if(!d._isDocumentOk(http)){
						var err = Error("Unable to load "+uri+" status:"+ http.status);
						err.status = http.status;
						err.responseText = http.responseText;
						throw err;
					}
				}catch(e){
					if(fail_ok){ return null; } // null
					// rethrow the exception
					throw e;
				}
				return http.responseText; // String
			}
		}
		
		d._windowUnloaders = [];
		
		// FIXME: PORTME
		d.windowUnloaded = function(){
			// summary:
			//		signal fired by impending window destruction. You may use
			//		dojo.addOnWIndowUnload() or dojo.connect() to this method to perform
			//		page/application cleanup methods. See dojo.addOnWindowUnload for more info.
			var mll = d._windowUnloaders;
			while(mll.length){
				(mll.pop())();
			}
		}

		// FIXME: PORTME
		d.addOnWindowUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
			// summary:
			//		registers a function to be triggered when window.onunload fires.
			//		Be careful trying to modify the DOM or access JavaScript properties
			//		during this phase of page unloading: they may not always be available.
			//		Consider dojo.addOnUnload() if you need to modify the DOM or do heavy
			//		JavaScript work.
			// example:
			//	|	dojo.addOnWindowUnload(functionPointer)
			//	|	dojo.addOnWindowUnload(object, "functionName")
			//	|	dojo.addOnWindowUnload(object, function(){ /* ... */});
	
			d._onto(d._windowUnloaders, obj, functionName);
		}

		// XUL specific APIs
		var contexts = [];
		var current = null;
		dojo._defaultContext = [ window, document ];

		dojo.pushContext = function(/*Object|String?*/g, /*MDocumentElement?*/d){
			//	summary:
			//		causes subsequent calls to Dojo methods to assume the
			//		passed object and, optionally, document as the default
			//		scopes to use. A 2-element array of the previous global and
			//		document are returned.
			//	description:
			//		dojo.pushContext treats contexts as a stack. The
			//		auto-detected contexts which are initially provided using
			//		dojo.setContext() require authors to keep state in order to
			//		"return" to a previous context, whereas the
			//		dojo.pushContext and dojo.popContext methods provide a more
			//		natural way to augment blocks of code to ensure that they
			//		execute in a different window or frame without issue. If
			//		called without any arguments, the default context (the
			//		context when Dojo is first loaded) is instead pushed into
			//		the stack. If only a single string is passed, a node in the
			//		intitial context's document is looked up and its
			//		contextWindow and contextDocument properties are used as
			//		the context to push. This means that iframes can be given
			//		an ID and code can be executed in the scope of the iframe's
			//		document in subsequent calls easily.
			//	g:
			//		The global context. If a string, the id of the frame to
			//		search for a context and document.
			//	d:
			//		The document element to execute subsequent code with.
			var old = [dojo.global, dojo.doc];
			contexts.push(old);
			var n;
			if(!g && !d){
				n = dojo._defaultContext;
			}else{
				n = [ g, d ];
				if(!d && dojo.isString(g)){
					var t = document.getElementById(g);
					if(t.contentDocument){
						n = [t.contentWindow, t.contentDocument];
					}
				}
			}
			current = n;
			dojo.setContext.apply(dojo, n);
			return old; // Array
		};

		dojo.popContext = function(){
			//	summary:
			//		If the context stack contains elements, ensure that
			//		subsequent code executes in the *previous* context to the
			//		current context. The current context set ([global,
			//		document]) is returned.
			var oc = current;
			if(!contexts.length){
				return oc;
			}
			dojo.setContext.apply(dojo, contexts.pop());
			return oc;
		};

		// FIXME:
		//		don't really like the current arguments and order to
		//		_inContext, so don't make it public until it's right!
		dojo._inContext = function(g, d, f){
			var a = dojo._toArray(arguments);
			f = a.pop();
			if(a.length == 1){
				d = null;
			}
			dojo.pushContext(g, d);
			var r = f();
			dojo.popContext();
			return r;
		};

	})();

	dojo._initFired = false;
	//	BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
	dojo._loadInit = function(e){
		dojo._initFired = 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(dojo._inFlightCount == 0){
			dojo._modulesLoaded();
		}
	}

	/*
	(function(){
		var _w = window;
		var _handleNodeEvent = function(evtName, fp){
			// summary:
			//		non-destructively adds the specified function to the node's
			//		evtName handler.
			// evtName: should be in the form "onclick" for "onclick" handlers.
			// Make sure you pass in the "on" part.
			var oldHandler = _w[evtName] || function(){};
			_w[evtName] = function(){
				fp.apply(_w, arguments);
				oldHandler.apply(_w, arguments);
			};
		};
		// FIXME: PORT
		// FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.
		_handleNodeEvent("onbeforeunload", function() { dojo.unloaded(); });
		_handleNodeEvent("onunload", function() { dojo.windowUnloaded(); });
	})();
	*/
	

	//	FIXME: PORTME
	// 		this event fires a lot, namely for all plugin XUL overlays and for
	// 		all iframes (in addition to window navigations). We only want
	// 		Dojo's to fire once..but we might care if pages navigate. We'll
	// 		probably need an extension-specific API
	if(!dojo.config.afterOnLoad){
		window.addEventListener("DOMContentLoaded",function(e){
			dojo._loadInit(e);
			// console.log("DOM content loaded", e);
		}, false);
	}

} //if (typeof window != 'undefined')

//Register any module paths set up in djConfig. Need to do this
//in the hostenvs since hostenv_browser can read djConfig from a
//script tag's attribute.
(function(){
	var mp = dojo.config["modulePaths"];
	if(mp){
		for(var param in mp){
			dojo.registerModulePath(param, mp[param]);
		}
	}
})();

//Load debug code if necessary.
if(dojo.config.isDebug){
	// logging stub for extension logging
	console.log = function(m){
		var s = Components.classes["@mozilla.org/consoleservice;1"].getService(
			Components.interfaces.nsIConsoleService
		);
		s.logStringMessage(m);
	}
	console.debug = function(){
		console.log(dojo._toArray(arguments).join(" "));
	}
	// FIXME: what about the rest of the console.* methods? And is there any way to reach into firebug and log into it directly?
}