/*
	JoookToolkit is a namespaced collection of functions that make JS coding
	just a bit more tolerable. The toolkit must be instantiated before usage.
	It's not a bad idea to always use the same variable name for the object.
	(e.g. "jtk")
	
	The LazyComponent class can be used to dynamically import JS and CSS 
	files. This is mainly meant to be used with bookmarklets to import 
	required resources and as a lazy loader for big web applications that 
	don't need to load everything at once.
	
	The JTKDelegate class is a delegate class for explicitly scoping  method
	calls to specific objects. This is very useful for creating timed method
	calls.
*/

function JoookToolkit(){
	
	
	// Test if a variable is defined
	this.isDefined = function(testVar){
		if(typeof(testVar) != 'undefined'){
			return true;
		}
		else{
			return false;
		}
	}
	
	
	// Add an event handler to an object
	this.addEvent = function(obj, evType, fn){
		if(obj.addEventListener){ 
			obj.addEventListener(evType, fn, false); 
			return true; 
		}
		else if(obj.attachEvent){ 
			var r = obj.attachEvent("on"+evType, fn); 
			return r; 
		}
		else{ 
			return false; 
		}
	}
	
	
	/*
		Select elements using CSS-esque selectors. Multiple selectors can be
		passed and are separated by commas. This method ALWAYS returns an ARRAY.
		(Even if no elements were found.) Check array.length to see if your
		selection returned any results and remember to use index [0] with
		selections that return an array with only one member. (e.g. when using
		the id selector)
		
		Only the following SINGLE selectors are supported:
		- #id (selector mode 0)
		- .class (selector mode 1)
		- type (selector mode 2)
		- * (selector mode 3)
		
		Unsupported selectors:
		- all combination selectors:
			- type#id (typed id)
			- type.class (typed class)
			- type <type|#id|.class> (descendant selector)
			- > (child selector)
			- + (adjacent sibling selector)
		- pseudo selectors (elements and classes)
		- attribute selectors
	*/
	this.select = function(selector, parent){
		var parentEmt = document;
		var retval = new Array();
		
		if(this.isDefined(parent)){
			parentEmt = parent;
		}
		
		if(this.isDefined(selector) && selector != ''){
			var selectors = selector.split(",");
			for(var i = 0; i < selectors.length; i++){
				var currentSelector = selectors[i].replace(/\s/, '');
				var selectionMode = -1;
				
				if(currentSelector.indexOf("#") == 0){
					selectionMode = 0;
				}
				else if(currentSelector.indexOf(".") == 0){
					selectionMode = 1;
				}
				else if(currentSelector.indexOf("*") == -1){
					selectionMode = 2;
				}
				else{
					selectionMode = 3;
				}
				
				switch(selectionMode){
					case 0:
						var emtId = currentSelector.substr(1);
						retval.push(parentEmt.getElementById(emtId));
						break;
					
					case 1:
						var children = parentEmt.childNodes;
						var matchingNodes = new Array();
						for(var j = 0; j < children.length; j++){
							var classnames = children[j].className.split(" ");
							for(var k = 0; k < classnames.length; k++){
								if(classnames[k] == currentSelector.substr(1)){
									retval.push(children[j]);
									break;
								}
							}
						}
						break;
					
					case 2:
						var children = parentEmt.getElementsByTagName(currentSelector);
						for(var j = 0; j < children.length; j++){
							retval.push(children[j]);
						}
						break;
					
					case 3:
						var children = parentEmt.childNodes;
						for(var j = 0; j < children.length; j++){
							retval.push(children[j]);
						}
						break;
				}
			}
			
		}
		return retval;
	}
	
	
	
	/*
		Get viewport size.
		Please note that this method always returns the size of the viewport on the CURRENT window. 
		If you want to get the size of the viewport on another window you have to make a separate 
		instance of this class as follows: (Not sure if this works... :) )
			var newWin = window.open();
			newWin.jtk = new JoookToolkit();
			var newViewportSize = newWin.jtk.getViewportSize();
	*/
	this.getViewportSize = function(){
		var winWidth = 0;
		var winHeight = 0;
		
		if(typeof(window.innerWidth) == 'number'){
			//Non-IE
			winWidth = window.innerWidth;
			winHeight = window.innerHeight;
		}
		else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)){
			//IE 6+ in 'standards compliant mode'
			winWidth = document.documentElement.clientWidth;
			winHeight = document.documentElement.clientHeight;
		}
		else if(document.body && (document.body.clientWidth || document.body.clientHeight)){
			//IE 4 compatible
			winWidth = document.body.clientWidth;
			winHeight = document.body.clientHeight;
		}
		
		return {width: winWidth, height: winHeight};
	}
	
	
	
	// Get element dimensions, returns an object
	this.getDimensions = function(obj){
		return {width: obj.offsetWidth, height: obj.offsetHeight};
	}
	
	
	// Get element position in window, returns an object
	this.getPosition = function(obj){
		var curleft = curtop = 0;
		if(obj.offsetParent){
			curleft = obj.offsetLeft;
			curtop = obj.offsetTop;
			while(obj = obj.offsetParent){
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			}
		}
		return {left: curleft, top: curtop};
	}
	
	
	// Set element opacity (percentage)
	this.setOpacity = function(obj, opa){
		var styleObj = obj.style;
		styleObj.opacity = (opa/100); // Opera
		styleObj.MozOpacity = (opa/100); // Mozilla+Firefox
		styleObj.KhtmlOpacity = (opa/100); // Konqueror
		styleObj.filter = "alpha(opacity=" + opa + ")"; // IE
	}
	
	
	// Replace element class with another
	this.replaceClass = function(obj, class1, class2){
		var classes = obj.className.split(" ");
		for(var i = 0; i < classes.length; i++){
			if(classes[i] == class1){
				classes[i] = class2;
			}
		}
		
		obj.className = classes.join(" ");
	}
}




/*
	Lazy loader for JS and CSS components. Can't really be bothered to write a
	documentation for this one. The code is pretty self-explanatory.
*/
function LazyComponent(){
	this.loaded = false;
	this.element = null;
	this.parentEmt = null;
	
	this.createCSSEmt = function(fileUrl){
		this.element = document.createElement("link");
		this.element.rel = "stylesheet";
		this.element.type = "text/css";
		this.element.href = fileUrl;
	}
	
	this.createJSEmt = function(fileUrl){
		this.element = document.createElement("script");
		this.element.type = "text/javascript";
		this.element.src = fileUrl;
	}
	
	this.load = function(targetEmt, fileUrl, optionsObj){
		this.parentEmt = targetEmt;
		
		var emtType = 1;
		
		if(isDefined(optionsObj)){
			if(isDefined(optionsObj.isCSS) && optionsObj.isCSS == true){
				emtType = 2;
			}
		}
		
		if(emtType == 2){
			this.createCSSEmt(fileUrl);
		}
		else{
			this.createJSEmt(fileUrl);
		}
		
		targetEmt.appendChild(this.element);
		
		this.loaded = true;
		
		if(isDefined(optionsObj)){
			if(isDefined(optionsObj.callBack)){
				optionsObj.callBack.call();
			}
		}
	}
	
	this.getElement = function(){
		return this.element;
	}
	
	this.remove = function(){
		if(this.element != null && this.loaded == true){
			this.loaded = false;
			return this.parentEmt.removeChild(this.element);
		}
		else{
			return false;
		}
	}
	
	this.replace = function(newUrl, optionsObj){
		if(this.element.nodeName == "link"){
			this.element.href = newUrl;
		}
		else{
			this.element.src = newUrl;
		}
		
		if(isDefined(optionsObj) && isDefined(optionsObj.callBack)){
			optionsObj.callBack.call();
		}
	}
}






/*
	New Joook functionality: JTKDelegate class
	Delegates are used to implicitly scope method calls to objects.
	This delegate class code is similar to that found in deathmonkeyz.com.
*/

function JTKDelegate(){}

JTKDelegate._defaultArgNum = 2;

JTKDelegate.create = function(obj, func){
	var argArr = new Array();
	for(var i = JTKDelegate._defaultArgNum; i < arguments.length; i++){
		argArr[i - JTKDelegate._defaultArgNum] = arguments[i];
	}
	
	return function(){
		func.apply(obj, argArr);
	}
}