/*
	Joook Toolkit is a namespaced collection of functions that make JS coding
	just a bit more tolerable. The toolkit can be used statically but I
	recommend instantiating it as most of the methods are used pretty frequently
	and shortening the class name into something more convenient (e.g. "jtk")
	makes for a lot less code.
	
	This file also includes the Lazy Component class that 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.
*/

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 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
	}
}




/*
	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);
	}
}
