/**
 *	HTML Builder version: 1.1
 *	-----------------------------------------------------------------
 *	(c) 2008 / Trey Runcie 
 *
 *	Author Trey Runcie	http://treyruncie.com/
 *
 *	The HTML Builder is for creating markup structure with javascript in a concise nested structure. 
 *	It uses the YUI Global Object and the YUI Dom object available from the Yahoo! User Interface Library.
 *	http://developer.yahoo.com/yui
 *
 *	-----------------------------------------------------------------
 */

YAHOO.namespace('treyruncie.html');
YAHOO.treyruncie.html = function(){
	Dom = YAHOO.util.Dom;
	Event = YAHOO.util.Event;
	return {
		/**
		* public function contentTag 
		* @desc use contentTag to create an element and add attributes and nodes
		* @param {String} tag the name of the HTML tag to be created
		* @param {Object} attr key value object added to the tag as attributes
		* @param {String} {Array} every parameter after tag and attr will be added as a child node of the parent element. 
		* 	Any parameter after the tag and attr params can be a String, HTML element, or Array of Strings or Array of HTML elements
		* @returns an HTML element and it's child elements
		*/
		contentTag:function(tag,attr){
			var elem = document.createElement(tag),node,argsArr = new Array();
			for (property in attr) {
				elem = this.addAttribute(elem,property,attr[property]);
			}
			/* argsPrep returns a walkable array from the arguments of this function */
			argsArr = this.argPrep(this.contentTag.arguments);
			for (var i=2; i < argsArr.length; i++) {
				/** 
				 * if we come to an argument that is an array stitch it into the argument array in place.
				 * if the argsArr element is an array we break off the argument array into three segments, 
				 * the-start-to-current-segment >> the-current-array-element >> and the-current+1-to-the-end-segment
				 * then we combine the three segments into one new SUPER array and move the counter back one and continue the loop looping
				 */
				if(this.is_array(argsArr[i])){
					/* part1 = slice off the start of the argsArr to the current postion */
					var part1 = argsArr.slice(0,i);
					/* part3 = slice off the the next element to the end of the array. */
					var part3 = argsArr.slice(i+1);
					/* part2 = is the current element which is an array */
					var part2 = argsArr[i];
					/* combine the first array segment with the isolated current array element with the last segment of the array */
					argsArr = part1.concat(part2,part3);
					/* move the counter back to before the loop came to the array element and then continue */
					i--;
				}else{
					if( this.is_string(argsArr[i]) ){
						node = document.createTextNode(argsArr[i]);
					}else{
						node = argsArr[i];
					}
					try{ elem.appendChild(node); }catch(err){ }
				}
			}
			return elem;
		},
		/**
		* public function tag
		* @desc use tag to create a self closing tag like input or br
		* @param {String} tag the name of the HTML tag to be created
		* @param {Object} key value object added to the tag as attributes
		* @returns an HTML element
		*/
		tag:function(tag,attr){
			var elem = document.createElement(tag);
			for (property in attr) {
				elem = this.addAttribute(elem,property,attr[property]);
			}
			return elem;
		},
		/**
		* private function addEvent
		* @desc adds and event to an element and returns the element for rendering in the DOM
		* @param {String} elem HTML Element that will have event attached
		* @param {String} evt name of the event to be attached.
		* @param {Function} fnCallback the action to take when the event is fired.
		* @param {Obj} Object to pass into the function 
		* @param {Bool} scope whether or not the function should run in the scope of the passed in object.
		* @returns an HTML Element with Event attached.
		*/
		addEvent:function(elem,evt,fnCallback,obj,scope){
			YAHOO.util.Event.on(elem,evt,fnCallback,obj,scope);
			return elem;
		},
		addAttribute:function(elem,key,value){
			if( elem.getAttributeNode(key) ){
				var attr_key, attr_name;
				for (var i = 0; i < elem.attributes.length; i++) {
					attr_key = key.toUpperCase();
					attr_name = elem.attributes[i].name.toUpperCase();
					if (attr_name == attr_key) {
						elem.attributes[i].value = value;
					}
				}
			}else{
				elem.setAttribute(key,value);
			}	
			return elem;	
		},
		/**
		* public function write
		* @desc takes elem and renders the element in a way defined by method. By default it renders the Element by appending it to the body.
		* @param elem HTML Element to be rendered in the DOM
		* @param objRef this is a String describing an element to target in the DOM when rendering
		* @param {String} method describes how to render the elem relative to the objRef possible values are:
			appendChild
			insertBefore
			insertAfter
			replaceChild
		*/
		write:function(elem,objRef,method){
			if(objRef){
				var target = Dom.get(objRef);
				try{
					this.writeMethod(elem,target,method);
					return elem;
				}catch(err){
					alert(err);
				}
			}else{
				document.body.appendChild(elem);
			}
			return elem;
		},
		/**
		* private function writeMethod
		* @desc renders the HTML Element based on a target and a method
		* @param elem HTML Element
		* @param target HTML Element
		* @param {String} method the name of a method to use when attaching elem to target
		* @returns elem
		*/
		writeMethod:function(elem,target,method){
			switch(method){
				case "appendChild":
					target.appendChild(elem);
					break;
				case "insertBefore":
					Dom.insertBefore(elem,target);
					break;
				case "insertAfter":
					Dom.insertAfter(elem,target);
					break;
				case "replaceChild":
					target.parentNode.replaceChild(elem,target);
					break;
				default:
					target.appendChild(elem);
					break;
			}
			return elem;
		},
		/**
		* private function is_string
		* @param v value to test 
		* @returns {Bool} true if v is a string false if it isn't
		*/
		is_string:function(v){
			return typeof(v)=='string' && isNaN(v);
		},
		/**
		* private function is_array
		* @param v value to test 
		* @returns {Bool} true if v is an array false if it isn't
		*/
		is_array:function(v){
			return typeof(v)=='object'&&(v instanceof Array);
		},
		/**
		* private function argPrep
		* @desc converts the arguments property of a function to a real Array
		* @param args {Array} this is the arguments property of a function 
		* @returns an array of the the args parameter passed in.
		*/
		argPrep:function(args){
			var ret = new Array();
			for (var i=0; i < args.length; i++) {
				ret[i] = args[i];
			};
			return ret;
		}		
	}
}();
