/*
	Class: nurse
	Utility functions to help Internet Explorer render CSS as other modern browsers can.
	
	Project home:
	http://code.google.com/p/style-nurse/
*/
nurse = (function() {
	var htcRegistry = {};
	
	function htcGetArgs(str) {
		return str.split(",");
	}
	function htcGetProps(str) {
		return str.split(" ");
	}
	
	function normalize_obj_arg (obj) {
		if(!obj) {
			return [];
		} else if(typeof obj == 'object' && 
			  obj.constructor == Array) { 
			return obj; 
		} else if(obj.nodeType) {
			return [obj]
		} else {
			return [];
		}
	}
	
	function $getFirstChild (node,name) {
		var result = [];
		if(!node) { return result; }
		if(name) { 
			result.push(node.children.tags(name)[0]);
		} else {
			for(var n, i = 0; (n = node.children[i]); i++) {
				if(n.nodeType == 1) {
					result.push(n)
					break;
				}
			}
		}
		return result;
	}
	
	function $getDirectChild (node,name) {
		var r = (name) ? node.children.tags(name) : node.children;
		var result = [];
		for(var i=0; i < r.length; i++) {
			result.push(r[i]);	
		}
		return result;
	}
	
	function $addStyleToNode(el,args) {
		var args = htcGetArgs(args); //styles, runtime		
		var styleObject = (args[1]) ? "runtimeStyle" : "style";
		var pairs = htcGetProps(args[0]);
		for(var i=0; i < pairs.length; i++) {
			el[styleObject][pairs[i]] = pairs[++i];
		}
	}
	
	function $next (node,name) {
		var result = [],
		next = node.nextSibling;
		if(name && next.nodeName == name) {
			result.push(next);
		} else if(next) {
			result.push(next);
		}
		return result;
	}
	
	function _addTextToNode(node, text, alignment, style) {
		var text = document.createTextNode(text.toString()),
		insert = text;
		
		if(insert.nodeValue==44){
			insert.nodeValue = String.fromCharCode(insert.nodeValue);
		}
		
		if(style) {
			insert = document.createElement('span');
			$addStyleToNode(insert, style+',runtime');
			insert.appendChild(text);
		} else {
			insert = text;
		}
		
		switch(alignment) {
			case "before": node.insertBefore(insert, node.firstChild); break;
			case "after": node.appendChild(insert); break;
		}
		
		return node;
	}
	
	var Construct0r = function() { }
	Construct0r.prototype={
		version: "0.2",
/*-----------------------------------------------------------------------------------
			
		Method: bind2
		
		Parse and Execute a method string, then register the id
		
		Parameters:
		id - element ID
		method_str - string to parse and execute using special syntax
			
-----------------------------------------------------------------------------------*/
		bind2: function(id, method_str) {
			if(!htcRegistry[id]) {
				this.exe(id, method_str.substring(1,method_str.length-1));
				htcRegistry[id] = 1;
			}
		},
/*-----------------------------------------------------------------------------------
			
		Method: exe
		
		Break apart the method str
		
		Usage:
>		$getFirstChild >$addClassToNode[p1-first-child]
		
		"$" - refer to a method
		">" - pass the result into the next method
		"[" - open arguments for method
		"," - separate arguments
		"]" - close arguments
		
		Parameters:
		id - element ID
		method_str - string to parse and execute using special syntax
			
-----------------------------------------------------------------------------------*/
		exe: function(id, method_str) {
			var _i = document.getElementById(id),
				excs = method_str.split("$");
				excs.shift();
			
			for(var i=0; i < excs.length; i++) {
				var exc = excs[i];
				
				var z, c, a="";
				
				//arguments
				z = exc.indexOf("[");
				if(z > -1) {
					c = exc.indexOf("]");
					a = exc.substring(z+1,c);
				} else {
					z = exc.indexOf(" ");
				}
				
				var excName = exc.substring(0, z);
				var f = this[excName];
				if(!f) continue;
				
				var _o = f(_i, a);
				
				//pass thru
				if(exc.charAt(exc.length-1) == ">") {
					_i = _o; //set input to output
				}
			}
		},
/*-----------------------------------------------------------------------------------
			
		Method: addClassToNode
		
		Usage:
>		$addClassToNode[myClassName]

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		addClassToNode: function(el,name){
			var e = normalize_obj_arg(el);
			var i = e.length-1;
			
			if(i<0) { 
				return e;
			}
			
			do {
				var current = e[i].className;
				if(current.length > 0) {
					if(current == name || 
					   new RegExp("(^|\\s)"+name+"(\\s|$)").test(current)) {
						continue;
					}
					e[i].className += ' ' + name;
				} else {
					e[i].className = name;
				}
			} while (i--);
			
			return e;
		},
/*-----------------------------------------------------------------------------------
			
		Method: addStyleToNode
		
		Usage:
>		$addStyleToNode[fontWeight bold backgroundColor red]
>		$addStyleToNode[fontWeight bold backgroundColor red,runtime]

		Returned Value:
		Array of elements
					
-----------------------------------------------------------------------------------*/
		addStyleToNode: function(el,args){
			var e = normalize_obj_arg(el);
			var i = e.length-1;
			
			if(i<0) { 
				return e;
			}
			
			do {
				$addStyleToNode(e[i], args);
			} while (i--);
			
			return e;
		},
/*-----------------------------------------------------------------------------------
			
		Method: getAdjacent
		
		Usage:
>		$getAdjacent

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		getAdjacent: function(nodelist) {
			var result = [];
			for(var i=0,n;n=n=nodelist[i];i++) {
				if(!n.previousSibling) { continue; }
				result.push(n);
			}
			return result;
		},
/*-----------------------------------------------------------------------------------
			
		Method: getDirectChild
		
		Usage:
>		$getDirectChild
>		$getDirectChild[UL]

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		getDirectChild: function(node,name) {
			var e = normalize_obj_arg(node);
			var _i, _o = []; //input and output
					
			for(var i=0, l = e.length; i < l; i++) {
				_i = $getDirectChild(e[i], name); //recursive method defined above
				if(!!_i.length) { _o = _o.concat(_i); }
			}
			
			return _o;
		},
/*-----------------------------------------------------------------------------------
			
		Method: getFirstChild
		
		Usage:
>		$getFirstChild
>		$getFirstChild[UL]

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		getFirstChild: function(node, name) {
			var e = normalize_obj_arg(node);
			var _i, _o = []; //input and output
					
			for(var i=0, l = e.length; i < l; i++) {
				_i = $getFirstChild(e[i], name); //recursive method defined above
				if(!!_i.length) { _o = _o.concat(_i); }
			}
			
			return _o;
		},
/*-----------------------------------------------------------------------------------
			
		Method: getLastChild
		
		Usage:
>		$getLastChild
>		$getLastChild[UL]

		Returned Value:
		Element or undefined
			
-----------------------------------------------------------------------------------*/
		getLastChild: function(node, name){
			if(name) { 
				var c = node.children.tags(name);
				return c[c.length];
			}
			for(var n, i = node.children.length-1; (n = node.children[i]); i--) {
				if(n.nodeType == 1) {
					return n;
				}
			}
			return;
		},
/*-----------------------------------------------------------------------------------
			
		Method: next
		
		Usage:
>		$next
>		$next[UL]

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		next: function(node, name) {
			var e = normalize_obj_arg(node);
			var _i, _o = []; //input and output
					
			for(var i=0, l = e.length; i < l; i++) {
				_i = $next(e[i], name); //recursive method defined above
				if(!!_i.length) { _o = _o.concat(_i); }
			}
			
			return _o;
		},
/*-----------------------------------------------------------------------------------
			
		Method: addTextToNode
		
		Usage:
>		$addTextToNode[ /]
>		$addTextToNode[ /,after]
>		$addTextToNode[ /,after,fontWeight normal]

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		addTextToNode: function(n,a) {
			var e = normalize_obj_arg(n),
			args = htcGetArgs(a); //content,position,style
			
			
			for(var i=0; i<e.length;i++) {
				_addTextToNode(e[i], args[0], args[1], args[2]);	
			}
			
			return e;
			
		},
/*-----------------------------------------------------------------------------------
			
		Method: addContentToNode
		Append a text node to an element using the custom nursecontent property assigned to the element.
		
		Usage:
>		$addContentToNode[after]
>		$addContentToNode[ /,after]
>		$addContentToNode[ /,after,fontWeight normal]

		Returned Value:
		Array of elements
			
-----------------------------------------------------------------------------------*/
		addContentToNode: function(n,a) {
			var e = normalize_obj_arg(n),
			args = htcGetArgs(a);
			
			for(var i=0, el; i<e.length;i++) {
				el = e[i];
				var content = el.currentStyle["nursecontent"];
				if(!content) continue;
				content = content.substring(1, content.length-1);
				_addTextToNode(el, content, args[0], args[1]);	
			}
			
			return e;
		}
	};

	return new Construct0r();
})();
