/*
@Author: Paul Visco of http://elmwoodstrip.org?u=paul
@Version: 3.12 04/24/04
@Release: 07/19/07
@Package: surebert
*/

var sb = {
	
	/**
	@Name: sb.$
	@Description: One of the most important parts of the surebert library. Can reference DOM elements in many way using CSS selectors.  The simplest use of it is to reference DOM elements by their id property.
	@Param: String USe CSS selectors to return the elements desired
	@Example:
	e.g.'#myForm' An element id.  When passed an element ID it returns a reference to the element with that id'
	
	e.g.'body' An tag name.  When passed a tag name it returns an array of all the tags that match that tag name.  If ther tag is found in sb.singleTags e.g. body, head, title then only one element is returned instead of an array
	
	e.g. '#myDiv' returns node with the id 'myDiv'
	
	e.g. '.myClass' returns all nodes with the class 'myClass', see also [class="myClass"] below
	
	e.g. 'p' returns all the p nodes
	
	e.g. '*' returns all nodes
	
	e.g. '#myDiv p' returns all the p tags that are decendents of #myDiv
	
	e.g. '#myDiv > p' returns all the p tags that are direct decendents of #myDiv
	
	e.g. 'p + b' returns all the b tags that are direct adjacent siblings of p tags
	
	e.g 'p:first-child' returns all the p tags that are the first child of their parent, be careful its not the first child of each p tag
	
	e.g. 'p:last-child' returns all the p tags that are the last child of their parent
	
	e.g. 'p:empty' returns all the p tags that are empty
	
	e.g  'p:nth-child(4)' returns all the p tags that are the 4th child of their parent
		
	e.g  'p:nth-child(odd)' returns all the p nodes that are theare odd numbered child of their parent
	
	e.g  'p:nth-child(even)' returns all the p nodes that are theare even numbered child of their parent
	
	e.g  '*:not(p)' returns all nodes that are not p tags
	
	e.g  '#myDiv *:not(p)' returns all nodes that are not p tags within #myDiv
	
	e.g  'input[name"choosen"]' returns all the input nodes with the name 'choosen'
	
	e.g. 'a[href="http://www.surebert.com"] return all the a tags that have the href http://www.surebert.com
	
	e.g. 'a[href$="google.com"] return all the a tags that end in google.com
	
	e.g. 'a[href^="http"] return all the a tags that start with http
	
	e.g. 'a[href*="surebert"] return all the a tags that have the substring "surebert" in them
	
	e.g. a[hreflang|="en"]	returns all a tags whose "hreflang" attribute has a hyphen-separated list of values beginning (from the left) with "en"
	
	e.g. 'p[class~="bob"] returns an array of all p tags whose "class" attribute value is a list of space-separated values, one of which is exactly equal to "bob"
	
	e.g. 'p, b, #wrapper' Commas allow you to make multiple selections at once.This example returns all b nodes, all p nodes and node with the id 'wrapper'
	
	*/
	
	$ : function() {
		//return items that are already objects
		if(typeof arguments[0] == 'object' && arguments.length ==1){return arguments[0];}
		
		//handle legacy dollarsign stuff
		if(arguments.length ==2 && typeof arguments[1] == 'string'){
			return sb.$.legacy.apply(this, arguments);
		}
		
		var selectors = sb.strings.trim.call(arguments[0]);
		var selector = selectors.split(",");
		var inheriters, nodes = [];
		var within = arguments[1];
		
		for(var s=0;s<selector.length;s++){
			selector[s] = selector[s].replace(/\s?([>\+\~])\s?/, "$1");
			inheriters = selector[s].split(" ");
			nodes = nodes.concat(sb.$.parseInheritors(inheriters, within));
		}
		
		//if there is only one match and there was only one requested or the request and match were for a single style HTML tag (head, body) then return only that match
		
		if(nodes.length ==1  && (!selectors.match(/,/) && selectors.match(/^\#\w+$/)) || sb.arrays.inArray.call(sb.dom.singleTags, selectors)){
			
			return nodes[0];
		} else if(selectors.match(/^\#\w+$/) && nodes.length ==0){
			return null;
		} else {
			return nodes;
		}
		
	},

	
	/**
	@Name: sb.addGlobals
	@Description: Used Internally.  Used as create a few useful globals if sbNoGlobals is not true.
	*/
	addGlobals : function(){
		var prop;
		
		for(prop in sb.functions){
			Function.prototype[prop] = sb.functions[prop];
		}
		
		for(prop in sb.strings){
			if(typeof String.prototype[prop] =='undefined'){
				String.prototype[prop] = sb.strings[prop];
			}
		}
		
		for(prop in sb.arrays){
			if(typeof Array.prototype[prop] =='undefined'){
				Array.prototype[prop] = sb.arrays[prop];
			}	
		}
		
		if(typeof Element !='undefined' && typeof sbSuperElements !='undefined'){
			for(prop in sb.element.prototype){
				Element.prototype[prop] = sb.element.prototype[prop];
			}
		}
		
		//add global links to internal sb functions
		sb.globals = {
			$ : sb.$,
			s$ : sb.s$,
			$_GET : sb.$_GET,
			ajax : sb.ajax,
			events : sb.events,
			forget: sb.cookies.forget,
			importNode : sb.dom.importNode,
			recall: sb.cookies.recall,
			remove : sb.dom.remove,
			replace : sb.dom.replace,
			remember: sb.cookies.remember,
			txt : sb.dom.txt,
			typeOf : sb.typeOf
		};
		
		for(var g in sb.globals){
			sb.createIfNotExists(g, sb.globals[g]);
		}
		
		if(!document.importNode){
			document.importNode = sb.dom.importNode;
		}	
	},
	
	/**
	@Name: sb.$_GET
	@Description: An array of all query params e.g. url?name=paul -> $_GET['name'] = 'paul'.  If you have do not have globals disabled there is a global reference to this name $_GET.  Keys that are not foudn return false;
	@Example: 
	//if the url of the page was http://www.surebert.com?name=paul you could reference that query data like this
	if($_GET['name'] =='paul){
		alert('hello paul');
	}
	*/
	$_GET : [],
	
	/**
	@Name: sb.base
	@Description: Used Internally.  Used as placeholder for sb.base until the true base is determined
	*/
	base : (typeof sbBase !='undefined') ? sbBase : '../surebert',
	
	/**
	@Name: sb.consol
	@Description: Used Internally.  Used as placeholder for sb.developer functions
	*/
	consol : {
		log : function(){},
		write : function(){},
		error : function(){},
		dump : function(){},
		swfDebug : function(){}
	},

	/**
	@Name: sb.createIfNotExists
	@Description:  Sets up global alias for a variable if one does not already exist.
	@Example:
	//checks to see if global jump function exists and creates it if it does not
	sb.createIfNotExists('jump', function(){alert('jump');});
	
	*/
	createIfNotExists : function(i, o){
		if(!window[i] && o!==null){
			window[i] = o;
		}
	},
	
	/**
	@Name: sb.include
	@Description:  Includes another surebert module.  Make sure you surebert files are in ../surebert or that you have set sb.base before using this.
	@Example:
	sb.include('sb.colors');
	
	*/
	include : function(module){
		return sb.load(sb.base+'/'+module+'.js');
	},
	
	/**
	@Name: sb.keepTrying
	@Description: You can pass it any number of functions as arrays and the one that works with return
	@Param: Any number of functions, can be either function references or inline anonymous functions
	@Return: returns the return value of the first funciton that returns true
	@Example:
	var x = sb.keepTrying(function1, function2, function3);
	*/
	keepTrying : function(){
		for(var x=0;x<arguments.length;x++){
			try{return arguments[x]();} catch(e){}
		}
	},
	
	/**
	@Name: sb.load
	@Description: Used to load external javascript from the same server synchronously and on demand.
	@Return: Returns 0 upon eval success or 1 if not
	@Example: 
	sb.load('../surebert/surebert.effects.js');
	
	if(sb.load('../js/myJavascript.js')){
	
		//run function from myJavascript.js

	}
	*/
	load : function(url){
		var evaled = 0;
		
		(function(){
			var load = new sb.ajax({
				url : url,
				async : 0,
				handler: function(r){
			
					try{
						eval(r);
						evaled=1;
					}catch(e){
						evaled=0;
						delete e.stack;
						sb.consol.error(url+ sb.messages[13]+"\n"+sb.objects.dump(e));
						
					}
					load=null;
				}
			}).fetch();}());
		return evaled;
	},
	/**
	@Name: sb.messages
	@Description: a placeholder used internally for holding error messages which are defined in sb.developer.  This array just keeps errors from occuring when referencing messages if sb.developer is not included.
	*/

	messages : new Array(50),
	
	/**
	@Name: sb.onbodyload
	@Description: an array of functions that run once the DOM loads, they are fired in order, funcitons can be function references or inline anonymous functions
	@Example:
	sb.onbodyload.push({myFunction});
	*/
	onbodyload : [],
	
	/**
	@Name: sb.onleavepage
	@Description: an array of functions that run once when leaving the page, they are fired in order
	@Example:
	sb.onleavepage.push({myFunction});
	*/
	onleavepage : [],
	
	/**
	@Name: sb.s$
	@Description: Takes a normal DOM node or DOM node list selection from the sb.$ and applies the prototypes of sb.element to it.  If it returns an array, as base don the results from sb.$, then the resulting elements in the array all have the properties of an sb.element
	
	If it returns an array of elements the array, the arary has all the properties of an sb.array e.g. prev(), next(), current(), end(), rewind(), forEach(), map(), filter(), etc - see sb.array docs for details and all the properties of an sb.elementArray, css(), show(), hide(),  addClassName(), removeClassName() etc - see sb.elementArray docs for details.  
	@Param: el Anything you can pass to sb.$ - see $ docs for more details
	
	@Example:
	s$('ol > li'); //returns an array of all the list items in an ordered list, each of which has the properties of a sb.element
	
	s$('#wrapper'); //returns the element with the id "wrapper" and gives it all the properties of a sb.element
	
	//when an array is returned, all of the elementArray methods avaiable return the array back so you can chain methods together
	e.g. $('ol li').css('background-color', 'blue').addClassName('selected');
	
	*/
	s$ : function(el){
		var nodes;
		try{
			nodes = sb.$(el);
			
			if(sb.typeOf(nodes) == 'array'){
				nodes = nodes.map(function(node){
					
					sb.objects.importPrototypesAsProperties(sb.element, node);
					node.getBounds();
					return node;
				});
				
				sb.objects.importPrototypesAsProperties(sb.nodeList, nodes);
			} else {
				sb.objects.importPrototypesAsProperties(sb.element, nodes);
				nodes.getBounds();
			}
			
			
			return nodes;
		} catch(e){
		
			sb.consol.error('"'+el+'"' +sb.messages[14]+e);
		}
	},
	
	/**
	@Name: sb.toArray
	@Description: converts other types of iterable objects into an array e.g. an arguments list or an element Nodelist returned from getElementsByTagName.
	@Param: Object Iterable non-array
	@Return: Array A normal iteratable array with all the properties of an array and the values of the iterable object it was passed.
	@Example: 
	var images = document.getElementsByTagName('img');
	images = sb.toArray(images);
	images.forEach(function(image,key,arr){
		alert(image.src);
	});
	*/
	toArray : function(o){
		var a=[];
		for(var x=0;x<o.length;x++){
			a.push(o[x]);
		}
		return a;
	},
	
	/**
	@Name: sb.typeOf
	@Description: returns the type of the object it is passed
	@Param: object o Any type of javascript object, string, array, function, number, etc
	@Return: String 'function', 'array', 'string', 'object', 'textnode', 'element', 'boolean', 'float', 'number', or returns value of object's custom typeOf() if it exists, 'null'
	@Example:
		var obj = {name : 'joe'}
		sb.typeOf(obj); //return 'object'
	*/
	typeOf : function(o){
		var type='';
		
		if(o === null){
			return 'null';
		} else if (o instanceof Function) { 
			type = 'function'; 
		} else if (o instanceof Array) {
			type = 'array';
		} else if(typeof o == 'number'){
			type = 'number';
			if(String(o).match(/\./)){
				type = 'float';
			}
		} else if(typeof o == 'string'){
			type = 'string';
		} else if(o === true || o === false){
			type='boolean';
		} else {
			type = (typeof o).toLowerCase();
		}
		
		if(typeof o =='object' ){
		
			if(typeof o.typeOf == 'function'){
				type = o.typeOf();
			} else if (o.nodeType){
				if (o.nodeType == 3) {
					type = 'textnode';
					
				} else if (o.nodeType == 1) {
					type = 'element';
				}
			} else if(typeof o.length !='undefined' && type !='array'){
				type = 'nodelist';
			} 
		}
		
		return type;
	},
	
	/**
	@Name: sb.uid
	@Description: a placeholder used internally when creating unqiue IDs for DOM elements
	*/
	uid : 0,
	
	/**
	@Name: sb.uniqueID
	@Description: produces a unique id, ideal for DOM element which are created on the fly but require unique ids
	@Return: String a unique id string for a dom elements id string e.g. 'uid_5'
	@Example:
	var myUniqueId = sb.uniqueID();
	//myUniqueId = 'uid_5' //<--just an example return would be unique each time it is called on a page
	*/
	uniqueID : function(){
		return 'uid_'+(sb.uid +=1);
	},
	
	/**
	@Name: sb.unixTime
	@Description: calculates the current time as a unix timestamp
	@Return: Number A unix timestamp
	@Example:
	var unixtime = sb.unixTime();
	//unixtime = 1170091311//<- just a possible example - would return current time
	*/
	
	unixTime : function(){
		return parseInt(String(new Date().getTime()).substring(0,10), 10);
	},
	
	/**
	@Name: sb.widget
	@Description: Used Internally. A placeholder for widgets used internally, will be fleshed out in future version. Do not use.
	*/
	widget : {}
	
};


/**
@Name: sb.browser
@Description: Find out what browser we are using and gets the query string and screen data
*/
sb.browser ={
	
	/**
	@Name: sb.browser.getAgent
	@Description: Determines the agent, version, and os of the client. Used Internally.  If you specify sbOutDatedBrowser as a function it will fire if the browser is opera < 9, firefox < 1.5, iexplorer <6 or safari < 1.3
	*/
	getAgent : function(){
		var outdated = (typeof sbOutdatedBrowser == 'function') ? sbOutdatedBrowser : function(){};
		
		var opera = new RegExp("Opera/(\\d{1}\.\\d{1})", "i");
		var safari = new RegExp("safari/(\\d{3})", "i");
		var firefox = new RegExp("firefox/(\\d{1}\.\\d{1})", "i");
		var os,agent = window.navigator.userAgent;
		var str;
		
		if(window.opera && document.childNodes) {
			this.agent = 'op';
			this.version =7;
			str = window.navigator.userAgent.match(new RegExp("Opera/(\\d{1}\.\\d{1})", "i"));
			this.version = str[1];
			
		} else if (document.all && !window.XMLHttpRequest && document.compatMode){
			this.agent = 'ie';
			this.version = 6;
		} else if (document.all && window.XMLHttpRequest && document.compatMode){
			this.agent = 'ie';
			this.version = 7;
	
		} else if(agent.match(safari)){
			str = agent.match(safari);
			this.agent = 'sf';
			if(str[1] <= 100){
				this.version = 1;
			} else if(str[1] <= 200){
				this.version =1.2;
			} else if(str[1] < 400){
				this.version =1.3;
			} else if(str[1] > 400){
				this.version =2;
			}
			
		
		} else if(agent.match(firefox)){
			this.agent = 'ff';
			str = agent.match(firefox);
			this.version = str[1];
		} else {
			this.agent='other';
		}
	
		if(agent.match(/mac/i)){
			this.os = 'mac';
		} else if (agent.match(/window/i)){
			this.os = 'win';
		} else if (agent.match(/linux/i)){
			this.os = 'lin';
		}
		
		if((this.agent.match(/ff/) && this.version < 1.5) || (this.agent.match(/ie/) && this.version < 6) || (this.agent.match(/sf/) && this.version < 1.3) || (this.agent.match(/op/) && this.version < 9)){
		
			outdated({agent : this.agent,version:this.version,os:this.os});
		}
		
		return this.agent;
	},
	
	/**
	@Name: sb.browser.measure
	@Description: Measures the inside view area of the window
	@Return Array Returns the an array of width and height of the inside of the client's view area
	@Example:
	var pos = sb.browser.measure();
	//pos = [800, 642]
	*/
	measure : function(){
	
		if( typeof(window.innerWidth) == 'number' ) {
		    sb.browser.w = window.innerWidth;
		    sb.browser.h = window.innerHeight;
		} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {

		    sb.browser.w = document.documentElement.clientWidth;
		    sb.browser.h = document.documentElement.clientHeight;
		} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {

		    sb.browser.w = document.body.clientWidth;
		    sb.browser.h = document.body.clientHeight;
		}
		
		return [sb.browser.w, sb.browser.h];
	},
	
	/**
	@Name: sb.browser.win
	@Description: opens a pop up window.  If this is use don anything other than click events it will trigger the op up filter.  Generally it is a good idea to avoid pop ups.  Toolbars are removed by default from the pop up windows.
	@Param: String url The address of the page you wish to open in the pop up window
	@Param: Number w The width of the window you wish to opem
	@Param: Number h The height of the window you wish to open
	@Param: String/Boolean sb Determines if the pop up window has a scroll bar can be 'y' or 1, or 'n' or 0
	@Return Object A refernce to the new open window
	@Example:
	var myWin = sb.browser.win('http://www.google.com', 800, 600);
	
	*/
	win : function(url, w, h, sb){
		if(sb =='y' || sb === 1){sb ='yes';}
		if(sb =='n' || sb === 0){sb ='no';}
		if(sb != 'yes' && sb !='no'){sb ='yes';}
	
		var newWin = window.open(url,'name','height='+h+',width='+w+',menubar=no,resizable=yes,toolbar=no,scrollbars='+sb);
		
		try{newWin.focus();} catch(e){}
		return newWin;
	},
	
	/**
	@Name: sb.browser.scrollPos
	@Description: Gets the window scroll data It is automatically populated on window.onscroll.
	@Return Array Returns the an array of x and y scroll pos.
	@Example:
	var pos = sb.browser.scrollPos();
	//pos = [400, 300]
	*/
	scrollPos : function(){
		var x,y;
		if(window.pageYOffset){
			y = window.pageYOffset;
		} else if (document.documentElement && document.documentElement.scrollTop){
			y= document.documentElement.scrollTop;
		} else if (document.body && document.body.scrollTop){
			y= document.body.scrollTop;
		} else if (document.documentElement && !document.documentElement.scrollTop){
			y = 0;
		}
		sb.browser.scrollY = y;
	
		if(window.pageXSOffset){
			x = window.pageXOffset;
		} else if (document.documentElement && document.documentElement.scrollLeft){
			x = document.documentElement.scrollLeft;
		} else if (document.body && document.body.scrollLeft){
			x = document.body.scrollLeft;
		} else if (document.documentElement && !document.documentElement.scrollLeft){
			x = 0;
		}
		
		sb.browser.scrollX = x;
		return [sb.browser.scrollX, sb.browser.scrollY];
	},
	
	/**
	@Name: sb.browser.removeSelection
	@Description: Removes any user based text selection, great for dragging scripts
	@Example:
	sb.browser.removeSelection();
	*/
	removeSelection : function(){
		try{
			if(window.getSelection){
				window.getSelection().removeAllRanges();
			} else if(document.selection){
				document.selection.empty();
			}
		}catch(e){}
	},
	
	/**
	@Name: sb.browser.populateGET
	@Description: Used Internally
	*/
	populateGET : function (){
		var i,s,val,key;
		var q = window.location.search.substring(1);
		var v = q.split("&");
	
		for (i=0;i<v.length;i++) {
			s = v[i].split("=");
			key = unescape(s[0]);
			val = unescape(s[1]);
			sb.$_GET[key] = val.replace("+", " ");
		 }
	},
	
	/**
	@Name: sb.browser.init
	@Description: Used Internally
	*/
	init : function(){
		if(window.location.search !==''){this.populateGET();}
		
		this.getAgent();
		this.measure();
	}
};

sb.browser.init();

/**
@Name: sb.$.getElementById
@Description: Used Internally
*/
sb.$.getElementById = function(selector){
	
	var parts = selector.split("#");
	var tag = parts[0];
	var id = parts[1];
	var el = document.getElementById(id);
	
	return el;
	
};

/**
@Name: sb.$.getElementByClassName
@Description: Used Internally
*/
sb.$.getElementByClassName = function(within, selector){

	var parts = selector.split('.');
	var tag = parts[0];
	var class_name = parts[1];
	
	var elements = sb.$.getElementsByTagName(within, tag);
	
	within = elements.filter(function(node,k,a){
		if(node.className && node.className.match(new RegExp('(^|\s)'+class_name+'(\s|$)'))){
			return true;
		}
	});
	
	return within;
	
};

/**
@Name: sb.$.getElementsByTagName
@Description: Used Internally
*/
sb.$.getElementsByTagName = function(within, tag) {
	
	tag = tag || '*';
	
	var matches = [];
	
	within.forEach(function(node,k,a){
		
		var elements = sb.toArray(node.getElementsByTagName(tag));
		matches = matches.concat(elements);
	});
	
	return matches;
};

/**
@Name: sb.$.getElementsByAttributes
@Description: Used Internally
*/
sb.$.getElementsByAttributes = function(within, selector){
	
	var tag,attr,operator,value;
	if (selector.match(/^(?:(\w*|\*))\[(\w+)([=~\|\^\$\*]?)=?['"]?([^\]'"]*)['"]?\]$/)) {
		tag = RegExp.$1;
		attr = sb.$.attrConvert(RegExp.$2);
		operator = RegExp.$3;
		value = RegExp.$4 ||'';
	}
	
	var elements = sb.$.getElementsByTagName(within, tag);
	
	within = elements.filter(function(el,k,a){
	
	el.attrVal = el.getAttribute(attr, 2);
	
	//if attribute is null
	if(!el.attrVal){
		return false;
	}
	
	switch(operator){
		case '=':
			if(el.attrVal != value){
				return false;
			}
			break;
			
		case '~':
			
			if(!el.attrVal.match(new RegExp('(^|\\s)'+value+'(\\s|$)'))){
				return false;
			}
			break;
			
		case '|':
			if(!el.attrVal.match(new RegExp('^'+value+'-?'))) {
				return false;
			}
			break;
			
		case '^':
			if(el.attrVal.indexOf(value) !== 0){
				return false;
			}
			break;
			
		case '$':
			if(el.attrVal.lastIndexOf(value)!=(el.attrVal.length-value.length)){
				return false;
			}
			break;
			
		case '*':
			if(!(el.attrVal.indexOf(value)+1)){
				return false;
			}
			break;
			
		default:
			if(!el.getAttribute(attr)){
				return false;
			}
	}
	
	return true;
		
	});
	
	return within;
	
};

/**
@Name: sb.$.getNextSibling
@Description: Used Internally
*/
sb.$.getNextSibling = function(node){
	while((node = node.nextSibling) && node.nodeType != 1){}
	return node;
};

/**
@Name: sb.$.getPreviousSibling
@Description: Used Internally
*/
sb.$.getPreviousSibling = function(node){
	while((node = node.previousSibling) && node.nodeType != 1){}
	return node;
};

/**
@Name: sb.$.getFirstChild 
@Description: Used Internally
*/
sb.$.getFirstChild = function(node){
	node = node.firstChild;
	while (sb.typeOf(node) == 'textnode') {
		node = sb.$.getNextSibling(node);
	}
	return node;	
};

/**
@Name: sb.$.getLastChild 
@Description: Used Internally
*/
sb.$.getLastChild = function(node){
	
	node = node.lastChild;
	while (sb.typeOf(node) == 'textnode') {
		node = sb.$.getPreviousSibling(node);
	}
	return node;
};

/**
@Name: sb.$.getElementsByParent
@Description: Used Internally
*/
sb.$.getElementsByParent = function(within, selector){
	var parts = selector.split(">");
			
	var par =parts[0];
	var chld = parts[1];
	
	var elements = sb.$(chld);
	elements = (!elements.length) ? [elements] : elements;
	
	elements = elements.filter(function(el,k,a){
	
		if(!par.indexOf('#')+1 && el.parentNode.nodeName.toLowerCase() == par){
				
			return true;
		} else if(par.indexOf('#')+1 && par.replace(/\#/, '') == el.parentNode.id){
			return true;
		}
		
		return false;
	});
	
	return elements;
};

/**
@Name: sb.$.getElementsByAdjacentSibling
@Description: Used Internally
*/
sb.$.getElementsByAdjacentSibling = function(within, selector){
	var parts = selector.split("+");
			
	var nodeName =parts[0];
	var adjacentNodeName = parts[1];
	
	var elements = sb.$(nodeName);
	elements = (!elements.length) ? [elements] : elements;
	//put in the proper adajcent siblings
	elements = elements.map(function(el,k,a){
		
		var node = sb.$.getNextSibling(el);
		if(node && node.nodeName.toLowerCase() == adjacentNodeName){
			return node;
		} 
		
		return false;
		
	});
	
	//remove any false eones
	elements = elements.filter(function(v){
		if(!v){
			return false;
		} else {
			return true;
		}
	});
	return elements;
			
};

/**
@Name: sb.$.parsePseudoSelectors
@Description: Used Internally
*/
sb.$.parsePseudoSelectors = function(within, selector){

	var nth,notSelector,elements = [],parts = selector.split(":");
	
	selector =parts[0];
	var pseudo = parts[1];
	
	var nodes = sb.$(selector, within);
	
	nodes.forEach(function(node,k,a){
		
		switch(pseudo){
			
			case 'before':
		
				var bf = new sb.element({
					nodeName : 'span',
					innerHTML : 'ddd'
				}).appendToTop(node);
				elements.push(bf);
			
				break;
				
			case 'first-child':
				
				if(node.parentNode && node == sb.$.getFirstChild(node.parentNode)){
					elements.push(node);
				}
				break;
				
			case 'last-child':
				if(node.parentNode && node == sb.$.getLastChild(node.parentNode)){
					elements.push(node);
				}
				break;
			
			case 'empty':
				if(node.innerHTML ===''){
					elements.push(node);
				}
				break;
				
			
				
			case 'only-child':
				
				if(node.parentNode.childNodes.length ==1){
					elements.push(node);
				}
				break;
				
			default: 
				
			if(pseudo.indexOf('not')+1){
				notSelector = pseudo.match(/not\((.*?)\)/);
				
				if(node.nodeName.toLowerCase() != notSelector[1]){
					elements.push(node);
				}
			} else if(pseudo.indexOf('nth-child')+1){
				nth = pseudo.match(/nth\-child\((.*?)\)/);
				if(nth[1].isNumeric()){
					nth = parseInt(nth[1],10)-1;
					
					if(nth == k){
						elements.push(node);
					}
				} else {
					switch(nth[1]){
						case 'odd':
							if(k %2 !==0){
								elements.push(node);
							}
							break;
							
						case 'even':
							if(k %2 ===0){
								elements.push(node);
							}
							break;
					}
				}
					
				
			}
			
		}
		
		 
	});

	return elements;
};

/**
@Name: sb.$.parseInheritors
@Description: Used Internally
*/
sb.$.parseInheritors = function(inheriters, within){
	
	var matches = [];
	within = within || [document];
	
	inheriters.within = within;
	
	inheriters.forEach(function(selector,k,a){

		var element;
		
		///we have just an id///
		if((selector.indexOf("#") === 0 && selector.match(/^\#\w+$/)) || selector.match(/\w+\#\w+/)) {
			
			element = sb.$.getElementById(selector);
		
			if(element){
				inheriters.within = [element];
				if(k+1 == a.length){
					matches = matches.concat(inheriters.within);
				}
			}
			
			return true; 
		}
		
		if(selector.indexOf(">")+1){
			inheriters.within = sb.$.getElementsByParent(inheriters.within, selector);
			
			if(k+1 == a.length){
				matches = matches.concat(inheriters.within);
				
			}
			
			return true;
			
		}
		
		if(selector.indexOf("+")+1){
			
			inheriters.within = sb.$.getElementsByAdjacentSibling(inheriters.within, selector);
			
			if(k+1 == a.length){
				matches = matches.concat(inheriters.within);
				
			}
			
			return true;
	
		}
		
		///look for attribute's by searching for sqaure brackets //
		if(selector.indexOf('[')+1){
			
			inheriters.within = sb.$.getElementsByAttributes(inheriters.within, selector);
		
			if(k+1 == a.length){
				matches = matches.concat(inheriters.within);
			}
			
			return true;
		}
		
		//look for pseudo selectors
		if(selector.indexOf(":")+1){
			
			inheriters.within = sb.$.parsePseudoSelectors(inheriters.within, selector);
			
			if(k+1 == a.length){
				matches = matches.concat(inheriters.within);
			}
			
			return true;
			
		}
		
		
		///look for classNames///
		var period_pos = selector.indexOf(".");
		//check for position of css attribute selectors to make sure period isn't in them
		var left_bracket_pos = selector.indexOf("[");
		var right_bracket_pos = selector.indexOf("]");
		
		if(period_pos+1 && !(period_pos > left_bracket_pos && period_pos < right_bracket_pos)) {
			
			inheriters.within = sb.$.getElementByClassName(inheriters.within, selector);
			if(k+1 == a.length){
				matches = matches.concat(inheriters.within);
			}
			
			return true;
			
		}
		
		if(selector.match(/\w+\#\w+/)){
		
			inheriters.within = [sb.$.getElementById(selector)];
		
			if(k+1 == a.length){
				matches = matches.concat(inheriters.within);
			}
			
			return true;
		}
		
		//Tag selectors - no class or id specified.
		inheriters.within = sb.$.getElementsByTagName(inheriters.within, selector);
				
		if(k+1 == a.length){
			matches = matches.concat(inheriters.within);
		}
		return true;
			
	});
	
	return matches;
};

/**
@Name: sb.$.attrConvert
@Description: Used Internally
*/
sb.$.attrConvert = function(attr){
	
	if(sb.browser.agent=='ie'){
		switch(attr){
			
			case 'cellindex':
				attr = 'cellIndex';
				break;
			case 'class':
				attr = 'className';
				break;
			case 'colspan':
				attr = 'colSpan';
				break;
			case 'for':
				attr = 'htmlFor';
				break;
			case 'rowspan':
				attr = 'rowSpan';
				break;
			case 'valign':
				attr = 'vAlign';
				break;
		}
	}
	return attr;
};

/**
@Name: sb.$.attrConvert
@Description: Used Internally Converts old sb.$ calls into new CSS selector compliant ones for backwards compatibility with old surebert code
*/
sb.$.legacy = function(){
	
	var hasAttr=0, obj = arguments[0], filt =  arguments[1];
		
	if(filt.indexOf('@')+1){
	
		filt = filt.replace(new RegExp("=(.*?)$","flags"), "=\"$1\"");
		
		filt = '['+filt.replace('@', '')+']';
		hasAttr=1;
	}
		
	//if first arg is an object
	if(obj.getElementsByTagName){
		
		//convert old sb @ to new attribute selector style - get rid of this soon
		
		return sb.$(filt, [obj]);
		
	//if both arguments are strings join and send to sb.$
	} else if (typeof obj =='string' && typeof filt =='string') {
		
		if(hasAttr){
			alert(obj+filt);
			return sb.$(obj+filt);
		} else { 
			return sb.$(sb.toArray(arguments).join(' '));
		}
	}
	
};

sb.objects = {
	
	/**
	@Name: sb.objects.serialize
	@Description: Serializes all the properties of an object into a post data style key value string
	@Param: Object o An object with properties
	@Return: String e.g. key=value&key=value

	*/
	serialize : function(o){
		var prop,val, str, arr, a=[];
		
		for(prop in o){
			if(sb.typeOf(o[prop]) == 'array'){
				o[prop].forEach(function(v,k){
					a.push(prop+'[]='+encodeURIComponent(v));
				});
				
			} else if(typeof o[prop] =='object' && !sb.typeOf(o[prop]) =='array'){
				
				for(var p in o[prop]){
					if(typeof o[prop][p] == 'object'){
						str = sb.objects.serialize(o[prop][p]);
						arr = str.split("&");
						str ='';
						arr.forEach(function(v,k){
							arr[k]= v.replace(/(.*?)=(.*?)/g, prop+"['"+p+"']['$1']=$2");
							
						});
						a.push(arr.join("&"));
					} else {
						a.push(prop+"['"+p+"']="+encodeURIComponent(o[prop][p]));
					}
				}
			} else {
				val = o[prop];
				a.push(prop+'='+encodeURIComponent(val));
			}
		}
		
		return a.join("&");
	},
	/**
	@Name: sb.objects.importPrototypes
	@Description: Adds the prototypes of one object to another
	@Param: Object from the object to copy the prototypes from
	@Param: Object to the object to add the prototypes to
	@Return: Object The original "to" object
	*/
	importPrototypes : function(from, toObj){
		from = from || {};
		for(var prop in from.prototype) {
			toObj.prototype[prop] = from.prototype[prop];
		}
		from = null;
		return toObj;
	},
	
	/**
	@Name: sb.objects.importProperties
	@Description: Adds the properties of one object to another
	@Param: Object from the object to copy the properties from
	@Param: Object to the object to add the properties to
	@Return: Object The original "to" object
	*/
	importProperties : function(from, toObj){
		
		from = from || {};
		for(var prop in from) {
			try{ toObj[prop] = from[prop];} catch(e){}
		}
		from = null;
		return toObj;
	},
	
	/**
	@Name: sb.objects.importPrototypesAsProperties
	@Description: Adds the prototypes of one object to another objects properties
	@Param: Object from the object to copy the prototypes from
	@Param: Object to the object to add the properties to
	@Return: Object The original "to" object
	*/
	importPrototypesAsProperties : function(from, toObj){
		from = from || {};
		for(var prop in from.prototype) {
			toObj[prop] = from.prototype[prop];
		}
		from = null;
		return toObj;
	},
	
	/**
	@Name: sb.objects.importProperitesAsPrototypes
	@Description: Adds the properties of one object to another objects prototypes
	@Param: Object from the object to copy the properties from
	@Param: Object to the object to add the prototypes to
	@Return: Object The original "to" object
	*/
	importPropertiesAsPrototypes : function(from, toObj){
		from = from || {};
		for(var prop in from) {
			toObj.prototype[prop] = from[prop];
		}
		from = null;
		return toObj;
	},
	
	/**
	@Name: sb.objects.importProperties
	@Description: Used Internally
	*/
	extend : function(from){
		return sb.objects.importProperties(from, this);
	},
	
	/**
	@Name: sb.objects.copy
	@Description: Makes a copy of an object and it's properties
	@Param: Object o the object to copy
	@Return: Object a copy of the object
	@Example:
		var o = {name : 'paul, language : 'javascript'};
		var f = sb.objects.copy(o);
	*/
	
	copy : function(o){
		var copy = {};
		for(var prop in o){
			copy[prop] = o[prop];
		}
		return copy;
	},
	
	/**
	@Name: sb.objects.alert
	@Description: Alerts the properties and their values for an object
	@Param: Object o The object to alert the properties of
	@Example:
		var o = {name : 'paul, language : 'javascript'};
		sb.objects.alert({o});
	*/
	alert : function(o){
		alert(sb.objects.dump(o));
	},
	
	/**
	@Name: sb.objects.dump
	@Description: Returns the properties of the object and their values for an object
	@Param: Object o the object to return the properties of
	@Param: Number pre If this parameter is set to 1 than, the data is returned in a pre tag to maitain formatting
	@Return: String The properties of the object
	@Example:
		var o = {name : 'paul, language : 'javascript'};
		sb.objects.dump({o});
	*/
	dump : function(o, pre){
			var prop,str ='';
			for(prop in o){
				try{
					str+="\n"+prop+' = '+o[prop];
				} catch(e){
					str += "\n"+prop+' = CANNOT PROCESS VALUE!';
				}
			}
			
			if(!pre){ return str;} else { return '<pre style="margin:5px;border:1px;padding:5px;">'+str+'</pre>';}
	
	}
};

/**
@Name: sb.ajax
@Description: sb.ajax is a constructor that can be used to instanitate objects which communicate in real time from the client to the server without refreshing the page, all properties can be passed as the only object argument
@Return: Object A new ajax communication object
@Example:
var myAjax = new sb.ajax({
	//optional 'get' is the default
	method : 'post',
	
	//optional 'text' is the default
	format : 'text',
	
	//optional no data needs to be sent to the server side script
	data : 'name=paul&friend=tim&day=monday',
	
	// the server side script to call
	url : 'process.php',
	
	//the handler function receives all data returned from the server side script, depending on the format specified, result has different properties, by default it is a text string
	handler : function(result){ 
		//alerts the text returned from the server side script
		alert(result); 
	}
});
*/
sb.ajax = function(params){ 
	
	try{this.o=new XMLHttpRequest();}catch(e){}
	try{this.o=new ActiveXObject("Msxml2.XMLHTTP");}catch(e){}
	try{this.o=new ActiveXObject("Microsoft.XMLHTTP");}catch(e){}
	if(!this.o){return null;}
	
	sb.objects.importProperties(params, this);
};

/**
@Name: sb.ajax.extend
@Description: every sb.ajax instance has an extend property that allows you to pass or override additional properties for the object
@Example:
var myAjax = new sb.ajax({
	url : 'process.php'
});
myAjax.extend({
	format : 'json',
	method : 'post'
});
*/
sb.ajax.extend = sb.objects.extend;

sb.ajax.extend({
	/**
	@Name: sb.ajax.defaultMethod
	@Description: The default transport method used for communicating with server side scripts.  If this is changed, all insatnces with non specified transport methods will use this one.  It is 'get' by default.  Another option is 'post'.
	*/
	defaultMethod : 'get',
	
	/**
	@Name: sb.ajax.defaultFormat
	@Description: The default way the ajax instances handles the data retreived from the scripts. This sets the default format for all sb.ajax instances that do not already specify a format.  It is text by default but you can override this in your script.  The options are;
	1. text - returns the data from the server side script as text and passes it to the instances handler method
	2. json - returns the data from the server side script as a JSON object whose properties can easily be accessed with javascript
	3. xml - returns the data from the server side script as an XML node which can be parsed with traditional XML parsing methods in javascript
	4. js - evaluated the data returned from the server side script as javascript
	5. send - only sends data and does not receive any data
	6. head - only reads the header data from the HTML transaction and passes that to the instances handler method.  If a header property is specified on the sb.ajax instance, then only that header is passed
	@Example:
	sb.ajax.defaultFormat = 'json';
	*/
	defaultFormat : 'text',
	
	/**
	@Name: sb.ajax.defaultURL
	@Description: The default url the ajax instances semd data to. This sets the url for all sb.ajax instances that do not already specify a url.
	@Example:
	sb.ajax.defaultURL = 'process.php';
	*/
	defaultURL : '',
	
	messages : {
		1:'This browser does not support surebert, please visit again with firefox, ie 5.5-7 for win, safari, netscape or opera.',
		2:'Page not found. status codes explained at http://surebert.com/errorCodes.html ',
		3:'page found but blank',
		4:'invalid xml',
		5:'log sent',
		6:'Data has arrived at the destination url',
		7:'Content Length exceed stated limit',
		8:'Error evaling javascript from server',
		9:'Dom node referenced by ajax object does not exist'
	}
});

sb.ajax.prototype = {
	
	/**
	@Name: sb.ajax.prototype.completed
	@Description: Is set to 0 when if the ajax call is not complete or set to 1 if it is compelete.  Used to check for complete state when using synchronous calls in safari.  Each instances completed status is reset on each fetch() call so that asynchronous calls can still be fetched more than once.
	@Type: Boolean
	*/
	completed : 0,
	
	/**
	@Name: sb.ajax.prototype.debug
	@Description: Determines if the data sent and received is debugged to the to surebert debug consol which.  This  only works if you include sb.developer.js  This makes debuggin much easier.
	@Type: Boolean
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php',
		debug : 1
	});
	
	//or added afterwards with
	myAjax.debug =1;
	*/
	debug : this.debug || 0,
	
	/**
	@Name: sb.ajax.prototype.data
	@Description: The data sent to the server side script specified in the url property.  The values are passed as key value pairs e.g. x=1&y=2&name=joe.  You should always escape or URIencode data that includes anything other than alphanumeric data.
	@Type: String
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php',
		data : 'name=paul&day=monday&value='+escape($('myInput').value)
	});
	
	//or added afterwards with
	myAjax.data = 'name=paul&day=monday&value='+escape($('myInput').value);
	*/
	data : this.data || '',
	
	/**
	@Name: sb.ajax.prototype.format
	@Description: The format the data is retreived in.  Can be json, text, xml, head, js, send - s.  This value overides any sb.ajax.defaultFormat value set or if the content type of the server page matches a specific format.
	1. text - returns the data from the server side script as text and passes it to the instances handler method
	2. json - returns the data from the server side script as a JSON object whose properties can easily be accessed with javascript.  This type is defaulted if the page is served with the term 'json' in the content type e.g. application/json 
	3. xml - returns the data from the server side script as an XML node which can be parsed with traditional XML parsing methods in javascript  This type is defaulted if the page is served with the term 'xml' in the content type e.g. application/xml 
	4. js - evaluated the data returned from the server side script as javascript.  This type is defaulted if the page is served with the term 'javascript' in the content type e.g. application/javascript 
	5. send - only sends data and does not receive any data
	6. head - only reads the header data from the HTML transaction and passes that to the instances handler method.  If a header property is specified on the sb.ajax instance, then only that header is passed
	@Type: Boolean
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php',
		format : 'json'
	});
	
	//or added afterwards with
	myAjax.format = 'json';
	*/
	format : this.format || '',
	
	/**
	@Name: sb.ajax.prototype.async
	@Description: Determines if the script is paused while the data is loaded.
	@Type: Boolean false performs synchronous and pauses, true performs asynchronously which is the default allowing other processes to continue
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php',
		async : 1
	});
	
	//or added afterwards with
	myAjax.async = 1;
	*/
	async : this.async,
	
	/**
	@Name: sb.ajax.prototype.onlog
	@Description: If set this function is passed the debug data on every send/receive
	@Type: Function
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php',
		onlog : function(log){
			ab.objects.alert(log);
		}
	});
	
	//or added afterwards with
	myAjax.onlog : function(log){
		ab.objects.alert(log);
	};
	*/
	onlog :'',
	
	/**
	@Name: sb.ajax.prototype.local
	@Description: Allows ajax object instances to fetch data from a local file instead of from a server.  Normally, the instance checks for the HTTP server response - e.g. 200, 404, 500, etc and if you grab a klocal file this does not exist.  If you are serving your pages from a web server you should never need to use this.
	@Type: Boolean
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php',
		local : 1
	});
	*/
	local : this.local || 0,
	
	/**
	@Name: sb.ajax.prototype.addToLog
	@Description: Used Internally
	*/
	addToLog : function(log){
	
		if(typeof(log)=='number'){
			log = {data:sb.ajax.messages[log],code:log,type:'error'};
		} else if(typeof(log)!='object'){
			log = {data:log,code:5,type:'log'};
		} 
	
		if(typeof(this.onlog)=='function'){
			this.onlog(log);
		} else {
			try{
				if(this.debug == 1 || sb.ajax.debug == 1 && sb.consol){
					if(log.type =='error'){
						sb.consol.error(log.data, 1);
					} else {
						sb.consol.log(log.data, 1);
					}
				}
			}catch(e){}
		}
	},
	
	/**
	@Name: sb.ajax.prototype.onreadystatechange
	@Description: Used Internally
	*/
	onreadystatechange : function() {
		
		if (this.o.readyState != 4 || this.completed == 1) {return; }
			this.completed =1;
			if(this.method =='get'){
				this.addToLog('URL:url('+this.url+'?'+this.data+")\nDATA: "+this.data+'\nMETHOD:'+this.method+'\n');
			} else {
				
				this.addToLog('URL:url('+this.url+")\nDATA: "+this.data+'\nMETHOD:'+this.method+'\n');
			}
			
		try{
			
			if(this.o.status != 200 && this.local !==1){
				
				this.addToLog({data:sb.ajax.messages[2]+"\nURL:url("+this.url+")\nDATA:("+this.data+")\nSTATUS: "+this.o.status+"\nSTATUS TEXT: "+this.o.statusText,code:2,type:'error'});
				return;
			}
		} catch(e){return;}
		
		if(this.format !='send'){
			if(this.o.responseText === ''){
				this.addToLog(3);
			} else{
				this.addToLog("HEADER: "+"\n"+this.o.getAllResponseHeaders()+"\n\nRECEIVED: \n"+this.o.responseText);
			}
		}
		
		if(typeof this.timer !='undefined'){
			this.timer.reset();
		}
		
		this.contentType = this.o.getResponseHeader("Content-Type");
		this.contentLength = this.o.getResponseHeader("Content-Length");
		
		if(this.contentLength > this.maxContentLength){
			
			this.addToLog(7);
			if(typeof this.onContentLengthExceeded == 'function'){
				this.onContentLengthExceeded();
			}
			this.o.abort();
			return;
		}
		
		if(this.format ===''){
			
		
			if(this.contentType.match('json')){
				this.format ='json';
			} else if (this.contentType.match('javascript')){
				this.format ='javascript';
			} else if (this.contentType.match('xml')){
				this.format ='xml';
			}
		}
				
		switch(this.format){
			
			case 'head':
				if(typeof this.header ==='undefined'){
					this.response = this.o.getAllResponseHeaders();
				} else {
					this.response = this.o.getResponseHeader(this.header);
				}
			break;
			
			case 'xml':
				if(this.o.responseXML !== null){ 
					this.response = this.o.responseXML.documentElement;
				} else { 
					this.addToLog(4);
				}
			break;
			
			case 'js':
				try{
					 this.response = this.o.responseText;
					eval(this.response); 
				}catch(e){
					this.addToLog(8);
				}
			break;
			
			case 'json':
				try{
					 eval('this.response='+this.o.responseText);
				}catch(e){
					this.addToLog(8);
				}
			break;
			
			case 'send':
				this.addToLog(6);
			break;
			
			default:
				this.response = this.o.responseText;
			break;
		}
	
		
		if(typeof(this.handler) =='function'){this.handler(this.response);}
		
		if(typeof this.node !='undefined'){
			
			if(sb.$(this.node)){
				this.node = sb.$(this.node);
				if(typeof this.node.value !='undefined'){
					this.node.value = this.o.responseText;
				} else {
					this.node.innerHTML = this.o.responseText;
				}
			} else {
				this.addToLog(9);
			}
		}
		
		this.o.abort();
		return; 
	},
	
	/**
	@Name: sb.ajax.prototype.fetch
	@Description: Sends any data specified to the external server side file specified in your instances .url property and returns the data recieved to the instances handler method
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php'
	});
	
	//fetches the data from the url specified
	myAjax.fetch();
	*/
	fetch : function(url) {
		this.completed =0;
		
		this.method = (typeof this.method !='undefined') ? this.method : sb.ajax.defaultMethod; 

		var t=this;
		url = url || t.url || sb.ajax.defaultURL;
		t.url =url;
		
		if(!t.o){t.addToLog(1);return;}
		
		t.o.onreadystatechange = function(){t.onreadystatechange();};
	
		if(t.method=='get' && t.data !== undefined){
			url = url+'?'+t.data;
		}
		
		if(typeof t.onmillisec =='function'){
			if(typeof sb.timer =='undefined'){sb.include('sb.timer');alert(sb.timer);}
			
			t.timer = new sb.timer({
				milliseconds : 1,
				handler :  function(){t.onmillisec();}
			});
		
			t.timer.begin();
		}
		
		if(typeof t.onfetch == 'function'){
			t.onfetch();
		}
		
		if(typeof(t.async) =='undefined'){
			t.async=true;
		}
		
		
		t.o.open(t.method, url, t.async);
	
		if(t.method=='post'){
			try{
				t.o.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			}catch(e){}
		}
		
		try{t.o.send(t.data); }catch(e){}
		
		
		if (!t.async ){ t.onreadystatechange();}
		
	},
	
	/**
	@Name: sb.ajax.prototype.abort
	@Description: You can use this to abort an ajax function that is fetching.  In addition, if you have defined an onabort() method for your sb.ajax instance it will fire whenever the fetch is canceled.
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php'
	});
	myAjax.fetch();
	
	//aborts a fetch already in progress, you could attach this event to a cancel button
	myAjax.abort();
	*/
	abort : function(){
		this.o.abort();
		
		if(typeof this.onmillisec !='undefined'){
			this.timer.reset();
		}
		
		if(typeof this.onabort =='function'){
			this.onabort();
		}
		
	},
	
	/**
	@Name: sb.ajax.prototype.extend
	@Description: You can easily extend sb.ajax objects with multiple properties
	@Example:
	var myAjax = new sb.ajax({
		url : 'process.php'
	});
	
	myAjax.extend({
		format :'text',
		handler : function(result){
			alert(result);
		}
	});
	*/
	extend : sb.objects.extend
};

/**
@Name: sb.css
@Description: Used internally
*/
sb.css = function(prop, val){

	if(val){
		sb.styles.write(this, prop, val);
	} else {
		return sb.styles.read(this, prop);
	}
};

sb.dom = {

	
	/**
	@Name: sb.dom.getBounds
	@Description: Used internally
	*/
	getBounds : function(params){
		params = params || {};
		params.pos = params.pos || 'abs';
		params.scroll = params.scroll || 0;
		var orig =this;
		var el=this,pos={top:0,left:0};
	
		do{
			pos.top += el.offsetTop;
			pos.left += el.offsetLeft;
			if(params.pos =='rel'){
				el = false;
			} else{
				try{el = el.offsetParent;}catch(e){el = false;}
				
			}
		} while(el);
		
		if(params.scroll ==1){
			if(sb.browser.scrollY){
				pos.top -=sb.browser.scrollY;
			}
			
			if(sb.browser.scrollX){
				pos.left -=sb.browser.scrollX;
			}
		}
		
		pos.bottom = pos.top+orig.offsetHeight;
		pos.right = pos.left+orig.offsetWidth;
		pos.h = orig.offsetHeight;
		pos.w = orig.offsetWidth;
		sb.objects.importProperties(pos, this);
		return pos;	
	},
	
	/**
	@Name: sb.dom.importNode
	@Description: Used internally - marked for deletion - do not use
	*/
	importNode : function(node, deep){
		var nodeHTML = node.xml||node.outerHTML;
		if(!nodeHTML) {return false;}
		if(typeof deep == "undefined"){return false;}
		var tmpNode = document.createElement("div");
		tmpNode.innerHTML = nodeHTML;
		return tmpNode.firstChild.cloneNode(deep);
	},
	
	/**
	@Name: sb.dom.className
	@Description: Used internally
	*/
	className : {
		
		toArray : function(){
			return this.className.split(' ');
		},
		
		contains : function(c){
			return sb.dom.className.toArray.call(this).inArray(c);
		},
		
		add : function(c){
			sb.$(this).className += ' '+c;	
		},
		
		remove : function(c){
			var a = sb.dom.className.toArray.call(this);
			sb.$(this).className = a.remove(c).join(' ');
		},
		
		toggle : function(c){
			
			if(sb.dom.className.contains.call(this, c)){
				sb.dom.className.remove.call(this, c);
			} else {
				sb.dom.className.add.call(this, c);
			}
		}
	},
	
	/**
	@Name: sb.dom.className
	@Description: Used internally
	*/
	createNamedElement : function(t, n) {
		var el;
		
		try {
			el = document.createElement('<input type="'+t+'" name="'+n+'">');
		} catch (el) { }
			if (!el || !el.name) { 
			el = document.createElement('input');
			el.type=t;
			el.name=n;
		}
		
		return el;
	},
	
	/**
	@Name: sb.dom.onready
	@Description: Used to run a function when a DOM element becomes available
	@Param: object o An object of parameters
	o.id - A reference to the id of the DOM node you are questioning the availability of, e.g. #navigation is the the ID of the DOM node I am polling for.
	
	@Example:

	//In this example the onloaded function fires when the node with the id #navigation is available  the onloaded function, receives a this which is essentialy the element passed through sb.$
	sb.dom.onready({
		id : '#navigation',
		onready : function(){
			alert(this.innerHTML);
		},
		interval : 100,
		tries : 10,
		ontimeout function(el){
			alert(el+' not found');
		},
		args : ['one', 'two']
	});
	
	*/
	
	onready : function(o){
		var found =0, timer, count=0;
		o.args = o.args || [];
		o.interval = o.interval || 10;
		
		o.tries = o.tries || 600;
		if(o.tries == -1){o.tries =99999999;}
		
		if(typeof o.onready=='function'){
			
			timer = window.setInterval(function(){
				
				count +=1;
				
				if(count >= o.tries){
					window.clearTimeout(timer);
					
					if(typeof o.ontimeout=='function'){
						o.ontimeout(o.id);
					}
					return;
				}
				
				if(o.id == 'body' && document.body){
					window.clearTimeout(timer);
					found=1;
					o.id = document.body;
				} else if(o.id !='body' && sb.$(o.id)){
					
					window.clearTimeout(timer);
					found=1;
				}
				
				if(found ==1){
					o.onready.apply(sb.$(o.id), o.args);
					
				}
				
			}, o.interval);
			
		} else {
			throw('sb.dom.onready: You object argument must have a func property that runs when the dom element '+o.id+' is available');
		}
	},
	
	/**
	@Name: sb.dom.remove
	@Description: Removes an element from the DOM
	@Param: Element node An element reference to the element that should be removed
	@Param: String node A string reference to a DOM node that can be passed to sb.$ to return a dom node reference e.g. '#myForm'
	@Example:
	sb.dom.remove('#newForm');
	
	//if globals are not disabled
	remove('#newForm');
	*/
	remove : function(node){
		node = sb.$(node);
		if(typeof node.parentNode !='undefined'){
			return node.parentNode.removeChild(node);
		}
	},
	
	/**
	@Name: sb.dom.replace
	@Description: Replaces an element with another one in the DOM
	@Param: Element newNode An element reference to the element that should replace the oldNode
	@Param: String oldNode A string reference to a DOM node that should replace the oldNode which can be passed to sb.$ to return a dom node reference e.g. '#myForm'
	@Param: Element newNode An element reference to the element that should be be replaced
	@Param: String oldNode A string reference to a DOM node that can be passed to sb.$ to return a dom node reference e.g. '#myForm'
	@Example:
	sb.dom.replace('#newForm', '#oldForm');
	
	//if globals are not disabled
	replace('#newForm', '#oldForm');
	*/
	replace : function(newNode, oldNode){
	
		newNode = sb.$(newNode);
		oldNode = sb.$(oldNode);
		if(typeof oldNode.parentNode !='undefined'){
			return oldNode.parentNode.replaceChild(newNode, oldNode);
		}
	},
	
	/**
	@Name: sb.dom.singleTags
	@Description: Used internally
	*/
	singleTags : ['html', 'body', 'base', 'head', 'title'],
	
	/**
	@Name: sb.dom.toggle
	@Description: Toggles an elments display between none and block
	@Param: The node you want to toggle the display of, either an obj  reference or an id string to reference it by e.g '#myDiv'
	@Example:
	sb.dom.toggle('#myNode');
	*/
	toggle : function(node){
		node = sb.s$(node);
		if(node.style){
			node.style.display = (node.css('display') ==='none') ? 'block' : 'none';
		}
	},
	
	/**
	@Name: sb.dom.txt
	@Description: Creates a text node which can be appended to other DOM nodes.  You could easily map this to a global txt function by adding the line 'txt = sb.dom.txt;'
	@Param: String str A text string that is the contents of the created text node
	@Return: Element A text node element
	@Example:
	myParagraph.appendChild(sb.dom.txt('hello world'));
	
	//if globals are not disabled you can simply use txt
	myParagraph.appendChild(txt('hello world'));
	*/
	txt : function(s){
		return document.createTextNode(s);	
	}
	
};

/**
@Name: sb.element
@Description: Used to create DOM nodes.  If a string is passed to the fuction it simply return document.createElement(str);
@Param: Object o An object of properties which are used to contruct the DOM object,  all properites are appending as properties to the dom object.  sb.elements have many methods whcih are all listed in the sb.element.prototype object below
@Param: String o If passed a nodeName as a string it simply returns document.createElement(nodeName);
@Param: Object sb.element If passed an sb.element it uses that element as a template and clones it
@Return: Element A DOM element hat can be inserted into the DOM or further manipulated
@Example: 
var myDiv = new sb.element({
	nodeName : 'div',
	className : 'redStripe',
	innerHTML : 'I am a redstriped div',
	events : {
		click : function(){
			alert(this.innerHTML);
		},
		mouseover : function(){
			this.style.backgroundColor='red';
		}
	},
	styles : {
		backgroundColor : 'blue',
		fontSize : '18px'
	},
	addAttributes : function{
		friend : 'xxx'
	}
});

myDiv.appendTo('body');

//OR just pass the nodeType
var myDiv = new sb.element('div');

myDiv.appendChild(myOtherDiv);
*/
sb.element = function(o){
	var el,c;
	o.nodeName = o.nodeName || o.tag;
	
	if(sb.typeOf(o) == 'string'){
		
		return document.createElement(o);
	} else if(sb.typeOf(o)=='object' || sb.typeOf(o) == 'sb.element'){
		if(typeof o.nodeName =='undefined') {o.nodeName='span';}
		
		if(o.nodeName == 'input'){
			o.name = o.name || '';
			o.type = o.type || 'text';
			el = new sb.dom.createNamedElement(o.type, o.name);
		} else {
			el = document.createElement(o.nodeName);
		}
		
		//delete o.nodeName;
	} else {
			el = o;
	}
	
	sb.objects.importProperties(this, el);
	
	if(typeof o.addAttributes !='undefined'){
		this.setAttributes.call(el, o.addAttributes);
		//delete o.addAttributes;	
	}
	
	if(typeof o.styles !='undefined'){
		this.styles.call(el, o.styles);
		delete o.styles;	
	}
	
	if(typeof o.children !='undefined'){
		for(c=0;c<o.children.length;c++){
			el.appendChild(new sb.element(o.children[c]));
		}
		delete o.children;
	}
	
	if(typeof o.events !='undefined'){
		for(var ev in o.events){
			el.event(ev, o.events[ev]);
		}
		//delete o.events;
	}
	
	
	if(el !=o){
		el.extend(o);
	}
	
	//remove attributes for ie or the props turn to attributes
	el.removeAttribute('tag');
	
	return el;
};

/**
@Name: sb.element.protoype
@Description: Methods of sb.element instances. Assume that myElement is an sb.element instance in all examples of sb.element.prototype
*/

sb.element.prototype = {

	/**
	@Name: sb.element.addClassName
	@Description: Adds a className to the sb.element, using this methods sb.element instances can have multiple classNames
	@Param: String c The classname to add
	@Example:
	myElement.addClassName('redStripe');
	*/
	addClassName : sb.dom.className.add,
	
	/**
	@Name: sb.element.addClassName
	@Description: Adds a className to the sb.element, using this methods sb.element instances can have multiple classNames
	@Param: String c The classname to add
	@Example:
	myElement.setAttributes({friend : 'tim', name : 'joe'});
	<myElement friend="tim" name="joe">
	*/
	setAttributes : function(o){
		
		for(var prop in o){
			this.setAttribute(prop, o[prop]);
		}
	},
	
	/**
	@Name: sb.element.append
	@Description: Appends another DOM element to the element as a child
	@Param: Element, String el Another DOM element reference or a string that can be passed through sb.$ to return a DOM node.
	@Example:
	myElement.append(myOtherElement);
	*/
	append : function(el){return this.appendChild(sb.$(el));},
	
	/**
	@Name: sb.element.appendTo
	@Description: Appends the element to another DOM element as a child
	@Param: Element, String el Another DOM element reference or a string that can be passed through sb.$ to return a DOM node.
	@Return: Element A refernce to the appended node
	@Example:
	//appends myElement to the page body
	myElement.appendTo('body');
	
	//appends myElement to a div with the ID "myDiv"
	myElement.appendTo('#myDiv');
	
	*/
	appendTo : function(el){
		return sb.$(el).appendChild(this);
	},
	
		/**
	@Name: sb.element.appendToTop
	@Description: Appends the element to the top DOM element as a child
	@Param: Element, String el Another DOM element reference or a string that can be passed through sb.$ to return a DOM node.
	@Return: Element A refernce to the appended node
	@Example:
	//appends myElement to the page body
	myElement.appendToTop('body');
	
	//appends myElement to a div with the ID "myDiv"
	myElement.appendToTop('#myDiv');
	
	*/
	appendToTop : function(el){
		el = sb.$(el);
	
		if(el.childNodes.length ===0){
			return this.appendTo(el);
		} else {
			return this.appendBefore(el.firstChild);
		}
	},

	/**
	@Name: sb.element.appendAfter
	@Description: Appends the element after another DOM element as a sibling
	@Param: Element, String el Another DOM element reference or a string that can be passed through sb.$ to return a DOM node.
	@Example:
	//appends myElement to the parent of "#myDiv" as a sibling of "#myDiv" directly after "#myDiv"
	myElement.appendAfter('#myDiv');
	
	*/
	appendAfter : function(after){
		var el = sb.s$(after);
		var nxtSib = el.getNextSibling();
		
		if(nxtSib){
			return nxtSib.parentNode.insertBefore(this, nxtSib);
		} else {
			return this.appendTo(el.parentNode);
		}
	},
	
	/**
	@Name: sb.element.appendBefore
	@Description: Appends the element before another DOM element as a sibling
	@Param: Element, String el Another DOM element reference or a string that can be passed through sb.$ to return a DOM node.
	@Example:
	//appends myElement to the parent of "#myDiv" as a sibling of "#myDiv" directly before "#myDiv"
	myElement.appendBefore('#myDiv');
	
	*/
	appendBefore : function(before){
		before = sb.$(before);
		return before.parentNode.insertBefore(this, before);
	},

	/**
	@Name: sb.element.clearStyles
	@Description: Clears any styles set for the DOM element by javascript
	@Example:
	myElement.style.height = '10px';
	myElement.clearStyles();
	//height would be '';
	
	*/
	clearStyles : function(){
		for(var style in this.style){
			try{
				this.style[style] = '';
			}catch(e){}
		}
	},
	
	/**
	@Name: sb.element.css
	@Description: used to set and get the CSS styles of an object, You can pass it either css style dash syntax of javascript camelBack e.g. background-color or backgroundColor.
	@Param: String prop The property to set or get.
	@Param: String val The value to set the property to.  If not set the function returns the value currently set
	@Example:
	
	//sets the backgroundColor peroperty to red
	myElement.css('background-color', 'red');
	
	myElement.css('background-color');
	//would return red
	*/
	
	css : sb.css,
	
	/*
	@Name: sb.element.event
	@Description: Used to set event cross-browser event handlers.  For more information see sb.events.
	@Param: String evt The event to handle e.g. mouseover, mouseout, mousedown, mouseup, click, dblclick, focus, blurr, scroll, contextmenu, keydown, keyup, keypress
	@Param: Function func The function to use as an event handler.  It is passed the e from the event in every brower as the first argument.  It also references "this" as the object the event is listening on.
	@Return: The event that is added is returned so that you can use the reference to remove it with sb.events.remove or the sb.element instances sb.eventRemove
	@Example:
	
	//sets the backgroundColor peroperty to red
	myElement.event('click', function(e){
		//alerts the x value of the click 
		alert(e.clientX);
		//alerts the innerHTML of myElement
		alert(this.innerHTML);
	});
	
	*/
	event : function (evt, func){
		this.events[evt] = func;
		return sb.events.add(this, evt, func);
	
	},
	
	/*
	@Name: sb.element.events
	@Description: An object of all events attached to the object, usign the event as the key
	*/
	events : {},
	
	/*
	@Name: sb.element.eventRemove
	@Description: Removes an event created with sb.element.prototype.event
	@Param: String evt An event reference returned from the sb.element instances event method above.
	
	@Example:
	
	//sets the backgroundColor peroperty to red
	var myEvt = myElement.event('click', function(e){
		alert(this.innerHTML);
	});
	
	myElement.eventRemove(myEvt);
	*/
	eventRemove : function (evt){
			return sb.events.remove(evt);
	},
	
	/**
	@Name: sb.element.event
	@Description: Used to set event cross-browser event handlers.  For more information see sb.events.
	@Param: String evt The event to handle e.g. mouseover, mouseout, mousedown, mouseup, click, dblclick, focus, blurr, scroll, contextmenu, keydown, keyup, keypress
	@Param: Function func The function to use as an event handler.  It is passed the e from the event in every brower as the first argument.  It also references "this" as the object the event is listening on.
	@Example:
	
	//add a property called name which is set to tim, and add a  property called type with a value of text
	myElement.extend({name : 'tim', type : 'text'});
	*/
	extend : function(o){
		sb.objects.importProperties(o, this);
	},
	
	/**
	@Name: sb.element.getBounds
	@Description: Used to calculate the bounds top, right, bottom, left, h, and w of a DOM node.  h and w are height and width.  Attaches those properties to the element itself
	@Return: Object An object with top, right, bottom, left, h, and w properties
	
	@Example:
	var pos = myElement.getBounds();
	alert(pos.left);
	//same as
	alert(myElement.left);
	*/
	
	getBounds : function(params){
		return sb.dom.getBounds.call(this, params);
	},
	
	/**
	@Name: sb.element.getBoundsRelative
	@Description: Used to calculate the bounds top, right, bottom, left, h, and w of a DOM node.  h and w are height and width.  Attaches those properties to the element itself
	@Return: Object An object with top, right, bottom, left, h, and w properties. Returns numbers relative to the elements parentNode
	
	@Example:
	var pos = myElement.getBoundsRelative();
	alert(pos.left);
	//same as
	alert(myElement.left);
	*/
	getBoundsRelative : function(){
			return this.getBounds({pos:'rel'});
	},
	
	/**
	@Name: sb.element.getBoundsWithScroll
	@Description: Used to calculate the bounds top, right, bottom, left, h, and w of a DOM node.  h and w are height and width.  Attaches those properties to the element itself
	@Return: Object An object with top, right, bottom, left, h, and w properties. Returns numbers including the scroll position
	
	@Example:
	var pos = myElement.getBoundsWithScroll();
	alert(pos.left);
	//same as
	alert(myElement.left);
	*/
	getBoundsWithScroll : function(){
		return this.getBounds({scroll:1});
	},
	
	/**
	@Name: sb.element.getNextSibling
	@Description: Finds the next sibling element of the element on which this is called
	@Return: Element A DOM element reference
	
	@Example:
	myElement.getNextSibling();
	*/
	getNextSibling : function(){
		return sb.$.getNextSibling(this);
	},
	
	/**
	@Name: sb.element.getPreviousSibling
	@Description: Finds the previous sibling element of the element on which this is called
	@Return: Element A DOM element reference
	
	@Example:
	myElement.getPreviousSibling();
	*/
	getPreviousSibling : function(){
		return sb.$.getPreviousSibling(this);
	},
	
	/**
	@Name: sb.element.hasClassName
	@Description: Checks to see if the element has the className specified.  Elements can have more than one className.
	@Return: Boolean True if the element contains the className and False if it doesn't
	@Param: String c The className to check for
	@Example:
	myElement.return('redStripe');
	*/
	hasClassName: function(c){
		return sb.dom.className.contains.call(this, c);
	},
	
	/**
	@Name: sb.element.hide
	@Description: Sets the display of the element to none, removing its from being displayed on the page
	
	@Example:
	myElement.hide();
	*/
	hide : function(){
		this.style.display = 'none';
	},
	
	/**
	@Name: sb.element.html
	@Description: Used to set or get the innerHTML of an super element
	
	@Example:
	myElement.html('<h1>hello</h1><p>dfdsfsdf</p>');
	
	//or
	var html = myElement.html();
	//returns html = '<h1>hello</h1><p>dfdsfsdf</p>';
	*/
	html : function(html){
		if(html){
			this.innerHTML = html;
		} else {
			return this.innerHTML;
		}
	},
	
	/**
	@Name: sb.element.isChildOf
	@Description: Checks to see if the element is a child of whatever element it is passed. 
	@Param: Object/String of You can specify the parent element as an id string e.g. #parent or as an element object reference
	@Return: Boolean True is the element is a child of the parent specified and false if it is not
	@Example:
	myElement.isChildOf('#parent');
	*/
	isChildOf : function (of) {
		var el;
		if(sb.$(of)){
			el = this;
			while(el.parentNode) {
				if(el.parentNode == sb.$(of)) {return true;}
			}
		}
		return false;
	},
	
	/**
	@Name: sb.element.mv
	@Description: Moves an element to a specific x, y and z position either absolutly or relatively is specified
	@Param: Number x The x position to move the element to
	@Param: Number y The y position to move the element to
	@Param: Number z The zIndex to move the element to
	@Param: String pos The positioning type to use, defaults to absolute
	
	@Example:
	myElement.mv(200,200,999);
	//move sthe node to absolute position 200,200 and a zIndex of 999
	*/
	mv : function(x,y,z,pos){
		pos = pos ||'absolute';
		if(this.style.position===''){this.style.position= pos;}
		this.style.left = x+'px';
		this.style.top = y+'px';
		this.style.zIndex = z;
		this.getBounds();
	},
	
	/**
	@Name: sb.element.opacity
	@Description: Sets the opacity of the element
	@Param: Float o Optional arguemnt specifies the percentage ocapcity.  0.0 is 100% transparent and 1.0 is 100% opaque.  If no argument is supplied it returns the current opacity of the element
	@Return: Float The current opacity of element is returned if no argument is given.
	@Example:
	myElement.opacity(0.3);
	//sets the elements opacity to 0.3
	
	myElement.opacity();
	//returns 0.3
	*/
	opacity : function(o){
		if(String(o).isNumeric()){
			sb.styles.write(this, 'opacity', o);
			
		} else {
			
			return this.css('opacity') || 1;
		}
	},
	
	/**
	@Name: sb.element.remove
	@Description: Removes an element from the DOM

	@Example:
	myElement.remove();
	*/
	remove : function(){
		if(typeof this.parentNode !='undefined'){
			this.parentNode.removeChild(this);
		}
	},
	
	/**
	@Name: sb.element.removeClassName
	@Description: Removes a className from the elements className array.  Elements can have more than one className
	@Param: String c Specified the className to remove from the element

	@Example:
	myElement.removeClassName('redStripe');
	*/
	removeClassName : function(c){
		sb.dom.className.remove.call(this, c);
	},
	
	/**
	@Name: sb.element.replace
	@Description: Replaces an element with another element in the DOM
	@Param: Object/String A reference to another DOM node, either as a string which is passed to the sb.$ function or as an element reference
	@Example:
	myElement.replace('#myOtherElement');
	*/
	replace : function(node){
		node = sb.$(node);
		if(typeof node.parentNode !='undefined'){
			node.parentNode.replaceChild(this, node);
		}
		node = null;
	},
	
	/**
	@Name: sb.element.show
	@Description: Switches an elements display back to whatever its default was.  Tis is the reciprocal method for myElement.hide();
	
	@Example:
	myElement.show();
	*/
	show : function(){
		this.style.display = (this.css('display')=='none') ? 'block' : this.css('display'); 
	},
	
	/**
	@Name: sb.element.styles
	@Description: 
	@Param: Object params An object with css style/value pairs that are applied to the object
	
	@Example:
	myElement.styles({
		backgroundColor : '#000000',
		fontSize : '18px',
		border : '1px solid #FF0000'
	});
	*/
	styles : function(params){
		try{
			sb.objects.importProperties(params, this.style);
		} catch(e){}
	},
	
	/**
	@Name: sb.element.toggle
	@Description: Switches an object's display between hidden and default
	
	@Example:
	myElement.toggle();
	*/
	toggle : function(){
		if(this.style){
			this.style.display = (this.css('display') ==='none') ? '' : 'none';
		}
	},
	
	/**
	@Name: sb.element.toggleClassName
	@Description: toggles a className on the sb.element, using this methods sb.element instances can have multiple classNames
	@Param: String c The classname to toggle
	@Example:
	myElement.toggleClassName('redStripe');
	*/
	toggleClassName : sb.dom.className.toggle,
	
	typeOf : function(){
		return 'sb.element';
	},
	
	/**
	@Name: sb.element.unsetAttributes
	@Description: Unsets the attributes of the element that are in the argument array
	@Param: Array a A list of strings which represent the values to unset
	@Example:
	myElement.unsetAttributes(['friend', 'nextKin']);
	*/
	unsetAttributes : function(a){
		var t=this;
		a.forEach(function(v){
			t.setAttribute(v, '');
		});
	},
	
	/**
	@Name: sb.element.wh
	@Description: Sets the width and height of the element
	@Param: String/Number w The element width desired, can be specified as a number e.g. 100 or as a percent '100%'
	@Param: String/Number h The element height desired, can be specified as a number e.g. 100 or as a percent '20%'
	@Example: 
	myElement.wh(100, 200);
	*/
	wh : function(w,h){
		this.css('width', w);
		this.css('height', h);
	},
	
	/**
	@Name: sb.element.xy
	@Description: Sets the left and top value of an element
	@Param: Number x The element x position desired
	@Param: Number y The element y position desired
	@Example:
	myElement.xy(20, 40);
	*/
	xy : function(x,y){
		this.style.left = x+'px';
		this.style.top = y+'px';
	}
};

/**
@Name: sb.functions
@Description: These propterties and methods are prototyped to the native Function object when globals are enabled.  Otherwise use call.
*/
sb.functions = {
	
	/**
	@Name: sb.functions.fireAfterDelay
	@Description: Fires a function after waiting the specified number of seconds or milliseconds.  You can run this on any function if globals are not disabled.  If they are you would need to use the call method demostrated below.
	@Return: Object A sb.fireAfterDelay instance which has an abort() method 
	@Example:
	//with globals on
	function hello(){
		alert('hello');
	}
	
	//fires the function after 10 seconds
	var myEvent = hello.fireAfterDelay({seconds : 10});
	
	//this would abort the event before it ran if executed before the ten seconds were up
	myEvent.abort();
	
	//with globals off
	sb.functions.fireAfterDelay.call(hello,1);
	*/
	fireAfterDelay : function(o){
	
		o.func = this;
		return new sb.fireAfterDelay(o);
		
	},
	
	/**
	@Name: sb.functions.fireRepeatedly
	@Description: Fires a function repeatedly on a timeout expressed in milliseconds or seconds
	@Param: Object o Timeout can be in either seconds or milliseconds
	milliseconds - the time between firing in milliseconds
	seconds - The time between firing in seconds
	@Return: Object Returns an object that has a stop method to end the repeated firing
	@Example:
	//with globals on
	function hello(){
		alert('hello');
	}
	
	var myRepeatFiring = hello.fireRepeatedly({seconds : 1})
	//to stop it
	//myRepeatFiring.abort();
	
	//with globals off
	sb.functions.fireRepeatedly.call(hello,1);
	*/
	fireRepeatedly : function(o){
		var milliseconds = o.milliseconds || 1000;
		if(o.seconds){
			milliseconds = o.seconds*1000;
		}
		
		var evt = window.setInterval(this, milliseconds);
	 	return {
	 		abort : function(){window.clearInterval(evt);}
	 	};
	},
	
	
	/**
	@Name: sb.functions.extend
	@Description: When globals are enabled this is prototyped to the native Function prototype allowing  you to extend the properties and methods of any function easily.  These properties and methods are applied to all insatnces of the contructor function.
	@Example:
	//create a constructor function
	var car = function(){};
	
	//to add a beep method to all car instances
	car.extend({
		numberOf : 0,
		make : function(){
			cars.numberOf++;
			return new this();
		}
	});
	
	//instantiate it
	var myCar = new car();
	
	//now you can use
	alert(car.numberOf);
	
	*/
	extend: function(o){
		sb.objects.importProperties(o, this);
	},
	
	/**
	@Name: sb.functions.extendInstances
	@Description: When globals are enabled this is prototyped to the native Function prototype allowing you to extend the prototype properties and methods easily.  These properties and methods are applied to all insatnces of the contructor function.
	@Example:
	//create a constructor function
	var car = function(){};
	
	//extend the constructor function by adding properties with extend.  Essentially this creates static properties on the constructor.
	car.extend({
		//lets count the number of cars made
		numberOf : 0,
		
		//return a new call and incremement the total number of cars
		create : function(){
			car.numberOf++;
			return new this();
		}
	});
	
	//instantiate it
	var myCar = car.create();
	
	//now you can use
	alert(car.numberOf);
	
	*/
	extendInstances : function(o){
		sb.objects.importPropertiesAsPrototypes(o, this);
	},
	
	/**
	@Name: sb.functions.setThis
	@Description: returns a function that calls the function and applies the this (1st argument) and arguments (all other arguments), great for setting scope of this on a method of an instance
	@Example:
	//a constructor
	var test = function(){};

	test.prototype = {
		
		jump : function(str){
			sb.consol.log(str);
		},
		
		begin : function(){
		
			window.setInterval(this.jump.setThis(this, 'hello'), 1000);
		}
	};
			*/
	setThis : function(){
		var func = this, args = sb.toArray(arguments), obj = args.shift();
		  return function() {
		    return func.apply(obj, args);
		  };
	}
		
};

/**
@Name: sb.arrays
@Description: These are used as native array prototypes if globals are not turned off.  Even when globals are turned off methods every, filter, forEach, indexOf, lastIndexOf, map, reduce, and reduceRight all are global if not already defined by you browser as part of javascript 1.6-1.8.  This allows you to use these javascript array method in any browser.
*/
	
sb.arrays = {
	
	/**
	@Name: sb.arrays.avg
	@Description:used to determine the average value from an array of values
	@Return: Number The average value
	@Example:
	//with globals on
	var myArray = [1,3,4,5];
	var average = myArray.avg();
	average = 3.25
	
	//with globals off
	sb.arrays.avg.call(myArray);
	*/
	avg : function(){
		var tl = sb.arrays.sum.call(this);
		return tl/this.length;
	},

	/**
	@Name: sb.arrays.copy
	@Description: makes a copy of an array
	@Return: Array A new array with the same value as the one it is making a copy of
	@Example:
	//with globals on
	var myNewArray = myArray.copy();
	
	//with globals off
	var myNewArray = sb.arrays.copy.call(myArray);
	*/
	copy : function(){
		return this.filter(function(){return true;});
	},

	/**
	@Name: sb.arrays.empty
	@Description: empties an array
	@Return: returns the array emptied
	@Example:
	//with globals on
	myArray.empty();
	
	//with globals off
	sb.arrays.empty.call(myArray);
	*/
	empty : function(){
		this.length =0;
		return this;
	},
	
	/**
	@Name: sb.arrays.every
	@Description: Checks to see if every value in an array returns true from the function provided
	@Param: Function func An anonymous function or a reference to a function.  Array data is passed to the function for each vlaue in the array.  Values passed are v,k,a which stand for value, key and array.  v is the current value as it loops through the array, k is the current key as it loops through tthe array and a is the entire array.
	@Return: Boolean True or False
	@Example:

	function over10(val, key, arr) {
		if(val > 10){return true;}
	}

	var myArray = [5, 10, 15];
	myArray.every(over10);
	returns false because not every number in the array is over 10
	
	*/
	every : function(func){
		var k;
		if(typeof func == 'function'){
			for(k=0;k<this.length;k++){
				
				if(func(this[k], k, this) !== true){
					
					return false;
				}
			}
			return true;
		}
	},
	
	/**
	@Name: sb.arrays.filter
	@Description: Filters values out of an array that do not return true from the test function.
	@Param: Function func An anonymous function or a reference to a function.  Array data is passed to the function for each vlaue in the array.  Values passed are v,k,a which stand for value, key and array.  v is the current value as it loops through the array, k is the current key as it loops through tthe array and a is the entire array.
	@Return: Array The new array contains only the values which were true.
	@Example:
	function over10(val, key, arr) {
		if(val > 10){return true;}
	}

	var myArray = [5, 10, 15];
	var newArray = myArray.filter(over10);
	//returns the array 10,15 because those two values are >=10
	
	*/
	filter : function(func){
		var n=[];
		if(typeof func == 'function'){
			this.forEach(function(v,k,arr){
				if(func(arr[k], k, arr) === true){
					n.push(v);
				}
			});
		}
		
		return n;
		
	},
	
	/**
	@Name: sb.arrays.forEach
	@Description: Runs a function on every value in an array
	@Param: Function func An anonymous function or a reference to a function.  Array data is passed to the function for each vlaue in the array.  Values passed are v,k,a which stand for value, key and array.  v is the current value as it loops through the array, k is the current key as it loops through tthe array and a is the entire array.
	@Example:
	//with globals on
	function addOne(val,key,arr){
		val = val+1;
		
	}
	var myArray=[1,2,3];
	myArray.forEach(addOne);
	
	//afterwards myArray = [2,3,4]
	*/
	forEach : function(func){
		var k;
		if(typeof func == 'function'){
			for(k=0;k<this.length;k++){
				func(this[k], k, this);
			}
		}
	},
	
	/**
	@Name: sb.arrays.inArray
	@Description: Checks to see if a value is contained in the array
	@Param: Object/String/Number val Method checks to see if val is in the array
	@Return: Boolean True or False
	@Example:
	//with globals on
	var myArray = [1,2,3];
	var answer = myArray.inArray(2);
	//answer is true
	
	//without globals on
	sb.arrays.inArray.call(myArray, 2);
	*/
	inArray : function(val){
		return this.some(function(v){return v===val;});
	},
	
	/**
	@Name: sb.arrays.indexOf
	@Description: Finds the index of the value given within the array.  Return the position of the first matching value.  Rememeber that array start at 0.
	@Param: Object/String/Number val The value you want to search for in the array. 
	@Return: Integer
	@Example:
	
	var myArray = [1,2,3,'a','b'];
	var answer = myArray.indexOf('a');
	//answer is 3
	
	*/
	indexOf : function(val){
		for(var k=0;k<this.length;k++){
			if(this[k] == val){
				return k;
			}
		}
		return -1;
	},
	
	/**
	@Name: sb.arrays.inject
	@Description: Finds the index of the value given within the array.  Return the position of the first matching value.  Rememeber that array start at 0.
	@Param: Object/String/Number val The value to inject into the array. 
	@Return: Integer
	@Example:
	//with globals on
	var myArray = ['zero', 'one', 'two'];
	var answer = myArray.inject(1, 'bagel');

	//answer is now ['zero', 'bagel', 'one', 'two'];
	//without globals
	
	sb.arrays.inject.call(myArray, 1, 'bagel');
	*/
	inject : function(index, val){
		if(index <0){return this;}
		var a = this.slice(0, index), b = this.splice(index, this.length-index);
		
		a[index] = val;
		return a.concat(b);
	},
	
	/**
	@Name: sb.arrays.lastIndexOf
	@Description: Finds the last index of the value given within the array.Rememeber that array start at 0.
	@Param: Object/String/Number val The value you want to search for in the array. 
	@Return: Integer
	@Example:
	
	var myArray = [1,2,3,2];
	var answer = myArray.lastIndexOf(2);
	//answer is 3
	*/
	lastIndexOf : function(val){
		var p=-1,k;
		for(k=0;k<this.length;k++){
			if(this[k] == val){
				p=k;
			}
		}
		return p;
	},
	
	/**
	@Name: sb.arrays.map
	@Description: Runs a function on every item in the array and returns the results in an array.
	@Param: Function func The function you want applied run on every value in the array.  It is automatically passed the current (value, key, and array) as arguments on eqach loop through the array.  The function can be either a reference to a global function or an inline anonymouse function.
	@Return: Array A new array with each value mapping to the result of the original arrays value after is is passed through the function specified.
	@Example:
	function addTen(val, key, array) {
		return val+10;
	}
	
	var myArray = [5, 10, 15];
	var answer = myArray.map(addTen);
	//answer = [15, 20, 25];
	
	*/
	map : function(func){
		var n=[];
		if(typeof func == 'function'){
			this.forEach(function(v,k,a){n.push(func(v,k,a));});
		}
		return n;
	},
	
	/**
	@Name: sb.arrays.max
	@Description: Finds the maximum value in an alpha/numeric array.  Sorts alphanumerically and chooses the highest.  Number have preference over letters, so 1 is higher than 'apple'
	
	@Return: String/Number Returns the max value
	@Example:
	
	var myArray = [5, 10, 15];
	var answer = myArray.max();
	//answer = 15;
	
	//without globals on
	sb.arrays.max.call(myArray);
	*/
	max : function(){
		 var max=this[0];
		 sb.arrays.forEach.call(this, function(v){max=(v>max)?v:max;});
		 return max;
	},
	
	/**
	@Name: sb.arrays.min
	@Description: Finds the maximum value in an alpha/numeric array.  Sorts alphanumerically and chooses the lowest.  Numbers are higher than letters, so 'apple' is lower than 1
	
	@Return: String/Number Returns the min value
	@Example:
	
	var myArray = [5, 10, 15];
	var answer = myArray.min();
	//answer = 5;
	
	//without globals on
	sb.arrays.min.call(myArray);
	*/
	min : function(){
		 var min=this[0];
		 sb.arrays.forEach.call(this, function(v){min=(v<min)?v:min;});
		 return min;
	},
	
	/**
	@Name: sb.arrays.mostCommon
	@Description: Finds the most common value in an array.  If no value is most common then it returns false.
	
	@Return: String/Number/Object Returns the most common value in the array or false if no value is most common.
	@Example:
	
	var myArray = [5, 10, 15];
	var answer = myArray.mostCommon();
	//answer = false;
	
	var myArray = [5, 5, 10, 15];
	var answer = myArray.mostCommon();
	//answer = 5;
	
	//without globals on
	sb.arrays.mostCommon.call(myArray);
	*/
	mostCommon : function(){
		var count=0,max=0,num=0,mode=0;
		this.sort();
		sb.arrays.forEach.call(this, function(v){
			
			if(num != v){
				num=v;
				count=1;
			} else {
				count++;
			}
			
			if(count > max){
				max = count;
				mode = num;
			}
		});
		if(max ==1){mode = false;}
		return mode;
	},
	
	/**
	@Name: sb.arrays.natsort
	@Description: Sort the array values in a natural alpha numeric way so that 1,10,2,3,4,5 becomes 1,2,3,4,5,10
	@Param: Number direction Accepts either 1 for ascending order or -1 for decending order. If not specified that ascending order is the default. 
	@Return: Array Returns The array sorted naturally.
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	var answer = myArray.natsort();
	//answer = [1,2,3,4,5,10];
	
	//without globals on
	sb.arrays.natsort.call(myArray);
	*/
	natsort : function(direction){
		direction = (direction ==-1) ? -1 : 1;
		if(direction == -1){
			this.sort(function(a,b){return (b-a);});
		} else {
			this.sort(function(a,b){return (a-b);});
		}
		return this;
	},
	
	/*
	@Name: sb.arrays.pointer
	@Description: Used to keep track of the array key we are referenceing with next, prev, end, current, rewind
	
	*/
	pointer : 0,
	
	/*
	@Name: sb.arrays.current
	@Description: Returns the array value of the key we are currently on as referenced by the pointer.  Starts at 0 and chanegs base don use of next(), prev(), rewind(), end()
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.current(); //returns 1 at first
	
	//after manipulations it changes reference based on the pointer
	myArray.next();
	
	//now it is the next one
	myArray.current(); //returns 10
	*/
	current : function(){
		return this[this.pointer];
	},
	
	/*
	@Name: sb.arrays.end
	@Description: Returns the array value of the last key and sets the array's pointer to that key
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.end(); //sets the pointer to the last array key and returns the value, in this case 5
	
	*/
	end : function(){
		this.pointer = this.length-1;
		return this[this.pointer];
	},
	
	/*
	@Name: sb.arrays.first
	@Description: Returns the first value in the array without moving the pointer.
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.first(); //returns 1
	
	*/
	first : function(){
		return this[0];
	},
	
	/*
	@Name: sb.arrays.last
	@Description: Returns the last value of the array without moving the pointer.
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.last(); //returns 5
	
	*/
	last : function(){
		return this[this.length-1];
	},

	/*
	@Name: sb.arrays.next
	@Description: Returns the next value of the array and moves the pointer forward to it.
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.current(); //returns 1
	myArray.next(); //returns 10
	myArray.current(); //returns 10
	*/
	next : function(){
		this.pointer +=1;
		return this[this.pointer];
	},
	
	/*
	@Name: sb.arrays.rewind
	@Description: Returns the first value of the array and moves the pointer back to the beginning.
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.rewind(); //returns 1
	*/
	rewind : function(){
		this.pointer=0;
		return this[this.pointer];
	},

	
	/*
	@Name: sb.arrays.prev
	@Description: Returns the next value of the array and moves the pointer forward to it.
	@Example:
	
	var myArray = [1,10,2,3,4,5];
	myArray.end(); //returns 5
	myArray.prev(); //returns 4
	myArray.current(); //returns 4
	*/
	prev : function(){
		this.pointer -=1;
		return this[this.pointer];
	},
	
	/**
	@Name: sb.arrays.random
	@Description: Grab a random value from the array.  The value is randomly selected each time the value is run.
	@Return: Object/String/Number Returns a random value from the array.  Type is the same as the value.
	@Example:
	var myArray = [1,10,2,3,4,5];
	var answer = myArray.random();
	//answer = 4; //<--could change each time
	
	//without globals on
	sb.arrays.random.call(myArray);
	*/
	random : function(){
 		return this[sb.math.rand(0,this.length)];
	},
	
	/**
	@Name: sb.strings.range
	@Description: Determines the range of values in a numeric array.  That is the highest value minus the loweest value
	@Return: Number The range of the values
	@Example:
	var myArray = [1,10,2,3,4,5];
	var answer = myArray.range();
	//answer = 9; //<--the difference between 10 (the highest number) and 1 (the lowest number)
	
	//without globals on
	sb.arrays.range.call(myArray);
	*/
	range : function(){
		var a = this.natsort();
		return this[a.length-1]-a[0];
	},
	
	/**
	@Name: sb.arrays.remove
	@Description: Removes a value or a set of values from an array.
	@Param: values Array If passed an array of values, all the values in the argument array are removed from the array being manipulated
	@Param: value Object/String/Number If a single object, string, number, etc is passed to the function than only that value is removed.
	@Return: Array Returns the array minus the values that were specified for removal.
	@Example:
	var myArray = [5, 10, 15];
	var answer = myArray.remove([10,5]);
	//answer =[15];
	
	var answer = myArray.remove(5);
	//answer =[10, 15];
	
	//without globals on
	sb.arrays.remove.call(myArray, [10, 15]);
	*/
	remove : function(values){
		
		return this.filter(function(v){
			if(sb.typeOf(values) !='array'){
				return v != values;
			} else {
				return !sb.arrays.inArray.call(values, v);
			}
		});
	},
	
	/**
	@Name: sb.arrays.shuffle
	@Description: Shuffle the values in an array randomly
	@Param: values Array If passed an array of values, all the values in the argument array are removed from the array being manipulated
	@Param: value Object/String/Number If a single object, string, number, etc is passed to the function than only that value is removed.
	@Return: Array Returns the array minus the values that were specified for removal.
	@Example:
	var myArray = [5, 10, 15];
	myArray.shuffle();
	//myArray =[10, 15, 5]; //<-could change each time

	//without globals on
	sb.arrays.shuffle.call(myArray);
	*/
	shuffle : function(){
		var i=this.length,j,t;
		
		while(i--)
		{
			j=Math.floor((i+1)*Math.random());
			t=this[i];
			this[i]=this[j];
			this[j]=t;
		}
		return this;
	},
	
	/**
	@Name: sb.arrays.sum
	@Description: Add up the values in an array

	@Return: Number Returns the sum of all the values in an array
	@Example:
	var myArray = [5, 5, 10, 15];
	var answer = myArray.sum();
	//answer =35;

	//without globals on
	sb.arrays.sum.call(myArray);
	*/
	sum : function(){
		var val = 0;
		sb.arrays.forEach.call(this, function(v){
			val +=v;
		});
		return val;
	},
	
	/**
	@Name: sb.arrays.unique
	@Description: Removes duplicate values from an array

	@Return: Array Returns an array of unique values from the original array
	@Example:
	var myArray = [5, 5, 10, 15];
	var answer = myArray.unique();
	//answer =[5,10,15];

	//without globals on
	sb.arrays.unique.call(myArray);
	*/
	unique : function(){
		var n=[];
		sb.arrays.forEach.call(this, function(v){if(!sb.arrays.inArray.call(n, v)){n.push(v);}});
		return n;
	},
	
	/**
	@Name: sb.arrays.regex
	@Description: Limit the values of an array to Values that do  not match the regex expression are excluded

	@Return: Array Returns an array of values that match the regex from the original array
	@Example:
	var myArray = [5, 10, 15];
	var answer = myArray.regex(/\d{2}/);
	//answer = [10,15] //because they are at least two digits as specified in the regex \d{2}
	*/
	regex: function(expression) {
		
		return sb.arrays.filter.call(this, function(v, k, a) {
		 	if(v.toString().match(expression)){return true;}
		});
		
	},

	/**
	@Name: sb.arrays.some
	@Description: Similar to sb.arrays.every - if some of the function results are true then some returns true
	@Param: Function func A function that every value of the array is passed to.  The function is passed (val, key, arr) on every pass of the loop.
	@Return: Boolean Returns true if some of the values return true when run through the function provided
	
	@Example:
	function isAboveFive(val, key, arr){
		if(val >5) {return true;}
	}
	var myArray = [5, 10, 15];
	var answer = myArray.some(isAboveFive);
	//answer = true //because some values return true when passed through the isAboveFive function
	
	*/
	some : function(func){
		var k;
		if(typeof func == 'function'){
			for(k=0;k<this.length;k++){
				if(func(this[k], k, this) === true){
					return true;
				}
			}
			return false;
		}
	}
	
};

/**
@Name: sb.arrays.each
@Description: An alias for sb.arrays.forEach
*/
sb.arrays.each = sb.arrays.forEach;
//ADD DOCS


/**
@Name: sb.nodeList
@Description: An extended form of an sb.array for an array holding DOM nodes.
*/
sb.nodeList = function(){
	var arr = [];
	sb.objects.importProperties(this, arr);
	
	return arr;
}

sb.nodeList.prototype = {
	
	/**
	@Name: sb.nodeList.prototype.addClassName
	@Description: Adds the specified className to every node in the nodeList
	@Example: 
	s$('ol li').addClassName('big');
	*/
	addClassName : function(name){
		this.forEach(function(node){
			node.addClassName(name);
		});
		return this;
	},
	
	
	/**
	@Name: sb.nodeList.prototype.setAttribute
	@Description: Sets an attribute to the specified value in every node in the nodeList
	@Example: 
	s$('ol li').addClassName('big');
	*/
	setAttribute : function(a,v){
		this.forEach(function(node){
			node.setAttribute(a,v);
		});
		return this;
	},
	
	/**
	@Name: sb.nodeList.prototype.css
	@Description: Sets a css attribute to the specified value for every node in the nodeList
	@Example: 
	s$('ol li').css('background-color', 'red');
	*/
	css : function(p,v){
		this.forEach(function(node){
			node.css(p,v);
		});
		return this;
	},
	
	/**
	@Name: sb.nodeList.prototype.hide
	@Description: Sets every element in the nodeList's display to none
	@Example: 
	s$('ol li').hide();
	*/
	hide : function(){
		this.forEach(function(node){
			node.hide();
		});
		return this;
	},
	
	/**
	@Name: sb.nodeList.prototype.html
	@Description: Sets the innerHTML for every element in the nodeList to the value given
	@Example: 
	s$('ol li').html('hello there');
	*/
	html : function(html){
		this.forEach(function(node){
			node.html(html);
		});
		return this;
	},
	
	/**
	@Name: sb.nodeList.prototype.parents
	@Description: returns a new nodeList that is the parents of the current nodes in the nodeList
	@Example: 
	s$('ol li').parents();
	*/
	parents : function(){
		var parents = [];
		this.forEach(function(node){
			parents.push(node.parentNode);
		});
		return sb.s$(parents);
	},
	
	/**
	@Name: sb.nodeList.prototype.removeClassName
	@Description: returns a new nodeList that is the parents of the current nodes in the nodeList
	@Example: 
	s$('ol li').removeClassName('big');
	*/
	removeClassName : function(name){
		this.forEach(function(node){
			node.removeClassName(name);
		});
		return this;
	},
	
	/**
	@Name: sb.nodeList.prototype.show
	@Description: Sets every element in the nodeList's display to '', whatever it defaults to
	@Example: 
	s$('ol li').show();
	*/
	show : function(){
		this.forEach(function(node){
			node.show();
		});
		return this;
	},
	
	/**
	@Name: sb.nodeList.prototype.toggle
	@Description: Toggle's the display between show() and hide()
	@Example: 
	s$('ol li').toggle();
	*/
	toggle : function(){
		this.forEach(function(node){
			node.toggle();
		});
		return this;
	}
	
};

/**
@Name: sb.nodeList.prototype.addClass
@Description: An alias for sb.nodeList.prototype.addClassName
*/
sb.nodeList.prototype.addClass = sb.nodeList.prototype.addClassName;

/**
@Name: sb.nodeList.prototype.attr
@Description: An alias for sb.nodeList.prototype.setAttribute
*/
sb.nodeList.prototype.attr = sb.nodeList.prototype.setAttribute;

/**
@Name: sb.nodeList.prototype.removeClass
@Description: An alias for sb.nodeList.prototype.removeClassName
*/
sb.nodeList.prototype.removeClass = sb.nodeList.prototype.removeClassName;

/*
s$applyGroupProperties : function(nodes){
		return nodes.map(function(node){
					
				sb.objects.importPrototypesAsProperties(sb.element, node);
				node.getBounds();
				return node;
			});
			
			nodes.addClassName = function(name){
				this.forEach(function(node){
					node.addClassName(name);
				});
				return nodes;
			};
			nodes.addClass = nodes.addClassName;
			
			nodes.setAttribute = function(a,v){
				this.forEach(function(node){
					node.setAttribute(a,v);
				});
				return nodes;
			};
			nodes.attr = nodes.setAttribute;
			
			nodes.css = function(p,v){
				this.forEach(function(node){
					node.css(p,v);
				});
				return nodes;
			};
			
			nodes.hide = function(){
				this.forEach(function(node){
					node.hide();
				});
				return nodes;
			};
			
			nodes.html = function(html){
				this.forEach(function(node){
					node.html(html);
				});
				return nodes;
			};
			
			nodes.parents = function(){
				var parents = [];
				this.forEach(function(node){
					parents.push(node.parentNode);
				});
				return s$(parents);
			};
			
			nodes.removeClassName = function(name){
				this.forEach(function(node){
					node.removeClassName(name);
				});
				return nodes;
			};
			nodes.removeClass = nodes.removeClassName;
			
			nodes.show = function(){
				this.forEach(function(node){
					node.show();
				});
				return nodes;
			};
			
			nodes.toggle = function(){
				this.forEach(function(node){
					node.toggle();
				});
				return nodes;
			};
		}
	}
	
	*/

/**
@Name: sb.math
@Description: Simple math function used in the surebert base code, more complex math functions are stored in sb.math.js
*/
sb.math = {

	/**
	@Name: sb.math.rand
	@Description: Used to generate a random number between a min and max value
	@Param: min Number The minimum value to return
	@Param: max Number The maximum value to return
	@Return Number Returns a random number between min and max
	@Example
	var x = sb.math.rand(1,10);
	*/
	rand : function(min,max){
		min = min || 0;
		max = max || 100;
		return Math.floor(Math.random()*max+min);
	},
	
	/**
	@Name: sb.math.hex2dec
	@Description: converts a hex value to a decimal value.  Opposite of sb.math.dec2hex
	@Param: hex String  The Hex value to convert to decimal
	@Return Number Returns the decimal value that represents the hex string
	@Example
	var x = sb.math.hex2dec('FF');
	//x=255
	*/
	hex2dec : function(hex){
		var val = parseInt(hex,16);
		val = (typeof(val) =='number') ? val : 0;
		return val;
	},
	
	/**
	@Name: sb.math.dec2hex
	@Description: converts a decimal value to a hex value
	@Param: dec String  The decimal value to convert to hex
	@Return Number Returns the hex value that represents the decimal number
	@Example
	var x = sb.math.dec2hex(255);
	//x='FF'
	*/
	dec2hex: function(dec){
		var hex =  dec.toString(16);
		hex = (hex.length <2)? hex+hex : hex;
		return hex;
	}
};

/**
@Name: sb.strings
@Description: String manipulation methods - these are prototyped to the native Strings when globals are not disabled
*/
sb.strings = {
	
	/**
	@Name: sb.strings.basename
	@Description: Grabs the basename from a url
	@Return: String The filename part of the original string
	@Example:
	var myString = 'http://www.google.com/logo.gif';
	var newString = myString.basename();
	//newString = 'logo.gif';
	
	//without globals
	sb.strings.basename.call(myString);
	*/
	basename : function(){
		var re = new RegExp("/\\/", "g");
		var str = this.replace(re, "/");
		var filename=str.split("/");
		return filename[(filename.length - 1)];
	},
	
	/**
	@Name: sb.strings.br2nl
	@Description: Converts HTML line breaks "<br />" to new lines "\n"
	@Return: String The original string but replaces breaks with actual new lines
	@Example:
	var myString = 'hello<br />there';
	var newString = myString.br2nl();
	//newString = "hello\nthere";
	
	//without globals
	sb.strings.br2nl.call(myString);
	*/
	br2nl : function(){
		var re = new RegExp("<[br /|br]>", "ig");
		return this.replace(re, "\n");
	},
	
	/**
	@Name: sb.strings.cleanFileName
	@Description: Cleans a filename up making it safe for upload, removes spaces, swicthes to camelStyle and strips extraneos punctuation
	@Return: String The original string but replaces breaks with actual new lines
	@Example: 
	var myString = 'hello there,, file . jpg';
	var newString = myString.cleanFileName();
	//newString = 'helloThereFile.jpg'
	
	//without globals
	sb.strings.cleanFileName.call(myString);
	*/
	cleanFileName : function(){
		var ext = this.match(/\.\w{2,3}$/);
		var str = this.replace(/ext/, '');
		str = str.replace(/\.\w{2,3}$/, '');
		str = str.replace(/[^A-Z^a-z^0-9]+/g, ' ');
		str = str.ucwords();

		str = str.replace(/ /g, '');
		str +=String(ext).toLowerCase();
		return str;
	},

	/**
	@Name: sb.strings.empty
	@Description: Checks to see if a string is empty or not
	@Return: Boolean Returns true if the string is empty, false otherwise
	@Example:
	var myString = '';
	var newString = myString.empty();
	//newString = true
	
	//without globals
	sb.strings.empty.call(myString);
	*/
	empty : function(){
		return (this ==='') ? true : false;
	},
	
	/**
	@Name: sb.strings.escapeHTML
	@Description: Checks to see if a string is empty or not
	@Return: Boolean Returns true if the string is empty, false otherwise
	@Example:
	var myString = '<p>hello</p>';
	var newString = myString.escapeHTML();
	//newString = '&lt;p&gt;hello&lt;/p&gt;'
	
	//without globals
	sb.strings.escapeHTML.call(myString);
	*/
	escapeHTML : function(){
		var str = this.replace(/</g, '&lt;');
		return str.replace(/>/g, '&gt;');
	},
	
	/**
	@Name: sb.strings.hex2rgb
	@Description: Converts a string from hex to rgb, used with sb.styles
	@Param: Boolean If true the value is returned as an array [r,g,b]
	@Return: String the hex color string as rgb
	@Return: Array the hex color string as an array [r,g,b]
	@Example:
	var myString = '#FFFFFF';
	var newString = myString.hex2rgb();
	//newString = 'rgb(255,255,255)';
	
	var newString = myString.hex2rgb(true);
	//newString = '[255,255,255]';
	
	//without globals
	sb.strings.hex2rgb.call(myString);
	*/
	hex2rgb : function(asArray){
		var hex = sb.strings.stripWhitespace.call(this).replace("#", "");
		var rgb = parseInt(hex, 16); 
		var r   = (rgb >> 16) & 0xFF;
		var g = (rgb >> 8) & 0xFF; 
		var b  = rgb & 0xFF;
		
		if(asArray){
			return [r,g,b];
		} else {
			return 'rgb('+r+', '+g+', '+b+')';
		}
	},
	
	/**
	@Name: sb.strings.hilite
	@Description: Hilites a string within a text block
	@Param: String needle The text to find
	@Param: String className The className to use for hiliting overrides default yellow background style
	@Return: String witht he needle underlied and hilited
	@Example:
	var myString = 'There was a dog on earth';
	
	var newString = myString.hilite('dog');
	//newString = 'There was a <u style="backgroundColor:yellow;">dog</u> on earth';
	
	//without globals
	sb.strings.hilite.call(myString, 'dog');
	*/
	hilite : function(needle, className){
		className = (typeof className != 'undefined') ? ' class="'+className+'" ' : ' style="background-color:yellow;" ';
		
		var matches = new RegExp( "("+needle+")", "ig");
		return this.replace(matches, "<u "+className+">$1</u>");
	},
	
	/**
	@Name: sb.strings.isNumeric
	@Description: Checks to see if a string is numeric (a float or number)
	@Return: Boolean True if the the string represnts numeric data, false otherwise
	@Example:
	var myString = '12';
	
	var answer = myString.isNumeric();
	//answer = true
	
	//without globals
	sb.strings.isNumeric.call(myString);
	*/
	isNumeric : function(){
		if(sb.typeOf(this) == 'number'){
			return 1;
		}
		var dat,valid = "0123456789.",x;
	
		for (x=0;x<this.length;x++){ 
			dat = this.charAt(x); 
			if (valid.indexOf(dat) == -1){
				 return false;
			}
		}
		return true;
	},
	
	/**
	@Name: sb.strings.linkify
	@Description: Converts all URLs in a text block into actual html links
	@Param: String target The target to open the links in, defaults to blank
	@Return: String The original text withe links converted to HTML
	@Example:
	var myString = 'Here http://www.surebert.com is a great javascript toolkit';
	
	var newString = myString.linkify();
	//newString = 'Here <a href="http://www.surebert.com" target="_blank">::link::</a> is a great javascript toolkit';
	
	//without globals
	sb.strings.linkify.call(myString, target);
	*/
	linkify : function(target){
		target = target || '_blank';
		var match_url = new RegExp("(\s|\n|)([a-z]+?):\/\/([a-z0-9\-\.,\?!%\*_\#:;~\\&$@\/=\+]+)", "i");
		return this.replace(match_url, "<a href=\"$2://$3\" title=\"$2://$3\" target=\""+target+"\">::link::</a>");
		
	},
	
	/**
	@Name: sb.strings.ltrim
	@Description: Trims all white space off the left side of a string
	@Return: String The original text with whitespace removed from the left
	@Example:
	var myString = '           hello';
	
	var newString = myString.ltrim();
	//newString = 'hello';
	
	//without globals
	sb.strings.ltrim.call(myString);
	*/
	ltrim : function(){
		return sb.strings.trim.call(this, 'l');
	},
	
	/**
	@Name: sb.strings.nl2br
	@Description: Replaces all new line "\n" with HTML break returns "<br />"
	@Return: String The original text with lines returns converted to HTML line breaks
	@Example:
	var myString = "hello\nworld";
	
	var newString = myString.nl2br();
	//newString = 'hello<br />world';
	
	//without globals
	sb.strings.nl2br.call(myString);
	*/
	nl2br : function(){
		var re = new RegExp("\n", "ig");
		return this.replace(re, "<br />");
	},
	
	/**
	@Name: sb.strings.numpad
	@Description: Pads all numbers under 9 with a zero on the left
	@Return: String The original number padded to left with zero
	@Example:
	var myString = 9;
	
	var newString = myString.numpad();
	//newString = '09'
	
	//without globals
	sb.strings.numpad.call(myString);
	*/
	numpad : function(){
		return (this<=9) ? '0'+this : this;
	},
	
	/**
	@Name: sb.strings.rgb2hex
	@Description: Takes an rgb string and converts it to hex color rgb(255,255,255) -> #FFFFFF
	@Param: Number asArray If set to 1 then it returns the values as an array
	@Return: String A hex color string
	@Return: Array A hex color array ['FF', 'FF', 'FF']
	@Example:
	var myString = 'rgb(255,255,255)';
	
	var newString = myString.rgb2hex();
	//newString = '#FFFFFF'
	
	//without globals
	sb.strings.rgb2hex.call(myString);
	*/
	rgb2hex : function(asArray){
		if(!this.match(/^rgb/i)){return false;}
		
		var re = new RegExp('rgb\\((\\d{1,}),(\\d{1,}),(\\d{1,})\\)', "ig");
		var colors = re.exec(sb.strings.stripWhitespace.call(this));
		var r= parseInt(colors[1], 10).toString(16);
		var g= parseInt(colors[2], 10).toString(16);
		var b= parseInt(colors[3], 10).toString(16);
		
		r = (r.length<2) ? r+r : r;
		g = (g.length<2) ? g+g : g;
		b = (b.length<2) ? b+b : b;
		
		if(asArray){
			return [r,g,b];
		} else {
			return '#'+r+''+g+''+b;
		}
	},
	
	/**
	@Name: sb.strings.ltrim
	@Description: Trims all white space off the right side of a string
	@Return: String The original text with whitespace removed from the right
	@Example:
	var myString = 'hello              ';
	
	var newString = myString.rtrim();
	//newString = 'hello';
	
	//without globals
	sb.strings.rtrim.call(myString);
	*/
	rtrim : function(){
		return sb.strings.trim.call(this, 'r');
	},
	
	/**
	@Name: sb.strings.substrCount
	@Description: Returns the numbers of times a substring is found in a string
	@Param: String needle The substring to search for within the string
	@Param: Boolean caseInsensitive Optional - If true the search is case insensitive
	@Return: Number The number of times the substring is found in the original string
	@Example:
	var myString = 'hello world on earth';
	
	var answer = myString.substrCount('world');
	//answer = 1;
	
	var answer = myString.substrCount('World', true);
	//answer = 1;
	
	//without globals
	sb.strings.substrCount.call(myString);
	*/
	substrCount : function(needle, caseInsensitive){
		var matches, ig = (caseInsensitive === undefined) ? 'g' : 'ig';
			
		var re = new RegExp(needle, ig);
		matches = this.match(re);
		if(matches !==null){
			return matches.length;
		} else {
		 	return false;
		}
	},
	
	/**
	@Name: sb.strings.substrReplace
	@Description: Mimics php substrReplace replacing part of the string with another string from an index to a length
	@Param: String replaceWith The string to replace with
	@Param: Integer start The index to start at
	@Param: Integer start The length to replace
	@Return: String The string with the replacement
	@Example:
	var myString = 'hello world';
	var answer = myString.substrReplace('girl', 0, 4);
	answer = 'girlo world';
	
	//without globals
	sb.strings.substrReplace.call(myString, 'girl', 0, 4);
	*/
	substrReplace : function(replaceWith, start, length){
		return this.replace(this.substring(start, (start+length)), replaceWith );
	},

	/**
	@Name: sb.strings.strstr
	@Description: Returns true if the substring is found in the string
	@Param: String needle The substring to search for within the string
	@Return: Boolean True if the string is found and false if it isn't
	@Example:
	var myString = 'hello world on earth';
	
	var answer = myString.strstr('world');
	//answer = true;
	
	//without globals
	sb.strings.strstr.call(myString);
	*/
	strstr: function(needle){
		var f= this.indexOf(needle)+1;
		return (f===0) ? 0 :1;
	},
	
	/**
	@Name: sb.strings.stripHTML
	@Description: Removes all HTML tags from a string
	@Return: String The original string without any HTML markup
	@Example:
	var myString = 'hello <p>world</p> on earth';
	
	var newString = myString.stripHTML();
	//newString = 'hello world on earth'

	//without globals
	sb.strings.stripHTML.call(myString);
	*/
	stripHTML : function(){
		var re = new RegExp("(<([^>]+)>)", "ig");
		var str = this.replace(re, "");
		var amps = ["&nbsp;", "&amp;", "&quot;"];
		var replaceAmps =[" ", "&", '"'];
		for(var x=0;x<amps.length;x++){
			str = str.replace(amps[x], replaceAmps[x]);
		}
		
		re = new RegExp("(&(.*?);)", "ig");
		str = str.replace(re, "");
		
		return str;
	},
	
	/**
	@Name: sb.strings.stristr
	@Description: Case insensitive version of strstr. It is always case insensitive
	@Param: String needle The substring to search for within the string
	@Return: Boolean True if the string is found and false if it isn't
	@Example:
	var myString = 'hello world on earth';
	
	var answer = myString.stristr('world');
	//answer = true;
	
	var answer = myString.stristr('World');
	//answer = true;
	
	//without globals
	sb.strings.stristr.call(myString);
	*/
	stristr : function(needle){
		return sb.strings.strstr.call(this, needle, 1);
	},
	
	/**
	@Name: sb.strings.stripWhitespace
	@Description: Removes all whitespace from a string
	@Return: String The original string without any whitespace
	@Example:
	var myString = 'hello world on earth';
	
	var newString = myString.stripWhitespace();
	//newString = 'helloworldonearth'

	//without globals
	sb.strings.stripWhitespace.call(myString);
	*/
	stripWhitespace : function(){
		return this.replace(new RegExp("\\s", "g"), "");
	},
	
	/**
	@Name: sb.strings.toCamel
	@Description: Converts all dashes to camelStyle
	@Return: String The original string with dashes converted to camel - useful when switching between CSS and javascript style properties
	@Example:
	var myString = 'background-color';
	
	var newString = myString.toCamel();
	//newString = 'backgroundColor'

	//without globals
	sb.strings.toCamel.call(myString);
	*/
	toCamel : function(){
		return String(this).replace(/-\D/gi, function(m){
			return m.charAt(m.length - 1).toUpperCase();
		});
	},
	
	/**
	@Name: sb.strings.toElement
	@Description: Converts a string of HTML code to a sb.element for dom manipulation
	@Param: String parentNodeType The nodetype of the parent element returned if there is not already a single parent element for all elements contained in the html string - see example two - defaults to span if it is not given
	@Example:
	//would return div as the element with all its children
	sb.dom.HTMLToElement('<div id="joe"><p class="test">hey there</p></div>');
	//would return all elements grouped under a span because they have no comment parent
	sb.dom.HTMLToElement('<p class="test">hey there</p><p class="test2">hey there2</p>');
	*/
	toElement : function(parentNodeType){
		parentNodeType = parentNodeType || 'span';
		
		var temp = new sb.element({
			nodeName : parentNodeType,
			innerHTML : this
		});
		
		if(temp.childNodes.length >1){
			return sb.s$(temp);
		} else {
			return sb.s$(temp.firstChild);	
		}
		
	},
	
	/**
	@Name: sb.strings.toFile
	@Description: transfers the contents of a string to an external file.  Passes the string as POST data with a key name of data.  The data is escaped.  Make sure the external file referenced in the url property of the params object has permissions set to writeable.  There is an example file server side file log.php in the surebert extras folder.  It writes to log.txt in the same folder.
	@Param: Object params Parameters defining the data transfer
	url - The url of the file to send the string to. Must be a local file because of security in xmlHTTP object
	onpass - Function The function that fires if the file the data is sent to returns the number 1. You should set you server side file to print the number 1 if the data arrives.
	onfail - Function The optional function that fires anytime the server side file returns a 0.
	debug - Boolean If set to 1 then the process is debugged to the sb.consol if sb.developer.js is included in your source.
	@Example:
	var myString = 'Here is a string';
	myString.toFile({
		url : '../extras/log.php',
		onpass : function(){
			alert('you');
		},
		onfail : function(){
			alert('bad');
		},
		debug :1
		
	});
	*/
	toFile : function(params){
		if(typeof params.url =='undefined'){return;}
		
		params.debug = params.debug || 0;
		params.onpass= params.onpass || function(){};
		params.onfail= params.onfail || function(){};
		
		params.handler = params.handler || function(){};
		var xfer = new sb.ajax({
			url:params.url,
			debug:params.debug,
			format :'text',
			method:'post',
			data:'data='+escape(this),
			handler:function(r){
				if(r==1){
					params.onpass();
				} else {
					params.onfail();
				}
			
			}
			
		});
	
		xfer.fetch();
	},
	
	/**
	@Name: sb.strings.toNumber
	@Description: Converts a numeric string into an integer or float
	@Return: Float If the original value has a decimal in it, a float is returned
	@Return: Number If the original value is an interger, an integer value is returned
	@Example:
	var myString = '12';
	
	var num = myString.toNumber() +2;
	//num = 14 //without running toNumber it would return '122'

	var myString = '12.4';
	
	var num = myString.toNumber() +2;
	//num = 14.4 //without running toNumber it would return '12.42'

	//without globals
	sb.strings.toNumber.call(myString);
	*/
	toNumber : function(){
		if(this.match(/\./)){
			return parseFloat(this, 10);
		} else {
			return parseInt(this, 10);
		}
	},

	/**
	@Name: sb.strings.trim
	@Description: trims whitespace from both left and right side of a string, also used internally for ltrim and rtrim
	@Return: String The original string with whitespace removed from left and right side
	@Example:
	var myString = '    hello world       ';
	
	var newString = myString.trim();
	//newString = 'hello world'

	//without globals
	sb.strings.trim.call(myString);
	*/
	trim : function(side) {
		var str='';
		switch(side){
			case 'l':
				str= this.replace(/^\s+/, "");
				break;
			case 'r':
				str= this.replace(/\s+$/, "");
				break;
			default:
				str = this.replace(new RegExp("^\\s+|\\s+$", "g"), "");
		}
	
		return str;
	},
	
	/**
	@Name: sb.strings.ucwords
	@Description: Converts all first letters of words in a string to uppercase.  Great for titles.
	@Return: String The original string with all first letters of words converted to uppercase.
	@Example:
	var myString = 'hello world';
	
	var newString = myString.ucwords();
	//newString = 'Hello World'

	//without globals
	sb.strings.ucwords.call(myString);
	*/
	ucwords : function(){
		var arr = this.split(' ');
		
		var str ='';
		arr.forEach(function(v){
			str += v.charAt(0).toUpperCase()+v.slice(1,v.length)+' ';
		});
		return str;
	}
};


/**
@Name: sb.styles
@Description: Methods used to manipulate CSS and javascript styles, the basis of sb.css and sb.elements and s$ elements css method
*/
sb.styles = {
	
	
	/**
	@Name: sb.styles.numRules
	@Description: used internally
	*/
	numRules : 1,
	
	/**
	@Name: sb.styles.newSheets
	@Description: Used Internally
	*/
	sheets : [],
	
	/**
	@Name: sb.styles.pxProps
	@Description: Used Internally. These properties get 'px' added to their value if no measurement is specified
	*/
	
	pxProps : ['fontSize', 'width', 'height', 'padding', 'border', 'margin', 'left', 'top'],
	
	/**
	@Name: sb.styles.writeRule
	@Description: Used to write or override CSS rules on the page
	@Example:
	sb.styles.writeRule('p', 'background-color;red;');
	//writes the following into the pages css
	p{
		background-color:red;
	}
	
	sb.styles.writeRule('.box', 'background-color;red;');
	//writes the following into the pages css
	.box{
		background-color:red;
	}
	
	sb.styles.writeRule('#myList', 'background-color;red;');
	//writes the following into the pages css
	#myList{
		background-color:red;
	}
	
	sb.styles.writeRule('#myList:hover', 'background-color:blue;font-size:30px;');
	//writes the following into the pages css
	#myList:hover{
		background-color:red;
		font-size:30px;
	}
	
	*/
	writeRule : function(domEl, rule){
		var sheet;
		if(typeof this.sheet ==='undefined'){
			if(document.createStyleSheet) {
				this.sheet = document.createStyleSheet('');
			} else {
			
				sheet = document.createElement('style');
				sheet.type="text/css";
				sheet.appendChild(document.createTextNode(domEl+'{'+rule+'}'));
				this.sheet = sb.$('head').appendChild(sheet);
				sheet=null;
			}
		}
		
		if(this.sheet.insertRule){
			this.sheet.insertRule(domEl+'{'+rule+'}', this.numRules);
		} else if(this.sheet.addRule){
			this.sheet.addRule(domEl, rule);
		} else if (document.styleSheets.length >0){
			document.styleSheets[document.styleSheets.length-1].insertRule(domEl+'{'+rule+'}', this.numRules);
		}
		
		this.numRules++;
	},
	
	/**
	@Name: sb.styles.read
	@Description: Reads the current style property on an a DOM node.  javascript .style properties return over css properties as is with display of styles.
	@Param: Element node The element which the style property should be read for
	@Param: String prop The property to read for the element.  Properties can be expressed as background-color or backgroundColor
	@Return: String The value of the property
	@Example:
	sb.styles.read(myElement, 'background-color');
	//might return 
	*/
	read : function(node, prop){
		node = sb.$(node);
		
		var val;
		if(prop.match(/^border$/)){
			prop = 'border-left-width';				
		} 
		
		if(prop.match(/^padding$/)){
			prop = 'padding-left';				
		}
		
		if(prop.match(/^margin$/)){
			prop = 'margin-left';				
		}
		
		if(prop.match(/^border-color$/)){
			prop = 'border-left-color';				
		}
				
		try{
			if (node.style[prop]) {
				val = node.style[prop];
				
			} else if (node.currentStyle) {
				
				prop = sb.strings.toCamel.call(prop);
				val = node.currentStyle[prop];
				
			} else if (document.defaultView && document.defaultView.getComputedStyle) {
					
				prop = prop.replace(/([A-Z])/g, "-$1");
				prop = prop.toLowerCase();
				
				val = document.defaultView.getComputedStyle(node,"").getPropertyValue(prop);
				
			} else {
				val=null;
			}
			
			if(prop == 'opacity' && val === undefined){
				val = 1;
			}
			
			if(val){
				val = val.toLowerCase();
				if(val == 'rgba(0, 0, 0, 0)'){val = 'transparent';}
				
				if(typeof sb.colors !='undefined'){
					if(sb.colors.html[val]){
						val = sb.strings.hex2rgb.call(sb.colors.html[val]);
					}
				}
				
				if(val.match("^#")){
					val = sb.strings.hex2rgb.call(val);
				}
				
				return val;
			} else {
				return null;
			}
			
		} catch(e){}
		
	}, 

	/**
	@Name: sb.styles.write
	@Description: Writes a style property for a DOM node.  
	@Param: Element node The element which the style property should be read for
	@Param: String The style property to write for the element, properties can be expressed as background-color or backgroundColor
	@Param: String The value of the property to set
	@Example:
	sb.styles.write(myElement, 'background-color', 'red');
	*/
	write : function(obj,prop,val){
		obj = sb.$(obj);
		val = String(val);
		if(sb.arrays.inArray.call(sb.styles.pxProps, prop) && val !=='' && !val.match(/em|cm|pt|px|%/)){
			val +='px';
		}
		
		prop = sb.strings.toCamel.call(prop);
		
		if(prop == 'opacity'){
			if(val <= 0){ val =0;}
			if(val >= 1){ val =1;}
			obj.style.opacity = val;
			
			if(typeof obj.style.filter == 'string'){
				obj.style.filter = "alpha(opacity:"+val*100+")";
			}
			
		} else {
			
			try{
			obj.style[prop] = val;
			}catch(e){}
		}
		
		return val;
	},
	
	/**
	@Name: sb.styles.copy
	@Description: used to copy style values set with javascript from one DOM node to another
	@Example:
	sb.styles.copy('#from', '#to');
	*/
	copy : function(from, toObj){
		from = sb.$(from);
		toObj = sb.$(toObj);
		
		for(var prop in from.style){
			try{toObj.style[prop] = from.style[prop];}catch(e){}
		}
	}
};

/**
@Name: sb.cookies
@Description: Used to handle cookies - which can rememebr values between client visits
*/
sb.cookies={

	days : 7,
	path : '/',
	domain : '',
	onlog : '',
	
	/**
	@Name: sb.cookies.recall
	@Description: Used to recall cookie values
	@Param: String name The name of the cookie who's value you are trying to recall
	@Return: String Returns the value stored for the cookie or false if the cookie is not found
	@Example:
	var answer = recall('myCookie');
	//answer = the value the cookie was set to with sb.cookies.remember
	//if globals are not enabled
	sb.cookies.recall('myCookie', 'paul');
	*/
	recall : function(name){
		
		var i,n, parts = document.cookie.split(';');
		
		for(i=0;i<parts.length;i++){
			n = parts[i].split('=');
			n[0] = n[0].replace(/ /, "");
			
			if(name==n[0]){
				return unescape(n[1]);
			} 
		}
		return false;
	},
	
	/**
	@Name: sb.cookies.remember
	@Description: Used to make the clients computer remember a value as a cookie
	@Param: String name The name (key) of the cookie which will hold the valuee
	@Param: String value The value the cookie holds, remember cookies are limited to <4k
	@Param: Days number The number of days to rememeber the value for.
	@Example:
	remember('name', 'paul');
	
	//if globals are not enabled
	sb.cookies.remember('name', 'paul');
	
	*/
	
	remember : function(name, value, days){

		days = days || sb.cookies.days;
	
	    var ck, date = new Date();
	
	    date.setTime(date.getTime()+(days*24*60*60*1000));
		 
		ck=name+'='+escape(value)+'; expires='+date.toGMTString()+'; path='+sb.cookies.path+';'+' host='+sb.cookies.domain;
		
		document.cookie =ck;
	},
	
	/**
	@Name: sb.cookies.forget
	@Description: Used to make the clients computer forget a cookie
	@Param: String name The name (key) of the cookie which will be forgotten
	@Example:
	forget('myCookie');

	//without globals
	sb.cookies.forget('myCookie');
	*/
	forget : function(name){
		
		this.remember(name, "", -1);
	},
	
	
	/**
	@Name: sb.cookies.forgetAll
	@Description: Forgets all cookies stored for your server
	@Example:
	sb.cookies.forgetAll();
	*/
	forgetAll : function(){
	
		var n,i,deleted =[], parts = document.cookie.split(';');
		for(i=0;i<parts.length;i++){
			n = parts[i].split('=');
			
			if(n[0] !== undefined){
				deleted.push(n[0]);
				this.remember(n[0], "", -1);
			}
		}
	},
	
	/**
	@Name: sb.cookies.listAll
	@Description: Used Internally
	*/
	listAll : function(){
		var i, c, list=[], parts = document.cookie.split(';');
		
		for(i=0;i<parts.length;i++){
			c = parts[i].split('=');
			list.push(c[0].replace(/ /, ""));
		}
		
		return list;
	}
	
};

/**
@Name: sb.events
@Description: Cross browser event handling that references the proper "this" and passes the event to the handler function.  Using sb.events, multiple events can be added to a single DOM node for the same event.  e.g. multiple onclick handlers
*/
sb.events = {
	
	/**
	@Name: sb.events.add
	@Description: Add an event listener to a DOM element, re-write of surebert events based on tips from http://www.digital-web.com/articles/seven_javascript_techniques/
	@Param: Element/String el A reference to a DOM element or a string that can be passed through sb.$ to return a dom el e.g. '#myList'
	@Param: String event The event to listen for without the on e.g. 'click'
		click - fires when the use mouses down and then up on an element
		contextmenu - fires when the user right-clicks on a DOM element - Not in opera
		mouseover - fires when the user hovers over a DOM element
		mouseout - fires when the user moves the mouse out from over a DOM element
		mousedown - fires when the user press the mouse button down over a DOM element
		mouseup - fires when the user lets the mouse button return to the up position over a DOM element
		keydown - fires when the user presses a key when in a DOM element
		keyup - fires when the user lets the key return to the up position in a DOM element
		keypress - fires when the key is pressed and then returns to the upstate in a DOM element
		blur - fires when a DOM element loses focus
		focus - fires when a DOM element gains focus
		submit - fires when a form is submitted
		onload - when a element such as body or img loads
		onunload - when user naviagtes away from the page
	@Param: Function handler The function that is run when the event occurs.  The this reference of the function is the element itself and the funciton is also passed an event object which holds data about the event e.g. clientX, clientY, target, etc  The funciton can be either an anonymous inline function or a function reference
	@Return Object Returns a reference to the event handler so that the event listener can be removed
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		alert(this.innerHTML);
	});
	*/
	
	add : function() {
		
	    if(window.addEventListener){
	   
	        return function(el, type, fn) {
	        	el = sb.$(el);
	        	var evt = {el:el, type:type, fn:fn};
	            el.addEventListener(type, fn, false);
	            return sb.events.record(evt);
	        };
	    } else if ( window.attachEvent){
	        return function(el, type, fn) {
	        	el = sb.$(el);
	        	
	            var f = function() {
	                fn.call(el, window.event);
	            };
	            var evt = {el:el, type:type, fn:f};
	            el.attachEvent('on'+type, f);
	             return sb.events.record(evt);
	        };
	    }
	}(),
	
	/**
	@Name: sb.events.getWheelDirection
	@Description: gets the mouse wheel delta e.g. the direction and amount it is being spun
	@Param: Object event An event reference as passed to a handler function as e
	@Return: Number The wheel delta either 1 or -1 depending on the direction
	@Example:
	function printDelta(e){
		document.title = sb.events.getWheelDirection(e);
	}
	
	//firefox/safari/opera
	sb.events.add('#box', 'DOMMouseScroll', printDelta);
	//ie
	sb.events.add('#box', 'mousewheel', printDelta);
	
	
	*/
	getWheelDirection : function(e){
		var delta = 0;
		if (e.wheelDelta) {
			delta = e.wheelDelta;
			if (window.opera) {delta = -delta;}
		} else if (e.detail) { delta = -e.detail;}
		delta = Math.round(delta);
		return (delta > 0 ) ? 1 : -1;
	},
	
	/**
	@Name: sb.events.keys
	@Description: gets key press data for an event making it easy to know which keys were pressed
	@Param: Object event An event reference as passed to a handler function as e
	@Return: Object A key press object witht the properties listed in the sb.events.keys method
	@Example:
	var myEvent = sb.events.add('#textArea', 'keydown', function(e){
		var keys = sb.events.keys(e);
		alert(keys.alt); //would be true if alt was pressed during the keypress - other keys listed below
	});
	
	*/
	keys : function(e){
		
		var k, key, pressed, prop;
		key = e.keyCode;
		
		k = {};
		k.pressed ='';
		k.esc = (key == 27) ? 1 :0;
		k.ret = (key == 13) ? 1 :0;
		k.tab = (key ==9) ? 1 : 0;
		k.shift = (e.shiftKey) ? 1 :0;
		k.ctrl = (e.ctrlKey) ? 1 :0;
		k.alt = (e.altKey) ? 1 :0;
		k.home = (e.keyCode == 36) ? 1 : 0;
		k.up = (e.keyCode == 38) ? 1 : 0;
		k.down = (e.keyCode == 40) ? 1 : 0;
		k.left = (e.keyCode == 37) ? 1 : 0;
		k.right = (e.keyCode == 39) ? 1 : 0;
		k.pageUp = (e.keyCode == 33) ? 1 : 0;
		k.pageDown = (e.keyCode == 34) ? 1 : 0;
		k.space = (e.keyCode == 32) ? 1 : 0;
		k.letter = String.fromCharCode(key).toLowerCase();
		if(!k.letter.match(/\w/)){
			k.letter ='';
		}
		for(prop in k){
			if(k[prop] === 1){
				k.pressed = prop;
			}
		}
		k.pressed += ' '+k.letter;
		
		return k;
	},
	
	/**
	@Name: sb.events.log
	@Description: used internally to keep track of all events registered on the page
	*/
	log : [],
	
	/**
	@Name: sb.events.preventDefault
	@Description: Used to prevent default actions from occurring on an event e.g. links that are clicked would do whatever is in the event handler but not the ordinary default event of goign to the page specified by the href attribute.
	@Param: Object event An event reference as passed to a handler function as e
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		sb.events.preventDefault(e);
	});
	
	*/
	preventDefault : function(e){
		 
		if(typeof e.stopPropagation == 'function'){
			e.preventDefault();
		} else {
			e.returnValue = false;
		} 
	},
	
	record : function(evt){
		sb.events.log.push(evt);
		return evt;
	},
	
	/**
	@Name: sb.events.relatedTarget
	@Description: Related targets are specified for events that have related targets. e.g. mouseover and mouseout.  when the event is mouseout the relatedTarget refers to the element the mouse is moving to.  When the event is mouseover, the relatedTarget refers to the element that the mouse is moving from. 
	@Param: Object event An event reference as passed to a handler function as e
	@Return: Element The related target DOM node as explained in the description
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		var target = sb.events.relatedTarget(e);
		alert(target.nodeName);
	});
	
	*/
	relatedTarget : function(e){
		var tar = false;
		switch(e.type){
			case 'mouseout':
				tar = e.relatedTarget || e.toElement;
				break;
			
			case 'mouseover':
				tar = e.relatedTarget || e.fromElement;
				break;
		}
		return tar;		
	},
	
	/**
	@Name: sb.events.remove
	@Description: Removes an event listener
	@Param: Object event An event listener reference as returned from sb.events.add
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		alert(this.innerHTML);
	});
	
	sb.events.remove(myEvent);
	*/
	remove : function(evt){
	
		if (evt.el.removeEventListener){
			evt.el.removeEventListener( evt.type, evt.fn, false );
		} else if (evt.el.detachEvent){
			evt.el.detachEvent( "on"+evt.type, evt.fn );
		}
		
	},
	
	/**
	@Name: sb.events.removeAll
	@Description: Removes all event listeners added with sb.events.add or sb.elements or s$'s event method

	@Example:
	sb.events.removeAll();
	*/
	removeAll: function(){
		sb.events.log.forEach(function(evt){
			sb.events.remove(evt);
		});
		sb.events.log=[];
	},

	/**
	@Name: sb.events.stopAndPrevent
	@Description: used to stop event bubbling and prevent default actions for an event, see sb.events.stopPropagation and sb.events.preventDefault for more info
	@Param: Object event An event reference as passed to a handler function as e
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		sb.events.stopAndPrevent(e);
		
	});
	
	*/
	stopAndPrevent : function(e){
		sb.events.stopPropagation(e);
		sb.events.preventDefault(e);
	},
	
	/**
	@Name: sb.events.stopAndPrevent
	@Description: Used to stop event bubbling e.g. when a ordered list is clicked the event bubbles to its children.
	@Param: Object event An event reference as passed to a handler function as e
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		sb.events.stopPropagation(e);
	});
	
	*/
	stopPropagation : function(e){
		 
		if(typeof e.stopPropagation == 'function'){
			e.stopPropagation();
		} else {
			e.cancelBubble = true;
		} 
	},
	
	/**
	@Name: sb.events.target
	@Description: Determines the target of the event, becaus eof event bubbling, this is not necessarily the this of the event. e.g when an orderlist is clicked, the target might have been one of the child list items, however, it's click event fires because teh chidlren are within it.  By referencing the target you can see which child was clicked.
	@Param: Object event An event reference as passed to a handler function as e
	@Return: Element The target DOM node as explained in the description
	@Example:
	var myEvent = sb.events.add('#myList', 'click', function(e){
		var target = sb.events.target(e);
		alert(target.innerHTML);
	});
	
	*/
	target : function(e){
	   var tar = (e.target !==undefined) ? e.target : e.srcElement;
	   if (tar.nodeType && tar.nodeType== 3){
	      tar = tar.parentNode;
	   }
	   return tar;
	}
	
};

/**
@Name: sb.fireAfterDelay
@Description: This constructor creates an object that fires a function on a timeout.  The function can be swicthed out at any point before the delay reaches the fires by resetting the .func property of your instance
@Param: Object params An object with the following properties, you must set either the seconds of milliseconds property
seconds - the number of seconds to wait before firing
milliseconds - the number of milliseconds to wait before firing
func - the function to fire after the delay
args - an array of arguments that get passed to the function when it fires
@Example:
var myWaiting= new sb.fireAfterDelay({
	func : function(){
		alert('hello');
	},
	seconds : 2
});
*/
sb.fireAfterDelay = function(o){
	sb.objects.importProperties(o, this);
	this.milliseconds = this.milliseconds || 1000;
	if(typeof this.seconds!='undefined'){
		this.milliseconds = this.seconds*1000;
	}
	var t=this,a;
	if(typeof o.args !='undefined'){
		a = (o.args.length) ? o.args : []; 
	}
	this.evt = window.setTimeout(function(){t.func.apply(t.func, a);}, this.milliseconds);
};

sb.fireAfterDelay.prototype = {
		
	/**
	@Name: sb.fireAfterDelay.prototype.abort
	@Description: Stops a sb.delayedFiring object instance from firing if exexuted before the delay has expired.
	@Example:
	var myWaiting= new sb.fireAfterDelay({
		func : function(){
			alert('hello');
		},
		seconds : 2
	});
	//if this is executed before the 2 seconds are up, then the dealyed firing is aborted
	myWaiting.abort();
	*/
	abort : function(){
		window.clearTimeout(this.evt);
	}
};

//###EVERYTHING BELOW THIS POINT IS FOR INTERNAL SETUP###
(function(){

	if(typeof sbNoGlobals === 'undefined'){
		//if gloabls are enabled add them
		sb.addGlobals();
	} else {
		
		//add only javascript 1.6-1.8 array globals
		for(var prop in sb.arrays){
			
			if(typeof Array.prototype[prop] =='undefined' && prop.match(/indexOf|lastIndexOf|filter|forEach|every|map|some|reduce|reduceRight/)){
				
				Array.prototype[prop] = sb.arrays[prop];
			}
		}
	}
})();

sb.dom.onready({
	id : 'body',
	onready : function(){
		sb.onbodyload.forEach(function(v){
			if(typeof v == 'function'){
				v();
			}
		});
	},
	tries : 600,
	ontimeout : function(){
		if(typeof sb.onbodynotready =='function'){
			sb.onbodynotready();
		}
	}
});

sb.events.add(window, 'resize', sb.browser.measure);
sb.events.add(window, 'scroll', sb.browser.scrollPos);
sb.events.add(window, 'unload', function(e){
	sb.onleavepage.forEach(function(v){
		if(typeof(v) =='function'){
			v(e);
		}
	});
	sb.events.removeAll();
});