/*

unbad object library Copyright © 2006-2010 - Sean Claflin

unbad object library is licensed under the MIT License
http://www.opensource.org/licenses/mit-license.php

*/

(ub = {
	init: function() {
		this.e.attach(window, 'load', function(e) {
			//fire our internal load event
			this.onload(e);
		}, false, this);
		this.e.attach(window, 'unload', function(e) {
			//fire our internal unload
			this.onunload(e);
			//clean up our mess
			this.destruct();
		}, false, this);
		this.$ = this.u.$;
		this.$c = this.u.$c;
	},
	destruct: function() {
		//run through the event stack backwards, detaching each event
		this.a.each(this.e.stack, function(event) {
			event.detach();
		}, this, true);
	},
	require: function(req) {
		if(typeof(req) == 'string') req = [req];
		ub.a.each(req, function(name) {
			if(!this[name])
				throw new Error(name +' has not been loaded.');
		}, this);
		return true;
	},
	a: {
		each: function(array, func, scope, reverse) {
			if(!scope) scope = array;
			if(reverse) for(var i=array.length-1;i>=0;i--) func.call(scope, array[i], i);
			else for(var i=0;i<array.length;i++) func.call(scope, array[i], i);
		},
		indexOf: function(array, el) {
			var res = -1;
			for(var i=0;i<array.length;i++)
				if (array[i] == el) { res = i; break; }
			return res;
		},
		contains: function(array, el) {
			return (ub.a.indexOf(array, el) >= 0);
		},
		append: function(array, el, nodupe) {
			if (!(nodupe && ub.a.contains(array, el))) {
				return array[array.length] = el;
			}
			return el;
		},
		clear: function(array) {
			array.length = 0;
		},
		insertAt: function(array, index, el) {
			array.splice(index, 0, el);
			return el;
		},
		removeAt: function(array, index) {
			return array.splice(index, 1);
		},
		remove: function(array, el) {
			var i = ub.a.indexOf(array, el);
			if (i >= 0){
				ub.a.removeAt(array, i);
			}
			return el;
		}
	},
	s: {
		ucFirst: function(str) {
			return str.charAt(0).toUpperCase() + str.substring(1);
		},
		camelCase: function(str) {
			var list = str.split('-');
			if (list.length == 1) return list[0];
			var camel = (str.indexOf('-') == 0) ? ub.s.ucFirst(list[0]) : list[0];
			for (var i = 1; i < list.length; i++) {
				camel += ub.s.ucFirst(list[i]);
			}
			return camel;
		},
		trim: function(str) {
			return str.replace(/(^\s+|\s+$)/g,'');
		},
		truncate: function(str, len, endCap) {
			len = (parseInt(len)) ? parseInt(len) : 30;
			if(str.length > len)
				return str.substring(0, len - endCap.length) + endCap;
			else return str;
		},
		disableTags: function(str) {
			return str.replace('>', '&gt;', str.replace('<', '&lt;'));
		},
		nl2br: function(str) {
			return str.replace(/\r\n|\n|\r/g, '<br />');
		},
		br2nl: function(str) {
			return str.replace(/<br \/>|<br\/>|<br>/g, '\n');
		},
		prepXML: function(str) {
			return str.replace(/&/g, '&amp;').
				replace(/</g, '&lt;').
				replace(/>/g, '&gt;').
				replace(/'/, '&apos;').
				replace(/"/, '&quot;');
		},
		printf: function() { 
			var len = arguments.length; 
			var str = arguments[0];   
			for(var i=1;i<len;i++) { 
				var re = new RegExp("\\{" + (i-1) + "\\}", "g"); 
				str = str.replace(re, arguments[i]); 
			} 
			return str; 
		} 
	},
	c: {
		set: function (name,value,days) {
			if (days) {
				var date = new Date();
				date.setTime(date.getTime()+(days*24*60*60*1000));
				var expires = "; expires="+date.toGMTString();
			}
			else var expires = "";
			document.cookie = name+"="+value+expires+"; path=/";
		},
		get: function (name) {
			var nameEQ = name + "=";
			var ca = document.cookie.split(';');
			for(var i=0;i < ca.length;i++) {
				var c = ca[i];
				while (c.charAt(0)==' ') c = c.substring(1,c.length);
				if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
			}
			return '';
		},
		erase: function (name) {
			this.set(name,'',-1);
		}
	},
	u: {
		$c: function (array){
			var _array = [];
			for(var i=0;i<array.length;i++)
				ub.a.append(_array, array[i]);
			return _array;
		},
		$: function () {
			function get$(el){
				if (typeof el == 'string') el = document.getElementById(el);
				return el;
			}
			if (arguments.length == 1)
				return get$(arguments[0]);
			var elements = [];
			ub.a.each($c(arguments), function(el){
				ub.a.append(elements, get$(el));
			});
			return elements;
		},
		extend: function(dest, src) {
			if(dest && src)
				for(p in src) dest[p] = src[p];
			return dest;
		},
		clone: function(o){
			if(o == null || typeof(o) != 'object')
				return o;
			var temp = new o.constructor();
			for(var key in o)
				temp[key] = ub.u.clone(o[key]);
			return temp;
		},
		repeat: function(func, count, scope) {
			if(!scope) scope = window;
			for(var i=0;i<count;i++) {
				if(func.call(scope, i) === false)
					break;
			}
		},
		setTimeout: function(func, delay, scope) {
			if(!scope) scope = window;
			setTimeout(function() {
				func.call(scope);
			}, delay);
		},
		randNum: function(floor, ceil) {
			return Math.floor((ceil-floor+1)*Math.random()) + floor;
		},
		randStr: function(len) {
			len = (parseInt(len)) ? parseInt(len) : 32;
			var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';
			var str = '';
			for(var i=0; i<len; i++) {
				var rand = Math.floor(Math.random() * chars.length);
				str += chars.substring(rand,rand+1);
			}
			return str;
		},
		viewportDims: function() {
			return [self.innerWidth || (document.documentElement.clientWidth || document.body.clientWidth),
				self.innerHeight || (document.documentElement.clientHeight || document.body.clientHeight)];
		},
		buildDOM: function(structure, pNode) {
			//try to make the DOM Node
			if(ub.u.isString(structure[0])) {
				try { var node = document.createElement(structure[0]); }
				catch(e) { throw new Error('Unable to create DOM Node of type: '+ structure[0]); }
			}
			//assume a node was passed and use it
			else var node = structure[0];
			//run through remaining elements
			for(var i=1;i<structure.length;i++) {
				//is this an Array Object?
				if(structure[i] instanceof Array) {
					ub.u.buildDOM(structure[i], node);
				}
				//is this an Object?
				else if(structure[i] instanceof Object)
					for(var p in structure[i]) {
						//is our object property a string?
						if(ub.u.isString(structure[i][p])) {
							if(p == 'class') {
								var classes = structure[i][p].split(' ');
								ub.a.each(classes, function(className) {
									ub.n.addClassName(node, ub.s.trim(className));
								}, this);
							}
							else if(p == 'style') {
								var styles = structure[i][p].split(';');
								ub.a.each(styles, function(style) {
									if(!ub.s.trim(style)) return;
									style = style.split(':');
									if(style[0] == 'float') { //intercept the "float" attribute
										ub.a.append(styles, 'styleFloat: '+ ub.s.trim(style[1]));
										ub.a.append(styles, 'cssFloat: '+ ub.s.trim(style[1]));
									}
									else
										node.style[ub.s.trim(ub.s.camelCase(style[0]))] = ub.s.trim(style[1]);
								}, this);
							}
							else node.setAttribute(p, structure[i][p]);
						}
						else
							node[p] = structure[i][p];
					}
				//is it a string?
				else if((typeof(structure[i])).toLowerCase() == 'string')
					node.appendChild(document.createTextNode(structure[i]));
			}
			if(pNode) pNode.appendChild(node);
			else pNode = node;
			return pNode;
		},
		//Experimental function - still buggy DO NOT USE
		parseXHTML: function(xhtml, pNode) {
			var parse = function() {
				var xmlDoc = null;
				//parse the passed xhtml string
				if(typeof DOMParser != 'undefined')
					xmlDoc = (new DOMParser()).parseFromString(xhtml, "application/xml");
				else if(typeof ActiveXObject != 'undefined') {
					xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
					xmlDoc.async = "false";
					xmlDoc.loadXML(xhtml);
				}
				return xmlDoc;
			}
			var convert = function(xml, pNode, recursed) {
				if(recursed) {
					//nodeType 1 = Element
					if(xml.nodeType == 1) {
						cNode = document.createElement(xml.nodeName);
						for(var i=0;i<xml.attributes.length;i++) {
							var name = xml.attributes[i].name;
							var val = xml.attributes[i].value;
							if(name == 'class') cNode.className = val;
							else cNode.setAttribute(name, val);
						}
						pNode.appendChild(cNode);
					}
					//nodeType 3 = Text
					if(xml.nodeType == 3) {
						pNode.appendChild(document.createTextNode(xml.nodeValue));
					}
					if(typeof cNode == 'undefined') return;
					pNode = cNode;
				}
				for(var i=0;i<xml.childNodes.length;i++) {
					convert(xml.childNodes[i], pNode, true);
				}
			}
			//make a default container if none was given
			if(!pNode) pNode = document.createElement('div');
			convert(parse('<wrapper>'+ xhtml +'</wrapper>').documentElement, pNode);
			return pNode;
		},
		formatNumber: function(value, decimals, separator, decimal) {
			value = parseFloat(value);
			decimals = parseInt(decimals);
			separator = (separator) ? separator : ',';
			decimal = (decimal) ? decimal : '.';
			var negative = (value < 0);
			var addCommas = function(str) {
				str += '';
				var tmp = '';
				while(str.length > 3) {
					tmp = separator + str.substr(str.length - 3) + tmp;
					str = str.substr(0, str.length - 3);
				}
				if(str.length) tmp = str + tmp;
				return tmp;
			}
			var padDecimal = function(str) {
				str = (str + '').replace(/^[0-9]*\./, '');
				while(str.length < decimals)
					str += '0';
				return str;
			}
			
			var intVal = parseInt(value);
			var floatVal = value - intVal;
			if(!decimals) return ((negative) ? '-' : '') + addCommas(Math.abs(intVal));
			floatVal = Math.round(floatVal * Math.pow(10, decimals)) / Math.pow(10, decimals);
			return ((negative) ? '-' : '') + addCommas(Math.abs(intVal)) + decimal + padDecimal(Math.abs(floatVal));
		},
		isArray: function(val) {
			return (val instanceof Array);
		},
		isObject: function(val) {
			return (typeof(val) == 'object');
		},
		isString: function(val) {
			return (typeof(val) == 'string');
		},
		isInt: function(val) {
			return (parseInt(val) == val);
		},
		isFloat: function(val) {
			return (parseFloat(val) == val);
		}
	},
	n: {
		getDims: function(node) {
			return [node.offsetWidth, node.offsetHeight];
		},
		setDims: function(node, dims) {
			node.style['width'] = dims[0] +'px';
			node.style['height'] = dims[1] +'px';
		},
		scrollOffset: function(node) {
			var top = 0, left = 0;
			//do not calculate body level scrollbars
			while (node && node != document.body) {
				top += node.scrollTop  || 0;
				left += node.scrollLeft || 0;
				node = node.parentNode;
			}  
			return [left, top];
		},
		cumulativeOffset: function(node) {
			var top = 0, left= 0;
			while (node) {
				top += node.offsetTop  || 0;
				left += node.offsetLeft || 0;
				node = node.offsetParent;
			} 
			return [left, top];
		},
		realOffset: function(node) {
			var delta = ub.n.scrollOffset(node);
			var pos = ub.n.cumulativeOffset(node);
			return [pos[0] - delta[0], pos[1] - delta[1]];
		},
		getCoords: function(node) {
			return [parseInt(node.style.left) || 0, parseInt(node.style.top) || 0];
		},
		setCoords: function(node, coords) {
			node.style.left = (coords[0]) ? coords[0] + 'px' : 0;
			node.style.top = (coords[1]) ? coords[1] + 'px' : 0;
		},
		setOpacity: function(node, opacity) {
			if (opacity == 0 && node.style.visibility != "hidden") node.style.visibility = "hidden";
			else if (node.style.visibility != "visible") node.style.visibility = "visible";
			if (window.ActiveXObject) node.style.filter = "alpha(opacity=" + opacity*100 + ")";
			node.style.opacity = opacity;
		},
		within: function(node, coords) {
			var realOffset = ub.n.realOffset(node);
			var dims = ub.n.getDims(node);
			return (
				coords[0] >= realOffset[0] &&
				coords[0] < (realOffset[0] + dims[0]) &&
				coords[1] >= realOffset[1] &&
				coords[1] < (realOffset[1] + dims[1])
			);
		},
		hasClassName: function(node, className) {
			var hasClass = false;
			ub.a.each(node.className.split(' '), function(cn) {
				if(ub.u.isArray(className)) {
					for(var i=0;i<className.length;i++) {
						if(cn == className[i]) {
							hasClass = true;
							break;
						}
					}
				}
				else if (cn == className) hasClass = true;
			});
			return hasClass;
		},
		removeClassName: function(node, className) {
			var newClassName = '';
			ub.a.each(node.className.split(' '), function(cn, i){
				if (cn != className){
					if (i > 0) newClassName += ' ';
					newClassName += cn;
				}
			});
			node.className = newClassName;
		},
		addClassName: function(node, className) {
			if(!ub.n.hasClassName(node, className))
				node.className += ((node.className) ? ' ' : '') + className;
		},
		getElementsByClassName: function(node, className) {
			if(node.getElementsByClassName) {
				return ub.u.$c(node.getElementsByClassName(className));
			}
			var children = ub.u.$c(node.getElementsByTagName('*'));
			var elements = [];
			ub.a.each(children, function(child){
				if (ub.n.hasClassName(child, className))
					ub.a.append(elements, child);
			});
			return elements;
		},
		ancestor: function(node) {
			if(node.parentNode)
				return ub.n.ancestor(this.parentNode);
			else return node;
		},
		contains: function(pNode, cNode) {
			if(!pNode || !cNode) return false;
			
			//anonymous div bug fix for FireFox.
			try { cNode = cNode.parentNode; }
			catch (e) { cNode = null; } 
			
			if(cNode == pNode)
				return true;
			else return ub.n.contains(pNode, cNode);
		},
		removeChildren: function(node, recurse) {
			for(var i=node.childNodes.length - 1;i>=0;i--) {
				if(recurse) ub.n.removeChildren(node.childNodes[i], recurse);
				node.removeChild(node.childNodes[i]);
			}
		},
		replaceChildren: function(pNode, cNode) {
			ub.n.removeChildren(pNode);
			pNode.appendChild(cNode);
			return pNode;
		},
		addNode: function(pNode, type, str, replaceChildren) {
			var node = document.createElement(type);
			if(str != null)
				node.appendChild(document.createTextNode(str));
			if(replaceChildren) ub.n.replaceChildren(pNode, node);
			else pNode.appendChild(node);
			return node;
		}
	},
	e: {
		stack: [],
		event: function() {
			var e =	function(type, target, keyCode, coords, altKey, ctrlKey, shiftKey) {
				this.type = type;
				this.target = target,
				this.keyCode = keyCode;
				this.coords = (coords) ? coords : [0,0];
				this.altKey = (altKey) ? true : false;
				this.ctrlKey = (ctrlKey) ? true : false;
				this.shiftKey = (shiftKey) ? true : false;
			};
			e.prototype.chType = function(type) {
				this.type = type;
				return this;
			}
			return e;
		}(),
		block: function() {
			//Not to be instanced by user but can be modified once instanced.
			var b = function(node, type, callback, noBubbling, scope, forceLegacy) {
				this.node = node;
				this.type = type;
				this.callback = callback;
				this.noBubbling = noBubbling || false;
				this.scope = scope || node;
				this.forceLegacy = forceLegacy || false;
			}
			b.prototype.detach = function() {
				ub.e.detach(this);
				this.detach = function() {};
			}
			return b;
		}(),
		//Attach an event handler to a DOM node or Object. When attached to a
		//DOM Node, native addEventListener or attachEvent are used if
		//available. Otherwise the failsafe of Object.onevent method is used.
		attach: function(node, type, callback, noBubbling, scope, forceLegacy) {
			var block = ub.a.append(
				ub.e.stack,
				new ub.e.block(node, type, callback, noBubbling, scope, forceLegacy)
			);
			//adding a non-prototyped function here to use the block as a
			//closure. We do this because it's passed as a function reference
			//via addEventListener and attachEvent and called later outside of
			//it's normal scope
			block.listener = function(e) {
				return ub.e.handler(e, block);
			};
			//W3C
			if(!forceLegacy && node.addEventListener)
				node.addEventListener(type, block.listener, false);
			//IE
			else if(!forceLegacy && node.attachEvent)
				node.attachEvent('on'+type, block.listener);
			//Failsafe and non-dom objects
			else {
				//check for node.__lsnrs object
				if(!node.__lsnrs)
					node.__lsnrs = {};
				//check for node.__lsnrs[type] array
				if(!node.__lsnrs[type])
					node.__lsnrs[type] = [];
				//append our block to the proper listener
				ub.a.append(node.__lsnrs[type], block);
				node['on'+type] = function(e) {
					var args = [];
					//All but IE provide an automagic event argument
					if(!e) {
						//stuff in a fake event if we absolutely have to
						ub.a.append(args, (
							(window.event) ? window.event : new ub.e.event('unknown'))
						);
					}
					for(var i=0;i<arguments.length;i++)
						ub.a.append(args, arguments[i]);
					//set a default return result of true
					var result = true;
					ub.a.each(node.__lsnrs[type], function(lsnr, i) {
						//add our block as the second argument
						if(i==0) args.splice(1, 0, lsnr);
						else args.splice(1, 1, lsnr);
						//call the handler, test for false return
						if(ub.e.handler.apply(this, args) === false)
							//set result to false. Note that this is flagged
							//even if only one handler for the event returned a
							//false value.
							result = false;
					}, this, true);
					return result;
				};
			}
			return block;
		},
		detach: function(block) {
			//remove the block from the stack
			ub.a.remove(this.stack, block);
			//W3C
			if(!block.forceLegacy && block.node.removeEventListener)
				block.node.removeEventListener(block.type, block.listener, false);
			//IE
			else if(!block.forceLegacy && block.node.detachEvent)
				block.node.detachEvent('on'+block.type, block.listener);
			//Failsafe and non-dom objects
			else if(block.node['on'+block.type]) {
				//got listeners?
				var lsnrs = (block.node.__lsnrs && block.node.__lsnrs[block.type]) ? block.node.__lsnrs[block.type] : false;
				if(lsnrs) {
					//remove this block
					ub.a.remove(block.node.__lsnrs[block.type], block);
					//no more listeners for this event type?
					if(!block.node.__lsnrs[block.type].length) {
						//delete our event type array
						delete block.node.__lsnrs[block.type];
						//delete our event handler
						block.node['on'+block.type] = function() {};
						//check to see if there are other attached event types
						var lsnrCount = 0;
						//check for other event types
						for(var p in block.node.__lsnrs) {
							if(typeof(block.node.__lsnrs[p]) == 'object') {
								lsnrCount++;
							}
						}
						//no more event type lsnrs?
						if(lsnrCount == 0) {
							//delete our __lsnrs extendo
							block.node.__lsnrs = null;
						}
					}
				}
			}
			//wipe out the block
			for(var p in block)
				delete block[p];
		},
		handler: function(e, block) {
			//if an event is queued to fire, but the block assigned to it
			//is removed, we should stop now.  No block = nothing to do.
			if(!block) return;
			//All but IE provide an automagic e argument
			if(!e && window.event) e = window.event;
			//failsafe, make up a new event 
			else if(!e) e = {
				type: 'Unknown',
				target: block.node,
				keyCode: null,
				coords: [0,0]
			};
			//create a new unbad event object
			var event = new ub.e.event(
				e.type,
				e.target || e.srcElement,
				e.keyCode || e.which,
				function() {
					//cross-browser coords discovery
					//TYVM PPK http://www.quirksmode.org/js/events_properties.html
					if(e.coords) return e.coords;
					else if (e.pageX || e.pageY) return [e.pageX, e.pageY];
					else if (e.clientX || e.clientY) {
						return [
							e.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft),
							e.clientY + (document.body.scrollTop || document.documentElement.scrollTop)
						];
					}
					else return [0,0];
				}.call(this),
				e.altKey,
				e.ctrlKey,
				e.shiftKey
			);
			//add relatedTarget property when the event is mouseover or mouseout
			if(event.type == 'mouseover')
				event.relatedTarget = e.relatedTarget || e.fromElement;
			else if(event.type == 'mouseout')
				event.relatedTarget = e.relatedTarget || e.toElement || {};
			
			//check to see if we should stop bubbling up
			if(block.noBubbling) {
				//W3C
				if (e.stopPropagation) e.stopPropagation();
				//IE
				e.cancelBubble = true;
			}
			//capture the native arguments collection, stuff into an array
			var args = [];
			for(var i=0;i<arguments.length;i++)
				ub.a.append(args, arguments[i]);
			//remove the e and block arguments, inject our event object
			args.splice(0, 2, event);
			//do our callback in the desired scope and pass our event object
			return (block.callback.apply(block.scope, args) !== false);
		}
	},
	q: {
		pairs: function() {
			try {
			var r = {};
			var _pairs = location.search.substring(1).split('&');
			for(var i=0;i<_pairs.length;i++) {
				var pair = _pairs[i].split('=');
				r[pair[0]] = pair[1];
			};
			return r;
			} catch(e) { return {}; }
		}(),
		get: function(name) {
			return this.pairs[name];
		}
	},
	f: {
		toData: function(f) {
			var data = {};
			ub.a.each(ub.$c(f.elements), function(element) {
				var val = '';
				if(element.type == 'checkbox' || element.type == 'radio')
					val = (element.checked) ? element.value : '';
				else val = element.value;
				if(element.name) {
					if(data[element.name]) {
						if(val) {
							if(typeof(data[element.name]) != 'object')
								data[element.name] = [data[element.name]];
							ub.a.append(data[element.name], val);
						}
					} else data[element.name] = val;
				}
			});
			delete f;
			return data;
		}
	},
	onload: function(e) {},
	onunload: function(e) {}
}).init();
