/*

############################
Current Differences between WSDOM/Element and jslib/Element
Please maintain these when updating this file:

- WSDOM's Create references WSDOM.Events and looks for that as the property name
- WSDOM Create's children parameter is looped through, while jslib/Element only calls addChild 

############################

Element.3.js
A library to handle DOM Elements

Documentation at http://dev2.wallst.com/playground/docs/
	and 		 http://wiki.wsod.local/wsodwiki/index.php/Element.3.js_:Dev

Most functions take an element handle or an element's id as a string as the first parameter
"set" functions can also take an array of elements or ids.

Element.get - returns a handle on an element
Element.create - extension of document.createElement. 2nd parameter is properties, 3rd parameter is children.
Element.addChild - appends an element to an element
Element.remove - remove an element from the DOM
Element.setDisplay - sets display: property of element(s)
Element.setVisibility - sets visibility: property of element(s)
Element.remove - removes element(s) from DOM
Element.setHTML - set innerHTML value of element(s).  Optional 3rd parameter to append content
Element.parseSelector - returns list of elements that match CSS selector string
Element.forEach - accepts a CSS selector or array of elements and iterates on each element
Element.setStyle - an inline way to set multiple style properties of an element - example: Element.setStyle(el,"border:2px solid red;width:400px;background:#888;");
Element.removeChildNodes - remove all nodes within an element
Element.cloneNode - extension of el.cloneNode that strips all DOM events
Element.insertBefore - keeps the Element syntax in your code
Element.insertAfter - because the DOM only offers insertBefore
Element.nextElement - returns next sibling of indicated tagname
Element.previousElement - returns previous sibling of indicated tagname
Element.getParent - returns the first parent element of a certain tag name
Element.getXY - returns an {x:,y:} object of an element's position relative to the html body
Element.setXY - set an element(s) position
Element.getSize - returns an {width:,height:} object of an element's total size
Element.getSizeXY  - returns an {x:,y:} object of an element's total size
Element.setSize - set the size of an element(s)
Element.setWidth - set the width of an element(s)
Element.setHeight - set the height of an element(s)
Element.getBorderSize - returns the current border size

Element.addClass  - adds a class attribute
Element.removeClass - removes a class attribute
Element.toggleClass - reverses a class attribute
Element.switchClass - takes a 3rd parameter boolean to turn on/off a class attribute
Element.hasClass - returns if an element is part of a class
Element.getStyle

//////////////////////////
Element Extras (separate file Element.3extras.js)
Element.isInsideOf
Element.isInsideOfNS
Element.isInsideOfEW
Element.debug
Element.setOpacity
Element.setDisabled
Element.setAttribute - same functionality as el.setAttribute but accepts an array of elements
Element.setProperty - sets a JavaScript property to a single or array of elements
Element.getParentBySelector - like getParent but with CSS Selector support
Element.isOnScreen - returns if an element is rendered on the screen.  Traverses up the DOM and checks .display and .visibility
*/

/**
 * @class Element contains functions for working with the DOM in a cross-browser way.  
 * See also: <a href="http://wiki.wsod.local/wsodwiki/index.php/Element.3.js_:Dev">http://wiki.wsod.local/wsodwiki/index.php/Element.3.js_:Dev</a>
 * @static 
 */
var Element_class = function() {

}

// -------------------------------------------------------
// 	DOM Helpers
// -------------------------------------------------------

/**
 * Returns a handle on an element with the specified ID
 * @param {String|| Number} el 
 * @return {element} Native DOM element if found
 */
Element_class.prototype.get = function(el) {
	if (typeof el == "string" || typeof el == "number") el = document.getElementById(el);
	return el;
};

/**
 * Element.create can be used in a variety of ways to create a new HTML element and (optionally) insert it into the DOM
 * @example var el = WSDOM.Element.create("div",{className:"primary",style:"color:red;font-weight:bold;"},"Default Text",document.body) 
 * 
 * @param {String} tag Element tag name such as 'div'
 * @param {Object} [attributes] Element attributes object literal, such as {id:'container3',className:'container'}  Note that 'class' is a reserved word, 
 * 		   be sure to use 'className' to set CSS classes
 * @param {Object || String} [children] Element or String text node to add as children
 * @param {Object} [parent] Element to append newly created node to (optional)
 * @param {Object} [ElementObjectInstance]
 * @return {element} Newly created native DOM element
 */
Element_class.prototype.create = function (tag, attributes, children, parent, ElementObjectInstance) {

	var element = document.createElement(tag);

	// mapping for IE specific attributes
	var attributeMap = {
		"for":["htmlFor"],
		"colspan":["colSpan"],
		"usemap":["useMap"]
	}

	for (var i in attributes)
	{
		if (i == "className" || i == "class")
		{
			element.className = attributes[i];
		}
		else if (document.all && attributeMap[i])
		{
			for (var j = 0; j <attributeMap[i].length; j++) {
				element.setAttribute(attributeMap[i][j], attributes[i]);
			}
			element.setAttribute(i, attributes[i]); // retain original
		}
		else if (i == "style")
		{
			this.setStyle(element,attributes[i]);
		}
		else if (i == "Events")
		{
			if (typeof Events != "undefined") {
				var elEvents = attributes[i];
				if (!this.isArray(elEvents)) {
					elEvents = [elEvents]
				}

				for (var j = 0; j < elEvents.length; j++) {
					elEvents[j].element = element;
					Events.add(elEvents[j]);
				}
			}
			else {
				alert(":: DEV ERROR :: \n Location: Element.3.js -- Element_class.prototype.create \n Type: Dependency \n Message: Expecting Events Lib for use of Events in Element.create")
			}
		}
		else
		{
			element.setAttribute(i, attributes[i]);
		};
	};

	// <map> also needs an ID to work correctly
	if (tag.match(/^map$/i) && attributes && attributes.name && !attributes.id) {
		element.setAttribute("id", attributes.name);
	}

	if (arguments.length > 2 && children != undefined && children !== "") {
		this.addChild(element, children);
	};

	if (parent) {
		this.addChild(parent,element);
	}

	return (ElementObjectInstance) ? new ElementObject(element) : element;
};

/**
 * Append an element to another element
 * @param {element} el Parent
 * @param {element || string} child Element or text to append
 */
Element_class.prototype.addChild = function (el, child) {
	el = this.get(el);

	if (!this.isArray(child)) {
		child = [child]
	}
	for (var i=0; i<child.length; i++) {
		if (typeof child[i] == "object") {
			el.appendChild(child[i]);
		}
		else if (typeof child[i] == "string" || typeof child[i] == "number") {
			// element.appendChild(document.createTextNode(children));
			el.innerHTML += child[i];
		};
	}

};



/**
 * Remove an element(s) from the DOM
 * @param {element || string} el Element handle or ID to remove
 */
Element_class.prototype.remove = function(el) {
	el = this.get(el);

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		el[i].parentNode.removeChild(el[i])
	}
};

/**
 * Sets the display property of an element to the specified value
 * @param {element || array || string} el Element, array of elements, or ID of desired element
 * @param {Object} d 
 */
Element_class.prototype.setDisplay = function(el,d) {

	el = this.get(el);

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		el[i].style.display = d;
	}
};

/**
 * Sets the visibility property of a desired element
 * @param {element || array || string} el Element, array of elements, or ID of element
 * @param {Object} d
 */
Element_class.prototype.setVisibility = function(el,d) {
	el = this.get(el);

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		el[i].style.visibility = d;
	}
};


/**
 * Remove all nodes under a specified element
 * @param {element || string} el Element handle or ID of element
 */
Element_class.prototype.removeChildNodes = function(el) {

	el = this.get(el);

	while (el.childNodes.length) {
		el.removeChild(el.firstChild);
	}
	return el;

};

/**
 * An extension of the native cloneNode() which also strips DOM events
 * @param {element} el Element(s) to clone
 * @param {Object} cloneChildren
 */
Element_class.prototype.cloneNode = function(el, cloneChildren) {
	//prevents IE from cloning events
	var cloneChildNodes = (cloneChildren) ? cloneChildren : false;

	if (document.all) {

		var node = el.outerHTML;

		if (cloneChildNodes && el.innerHTML) {
			node.innerHTML = el.innerHTML;
		}

		var container = document.createElement("DIV");
		container.innerHTML = node;

		node = container.firstChild;

	} else {
		var node = el.cloneNode(cloneChildNodes);
	}

	return node;

};

/**
 * Find the first parent with a given tag name
 * @param {Object} el
 * @param {Object} tag
 * @param {Object} includeSelf
 */
Element_class.prototype.getParent = function(el, tag, includeSelf) {

	var el = this.get(el);

	if (!tag) { tag = el.tagName; }

	if (!includeSelf && el.parentNode) { // grab immediate parent
		el = el.parentNode;
	}

	if (el.tagName && el.tagName.match(/^BODY$/i) && !tag.match(/^BODY$/i)) {
		return null;
	}

	if (el.nodeType == 1 && el.tagName.toLowerCase() == tag.toLowerCase()) {
		return el;
	}
	else {
		return this.getParent(el.parentNode, tag, true);
	}
}



/**
 * Set the innerHTML of a node, the optional third parameter allows you to append rather than overwrite
 * @param {element} el Parent node to set 
 * @param {string} v innerHTML string
 * @param {boolean} [appendV] If true, append, else overwrite 
 */
Element_class.prototype.setHTML = function(el,v,appendV) {
	el = this.get(el);

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
			el[i].innerHTML = (appendV) ? (el[i].innerHTML+v) : v;
	}
};

/**
 * parseSelector finds and elements based on the selector string.  You can match based on tag name, attributes, and styles, as well as stack selectors.
 * See <a href="http://wiki.wsod.local/wsodwiki/index.php/Element.parseSelector:Dev">http://wiki.wsod.local/wsodwiki/index.php/Element.parseSelector:Dev</a> 
 * for much more info. 
 * @example Element.parseSelector('tr.highlight', myTable);             // returns all TRs belonging to class highlight within myTable
Element.parseSelector('input[type="checkbox"]', myForm);    // returns all check boxes
Element.parseSelector('div[position=..'absolute'] ', myDiv);   // returns all divs with absolute positioning
 * @param {string} selector The selector string to search for
 * @param {element} [node] The parent element to search under.  It is important to try and specify as small a subset of the DOM as possible by 
 * 		   setting this parent element, as running parseSelector on the entire DOM can get quite slow
 * @param {string || number} [specific] This will allow you to return a specific element from the matched list, 
 * 		   valid options are "first", "last", or any number for the nth match
 */
Element_class.prototype.parseSelector = function() {

	var context = this;
	var SEPERATOR = /\s*,\s*/;
	
	function parseSelector(selector, node, num) {

		node = node || document.documentElement;
		node = this.get(node);

		var argSelectors = selector.split(SEPERATOR);
		var result = [];
		
		for(var i = 0; i < argSelectors.length; i++) {

			// if the selector starts with #, we can use getElementById to improve performance
			// only do this if we're searching from the root (selectors like '.myDiv #myEl' don't make much sense,
			// but should still work as expected).
			if(node == document.documentElement) {
				var matches = argSelectors[i].match(/^\s*#([^\s.:@[~+>]*)\s+(.*)$/); // check for simple #id
				if(matches) {
					node = document.getElementById(matches[1]) || [];
					argSelectors[i] = matches[2];
				}
			}

			var stream = toStream(argSelectors[i]), prevToken;

			if (this.isArray(node)) {
				var nodes = node;
				// remove * and " " put in by toStream if it starts with a non alphanumeric
				if (!argSelectors[i].match(/^[A-Za-z1-9]/)) {
					while (stream[0] && stream[0].match(/[* ]/)) { stream.shift(); }
				}
			}
			else {
				var nodes = [node]
			}

			for(var j = 0;j < stream.length;) {

				var token = stream[j++];

				var args = '';
				if (token == "[") {
					args = stream[j];
					while(stream[j++] != ']' && j < stream.length) { args += stream[j] };
					args = args.slice(0, -1);
					var filter = "";
				}
				else {
					var filter = stream[j++];
				}

				if(stream[j] == '(') {
					while(stream[j++] != ')' && j < stream.length) args += stream[j];
					args = args.slice(0, -1);
				}

				prevToken = token;
				nodes = select(nodes, token, filter, args);
			}
			result = result.concat(nodes);
		}
		
		// simple selection of an individual node
		  if (num != undefined) {

			if (result.length) {
				var REMatch;

				if (num == "first") {
					return result[0]
				}
				else if (num == "last") {
					return result[result.length-1]
				}
				else if (REMatch = String(num).match(/nth\((.*)\)/) || num == "even") { // even is the same as nth(2)
					if (num == "even") num = 2;
					else num = REMatch[1];

					var result2 = [];
					for (var i=0,len=result.length; i<len;i++) {
						if (i%num == 0) { result2.push(result[i]); }
					}
					return result2;
				}
				else if (num == "odd") {
					var result2 = [];
					for (var i=0,len=result.length; i<len;i++) {
						if (i%2 != 0) { result2.push(result[i]); }
					}
					return result2;
				}
				else if (!isNaN(num) && result.length >= num) {
					return result[num]
				}
				else {
					return null; // num passed, but no matches or unsupported type
				}
			}
			else {
				return null;
			}

		  }

		return result;
	}

	var WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g;
	var IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
	var STANDARD_SELECT = /^[^\s>+~]/;
	var STREAM = /[\s#.:>+~[\]()@!]|[^\s#.:>+~[\]()@!]+/g;

	function toStream(selector) {
		var stream = selector
			.replace(WHITESPACE, '$1')
			.replace(IMPLIED_ALL, '$1*$2');
		if(STANDARD_SELECT.test(stream)) {
			stream = ' ' + stream;
		}
		return stream.match(STREAM) || [];
	}

	function select(nodes, token, filter, args) {
		return (selectors[token]) ? selectors[token](nodes, filter, args) : [];
	}

	var util = {
		toArray: function(enumerable) {
			var a = [];
			for(var i = 0; i < enumerable.length; i++) a.push(enumerable[i]);
			return a;
		},

		push: function(arr,val) {
			arr.push(val)
      		//for(var i = 1; i < arguments.length; i++) arr[arr.length] = arguments[i];
      		return arr.length;
    	}
	};

	var dom = {
		isTag: function(node, tag) {
			return (tag == '*') || (
				tag.toLowerCase() == node.nodeName.toLowerCase().replace(':html', '')
			);
		},

		previousSiblingElement: function(node) {
			do node = node.previousSibling; while(node && node.nodeType != 1);
			return node;
		},

		nextSiblingElement: function(node) {
			do node = node.nextSibling; while(node && node.nodeType != 1);
			return node;
		},

		hasClass: function(name, node) {
			return (node.className || '').match('(^|\\s)'+name+'(\\s|$)');
		},

		getByTag: function(tag, node) {
			/*	IE5.x does not support document.getElementsByTagName("*")
				therefore we're falling back to element.all */
			if(tag == '*') {
			  var nodes = node.getElementsByTagName(tag);
			  if(nodes.length == 0 && node.all != null) return node.all
			  return nodes;
		  }
			return node.getElementsByTagName(tag);
		}
	};

	var selectors = {
		'#': function(nodes, filter) {
			for(var i = 0; i < nodes.length; i++) {
				if(nodes[i].getAttribute('id') == filter) return [nodes[i]];
			}
			return [];
		},

		' ': function(nodes, filter) {
			var result = [];
			for(var i = 0; i < nodes.length; i++) {
				result = result.concat(util.toArray(dom.getByTag(filter, nodes[i])));
			}
			return result;
		},

		'>': function(nodes, filter) {
			var result = [];
			for(var i = 0, node; i < nodes.length; i++) {
				node = nodes[i];
				for(var j = 0, child; j < node.childNodes.length; j++) {
					child = node.childNodes[j];
					if(child.nodeType == 1 && dom.isTag(child, filter)) {
						result.push(child);
					}
				}
			}
			return result;
		},

		'.': function(nodes, filter) {
			var result = [];
			for(var i = 0, node; i < nodes.length; i++) {
				node = nodes[i];
				if(dom.hasClass([filter], node)) result.push(node);
			}
			return result;
		},

		'!': function(nodes, filter) {
			var result = [];
			for(var i = 0, node; i < nodes.length; i++) {
				node = nodes[i];
				if(!dom.hasClass([filter], node)) result.push(node);
			}
			return result;
		},

		':': function(nodes, filter, args) {
			return (pseudoClasses[filter]) ? pseudoClasses[filter](nodes, args) : [];
		},


		'+': function(nodes, filter) {
			var result = [];
			for(var i = 0, node; i < nodes.length; i++) {
				node = nodes[i];
				var sibling = parseSelector.dom.nextSiblingElement(node);
				if(sibling && parseSelector.dom.isTag(sibling, filter)) {
					result.push(sibling);
				}
			}
			return result;
		},
		'~': function(nodes, filter) {
			var result = [];
			for(var i = 0, node; i < nodes.length; i++) {
				node = nodes[i];
				var sibling = parseSelector.dom.previousSiblingElement(node);
				if(parseSelector.dom.isTag(sibling, filter)) result.push(sibling);
			}


			return result;
		},
		'[': function(nodes, filter, args) { // CSS Attribute
			args = args.replace(/'/g,'"'); // allow single quotes

			var attributeProps = [];
			if (!/[<>=]/.test(args)) { // has operator test
				attributeProps = ["",args,"",""];
			}
			else {
				var params = args.match(/([^!^$*\/.<>=]*)(!\*=|\*=|\$=|!\$=|\^=|\/=|!\/=|<=|>=|<|>|!=|=)(\.*)(i?)(["'`])([^\5]*)(\5)/);
									//   (attName)       (operator)                                 (type)   (caseinsenstive)[quote](value)[quote]
				if (params) { attributeProps = params; }
			}

			//alert(attributeProps )
			attributeProps = {name:attributeProps[1],operator:attributeProps[2],isStyle:attributeProps[3]=="..",isProperty:attributeProps[3]==".",casei:attributeProps[4]?true:false,value:attributeProps[6],isValue:(attributeProps[5]=="`")};

			// Leave this line for debuging purposes
			//alert(args + "\n att: " + attributeProps.name + "\n operator: " + attributeProps.operator + "\n val: "  + attributeProps.value + "\n isStyle: " + attributeProps.isStyle + "\n isProperty: " + attributeProps.isProperty + "\n isValue: " + attributeProps.isValue + "\n casei: "  + attributeProps.casei)

			if (attributeProps.casei) { attributeProps.value = attributeProps.value.toLowerCase(); }

			var result = [];
			for(var i = 0, node, att, val, el; i < nodes.length; i++) {
				node = el = nodes[i];

				if (attributeProps.isStyle) {
					att = context.getStyle(node,attributeProps.name);
					if (!att) continue;
				}
				else if (attributeProps.isProperty) {
					if (node[attributeProps.name] != undefined) {
						var att = node[attributeProps.name];
					}
					else {
						continue;
					}
				}
				else {
					att = node.getAttribute(attributeProps.name);
					if (!att) continue;
				}

				if (/[*^$\/]/.test(attributeProps.operator)) { // so .match complies
					att = String(att)
				}

				if (attributeProps.casei) { att = att.toLowerCase(); }

				val = attributeProps.value;

				//alert("Tag: " + el.tagName + ", ID: " + el.id + " , Att: " + att + ", Operator: " + attributeProps.operator + ", Val: " + val);
				if (attributeProps.isValue) { val = eval(val.replace(/`/g,"'")); }

				if (!attributeProps.operator) {
					result.push(nodes[i])
				}
				else {


					switch(attributeProps.operator){
						case '=': if (att == val) result.push(node); break;
						case '!=': if (att != val) result.push(node); break;
						case '*=': if (att.match(val)) result.push(node); break;
						case '!*=': if (!att.match(val)) result.push(node); break;
						case '^=': if (att.match('^'+val)) result.push(node); break;
						case '!^=': if (!att.match('^'+val)) result.push(node); break;
						case '$=': if (att.match(val+'$')) result.push(node); break;
						case '!$=': if (!att.match(val+'$')) result.push(node); break;
						case '/=': if (att.match(val)) result.push(node); break;
						case '!/=': if (!att.match(val)) result.push(node); break;
						case '>=': if (att >= val) result.push(node); break;
						case '>': if (att > val) result.push(node); break;
						case '<=': if (att <= val) result.push(node); break;
						case '<': if (att < val) result.push(node); break;
					}
				}
			}
			return result;

		}

	};

	parseSelector.selectors			= selectors;
	var pseudoClasses = { };
	parseSelector.pseudoClasses 			= pseudoClasses;
	parseSelector.util 				  = util;
	parseSelector.dom 				  = dom;
	return parseSelector.apply(this,arguments);
};

/*---------------------------------
	is -
	Is an element a selector?
----------------------------------*/

Element_class.prototype.is = function( domNode, selector, parent, propagate ) {

	parent = parent || domNode.parentNode;
	
	var queriedEls = this.parseSelector(selector, parent);
		
	var  i
		,l = queriedEls.length;
	
	while(domNode && domNode !== parent) {
		
		for(i = 0; i < l; i++) {
			if(domNode === queriedEls[i]) {
	
				return true;
			}
		}
		if(propagate) {
			domNode = domNode.parentNode;
			continue;
		}
		break;
	}
	return false;
};


Element_class.prototype.forEach = function(el, callback, context) {

	var returnEls = [];

	if (typeof el == "string") {
		el = this.parseSelector(el);
	}
	else if (!this.isArray(el)) {
		el = [el]
	}

	var success;

	for (var i=0,elLen = el.length; i<elLen; i++) {
		success = context ? callback.call(context,el[i],i,el) : callback(el[i],i,el)
		if (success === false) {
			break;
		}
		returnEls.push(el[i]);
	}

	return returnEls;
}

/**
 * Insert an element before another
 * @param {Object} el
 * @param {Object} sibling
 */
Element_class.prototype.insertBefore = function (el, sibling) {

	el = this.get(el);	if (!el) return;

	sibling = this.get(sibling);

	if (!el || !sibling || !sibling.parentNode)
	{
		return null;
	};

	sibling.parentNode.insertBefore(el, sibling);
};

/**
 * Insert an element after another
 * @param {element} el
 * @param {Object} sibling
 */
Element_class.prototype.insertAfter = function (el, sibling) {

	el = this.get(el);	if (!el) return;

	sibling = this.get(sibling);

	if (!el || !sibling || !sibling.parentNode)
	{
		return null;
	};

	return sibling.nextSibling ? sibling.parentNode.insertBefore(el, sibling.nextSibling) : sibling.parentNode.appendChild(el);
};

//2006-02-16 AR - fixed this so that an null tagName would return the next element
/**
 * Returns the next sibling of a given tagname
 * @param {element} el
 * @param {string} tagName
 * @return {element} The next sibling
 */
Element_class.prototype.nextElement = function(el, tagName) {
	tagName = tagName ? String(tagName).toLowerCase() : null;

	var sibling = this.get(el);
	while(sibling = sibling.nextSibling) {
		if (sibling.nodeType == 1 && ( tagName == null || sibling.tagName.toLowerCase() == tagName ) ) {
			return sibling;
		}
	}
	return null;
}

//2006-02-16 AR - added previousElement
/**
 * Returns the previous sibling of a given tagname
 * @param {element} el
 * @param {string} tagName
 * @return {element} The previous sibling
 */
Element_class.prototype.previousElement = function(el, tagName) {
	tagName = tagName ? String(tagName).toLowerCase() : null;
	var sibling = this.get(el);
	while(sibling = sibling.previousSibling) {
		if (sibling.nodeType == 1 && ( tagName == null || sibling.tagName.toLowerCase() == tagName ) ) {
			return sibling;
		}
	}
	return null;
}

/**
 * Gets the {x: , y: } pair of coordinates for the element relative to the body
 * @param {Object} el
 * @return {Object} {x: , y: } object coordinate position
 */
Element_class.prototype.getXY = function(el) {

	el = this.get(el);	if (!el) return;

	var x = 0, y = 0;

	while (el.offsetParent) {
		x += el.offsetLeft;
		y += el.offsetTop;
		el = el.offsetParent;
	}

	return { x: x, y: y };
};

/**
 * Sets the position of an element relative to the body
 * @param {element} el
 * @param {number} x
 * @param {number} y
 */
Element_class.prototype.setXY = function(el, x, y) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		if (x !== null) el[i].style.left = x + "px";
		if (y !== null) el[i].style.top = y + "px";
	}
};

/**
 * Gets the {width:, height:} object of the element's total size
 * @param {element} el
 * @return {object} {width: height:} size object
 */
Element_class.prototype.getSize = function(el) {

	el = this.get(el);	if (!el) return;

	var height = el.offsetHeight;
	var width = el.offsetWidth;

	return { height: height, width: width };
};

// more useful if in same units as pos
/**
 * Gets the {x: width, y: height} representation of the element's total size
 * @param {element} el
 * @return {object} {x: , y:} size representation
 */
Element_class.prototype.getSizeXY = function(el) {
	var size = this.getSize(el);
	return {x: size.width, y: size.height};
};

/**
 * Sets the size of an element(s)
 * @param {element} el
 * @param {number} width pixel number
 * @param {number} height pixel number
 */
Element_class.prototype.setSize = function(el, width, height) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		this.setWidth(el[i], width);
		this.setHeight(el[i], height);
	}
};

Element_class.prototype.setWidth = function(el, width) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		el[i].style.width = width + "px";
	}
};

Element_class.prototype.setHeight = function(el, height) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		el[i].style.height = height + "px";
	}
};

Element_class.prototype.getBorderSize = function(el) {

	el = this.get(el);

	var height = el.offsetHeight - el.clientHeight;
	var width = el.offsetWidth - el.clientWidth;

	return { height: height, width: width };
}


// -------------------------------------------------------
//  className modifications
// -------------------------------------------------------

/**
 * Changes the className of an element, the optional third parameter will allow you to remove the className
 * @param {element} el
 * @param {string} classname
 * @param {boolean} [b]
 */
Element_class.prototype.switchClass = function(el, classname, b) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}

	if (b) {
		this.addClass(el, classname);
	}
	else {
		this.removeClass(el, classname);
	}

	if (el.length) {
		return el[0].className;
	}

};

/**
 * Removes a CSS class and replaces it with a new class
 * @param {element} el 
 * @param {string} sClassNameOld Old class to remove
 * @param {string} sClassNameNew New class to add
 * @param {boolean} bConditional
 */
Element_class.prototype.replaceClass = function(el, sClassNameOld, sClassNameNew, bConditional) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}

	if(bConditional) {

		for(var i=0;i<el.length;i++) {

			if(this.hasClass(el[i],sClassNameOld)) {

				this.removeClass(el[i],sClassNameOld);
				this.addClass(el[i],sClassNameNew);

			}

		}

	} else{

		this.removeClass(el, sClassNameOld);
		this.addClass(el, sClassNameNew);

	}

	if (el.length) {
		return el[0].className;
	}

};

/**
 * Append a CSS class to an element
 * @param {element} el
 * @param {string} classname
 */
Element_class.prototype.addClass = function(el, classname) {

	el = this.get(el);	if (!el) return;

	if (!this.isArray(el)) {
		el = [el]
	}
	for (var i=0; i<el.length; i++) {
		if (!this.hasClass(el[i], classname)) {
    			el[i].className += (el[i].className?" ":"") + classname;
		}
	}

	if (el.length) {
		return el[0].className;
	}
};

/**
 * Remove a CSS class from an element
 * @param {element} el
 * @param {string} classname
 */
Element_class.prototype.removeClass = function(el, classname) {

	el = this.get(el);

	if (!this.isArray(el)) {
		el = [el]
	}

	var re = this._getClassnameRegEx(classname);

	for (var i=0; i<el.length; i++) {
			el[i].className = el[i].className.replace(re, "$1$3");
	}

	if (el.length) {
		return el[0].className;
	}

};

/**
 * Removes a CSS class if it already exists on an element, else it adds it in
 * @param {element} el 
 * @param {string} classname
 */
Element_class.prototype.toggleClass = function(el, classname) {
	el = this.get(el);
	//alert(el.id)

	if (!this.isArray(el)) {
		el = [el]
	}

	for (var i=0; i<el.length; i++) {
		if (this.hasClass(el[i], classname)) {
			this.removeClass(el[i], classname);
		} else {
			this.addClass(el[i], classname);
		}
	}

	if (el.length) {
		return el[0].className;
	}
};

/**
 * Checks to see if a CSS class exists on an element
 * @param {Object} el
 * @param {Object} classname
 * @return {boolean}
 */
Element_class.prototype.hasClass = function(el, classname) {

	el = this.get(el);

	return (el.className && el.className.match(this._getClassnameRegEx(classname)) != null);
};

Element_class.prototype._getClassnameRegEx = function(classname) {
	return new RegExp("(\\s|^)(" + classname + ")(\\s|$)", "g")
};

// -------------------------------------------------------
//  style gets and sets
// -------------------------------------------------------

/**
 * Gets the computed style
 * @param {element} el
 * @param {string} styleProp
 */
Element_class.prototype.getStyle = function (el, styleProp) {

	el = this.get(el);	if (!el) return;

	if (document.defaultView && document.defaultView.getComputedStyle) { // Mozilla and Safari

		var computedStyle = document.defaultView.getComputedStyle(el, null);
		return (computedStyle) ? computedStyle.getPropertyValue(styleProp) : null;

	} else if (el.currentStyle) { // IE

		styleProp = styleProp.replace(/\-(.)/g, function () {

			return arguments[1].toUpperCase();

		});

		return el.currentStyle[styleProp];

	}

	return null;

}

/**
 * Set the element's inline style with a css string
 * @param {element} el
 * @param {string} styles String style representation like 'width:200px; color: #CCC;'
 */
Element_class.prototype.setStyle = function (el, styles) {

	el = this.get(el);	if (!el) return;

	var pairs = [];
	styles = styles.split(";");
	for (var i=0; i<styles.length; i++) {
		//var nv = styles[i].split(":");
		var nv = styles[i].replace(":","{:}").split("{:}");
		if (nv.length > 1) {
			nv[0] = nv[0].replace(/\-(.)/g, function() {
				return arguments[1].toUpperCase();
			}).replace(/\s/g, "");
			pairs.push({n:nv[0],v:nv[1].replace(/^\s*|\s*$/g, "")});
		}
	}

	if (!this.isArray(el)) {
		el = [el]
	}

	var attributeMap = {
		"float":["cssFloat","styleFloat"]
	}

	for (var i=0; i<el.length; i++) {
		for (var j=0; j<pairs.length; j++) {
			if (attributeMap[pairs[j].n]) {
				for (var k = 0; k <attributeMap[pairs[j].n].length; k++) {
					pairs.push({n:attributeMap[pairs[j].n][k],v:pairs[j].v});
				}
			}
			el[i].style[pairs[j].n] = pairs[j].v;
		}
	}

}



// -------------------------------------------------------
// 	Element Helpers
// -------------------------------------------------------


Element_class.prototype.isArray = function(o) {
	return (o instanceof Array);
};


if (typeof WSDOM != "undefined") {
	WSDOM.using("WSDOM.Element.3", "WSDOM.Events.2");
	WSDOM.defineClass("Element", null, Element_class);
	WSDOM.loadSingleton("WSDOM.Element.3");
}
else {
	Element = new Element_class();
}
