/*
	ADLib (Another DHTML Library) ver. 1.0
	Copyright 2007 Bart Melton

	The ADLib Javascript Library is released under the Open Source MIT License (http://adlib.ozonecreations.com/license.html)
*/



/*
	Struct: adlib
	Library version and constants
	
	Const: adlib.version
	the current version of the library
*/
var adlib={
	version:'1.0',
	constants:{},
	CSS:{},
	DOM:{}
};
var fx=new Object();
var widgets=new Object();


/******************************************************
	File: Type Checking
	
	About:
	Functions for checking core variable types
	
	Core Checks:
	- String
	- Number
	- Array
	- Boolean
	- Object
	- Function
	- Node
	- Hash
	- Collection
	- RegExp
	- Date 
	
	Additional type checks:
	- defined
	- non-empty string
	- global variable
*********************************************************/

/*
	Function: isString	
	determines if the item is of type string
	
	Returns:		
		(Boolean) true if of type string
*/
/*
	Function: isObject	
	determines if the item of type Object (and not an Array,Hash, or Collection)
	
	parameters:
		(Any) n - the object to be checked
	
	Returns:		
		(Boolean) true if of type Object 
*/
/*
	Function: isFunction	
	determines if the item of type Function
	
	parameters:
		(Any) n - the object to be checked
	
	Returns:		
		(Boolean) true if of type function
*/
/*
	Function: isBoolean	
	determines if the item of type Boolean
	
	parameters:
		(Any) n - the object to be checked
	
	Returns:		
		(Boolean) true if of type boolean
*/

function isBoolean(f){
	return (typeof f=="boolean");
}
function isString(f){
	return (typeof f=="string");
}
function isFunction(f){
	return (typeof f=="function" && !(f instanceof RegExp));
}
function isObject(f){
	return (typeof f=="object" && !(f instanceof Hash)&& !(f instanceof Array)&&!(f instanceof Collection));
}

/*
	Function: isArray	
	determines if the item of type Array
	
	parameters:
		(Any) n - the object to be checked
		
	Returns:		
		(Boolean) true if of type Array
*/
/*
	Function: isHash	
	determines if the item of type <Hash>
		
	parameters:
		(Any) n - the object to be checked
		
	Returns:		
		(Boolean) true if of type Hash
*/
/*
	Function: isCollection
	determines if the item of type <Collection>
		
	parameters:
		(Any) n - the object to be checked
		
	Returns:		
		(Boolean) true if of type Collection
*/
/*
	Function: isRegExp
	determines if the item of type RegExp
	
	parameters:
		(Any) n - the object to be checked
		
	Returns:		
		(Boolean) true if of type Array
*/
/*
	Function: isDate
	determines if the item of type RegExp
	
	parameters:
		(Any) n - the object to be checked
		
	Returns:		
		(Boolean) true if of type Array
*/
(function(){
	var a=["Array","RegExp","Date","Hash","Collection"];
	for(var x=0; x<a.length; x++){
		window["is"+a[x]]=new Function('v','return v instanceof '+a[x]);
	}
})();

/*
	Function: isNumber	
	determines if the item of type Number and  _not isNaN_
	
	parameters:
		(Any) n - the object to be checked
	
	Returns:		
		(Boolean) true if of type number and not isNaN
*/
function isNumber(n){
	return ((typeof n=="number")&&(!isNaN(n)));
};

/*
	Function: isNode	
	determines if the item an Element node
	
	parameters:
		(Any) n - the object to be checked
	
	Returns:		
		(Boolean) true if of type boolean

*/
function isNode(n){
	return 	(n&&n.nodeType==Node.ELEMENT_NODE);
};

function isCollectable(n){
	return isNode(n)||n==window||n==document;
}


/*
	Function: isDefined	
	determines if the item is _not_undefined_ and _not_null_
	
	parameters:
		(Any) o - the item to be checked
	
	Returns:		
		(Boolean) true if not null and not undefined
*/
function isDefined(o){
	return ((typeof o!="undefined")&&(o!=null));
};

/*	
	Function: isValidText	
	determines if if the input is
	- defined
	- not an empty string
	- not "null" (null converted to a string)
	- not "undefined" (undefined converted to a string)
	- not whitespace only
	
	parameters:
		(Any) t - the item to be checked
	
	Returns:		
		(Boolean) true if the input meets the above criteria
		
*/
function isValidText(t){
	if(!isDefined(t)
		||!isString(t)
		||(t=="")
		||(t=="null")
		||(t=="undefined")
		||(t.toString().strip()=="")
		){
		return false;
	}
	return true;
};

/*
	Function: checkFunction	
	determines if the input is 
	- of type function
	
		OR
		
	- if it is the name of a valid (global) function (string input)
	
	*e.g*: to check for function abc(){} use checkFunction("abc")
	
	
	Returns:	
		
		Boolean
*/
function checkFunction(f){
	return (((isDefined(window[f]))&&isFunction(window[f]))||(isFunction(f)));
}
/*
	function: isGlobalVar	
	determines if the input is defined in the global namespace and not a function
	
	parameters:
		(String) f - the name of the variable to be checked
		
	Returns:		
		(Boolean) true if the input is a global variable name
*/
function isGlobalVar(v){
	return ((isDefined(window[v]))&&!isFunction(window[v]));
};


/*
	function: $type
	returns an appropriate "type" for the input ("string","number","boolean","array","hash","regexp","date","function","node","object")
	
	paramters:
		(any) value - the variable to be checked
	
	returns:
		(string) the type of the value or "undefined" or "other" if fails to find a match
	
*/

function $type(value){
	var o=["String","Number","Boolean","Collection","Array","Hash","RegExp","Date","Function","Node","Object","NaN"];
	if(!isDefined(value)){
		return "undefined";
	}
	var r=o.grep(function(v){return window["is"+v](value);});
	return (r!=null)?o[r].toLowerCase():"other";
}; 

/******************************************************
	Script: Constants	

	Section: Main
*********************************************************/

/******************************************************
	Section: Browser Sniff
	
	Struct: bis
	Browser, OS, Version booleans
	
	Items are booleans (unless otherwise noted)
*********************************************************/


/*
	Const: bis.ie 
	Internet Explorer

	Const: bis.safari
	Safari Browser
	
	Const: bis.firefox
	Firefox Browser

	Const: bis.opera
	Opera Browser
	
	Const: bis.konquerer
	Konquerer Browser
	
	Const: bis.gecko
	Mozilla Gecko layout engine based (Mozilla, Firefox, Netscape 6+, etc)

	Const: bis.webkit
	Webkit engine based (Apple rendering engine: Safari, Camino etc)
	
	Const: bis.win
	Windows OS
	
	Const: bis.mac
	Macintosh OS
	
	Const: bis.smode
	Browser is in strict-mode (compliant mode)
	
	Const: bis.v
	Browser version number
*/
adlib.av=navigator.appVersion;
adlib.ua=navigator.userAgent.toLowerCase();
var bis = {	
	ie: ((isDefined(window.ActiveXObject)) && (adlib.av.indexOf('MSIE')!=-1)),
	opera: (adlib.ua.indexOf('opera')!=-1),
	konquerer: (navigator.vendor == 'KDE'),
	webkit: (/WebKit/i.test(adlib.ua)?true:false),
	safari: (/WebKit/i.test(adlib.ua)&&/Safari/i.test(adlib.ua)),
	gecko:((navigator.product=='Gecko')&&(adlib.av.indexOf('KHTML')==-1)),
	firefox:(adlib.ua.indexOf('firefox')!=-1),
	win: (adlib.av.indexOf('Windows')!=-1),
	mac: (adlib.av.indexOf('Macintosh')!=-1),
	v:0	
};
bis.smode=(((document.compatMode)&&(document.compatMode == "CSS1Compat"))||(bis.safari));
if(bis.ie){bis.v=parseFloat(adlib.av.substring(adlib.av.indexOf('MSIE')+4));}
else if(bis.firefox){bis.v=parseFloat(adlib.ua.substring(adlib.ua.indexOf('firefox')+8));}
else if(bis.opera){bis.v=parseFloat(adlib.ua.substring(adlib.ua.indexOf('opera')+6));}
else if(bis.safari){bis.v=parseFloat(adlib.ua.substring(adlib.ua.indexOf('safari')+7));}
delete adlib.av;
delete adlib.ua;

/******************************************************
	Section: General
*********************************************************/
/*
	Const: _NL
	new line character (jsMin safe)
*/
var _NL='\x0A'; 
/*
	Const: _BR
	HTML BR tag as string
*/
var _BR='<br />';

/*
	Const: _Clearer
	clearing div (add the following class or similar to stylesheet):
	
	.clearer{display:block; margin:0; padding:0; clear:both; font-size:1px; width:1px; height:1px; overflow:hidden;}
*/
var _Clearer='<div class="clearer"></div>';

/*
	Function: _Empty
	Empty function, does nothing
	
	Useful in defaulting properties to be overriden or removing functions without causing failures
*/
function _Empty(){};

/*
	Const: adlib.CSS.inlineElements
	an array of elements whose default display value is "inline"
	
	see:
		<DOM.styles.getDefaultDisplay>()
*/
adlib.CSS.inlineElements=['a','b','button','cit','del','dfn','em','i','img','input','ins','label','legend','s','select','span','strike','strong','sub','sup','u'];
/*
	Const: adlib.CSS.blockElements
	an array of elements whose default display value is "block"
	
	see:
		<DOM.styles.getDefaultDisplay>()
*/
adlib.CSS.blockElements=['blockquote','canvas','code','dd','div','dl','dt','embed','h1','h2','h3','h4','h5','h6','hr','iframe','li','object','ol','p','pre','textarea','ul','xmp'];
/*
	Const: adlib.CSS.tableElements
	an object of table elements where the key is the element name and the value is the default display value
	
	see:
		<DOM.styles.getDefaultDisplay>()
*/
adlib.CSS.tableElements={
	table:bis.ie ? "block" :'table',
	tbody:bis.ie ? "block" :'table-row-group',
	td:bis.ie ? "block" : 'table-cell',
	tfoot:'table-footer-group',
	th:bis.ie ? "block" : 'table-cell',
	thead:'table-header-group',
	tr:bis.ie ? "block" : 'table-row',
	caption:bis.ie ? "block" : 'table-caption'
};

/* 
	Const: Node
	Defines "Node" and all of the node type constants (e.g. Node.ELEMENT_NODE) in browsers that do not support "Node"
*/

if(!Node){
	var Node={
		ELEMENT_NODE: 1,
		ATTRIBUTE_NODE: 2,
		TEXT_NODE: 3,
		CDATA_SECTION_NODE: 4,
		ENTITY_REFERENCE_NODE: 5,
		ENTITY_NODE: 6,
		PROCESSING_INSTRUCTION_NODE: 7,
		COMMENT_NODE: 8,
		DOCUMENT_NODE: 9,
		DOCUMENT_TYPE_NODE: 10,
		DOCUMENT_FRAGMENT_NODE: 11,
		NOTATION_NODE: 12
	};
}



/******************************************************
	Script: Classes and Objects
	
	About:
	Functions for creating and extending classes and 
	searching/manipulating non-DOM objects
*********************************************************/


/*
	Class: Class
	Object used for class creation
*/
var Class = {
/*
	static: create
	function to create a new class prototype (blatant clone of Prototype's Class.create)
	
	parameters:
		(Object) Any number of objects. The returned prototype <inherits> each argument
		
	Returns:
		(Class) a new class prototype
*/
  create: function() { 
	  	var obj=function() {
			if(this.init){
			    this.init.apply(this, arguments);
			}
	    };
		for(var x=0; x<arguments.length; x++){
			Class.inherits(obj.prototype,arguments[x]);
		} 	
	    return obj; 
  	},
/*
	static: inherits
	Copies properties from one object to another (blatant clone of Prototype's Object.extend)
	
	parameters:
		(Object) target - the object to which the new properties are applied
		(Object) source - the object who's properties are to be copied
		(Boolean) owrite - (optional) allow for optional overwriting (default=false). 
							If true, source[x] will overwrite target[x], otherwise source[x] will be ignored
	
	returns:
		nothing
	
	note:
		overwriting native object functions such as toString does not work in IE6.  Use <method> instead.
	
	
*/
	inherits:function(target,source,owrite){
		var ovr=(isBoolean(owrite)?owrite:false);
		for(var x in source){
			if((ovr)||(!isDefined(target[x]))){
				target[x]=source[x];
			}
		}
	},
/*
	static: method
	adds a single method to an object
	
	parameters:
		(Object) target - the object to which the new properties are applied
		(String) name - the name of the method to be added
		(Function) funct - the function to be assigned to name
		(Boolean) owrite - (optional) allow for optional overwriting (default=true). 
							If true, source[x] will overwrite target[x], otherwise source[x] will be ignored
	
*/
	method:function(target,name,funct,owrite){
		var ovr=(isBoolean(owrite)?owrite:true);
		if((ovr)||(!isDefined(target[name]))){
			target[name]=funct;
		}
	}		
};

/*
	Class: Object
*/

/*
	static: hasProperty
	Walks through a chain of properties to see if the last one exists. Useful for checking a deep property when you don't know if the middle items exist (which would throw an error)
	
	Parameters:
		(Object) obj - the object to be searched
		(String) child1(,...childN) - any number of properties to be chained together
		
	returns:
		(Object/false) the object found or false
		
	example:
		Object.hasProperty(myObj,'a','b','c') is equivalent to myObj['a']['b']['c']
*/
Object.hasProperty=function(obj){
	var tmp=obj;
	for(var x=1; x<arguments.length; x++){
		if(arguments[x] in tmp){
			tmp=tmp[arguments[x]];
		}
		else{
			return false;
		}
	}
	return tmp;
};

/*
	Static: getPropertyTo
	checks the object for properties that exist up to the last argument or up to the first check that fails that fails
	
	Parameters:
		(Object) obj - the object to be searched
		(String) child1(,...childN) - any number of properties to be chained together
		
	returns:
		(Array) an array of property names up to the end of the arguments list OR the last successful one
	
	example:
(start code)
		myObj.employee.id.name.first
		Object.getPropertyTo(myObj,'employee','id'); returns ['employee','id']
		Object.getPropertyTo(myObj,'employee','id','name','last'); returns ['employee','id','name']
(end)
		
		
*/
Object.getPropertyTo=function(obj){
	var tmp=obj;
	var last=false;
	for(var x=1; x<arguments.length; x++){
		if(arguments[x] in tmp){
			if(!last){
				last=new Array();
			}
			last.push(arguments[x]);
			tmp=tmp[arguments[x]];
		}
		else{
			break;
		}
	}
	return last;
};



/*
	Static: setChild
	sets a child sub-node on an object, creating any missing nodes along the way
	
	parameters:
		(Object) obj - the object who's value is to be set
		(Array) path - an array of the object child nodes to step into (or create)
		(Any) value - the value to be set
		(Any) type - (optional) the variable type to set any missing children to (default = "Object")
		
	example:
(start code)
		myObj.a.b
		Object.setChild(myObj, ['a','b','c','d'], 'my value')
(end)		
	
		results in 
		
(start code)		
		myObj.a.b.c.d='my value'
		(Both 'c' and 'd' are created)
(end)
*/
Object.setChild=function(obj,path,value,type){
	var typ=(type?type:"Object");
	var obj2=obj;
	var str="arguments[0]";
	for(var x=0; x<path.length; x++){
		str+='[arguments[1]['+x+']]';
		if(!isDefined(obj2[path[x]])){
			eval(str+'=new '+typ+'()');
		}
	}
	eval(str+'=arguments[2]');
};
/*
	Static: deleteChild
	deletes a child sub-node from an object (if the sub-node exists)
	
	parameters:
		(Object) obj - the object who's child is to be deleted
		(Array) path - an array of the object child nodes to step down
		
	returns:
		(Boolean) - true if the child-node does not exist or is deleted, 
					false if any nodes leading up to the designated child-node do not exist.
*/
Object.deleteChild=function(obj,path){
	var c=path.pop();
	for(var x=0; x<path.length;x++){
		if(isDefined(obj[path[x]])){
			obj=obj[path[x]];
		}
		else{
			return false;
		}
	}
	obj[c]=void 0;
	delete obj[c];
	return true;
};

/*
	Static: select
	loops through the arguments until one argument matches a property name 
	(excluding properties where the value is a function)
	
	Parameters:
		(Object) obj - the object to be searched
		(String) child1(,...childN) - any number of property names to be tried
		
	returns:
		(Any) the objects property value of the first argument to match or null if none match
		
	example:
(start code)
		myobj.firstName='joe';
		myobj.empId='abc123';
		myobj.lastName='smith';
		Object.select(myobj,'name','ssn','empId','lastName','firstName');		
		returns 'abc123'
(end)
		
*/		
Object.select=function(obj){
	if(isDefined(obj)){
		for(var x=1; x<arguments.length; x++){
			if(isDefined(obj[arguments[x]])&&!isFunction(obj[arguments[x]])){
				return obj[arguments[x]];
			}
		}
	}
	return null;
};

/*
	Static: selectProperty
	loops through the arguments until one argument matches a property name 
	
	Parameters:
		(Object) obj - the object to be searched
		(String) child1(,...childN) - any number of property names to be tried
		
	returns:
		(String) the objects property name of the first argument to match or null if none match
		
	example:
(start code)
		myobj.firstName='joe';
		myobj.empId='abc123';
		myobj.lastName='smith';
		Object.select(myobj,'name','ssn','empId','lastName','firstName');		
		returns 'empId'
(end)
		
*/	
Object.selectProperty=function(obj){
	if(isDefined(obj)){
		for(var x=1; x<arguments.length; x++){
			if(isDefined(obj[arguments[x]])){
				return arguments[x];
			}
		}
	}
	return null;
};



/*
	Function: $test
	Checks if the parameter exists (!null)
	
	Parameter: 
		(Object) obj - the object to be tested
	
	Returns: 
		(Boolean) true if obj is defined
	
	Throws: 
		Object does not exist
	
	See: 
		<isDefined>
*/
function $test(obj){//works
	return isDefined(obj);
};
/********************End OOP*******************/

/******************************************************
	Script: String
	
	About:
	Extensions to the core String object
	
	Extends:
		- String.prototype
*********************************************************/

Class.inherits(String.prototype,{
/*
	function: strip
	Removes all whitespace from the beginning and end of the String
	
	returns:
		(String) the string without the whitespace
*/
	strip:	function(){
				var rge=/^(\s*)/;
				var rge2=/(\s*)$/;
				val=this.replace(rge,"").replace(rge2,"");
				return val;
			},
/*
	function: normalizeWhitespace
	<strips> the white space, converts all tabs to spaces and converts any instances of multiple spaces to a single space
		
	returns:
		(String) the string without the extra whitespace
*/
	normalizeWhitespace:function(){
		var str=this.replace(/\t/g,' ').strip();
		return str.replace(/\s{2,}/g,' ');		
	},
/*
	function: addParam
	adds a parameter (URL encoded) to a string.
	
	Parameters:
		(String) param - the name of the parameter to be added
		(String) pval - the value of the parameter (will be URIComponent encoded)
		
	returns:
		(String) the string with the parameter added
	
	note:
		- if the string already has a '?' then just the param "&paramName=Value" is added
		- if the string does not have a '?' then the param is added as "?paramName=value"
		- if no value is provided, then the param is added as '&paramName'
*/
	addParam:	function(param,pval){//works
					var val=this.valueOf();
					var pOut=param;
					if(isDefined(pval)){
						pOut+='='+encodeURIComponent(pval);
					}
					if(pOut!=''){
						if(val.indexOf('?')>-1){
							val+='&'+pOut;
						}else{
							val+='?'+pOut;
						}
					}
					return val;
				},
/*
	function: getParam
	retrieves and decodes a parameter from the String
		
	Parameters:
		(String) parameterName - the name of the parameter to be retrieved
		
	returns:
		(String) the value of the parameter (URIComponent decoded) or null
*/
	getParam:	function(parameterName){
					var _this=this.toString();
					if(_this.indexOf('?')==-1){
						return null;
					}	
					var lqry=_this.substring(_this.indexOf('?')+1);
					var params = lqry.split("&");
					for(var x=0; x<params.length; x++){
						if(parameterName == params[x].split("=")[0]){ 
							return decodeURIComponent(params[x].split("=")[1]); 
						} 
					} 
					return null;
				},
/*
	function: template
	takes an object and replaces the flagged items in the string if the object has a property name that matches the flagged item. Items in which the object does not have a corresponding property are left untouched.
		
	Parameters:
		(Object) obj - the object to run the template against
		(String) leftDelimiter - (optional) the left-hand flag to signal the start of a replacement (default='<=')
		(String) rightDelimiter - (optional) the right-hand flag to signal the start of a replacement (default='=>')
		
	returns:
		(String) the string with all matched items replaces
		
		
	example:
(start code)
		var myObj=new Object();
		myObj.value='val1';
		myObj.display='Value #1';
		'<option val="<=value=>"><=display=></option>'.template(myObj) returns
		'<option val="val1">Value #1</option>'
(end)
*/
	template:	function(obj,leftDelimeter,rightDelimeter){//works
					var lDel=((leftDelimeter!=null)?leftDelimeter:'<=');
					var rDel=((rightDelimeter!=null)?rightDelimeter:'=>');
					var rge=new RegExp("("+lDel+"\\s*)([\\w-]*)(\\s*"+rDel+")",'g'); 
					var val=this.valueOf();
					while(tmp=rge.exec(val)){
						var spot=rge.lastIndex;
						var tmp2=tmp[0].replace(rge,"$2");
						if(!(/\D/.test(tmp2))){
							tmp2=parseInt(tmp2);
						}
						if(isDefined(obj[tmp2])){
							val=val.replace(tmp[0],obj[tmp2]);
							rge.lastIndex=0;
						}
						else{
							rge.lastIndex=spot;
						}
					}
					rge.lastIndex=0;
					return val;
				},
/*
	function: contains
	checks whether the String contains a substring 
		
	Parameters:
		(String) str - the substring to be matched
		(Boolean) exact - if exact, it will only match if the String==substring or the substring is present with only whitespace around it
		
	returns:
		(Boolean) boolean whether the substring is in the string
		
	Example:
		- "javascript is fun".contains('script') = true
		- "javascript is fun".contains('script',true) = false
		- "javascript is fun".contains('is') = true
		- "javascript is fun".contains('is',true) = true
*/
	contains: function(str,exact){
		if(exact){
			var expr = new RegExp('(^|\\s)('+str+')(\\s|$)','g');
			return (this.match(expr)!=null);			
		}
		return (this.indexOf(str)!=-1);
	},
/*
	function: startsWith
	checks whether the String starts with the sub-string
		
	Parameters:
		(String) str - the value to be checked
		
	returns:
		(Boolean) boolean whether str is the beginning of the string
*/
	startsWith:function(str){
		return (this.indexOf(str)==0);
	},
/*
	function: endsWith
	checks whether the String ends with the sub-string
		
	Parameters:
		(String) str - the value to be checked
		
	returns:
		(Boolean) whether str is the end of the string
*/
	endsWith:function(str){
		return (this.indexOf(str)+str.length==this.length);
	},
/*
	function: camelize
	converts a string from "dash" notation (css notation) to word-case notation(javascript notation)
		
	returns:
		(String) the converted value
	
	example:
		'border-top'.camelize() returns 'borderTop'
*/
	camelize: function(){//works
				var styleName=this.valueOf();
				var rg=/([a-zA-Z]+)(-([a-zA-Z]{1})([a-zA-Z]*))/i;
				while(res=styleName.match(rg)){
					var c=res[3].toUpperCase();
					styleName=styleName.replace(rg,'$1'+c+'$4');
				}		
				return styleName;
			},
/*
	function: toHash
		splits the string by multiple delimeters
		
	Parameters:
		(Array) dels - an array of the delimiters to split by (splitting is done in order)
		
	returns:
		(Hash) a hash (or nested hashes) of the name/value pairs
		
	Example:
(start code)
		var x='item1=abc&item2=def'.toHash(['&','=']) returns
		x['item1']='abc';
		x['item2']='def';
(end)
		
*/		
	toHash:function(dels){
			if(isString(dels)){
				dels=[dels];
			}
			var str=this.valueOf();
			var cnt=0;
			var values=new Hash();
			var del1=dels.shift();
			var del2=null;
			if(dels.length>0){
				del2=dels.shift();
			}
			var tmp=str.split(del1);
			if(tmp.length==1){
				if(del2==null){
					return str;
				}
				else{
					var tmp2=str.split(del2);
					if(tmp2.length==1){
						return str;
					}
					else{
						values[tmp2[0].strip()]=tmp2[1];
					}
				}
			}
			else if(del2){
				tmp.forEach(function(val,key){
					if(isValidText(val)){
						var tmp2=val.split(del2);
						if(tmp2.length==1){
							values[val.strip()]='';
						}
						else if(dels.length==0){
							values[tmp2[0].strip()]=tmp2[1];
						}
						else{
							values[tmp2[0].strip()]=tmp2[1].strip().multiSplit(dels.clone());
						}
					}
				});
			}
			else{
				values.fill(tmp);
			}	
			return values;
		},
/*
	function: pad
		pads the string with a character at the beginning, end or both
		
	Parameters:
		(Integer) count - the number of characters to be added (to each side if side=both)
		(String) side - either 'l' (left),'r' (right),'b' (both)
		(String) ch - the character to pad the string with (default=space)
		
	returns:
		(String) the padded string
*/
	pad:function(count,side,ch){
		var chr=ch||' ';
		var sides=side||'b';
		var out='';
		for(var x=0; x<count; x++){
			out+=chr;
		}
		switch(sides){
			case 'l': out+=this; break;
			case 'r': out=this+out; break;
			case 'b': out+=this+out;
		}
		return out;
	},
/*
	function: toArray
	converts a string to a true array so that Array functions can be run on it
		
	returns:
		(Array) an array representation of the string
		
	example:
		'abc'.toArray() returns ['a','b','c']
*/
	toArray:function(){
		return this.split('');
	},
/*
	function: toCharCodeArray
	returns an array of the character codes for each character in the string. 
		
	returns:
		(Array) an array of the character codes for each character code in the string.
		
	note:
		Useful for generating a unique string to use as hash keys (prepend a letter) as it eliminates characters that could throw errors.  No other real uses I can think of.
		
*/
	toCharCodeArray:function(){
		var r=[];
		for(var x=0; x<this.length; x++){
			r[x]=this.charCodeAt(x);
		}
		return r;
	}
		
});

Class.inherits(String.prototype,{
/*
	Alias: multiSplit
	<toHash>
*/
	multiSplit:String.prototype.toHash
});

/****************** end string manipulations **********************/


/******************************************************
	Script: Iterator
	
	About:
	Class extender to add functionality for iterating through properties
	
	Class: iterator
	Prototype for extending objects with iteration functionality, *NOT* a class by itself
*********************************************************/

var Iterator={ 
	
/*
	function: each
	begins (and resets) the iteration process at the first element moving forward
	
	parameters:
		(Function) funct - the function that is to be run on each element (funct(value,key))
	
	returns:
		(Any) the value returned by the function
*/
	each:function(funct){
		this.reset();
		this.count();
		this.iteration.f=funct;
		return this.again();
	},
/*
	function: _each
	begins (and resets) the iteration process at the last element moving backwards
	
	parameters:
		(Function) funct - the function that is to be run on each element (funct(value,key))
	
	returns:
		(Any) the value returned by the function
*/
	_each:function(funct){
		this.reset();
		this.iteration.f=funct;
		this.iteration.start=this.count()-1;
		this.iteration.step=this.count()-1;
		this.iteration.dir=false;
		return this.again();
	},
/*
	function: next
	steps to the next element in the list (direction based on how it was started)
	
	returns:
		(Any) the value returned by the function (set by <each> or <_each>)
*/
	next:function(){
		var st=this.iteration.step;
		this.iteration.dir?st++:st--;
		if(st<0||st>=this.count()){
			this.clearIteration();
			return null;
		}
		this.iteration.step=st;
		return this.again();
	},
/*
	function: previous
	steps to the previous element in the list (direction based on how it was started)
	
	returns:
		(Any) the value returned by the function (set by <each> or <_each>)
*/
	previous:function(){
		var st=this.iteration.step;
		this.iteration.dir?st--:st++;
		if(st<0||st>=this.count()){
			this.clearIteration();
			return null;
		}
		this.iteration.step=st;
		return this.again();
	},
/*
	function: again
	repeats the call on the current element in the list
	
	returns:
		(Any) the value returned by the function (set by <each> or <_each>)
*/
	again:function(){
		var k=this.iteration.items[this.iteration.step];
		return this.iteration.f(this[k],k);
	},
/*
	function: count
	fetches the length of the iterator
	
	returns:
		(Integer) the number of elements in the iterator
*/
	count:function(){
		if(!this.iteration){
			this.reset();
		}
		if(this.iteration.items.length==0){
			this.iteration.items=this.items();
		}
		return this.iteration.items.length;	
	},
/*
	function: reset
	resets the iteration markers to the default
	
	Returns:
		Nothing
*/
	reset:function(){
		this.iteration={items:[],step:0,start:0,dir:true,f:null};
	},
/*
	function: clearIteration
	deletes the iteration property (called when <next>/<previous> hit the end. Should be called manually if aborting any iteration functions to prevent the iteration property from appearing in browser native forEach etc loops.
	
	Returns:
		Nothing
*/
	clearIteration:function(){
		this.iteration=void 0;
		delete this.iteration;
	},
/*
	function: items
	recalculates the keys in the iterator
	
	Returns:
		Nothing
*/
	items:function(){
		if(!this.iteration){
			this.reset();
		}
		var a=[];
		this.iteration.items.clear();
		for(var x in this){
			if(!this.noEnum(x)){
				a.push(x);
			}		
		}
		this.iteration.items=a;
		return a;
	},
/*
	function: noEnum
	determines if a property should be enumerated (to use in for(x in y) loops to eliminate iterator/array/hash extensions)
	
	parameters:
		(String/Number) x - the name of the property to be checked
		(Boolean) noProto - (optional) eliminates any additional properties that are a function or are prototype properties (of any kind)
		
	returns:
		(Boolean) true - If the property should *NOT* be enumerated 
		
	example:
		var a=new Hash();
		
		...
		
		for(var x in a){
		
			if(a.noEnum(x)){continue}
			
			...
		
		}
*/
	noEnum:function(x,noProto){
		if(
			((this[x]==Iterator[x])||((this.iteration)&&(this[x]==this.iteration)))||// don't include iteration items	
			((this instanceof Hash)&&(this[x]==Hash.prototype[x])) || //if is a hash, skip hash prototype items
			((this instanceof Array)&&(this[x]==Array.prototype[x])) ||// if array, skip array prototype items
			((this instanceof Collection)&&(this[x]==Collection.prototype[x])||(x=="length")) ||// if collection, skip collection prototype items
			((noProto)&&((this[x]==this.prototype[x])||(isFunction(this[x])))) // if "noProto" is true and x is a function or in this.prototype
			){ 
				return true;
			}
		
		return false;
	}
};

Class.inherits(Iterator,{
/*
	Alias: step
	<next>
*/
	step:Iterator.next,
/*
	Alias: stepback
	<previous>
*/
	stepback:Iterator.previous,
/*
	Alias: repeat
	<again>
*/
	repeat:Iterator.again
});
		
/******************** end Iterator********************/


/******************************************************
	Script: Array
	
	About:
	Extensions to the core Array object.  Functions which are duplicates of the Javascript 1.6 specification are only added to browser which do not support the new methods.
	
*********************************************************/

/********************************************************
	Class: Array	
	Extends: 
		- Array.prototype
	
	inherits:
		- <Iterator>
*********************************************************/

adlib.ArrayProts={
	/*
	function: forEach
	Runs a function on every item in the array
	
	Parameters:
		(Function) funct - the function that is to be run on each element. 
		(The function should accept parameters (value, key, array))  
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		Nothing
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			forEach:function(funct,obj){
				var ln=this.length;
				for(var key=0; key<ln; key++){
					if(typeof this[key] !="undefined"){
						if(obj){
							funct.apply(obj,[this[key],key,this]);
						}
						else{
							funct(this[key],key,this);
						}				
					}
				}
			},
/*
	function: every
	Runs a function on every item in the array and returns true if the function returns true for each item
	
	Parameters:
		(Function) funct - the function that is to be run on each element
		The function should accept parameters (value, key, array)
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		(Boolean) true if "funct" returns true on all elements in array
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			every:function(funct,obj){
				var rt;
				for(var key=0; key<this.length; key++){
					rt=obj?funct.apply(obj,[this[key],key,this]):funct(this[key],key,this);
					if(!rt){
						return false;
					}
				}
				return true;
			},
/*
	function: filter
	Runs a function on every item in the array and returns an array of values where the function returns true
	
	Parameters:
		(Function) funct - the function that is to be run on each element
		The function should accept parameters (value, key, array)
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		(Array) an array of values where the function returns true
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			filter:function(funct,obj){
				var ret=[];
				var rt;
				for(var key=0; key<this.length;key++){	
					rt=obj?funct.apply(obj,[this[key],key,this]):funct(this[key],key,this);
					if(rt){
						ret.push(this[key]);
					}
				}
				return ret;
			},
/*
	function: map
	Runs a function on every item in the array and returns an array of values returned by the function
	
	Parameters:
		(Function) funct - the function that is to be run on each element
		The function should accept parameters (value, key, array)
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		(Array) an array of the values returned by the function
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			map:function(funct,obj){
				var ret=[];
				for(var key=0; key<this.length; key++){
					ret.push(obj?funct.apply(obj,[this[key],key,this]):funct(this[key],key,this));
				}
				return ret;
			},
/*
	function: some
	Runs a function on every item in the array and returns true if the function returns true for any item
	
	Parameters:
		(Function) funct - the function that is to be run on each element
		The function should accept parameters (value, key, array)
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		(Boolean) true - if "funct" returns true for any elements in array
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			some:function(funct,obj){
				var rt=false;
				for(var key=0; key<this.length; key++){
					rt=obj?funct.apply(obj,[this[key],key,this]):funct(this[key],key,this);
					if(rt){
						return true;
					}
				}
				return false;
			},
/*
	function: indexOf
	finds the first instance of a value within the array
	
	Parameters:
		(Any) value - the value to search for
		(Integer) fromIndex - the index to start the search (default=0)
		
	Returns:
		(Integer) the first index # of the item or -1 if not found
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			indexOf:function(elem,fromIndex){
				var f=fromIndex||0;
				if(Math.abs(f)>this.length){
					return -1;
				}
				if(f<0){
					f=this.length+f;
				}
				for(var x=f; x<this.length; x++){
					if(this[x]===elem){
						return x;
					}
				}
				return -1;
			},
/*
	function: lastIndexOf
	finds the last instance of a value within the array (backwards search)
	
	Parameters:
		(Any) value - the value to search for
		(Integer) fromIndex - the index to start the search (default=array.length)
		
	Returns:
		(Integer) the index # of the item or -1 if not found
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
			lastIndexOf:function(elem,fromIndex){
				var f=fromIndex||this.length;
				if(Math.abs(f)>this.length){
					return -1;
				}
				if(f<0){
					f=this.length+f;
				}
				for(var x=f; x>=0;x--){
					if(this[x]===elem){
						return x;
					}
				}
				return -1;			
			}
	};

for(var x in adlib.ArrayProts){
	if(!Array.prototype[x]){
		Class.method(Array.prototype,x,adlib.ArrayProts[x]);
	}
};

Class.inherits(Array.prototype,{
/*
	function: filterKeys
	Runs a function on every item in the array and returns an array of keys where the function returns true
	
	Parameters:
		(Function) funct - the function that is to be run on each element
		The function should accept parameters (value, key, array)
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		(Array) an array of keys where the function returns true
	
	See:
		- http://developer.mozilla.org/en/docs/New_in_JavaScript_1.6
		- http://www.webreference.com/programming/javascript/ncz/column4/index.html
*/
	filterKeys:function(funct,obj){
		var ret=[];
		var rt;
		for(var key=0; key<this.length;key++){	
			rt=obj?funct.apply(obj,[this[key],key,this]):funct(this[key],key,this);
			if(rt){
				ret.push(key);
			}
		}
		return ret;
	},

/*
	function: contains
	equivalent to <indexOf> for "non-dense" arrays
	
	Parameters:
		(Any) value - the value to be matched
	
	Returns:
		(Integer) the index # of the item or false if not found
*/
	contains:function(val){
		for(var x in this){
			if(this.noEnum(x)){
				continue;
			}
			if(this[x]==val){
				return x;
			}
		}
		return false;
	},	
		
/*
	function: grep
	Loops through the keys in the array and returns the first key in which the value matches the pattern	
	
	parameters (type 1):
		(RegExp) pattern - the RegExp to be matched
		
	parameters (type 2):
		(String) str - a string to be matched as this[x].contains(str)
		
	parameters (type 3):
		(Function) funct - a function that should be run instead (should take 2 parameters (value, key) and return true/false)
	
	returns:
		(Integer) the key of the first item matched or null
*/
	grep:function(pat){
		for(var x in this){
			if(this.noEnum(x)){continue ;}
			if(((isRegExp(pat))&&(pat.test(this[x])))||
				((isString(pat))&&(this[x].contains(pat)))||
				((isFunction(pat))&&(pat(this[x],x)))){
				return x;
			}
		}
		return null;
	},
/*
	function: grepAll
	Loops through the keys in the array and returns all keys in which the value matches the pattern
	
	parameters:
		parameters (type 1):
		(RegExp) pattern - the RegExp to be matched
		
	parameters (type 2):
		(String) str - a string to be matched as this[x].contains(str)
		
	parameters (type 3):
		(Function) funct - a function that should be run instead (should take 2 parameters (value, key) and return true/false)
	
	
	returns:
		(Array) an array of keys of all items matched or null if no matches
*/
	grepAll:function(pat){
		var ret=[];
		for(var x in this){
			if(this.noEnum(x)){continue ;}
			if(((isRegExp(pat))&&(pat.test(this[x])))||
				((isString(pat))&&(this[x].contains(pat)))||
				((isFunction(pat))&&(pat(this[x],x)))){
				ret.push(x)
			}
		}
		return (ret.length>0)?ret:null;				
	},
/*
	function: fill
	takes a function or single value and fills the array [count] times *(in place)*
	
	Parameters:
		(Any) value - a single value (this[x]=val) or a function (this[x]=function(x))
	
	Returns:
		nothing
*/		
	fill: function(val,count){
		var f=isFunction(val);
		for(var x=0; x<count; x++){
			this[x]=(f?val(x):val);
		}
	},
/*
	function: insert
	inserts a value (or array) into an array at location x moving other items out
	then normalizes the array *(in place)*
	
	Parameters:
		(Integer) location - the insertion point for the new value
		(Any) value - the value (single value or array) to be inserted
	
	Returns:
		nothing
	
	
*/		
	insert:function(loc,val){	
		var st=this.splice(0,loc);
		st=st.concat(val);
		st=st.concat(this);
		this.clear();
		for(var x in st){
			if(st.noEnum(x)){
				continue;
			}
			this.push(st[x]);
		}
	},
/*
	function: normalize
	Converts a non-dense array into a dense array *(in place)*
	
	Returns:
		nothing	
*/		
	normalize:function(){
		var tmp=[];
		for(var x in this){
			if(this.noEnum(x)){
				continue;
			}
			tmp.push(this[x]);
		}
		this.clear();
		tmp.unshift(0);
		tmp.unshift(0);
		this.splice.apply(this,tmp);
	},
	

/*
	function: clear
	resets the Array to an empty array ([]) *(in place)*
	
	returns:
		nothing
*/		
	clear:function(){
		this.length=0;
	},
/*
	function: exec
	runs a function on every element in the Array and sets the value of that item to the return of the function *(in place)*
	
	Parameters:
		(Function) funct - the function to run on each value in the array (should take 2 parameters (value, key)
		
	Returns:
		Nothing
*/	
	exec:function(f){
		for(var x in this){
			if(this.noEnum(x)){continue ;}
			this[x]=f(this[x],x);
			
		}
	},
/*
	function: removeDuplicates
	removes duplicate values from the array then normalizes it *(in place)*	
	
	Returns:
		Nothing
*/
	removeDuplicates:function(){
		this.normalize();
		for(var x=0; x<this.length; x++){
			if(this[x]==void 0){
				continue;
			}
			for(var y=x+1; y<this.length; y++){
				if(this[y]==void 0){
					continue;
				}
				if(this[x]==this[y]){
					delete this[y];
				}
			}
		}
		this.normalize();
	},
/*
	function: clone
	returns a copy of the array
	
	returns:
		(Array) a *COPY* of the array
*/
	clone:function(){
		return (new Array()).concat(this);
	},

/*
	Alias: cut
	Array.splice
*/
		cut:Array.prototype.splice,
/*
	Alias: select
	<filter>
*/
		select:Array.prototype.filter
});

Class.inherits(Array.prototype,Iterator,false);
/****************** end array additions **********************/

/******************************************************
	Group: Array - Utilities
	
	About:
	Utility functions for manipulating arrays
	
*********************************************************/


/* 
	function: array_weld
	Takes any number of arrays and returns an array of arrays where the corresponding values from each parameter is 
	put into an array at the corresponding return point.  The returned array will have a length of the longest argument.
	
	parameters:
		(Array) arg0(,...argN) - any number of arrays of any length
	
	returns:
		(Array) an array of the parameters welded together
		
	example:
(start code)
		var ob1=['testDiv1','testDiv2','testDiv3','testDiv4','testDiv5'];
		var ob2=[[1,1],[2,2],[3,3],[4,4],[5,5]];
		var ob3=['a','b','c'];
		
		var result=array_weld(ob1,ob2,ob3);
(end)		

	result will equal

(start code)
	res[0]=['testDiv1',[1,1],'a'];
	res[1]=['testDiv2',[2,2],'b'];
	res[2]=['testDiv3',[3,3],'c'];
	res[3]=['testDiv4',[4,4],null];
	res[4]=['testDiv5',[5,5],null];
(end)

*/
function array_weld(){
	var ars=args2array(arguments);
	var ln=0;
	var ret=new Array();
	for(var x=0; x<ars.length; x++){
		if(ars[x].length>ln){
			ln=ars[x].length;
		}
	}
	for(var x=0; x<ln; x++){
		ret[x]=new Array();
		for(var y=0; y<ars.length; y++){
			ret[x].push(ars[y][x]);
		}
	}
	return ret;
};


/* 
	function: args2array
	converts an arguments object into a true array
	
	parameters:
		(Arguments) args - an arguments object from another function
	
	returns:
		(Array) a true array of the args
*/
function args2array(args){
	var r=[];
	if(isDefined(args)){
		if(isDefined(args.length)&&args!=window&&args!=document&&!isNode(args)){
			for(var x=0; x<args.length; x++){
				r[x]=args[x];
			}
		}
		else if(isHash(args)){
			return args.toArray();
		}
		else{
			r.push(args);
		}	
	}
	return r;
};


/******************************************************
	Script: Hash
	
	About:
	Functionality for creating, iterating, searching, and manipulating Hashes

	
	Class: Hash
	
	inherits:
		- <Iterator>
	
********************************************************/

var Hash=Class.create();

Class.inherits(Hash.prototype,{
/*
	function: forEach
	Runs a function on every item in the hash
	
	Parameters:
		(Function) funct - the function that is to be run on each element.  
		The function should accept parameters (value, key, array)
		
		(Object) obj - (optional) the object the function should be applied to 
		
	Returns:
		Nothing
	
	
*/
	forEach:function(funct,obj){
		for(var key in this){
			var value=this[key];
			if(!this.noEnum(key)){
				if(obj){
					funct.apply(obj,[value,key,this]);
				}
				else{
					funct(value,key,this);
				}				
			}
		}
	},

/*
	function: map
	runs a function against each value in the hash and returns an array of the values returned by the function
	
	parameters:
		(Function) funct - the function to be executed on each item in the hash (function(value,key))
	
	returns:
		(Array) an array of the values returned by the function
*/
	map:function(funct){
		var ret=[];
		var vl=this.each(funct);
		while(vl!=null){
			ret.push(vl);			
			vl=this.next();
		}
		return ret;
	},
/*
	function: every
	runs a function against each value in the hash and returns true if the function returns true for all values
	
	parameters:
		(Function) funct - the function to be executed on each item in the hash (function(value,key))
	
	returns:
		(Boolean) true if the function returns true on all item in the hash
*/
	every:function(funct){
		var rt=this.each(funct);
		while(rt){
			rt=this.next();
		}
		return (rt==null?true:false);
	},
/*
	function: filter
	runs a function against each value in the hash and returns an array of the hash values in which the function returns true
	
	parameters:
		(Function) funct - the function to be executed on each item in the hash (function(value,key))
	
	returns:
		(Array) an array of the hash values in which the function returns true
*/
	filter:function(funct){
		var ret=[];
		var rt=this.each(funct);
		while(rt!=null){
			if(rt){
				ret.push(this[this.iteration.items[this.iteration.step]]);
			}
			rt=this.next();
		}
		return ret;
	},
/*
	function: some
	runs a function against each value in the hash and returns true if the function returns true for any values
	
	parameters:
		(Function) funct - the function to be executed on each item in the hash (function(value,key))
	
	returns:
		(Boolean) true if the function returns true on at least 1 item in the hash
*/
	some:function(funct){
		var rt=this.each(funct);
		while(rt!=null){
			if(rt){
				return true;
			}
			rt=this.next();
		}
		return false;
	},
/*
	function: indexOf
	Loops through the keys in the hash and returns the first key in which the value matches the query	
	
	parameters:
		(Any) elem - the value to be matched
	
	returns:
		(String) the key of the first item matched or null
*/
	indexOf:function(elem){
		for(var x in this){
			if(this.noEnum(x)){continue ;}
			if(this[x]==elem){
				return x;
			}
		}
		return null;
	},
/*
	function: filterKeys
	runs a function against each value in the hash and returns an array of the hash keys in which the function returns true
	
	parameters:
		(Function) funct - the function to be executed on each item in the hash (function(value,key))
	
	returns:
		(Array) an array of the hash keys in which the function returns true
*/
	filterKeys:function(funct){
		var ret=[];
		var rt=this.each(funct);
		while(rt!=null){
			if(rt){
				ret.push(this.iteration.items[this.iteration.step]);
			}
			rt=this.next();
		}
		return ret;
	},
/*
	function: cut
	deletes items from the hash and returns a <hash> of the deleted items
	
	parameters (type 1):
		(String) arg1(,...argN) - any number of arguments can be entered.  If the argument (key) is in the hash, it is cut
		
	parameters (type 2):
		(Array) an array of keys to be cut.
		
	returns:
		(Hash) new hash with the deleted items
*/
	cut:function(){
		var o=new Hash();
		var obj=isArray(arguments[0])?arguments[0]:arguments;
		for(var x=0; x<obj.length;x++){
			o[obj[x]]=this[obj[x]];
			delete this[obj[x]];
		}
		return o;
	},
/*
	function: paste
	loops through an object and appends the key/value pairs to the hash *(in place)*
	
	parameters:
		(Hash/Object) obj0,...objN - the object to be appended
			
	Returns:
		Nothing
*/
	paste:function(){
		for(var y=0, a=arguments[0]; y<arguments.length; y++,a=arguments[y]){
			for(var x in a){
				this[x]=a[x];
			}
		}
	},
/*
	function: copy
	copies key/value pairs from the hash
	
	parameters:
		(String) arg1(,...argN) - any number of arguments can be entered.  If the argument (key) is in the hash, it is copied
		
	returns:
		(Hash) new hash with the copied items
*/
	copy:function(){
		var obj=new Hash();
		var itm=(arguments.length>0?arguments:this.items());
		for(var x=0; x<itm.length; x++){
			switch($type(this[itm[x]])){
				case 'Array': obj[itm[x]]=this[itm[x]].clone(); break;
				case 'Hash': obj[itm[x]]=this[itm[x]].copy(); break;
				default: obj[itm[x]]=this[itm[x]].valueOf();
			}
		}
		return obj;
	},
/*
	function: toArray
	converts the hash to an array (no keys)
		
	returns:
		(Array) an array of the values in the hash
*/
	toArray:function(){
		return this.map(function(value){
			return value;
		});
	},

/*
	function: fromString
	replaces the Hash with a new Hash created by splitting the string.
		
	parameters:
		(String) str - the string to be split
		(String) del1 - the delimeter to be used between key/value pairs
		(String) del2 - the delimeter to be used between the key and value
			
	Returns:
		Nothing
*/
	fromString:function(str,del1,del2){
		this.clear();
		this.paste(str.toHash([del1,del2]));
	},
/*
	function: toQuery
	replaces the Hash with a new Hash created by splitting the string.
		
	parameters:
		(Boolean) removeQ - boolean, should the leading '?' be stripped from the string before it is returned
		
	returns:
		(String) the hash converted to a query string 
*/
	toQuery:function(removeQ){
		var str='';
		this.forEach(function(value,key){
			str=str.addParam(key,value);
		});
		if(removeQ){
			str=str.substring(1);
		}
		return str;
	},
/*
	function: fromQuery
	replaces the Hash with a new Hash created by splitting the string based on Query delimeters (&,=).
	
	parameters:
		(String) str - the query string to be converted (leading '?' will be stripped)
			
	Returns:
		Nothing
*/
	fromQuery:function(str){
		this.clear();
		if(str.charAt(0)=='?'){
			str=str.substring(1);
		}
		this.paste(str.toHash(['&','=']));
	},
/*
	function: run
	applies <Hash.forEach> to "this"
	
	parameters:
		(Function) funct - the function to be run
			
	Returns:
		Nothing
*/
	run:function(funct){
		this.forEach(funct,this);
	},	
/*
	function: grep
	Loops through the keys in the hash and returns the first key in which the value matches the pattern	
	
	parameters (Type 1):
		(RegExp) pat - the RegExp to be matched
		
	parameters (Type 2):
		(String) str - a string to be matched as this[x].contains(str)
		
	parameters (Type 3):
		(Function) funct - a function that should be run instead (should take 2 parameters (value, key) and return true/false)
	
	returns:
		(String) the key of the first item matched or null
*/
	grep:function(pat){
		for(var x in this){
			if(this.noEnum(x)){continue ;}
			if(((isRegExp(pat))&&(pat.test(this[x])))||
				((isString(pat))&&(this[x].contains(pat)))||
				((isFunction(pat))&&(pat(this[x],x)))){
				return x;
			}
		}
		return null;
	},
/*
	function: grepAll
	Loops through the keys in the hash and returns all keys in which the value matches the pattern
	
	parameters (Type 1):
		(RegExp) pat - the RegExp to be matched
		
	parameters (Type 2):
		(String) str - a string to be matched as this[x].contains(str)
		
	parameters (Type 3):
		(Function) funct - a function that should be run instead (should take 2 parameters (value, key) and return true/false)
	
	returns:
		(Array) an array of keys of all items matched or null if no matches
*/
	grepAll:function(pat){
		var ret=[];
		for(var x in this){
			if(this.noEnum(x)){continue ;}
			if(((isRegExp(pat))&&(pat.test(this[x])))||
				((isString(pat))&&(this[x].contains(pat)))||
				((isFunction(pat))&&(pat(this[x],x)))){
				ret.push(x);
			}
		}
		return (ret.length>0)?ret:null;				
	},
/*
	function: fill
	assigns a value to all specified keys *(in place)*
	
	Parameters (type 1):
		(Array) val - an array of keys to fill with ''
	
	Parameters (type 2):
		(Hash/Object) val - an object of key value pairs
	
	Parameters (type 3):			
		(Any) val - the value to be assigned
		(String) args - any additional number of keys as arguments
			
	Returns:
		Nothing
*/
	fill:function(val){
		if(arguments.length==1){
			if(isArray(arguments[0])){
				arguments[0].forEach(function(value){
					this[value]='';
				},this);
			}
			else{
				for(var x in arguments[0]){
					this[x]=arguments[0][x];
				}
			}
		}
		else if(arguments.length==2){
			arguments[1].forEach(function(value){
				this[value]=val;
			},this);
		}
		else{
			for(var x=1; x<arguments.length; x++){
				this[arguments[x]]=val;
			}
		}
	},
/*
	function: clear
	deletes all keys/values in the Hash *(in place)*
		
	Returns:
		Nothing
*/
	clear:function(){
		this.forEach(function(value,key){
			this[key]=void 0;
			delete this[key];
		},this);	
	},
/*
	function: pasteArray
	takes an array of keys and an array of values and adds them to the Hash *(in place)*
	
	parameters:
		(Array) keys - an array of the keys to be added/overwritten
		(Array) vals - an array of the values to be added (this[keys[x]]=vals[x])
		
	Returns:
		Nothing
*/
	pasteArray:function(keys,vals){
		var _t=this;
		keys.forEach(function(value,key){
			_t[value]=vals[key];
		});
	},
/*
	function: hasKey
	checks if the hash has a specified key (even if the value is undefined or null
	
	parameters:
		(String) key - the key to check for

	returns:
		(Boolean) true if the key is present
*/	
	hasKey:function(key){
		return this.some(function(v,k){
			return k==key;
		});
	},
/*
	function: diff
	checks the hash values of the hash against another hash
	
	parameters:
		(Hash) obj - the hash to compare against

	returns:
		(Boolean) true if there is a difference in at least one value
*/	
	diff:function(obj){
		return this.some(function(v,k){
			return obj[k]!=v;
		});		
	}
});


/*
	function: toString
	converts the Hash to a string (similar to Array.join())
		
	parameters:
		(Array) dels - an array of the delimiters to use to join the hash.  For nested Hashes, additional delimeters will be passed down.  (reverse of <String.multiSplit>)
	
	returns:
		(String) a string representation of the Hash
*/
Class.method(Hash.prototype,"toString",function(dels){
		var d=dels||[];
		var tmp='';
		var d1=',';
		var d2='=';
		if(d.length>0){
			d1=dels.shift();
			if(d.length>0){
				d2=dels.shift();
			}
		}
		this.forEach(function(value,key){
			if(tmp.length>0){
				tmp+=d1;
			}
			tmp+=key+d2+(isHash(value)?value.toString(d.clone()):(isArray(value)?value.join(d1):value));
		});
		return tmp;
	});


Class.inherits(Hash.prototype,Iterator,false);

Class.inherits(Hash.prototype,{
/*
	Alias: insert
	<paste>
*/
	insert:Hash.prototype.paste,
/*
	Alias: concat
	<paste>
*/
	concat:Hash.prototype.paste,
/*
	Alias: append
	<paste>
*/
	append:Hash.prototype.paste,
/*
	Alias: clone
	<copy>
*/
	clone:Hash.prototype.copy,
/*
	Alias: findAll
	<filter>
*/
	findAll:Hash.prototype.filter,
/*
	Alias: find
	<indexOf>
*/
	find:Hash.prototype.indexOf,
/*
	Alias: merge
	<toString>
*/
	merge:Hash.prototype.toString,

/*
	Alias: contains
	<Array::contains>
*/
	contains:Array.prototype.contains,
/*
	Alias: keys
	<iterator::items>
*/
	keys:Iterator.items,
/*
	Alias: values
	<toArray>
*/
	values:Hash.prototype.toArray,
/*
	Alias: select
	<filter>
*/
	select:Hash.prototype.filter,
/*
	Alias: join
	<toString>
*/
	join:Hash.prototype.toString,
/*
	Alias: init
	<fill>
*/
	init:Hash.prototype.fill
});

/***************** end Hash **************************/

/******************************************************
	Script: Collections
	
	About:
	An array like Class for gathering, storing and manipulating DOM nodes.  (Similar to the browser native HTMLCollection).
	
*********************************************************/


/********************************************************
	Class: Collection		
	
	inherits:
		- <Iterator>
		- <Array.forEach>
		- <Array.every>
		- <Array.map>
		- <Array.some>
		- <Array.indexOf>
		- <Array.lastIndexOf>
		- <Array.filterKeys>
		- <Array.exec>
		- Array.splice
		- Array.push
		- Array.pop
		- Array.shift
		- Array.unshift
		- <DOM.search>
		- all DOM styling functions (e.g. <setStyle>)
		- all functions created with <DOM.make.elementStyleShortcut>
		
		
	Note:
		When a Collection runs a function with a return value , the return value data type is different based on what the function being run returns.  
		
		If *all* return values are of type Node, null, or Collection, a single Collection is return with all of the values, otherwise an Array of the return values is returned. 
		
		E.g. Collection.<$css> returns a single Collection of the values return by $css being run on all values in the Collection instead of an Array of Collections. Collection.<getStyle> returns 
		an Array of whatever is returned by the function being run on all values in the collection.
		
		
		
	
*********************************************************/



var Collection=Class.create();

Class.inherits(Collection.prototype,Iterator);
Class.inherits(Collection.prototype,adlib.ArrayProts);
/*
	function: filter
	Same as <Array.filter> except the return type is a Collection.	
		
*/

Class.method(Collection.prototype,"filter",function(funct,obj){
		var ret=new Collection();
		var rt;
		for(var key=0; key<this.length;key++){	
			rt=obj?funct.apply(obj,[this[key],key,this]):funct(this[key],key,this);
			if(rt){
				ret.push(this[key]);
			}
		}
		return ret;
	});
Class.inherits(Collection.prototype,{
	init:function(a){
		this.push.apply(this,[]);
		this.clear();
		if(a){
			this.push.apply(this,args2array(a));
			DOM.extend(this);
		}
	},
	filterKeys:Array.prototype.filterKeys,
	clear:Array.prototype.clear,
	splice:Array.prototype.splice,
	exec:Array.prototype.exec,	
	push:Array.prototype.push,
	shift:Array.prototype.shift,
	pop:Array.prototype.pop,
	unshift:Array.prototype.unshift,	
/*
	function: normalize
	Same as <Array.normalize>.
*/		
	normalize:function(){
		var tmp=new Collection();
		for(var x in this){
			if(this.noEnum(x)){
				continue;
			}
			tmp.push(this[x]);
		}
		this.clear();
		tmp.unshift(0);
		tmp.unshift(0);
		this.splice.apply(this,tmp);
	},
	
/*
	function: concat
	Same as Array.concat except the return type is a Collection.	
		
*/
	concat:function(){
		var q=new Collection();
		if(this.length>0){
			q.push.apply(q,args2array(this));
		}
		for(var x=0; x<arguments.length; x++){
			var a=args2array(arguments[x]);
			if(a.length>0){
				q.push.apply(q,a);
			}
		}
		return q;
	},
/*
	function: reverse
	Same as Array.reverse except the return type is a Collection.	
		
*/
	reverse:function(){
		return new Collection(Array.prototype.reverse.apply(this));
	},
/*
	function: clone
	Same as <Array.clone> except the return type is a Collection.	
		
*/
	clone:function(){
		return (new Collection()).concat(this);
	},
/*
	function: removeDuplicates
	Same as <Array.removeDuplicates> *(in place)*
	
	Note:
		This also <prune>s the Collection
		
*/
	removeDuplicates:function(){
		if(this.length==0){
			return;
		}
		var tmp=this.toHash();		
		this.clear();
		this.push.apply(this,tmp.toArray());
	},
/*
	function: toHash
	Converts the Collection to a <Hash> 
	
	Returns:
		(Hash) - a <Hash> of the Collection.  The key values are the Node's _uniqueId. (see <DOM.extend>).
		
	Note:
		This also <prune>s the Collection
		
*/
	toHash:function(){
		var h=new Hash();
		for(var x=0; x<this.length; x++){
			if(isCollectable(this[x])){
				var k=(this[x]==window)?"window":(this[x]==document)?"document":this[x]._uniqueId;
				h[k]=this[x];
			}
		}
		return h;
	},
	toArray:function(){
		return args2array(this);
	},
/*
	function: prune
	Removes all "null" and non-Element/window/document values within the Collection *(in place)*
*/
	prune:function(){
		for(var x=0; x<this.length; x++){
			if(!isCollectable(this[x])){
				delete this[x];
			}
		}
		this.normalize();
	}

});



/******************************************************
	Script: DOM Searching Methods
	
	About:
	Functionality to manipulate or search the DOM
*********************************************************/
var DOM=Class.create();
/*
	Section: Global Functions
*/
/*
	Function: $
	document.getElementById()
	
	Parameter: 
		(idRefArray) arg0,...argN - any number of <idRefArray>s (all array parameters are expanded as if individual arguments)
	
	Returns:
		1 - (Object) a single node pointer 
		2 - (Collection) a <Collection> of node pointers ;
		3 - (null) null
		
	example:
		1 - $('test')==document.getElementById('test')
		2 - $('test1','test2') returns a <Collection> of length 2
		3 - $('test1',['test2','test3'],'test4') returns a <Collection> of length 4
		
*/
	function $(){//works
		var ret=new Collection();
		var targs=[];
		for(var x=0; x<arguments.length; x++){
			if(isCollection(arguments[x])){
				arguments[x]=arguments[x].toArray();
			}
		}
		targs=targs.concat.apply(targs,arguments);
		for(var x=0; x<targs.length; x++){
 			if(isString(targs[x])){
				ret.push(document.getElementById(targs[x]));
			}
			else if(isCollectable(targs[x])){
				ret.push(targs[x]);				
			}
			else{
				ret.push(null);
			}
		}
		DOM.extend(ret);
		return (ret.length==1)?ret[0]:ret;
	};
/*
	function: $e
	document.createElement()
	
	parameters:
		(String) elementType - type of node to be created
		(Object) attributes - object with properties to be appended 
	
	returns: 
		(Node) new element node of type elementType 
	
	example:
		$e('div',{align:"left",class:"blue"})
		
		is equivalent to
		
		<div align="left" class="blue"></div>
*/

	function $e(etype,atrs){
		var obj=document.createElement(etype);
		for(var x in atrs){
			if((atrs.noEnum)&&(atrs.noEnum(x))){continue;}
			switch(x){
				case 'className': setClass(obj,atrs[x]); break;
				case 'style': applyStyle(obj,atrs[x]); break;
				default: obj.setAttribute(x,atrs[x]); break;
			}
		}
		DOM.extend(obj);
		return obj;
	};

/*
	Class: DOM
	Abstract class to contain the functions for later inheritance 

	Notes:
		- all DOM functions will automatically run <DOM.extend> on all returns
		- Browsers that support Element inherit automatically
		- $t('*') is run onDOMContentLoaded in all browsers so that all elements in the initial page are extended	
*/

/*
	Static: DOM.extend
	extends a node with all DOM functions (search, style, events).   Also adds a property of "_uniqueId".
	
	parameters:
		(<idRefArray>) elem - an elem or array of elements to be extended
		
	returns:
		nothing
	
	note: 
		This is run automatically by all DOM functions. 
*/
DOM.extend=function(elem){
	if(!elem){return}
	elem=isArray(elem)||isCollection(elem)?elem:[elem];
	elem.forEach(function(v){
		if(!v){
			return;
		}
		if(!v._uniqueId){			
			v._uniqueId='e'+(Math.floor(Math.random()*1000000000));
		}		
		if(v&&(v!=window)&&!v._isExtended){
			for(var x in DOM){
				if(isFunction(x)||x=="prototype"){
					continue;
				}
				Class.inherits(v,DOM[x]);
			}
		}
	});
};



/*
	Static: DOM.isDescendantOf
	checks if one node is a descendant of another node.
	
	parameters:
		(<idRef>) child - the child node 
		(<idRef>) parent - the parent node (document always returns true)
		
	returns:
		(Boolean) true/false
*/
DOM.isDescendantOf=function(c,p){
	c=$(c);
	p=$(p);
	var r=false;
	if(isCollectable(c)&&isCollectable(p)){
		if((p==document)||(p==window)){return true;}
		if((c==document)||(c==window)||(c==p)){return false;}
		var o=c.$parent();
		while(o!=p&&o!=document){
			o=o.$parent();
		}
		r=(o==p);
	}
	return r;
};




DOM.search={};	
adlib.CSS.store={
	nodes:new Hash(),
	counter:0,
	reset:function(){
		adlib.CSS.store.counter=0;
		adlib.CSS.store.nodes.clear();
	}	
};
adlib.CSS.rxStrings=new Hash({
	nthOpts:"((((-?)(\\d*)?)(n)(([\\+-])\\d*)?)|(\\d+)|(odd|even))",
	nameType:"([a-zA-Z][\\w-]*)"
});

adlib.CSS.multiTokens=new Hash({
	pseudo:'\\:((only|first|last)-(child|of-type))',		
	fpos:'\\:(even|odd|first|last|only)',
	pos:'\\:(eq|nth|gt|lt)\\(\\s*(\\d+)\\s*\\)',	
	containsText:'\\:contains\\(([^\\)]+)\\)',
	nth:'\\:(nth-(last-)?(child|of-type))\\('+adlib.CSS.rxStrings.nthOpts+'\\)',
	classType:'\\.'+adlib.CSS.rxStrings.nameType,
	attribute:'\\['+adlib.CSS.rxStrings.nameType+'(\\s*([\\^\\$\\*~\\|\\!])?=\\s*([\"]?)([^\\]\"]+)\\4?\\s*)?\\]'
});

adlib.CSS.singleTokens=new Hash({
	vis:'\\:(visible|hidden)',
	xabled:'\\:(en|dis)abled',
	fSel:'\\:(checked|selected)',
	empty:'\\:empty',
	id:'\\#'+adlib.CSS.rxStrings.nameType,	
	tag:"(\\*|[a-zA-Z][\\w-]*)"
});

adlib.CSS.uniqueTokens=new Hash({
	not:(function(){
				var ret="\\:not\\(\\s*(";
				var tmpStr="";
				adlib.CSS.multiTokens.forEach(function(v,k){
					tmpStr+=v+"|";
				});
				adlib.CSS.singleTokens.forEach(function(v,k){
					tmpStr+=v+"|";
				});
				ret+=tmpStr.substring(0,tmpStr.length-1)+")\\s*\\)";
				return ret;
			})(),
	combinators:"((\\s*(>|\\+|~)\\s*)|(\\s+))"
});			
adlib.CSS.tokenStrings=new Hash();
adlib.CSS.tokenStrings.paste(adlib.CSS.multiTokens,adlib.CSS.singleTokens,adlib.CSS.uniqueTokens);

adlib.CSS.tokens=new Hash();
adlib.CSS.tokenStrings.forEach(function(v,k){
	adlib.CSS.tokens[k]=new RegExp("^"+v);
});
delete adlib.CSS.tokenStrings;
adlib.CSS.rx=new Hash();
adlib.CSS.rxStrings.forEach(function(v,k){
	adlib.CSS.rx[k]=new RegExp("^"+v);
});


adlib.CSS.helper={
	checkAttributeValue:function(attVal,val, del){
		switch(del){
			case '|': 	return (attVal==val||attVal.startsWith(val+'-'));
					  	break;
			case '~': 	return attVal.contains(val,true);
					  	break;
			case '^': 	return (attVal.startsWith(val)); 
						break;
			case '$': 	return (attVal.endsWith(val)); 
						break;
			case '*': 	return (attVal.contains(val)); 
						break;
			case '!': 	return (attVal!=val); 
						break;
			default: return (attVal==val);
		}
	},
// checks if the element has a attribute
	checkAttributeStatus:function(nd,attr,val,mod){
		var att=(attr=='class')?nd.className:nd.getAttribute(attr);
		if(att){
			if(!isValidText(val)){
				return true;
			}
			var del=mod||'=';
			return adlib.CSS.helper.checkAttributeValue(att,val,del);
		}
		return false;
	},
// filters Nth rules
	filterFactorElements:function(nodes,opts){	
		var ret=new Collection(),step=parseInt(opts[3]),aval=parseInt(opts[7]);
		if(isNaN(step)){
			step=opts[4]?-1:1;		
		}
		if(isNaN(aval)){
			aval=0;
		}
		aval-=1;
		if(nodes.length>0&&(!(step<0&&aval<0))){	
			while(aval<0||(aval>=nodes.length&&step<0)){
				aval+=step;
			}	
			for(var x=aval; x<nodes.length&&x>=0; x+=step){
				if(nodes[x]){
					ret.push(nodes[x]);
				}
			}			
		}
		return ret;	
	},
// gets the Nth child
	getNth:function(node,factor,last,nType){
		if(nType){
			nType=nType.toLowerCase();
		}
		if((factor=="odd")||(factor[1]=="odd")||(factor[4]=="odd")){
			factor='2n+1';
		}
		if((factor=="even")||(factor[1]=="even")||(factor[4]=="even")){
			factor='2n';
		}
		var opts=isString(factor)?adlib.CSS.rx.nthOpts.exec(factor):factor.length>9?factor.slice(3):factor;
		var r=new Collection();
	
		if(opts[9]){
			r=node.childAt(parseInt(opts[9])-1,last,nType);
		}
		else{
			var ch=node.childElements((nType)?nType.toLowerCase():null);
			if(last){
				ch.reverse();
			}	
			r=adlib.CSS.helper.filterFactorElements(ch,opts);
			if(last){
				r.reverse();
			}
		}
		return r;
	},
// parses a CSS string into a Hash (option restriction set - array)
	parse:function(cssStr,set){
		var strs=cssStr.split(/\s*[,;]\s*/);
		strs.removeDuplicates();
		var groups=new Array();
		strs.forEach(function(cssString){
			var grp=new Hash();
				var matched=true, targ=grp;
				cssString=cssString.strip();			
				var tSet=set?adlib.CSS.tokens.copy(set):adlib.CSS.tokens;
				while(matched){
					matched=tSet.some(function(val,key){
						var mtch=val.exec(cssString);
						if(mtch){	
							cssString=cssString.substring(mtch[0].length);
							if(key=="combinators"){
								targ[key]=new Array(mtch,new Hash());
								targ=targ[key][1];
							}
							else{
								if(key=='pos'||key=='fpos'){
									var q=null;
									switch(mtch[1]){
										case 'only':
										case 'first':
										case 'last': key='pseudo';
													 mtch[2]=mtch[1];
													 mtch[3]='child';
													 break;
										case 'eq':
										case 'nth': q=mtch[2];
										case 'odd':
										case 'even': q=(q!=null?q:mtch[1]);
										case 'gt': q=(q!=null?q:'n+'+mtch[2]);
										case 'lt': q=(q!=null?q:'-n+'+mtch[2]);
													 key='nth';
													 mtch=adlib.CSS.tokens.nth.exec(':nth-child('+q+')');
													 break;
									}
								}
								if(adlib.CSS.multiTokens[key]||key=="not"){
									if(!isDefined(targ[key])){
										targ[key]=[];
									}
									targ[key].push(mtch);
								}
								else{
									
									targ[key]=mtch;
								}
								
							}
							return true;
						}
						return false;						
					});
				}
				if(cssString.strip().length>0){
					return;
				}
			groups.push(grp);
		});
		return groups;
	},
// does an initial node selection for a new CSS group (new or after " " combinator)
	doNewCSSGroup:function(val,globalList){
		var sType=null;
		var wList=new Collection();
		if(val['id']){
			sType='id';					
			for(var x=0; x<globalList.length; x++){
				var tmp=globalList[x].$(val["id"][1]);
				if(tmp){					
					wList=new Collection(tmp);
					break;
				}							
			}
		}
		else{
			var t="*";
			if(val["tag"]){
				sType='tag';
				t=val["tag"][1];
			}	
			if(adlib.CSS.store.counter==1){
				wList=globalList.$t(t);
			}
			else{
				var g=globalList.toHash();
				for(var x in g){
					if(g.noEnum(x)||g[x]==null){
						continue;
					}
					tmp=g[x].$t(t).toHash();
					g.cut.apply(g,tmp.keys());
					wList=wList.concat(tmp.toArray());
				}				
			}
		}
		if(globalList.length>1&&adlib.CSS.store.counter>1){
			wList.removeDuplicates();				
		}
		return [wList,sType];
	},
// filters a node group based on a CSS group (see parse)
	filterNodes:function(val,workingList,invert){
		var _OT="of-type";
		var fltr=invert?true:false;
		var exprs=[];
		workingList=workingList.filter(function(v){
			var NNm=v._nn();
			if(
				// ID
				(val.id&&(v.id!=val.id[1]))||
				// TAG
				(val.tag&&(NNm!=val.tag[1].toLowerCase()))||
				// EMPTY
				((val.empty)&&(!v.isEmpty()))
				){
				return fltr;
			}
			if(val.classType){					
				var c=v.className;
				for(var x=0; x<val.classType.length; x++){
					if(!c.contains(val.classType[x][1])){
						return fltr;
					}
				}					
			}
			if(val.attribute){
				for(var x=0,j=val.attribute; x<val.attribute.length; x++){
					if(!adlib.CSS.helper.checkAttributeStatus(v,j[x][1],j[x][5],j[x][3])){
						return fltr;
					}
				}
			}
			if(val.pseudo){
				for(var x=0,j=val.pseudo; x<val.pseudo.length; x++){
					var tag=(j[x][3]==_OT)?NNm:null;
					var p=v.$parent();
					switch(j[x][2]){
						case "first":	if(p.first(tag)!=v){
											return fltr;
										} 
										break;
						case "last":	if(p.last(tag)!=v){
											return fltr;
										}
										break;
						case "only":	if(adlib.CSS.helper.getChildren(p,tag).length!=1){											
											return fltr;
										}
										break;
					}												
				}
			}	
			if(val.nth){
				for(var x=0,j=val.nth; x<val.nth.length; x++){
					var f=j[x][2]?"nthLast":"nth";								
					var r=adlib.CSS.helper.getNthStore(v.$parent(),(j[x][3]==_OT?NNm:null),j[x],f);
					if((isCollectable(r)&&r!=v)||(isCollection(r)&&r.indexOf(v)==-1)||r==null){
						return fltr;
					}
				}							
			}
			
			if(val.fSel){
				var c=isDefined(v.checked);
				var s=isDefined(v.selected);
				if(!c&&!s){
					return fltr;
				}
				switch(val.fSel[1]){
					case "checked": if(!c||(c&&v.checked==false)){
										return fltr;
									}
									break;
					case "selected": if(!s||(s&&v.selected==false)){
										return fltr;
									}
									break;
				}
			}
			if(val.xabled){
				var a=v.disabled;
				if((val.xabled[1]=='dis'&&!a)||(val.xabled[1]=='en'&&a)){
					return fltr;
				}
			}
			if(val["vis"]){
				var a=(v.getStyle('display')=='none'||v.getStyle('visibility')=='hidden'||(NNm=='input'&&v.type=='hidden'));
				if((val.vis[1]=="hidden"&&!a)||(val.vis[1]=="visible"&&a)){
					return fltr;
				}							
			}
			
			if(val.containsText){
				for(var x=0,j=val.containsText; x<val.containsText.length; x++){
					var r=false;
					for(var y=0; y<v.childNodes.length; y++){
						if((v.childNodes[y].nodeType==3)&&(v.childNodes[y].nodeValue.contains(j[x][1]))){
							r=true;
							break;
						}
					}
					if(!r){
						return fltr;
					}
				}
			}
			return !fltr;				
		});
		if(val.not){
			for(var x=0,j=val.not; x<val.not.length; x++){
				workingList=adlib.CSS.helper.notFilterNodes(j[x][1],workingList);
			}
		}
		return workingList;					
	},
// inverts a filter for "not" selectors and runs filterNodes
	notFilterNodes:function(val,nodeList){
		var tmp=new Hash();
		adlib.CSS.tokens.every(function(v,k){
			var x=v.exec(val);
			if(x){
				tmp[k]=(adlib.CSS.multiTokens.hasKey(k))?[x]:x;
				return false;
			}
			return true;
		});
		return adlib.CSS.helper.filterNodes(tmp,nodeList,true);
	},
// stores nodes
	getNodeStore:function(id){
		if(!adlib.CSS.store.nodes[id]){
			adlib.CSS.store.nodes[id]=new Hash();
		}
		return adlib.CSS.store.nodes[id];
	},
// gets the children of a node and stores them
	getChildren:function(node,type){
		var a=adlib.CSS.helper.getNodeStore(node._uniqueId);
		var mString="childElements"+(type?"_"+type:"");
		if(!a[mString]){
			a[mString]=node.childElements(type);
		};
		return a[mString];
	},
// gets the nth children of a node and stores them
	getNthStore:function(node,type,opts,func){
		var a=adlib.CSS.helper.getNodeStore(node._uniqueId);
		var mString=func+((type)?type+"_":"")+opts[4].toCharCodeArray();
		if(!a[mString]){
			a[mString]=node[func](opts,type);
		}
		return a[mString];
	},
	doCSSGroup:function(value,workingNodes,sFilter){
		adlib.CSS.store.counter++;
		var sPoint,nodeList;
		if(!sFilter){
			sPoint=adlib.CSS.helper.doNewCSSGroup(value,workingNodes);
			nodeList=sPoint[0];
			delete value[sPoint[1]];	
			value.reset();
		}
		else{
			nodeList=workingNodes;
		}
		if(nodeList&&nodeList.length>0&&value.count()>0){		
			if(value.count()>0&&(!(value.count()==1&&value.combinators))){
				nodeList=adlib.CSS.helper.filterNodes(value,nodeList);
			}
			if((nodeList.length>0)&&value.combinators){		
				nodeList=adlib.CSS.helper.doSubGroup(value.combinators,nodeList);						
			}
		}
		return nodeList;					
	},
	doSubGroup:function(val,workingList){
		var c=val[0][1].strip();
		var filter=true;
		var tmpList=new Collection();
		var tag=val[1]["tag"]?val[1]["tag"][1]:null;
		switch(c){
			case '>':	workingList.forEach(function(v){
							tmpList=tmpList.concat(v.childElements(tag));
						});
						break;
			case '+':	workingList.forEach(function(v){
							var q=$(v).$next();
							if(q&&q._nn()==tag){
								tmpList.push(q);
							}
						});
						break;
			case '~':	var wList=new Hash();
						workingList.forEach(function(v){	
							if(!wList[v._uniqueId]){
								var ar=v.$next(true,tag);
								ar.filter(function(item){
									if(!wList[item._uniqueId]){
										wList[item._uniqueId]=true;
										return true;
									}
									return false;
								});
								if(ar){
									tmpList=tmpList.concat(ar);
								}
							}
						});
						break;
			default:	filter=false;	
						tmpList=workingList;			
		}
		if(tag&&filter){
			delete val[1]["tag"];
		}
		return (tmpList.length>0&&val[1].count()>0)?adlib.CSS.helper.doCSSGroup(val[1],tmpList,filter):tmpList;				
	},
	_getSibling:function(el,dir,nType){
		var r=el[dir+"Sibling"];
		while(r){
			if(r.nodeType==1){
				if(!nType||($(r)._nn()==nType)){
					break;
				}
			}
			r=r[dir+"Sibling"];
		}
		return r;
	},
	_childrenAround:function(el,start,before,nType){
		if(isNumber(start)){
			var c=el.childElements(nType);
			return before?c.slice(0,start):c.slice(start+1);
		}	
		else{
			var ret=new Collection();
			var tp=before?"previous":"next";
			var r=$(start);
			while(r=r[tp+"Sibling"]){
				if(r.nodeType==1){
					if(!nType||(nType.toLowerCase()==$(r)._nn())){					
						ret.push(r);
					}
				}
			}
			if(before){
				ret.reverse();
			}
			DOM.extend(ret);
			return ret;
		}
	},
	_getSingleChild:function(el,nType,last){		
		var r=el[(last?"last":"first")+"Child"];
		while(r&&(r.nodeType!=1||(nType&&(nType.toLowerCase()!=r._nn())))){			
			r=r[(last?"previous":"next")+"Sibling"];
		}
		return r;
	}
};

/* 
	Class: DOM.search
	Element extensions for searching and manipulating the DOM.
	
*/
Class.inherits(DOM.search,{	
	_isExtended:true,

/* 
	Function: $
	Takes an <idRefArray> and returns all nodes that are a descendant of the current node.
	
	See:
		see global <$> function for parameters and return values
	
*/
	$:function(){
		var a=window.$.apply(window,arguments);
		if(a){
			a=isCollection(a)?a:new Collection(a);
			var ret=a.map(function(v){
				return (v&&DOM.isDescendantOf(v,this))?v:null;
			},this);
			return (ret.length>1?ret:ret[0]);
		}
		return null;
	},



/*
	function: $t
	[currentElement].getElementsByTagNames() (multiple tag names)
	
	parameters:
		(String) arg0,...argN - Takes any number of tag names (case insensitive) as parameters
	
	returns:
		(Collection) a DOM ordered <Collection> of elements that match 
	
	Note:
		- This function is extended into the global namespace 

*/
	$t:function(){
		var z=[];
		var ar=args2array(arguments);
		if(ar.length>1){
			z=new Collection(this.getElementsByTagName('*'));
			z=z.filter(function(val){
				return	ar.some(function(v){
					return (val._nn()==v.toLowerCase());						
				});
			});			
		}
		else{
			z=new Collection(this.getElementsByTagName(ar[0]));
		}		
		return z;
	},


/*
	function: $c
	[currentElement].getElementsByClassname() 
	
	parameters:
		(String) classname - the classname to match
		(String) (,tagName1...)... - (optional) any number of tag names can be added as additional parameters to limit
	
	returns:
		(Collection) a DOM ordered <Collection> of elements that match 
	
	Note:
		- adding tags will improve performance on large documents as tags are filtered first, then classes
		- This function is extended into the global namespace 
	
	example:
		$c('myClass','div','p') will return all <div> and <p> that have class "myClass"

*/
	$c:function(classname){//works
		var nodes=arguments.length>1?this.$t.apply(this,args2array(arguments).slice(1)):new Collection(this.getElementsByTagName('*'));
		return nodes.filter(function(val){
			return val.hasClass(classname);
		});			
	},
	
/*
	function: $a
	[currentElement].getElementsByAttribute()
	
	parameters:
		(String) attribute - the attribute to be searched for
		(String) value - the specific attribute value to match.  
		(String) (,tagNames1...) - any number of tag names (case insensitive) as additional parameters to limit the search
		
	returns:
		(Collection) a DOM ordered <Collection> of elements that match 
		
	note:			
		- Can prepend "|","~","^","$","*","!" to the value for additional match criteria (see CSS2 and CSS3 attribute selectors and != means "not" ).
		- if passing tag-names, then pass '' or null to skip value searching
		- adding tags will improve performance on large documents as tags are filtered first, then attributes
		- This function is extended into the global namespace 
*/
	$a:function(attribute,value){
		var nodes=arguments.length>2?this.$t.apply(this,args2array(arguments).slice(2)):new Collection(this.getElementsByTagName('*'));
		var as=nodes.filter(function(nd){
			var del='=';
			var val=value;
			if(isValidText(value)){
				if(value.match(/^[~\|\^\$\*\!]/)){
					val=value.substring(1);
					del=value.charAt(0);						
				}
				return adlib.CSS.helper.checkAttributeStatus(nd,attribute,val,del);
			}
			else{
				return true;
			}
			return false;
		});		
		return as;
	},
/*
	function: $anc
	Walks up the DOM tree X times
	
	Parameters (type 1):
		(Number) depth - the number of steps up the DOM tree to move (always stops at document)
	
	Parameters (type 2):
		(String) cssString - A CSS string to match in the ancestor (Does not support combinator selectors ">","~"," ")
		(Number) filters  - (optional) the number of times the CSS selector needs to match (e.g. $anc('div.blue',2) finds the 2nd div with a class blue)
		
	Returns (type 1): 
		(Node) the element found or document if document is hit first
		
	Returns (type 2):
		(Node) the element found or null
		
	Example (type 1):
		mynode.$anc(2) == mynode.parentNode.parentNode
	
	Example (type 2):
		<tr id="myRow">
		
			<td><p id="mynode">example</td>
			
		</tr>
	
		$('mynode').$anc('tr') == $('myRow');	
*/
	$anc:function(){
		var obj=null;
		if(isNumber(arguments[0])){
			obj=this;
			for(var x=0; x<arguments[0]; x++){
				obj=obj.$parent();
				if(obj==document){
					break;
				}
			}
		}
		else if(isString(arguments[0])){
			var grps=adlib.CSS.helper.parse(arguments[0])[0];
			var a=this.$ancestors();
			var nds=grps.count()>0?adlib.CSS.helper.filterNodes(grps,a):[];
			var cnt=(arguments[1]!=null&&isNumber(arguments[1]))?arguments[1]:1;
			obj=(cnt<=nds.length)?nds[cnt-1]:null;
		}
		adlib.CSS.store.reset();
		return obj;
	},
/* 
	function: $ancestors
	Collects all ancestors (up to but not including document) of the current node
	
	returns:
		(Collection) a <collection> of all ancestors of the current node
*/
	$ancestors:function(){
		var r=new Collection();
		var p=this.$parent();
		while(p&&p!=document){
			r.push(p);
			p=p.$parent();
		}
		return r;
	},
		
		
/*
	function: isDescendantOf
	An alias to the global DOM.isDescendantOf(this,node)
	
	parameters:
		(Node) n - the node to check if this node is a descendant of
		
	returns:
		(Boolean) true if this node is below node N in the DOM tree
	
*/	
	isDescendantOf:function(n){
		return DOM.isDescendantOf(this,n);
	},
	
/*
	function: $css
	[currentElement].getElementsByCSS() (full CSS3 support)
	
	parameters:
		(string) cssString - the CSS string to match.  Multiple CSS Strings can be passed by added a ',' or ';' between declarations

	returns:
		(Collection) a DOM ordered <Collection> of elements that match 
	
	see:
		http://www.w3.org/TR/2005/WD-css3-selectors-20051215/
		
	Supported Selectors:
		- element
		- *
		- .classname
		- #id
		- [attributeName]
		- [attributeName=value] 
		- [attributeName~=value] (equals value or contains value with only spaces on either side)
		- [attributeName|=value] (starts with value followed by a hyphen)
		- [attributeName^=value] (startes with value)
		- [attributeName$=value] (ends with value)
		- [attributeName*=value] (contains value)
		- parentNode > childNode
		- node + AdjacentSiblingNode
		- node ~ GeneralSiblingNode
		- :nth-child()
		- :nth-last-child()
		- :nth-of-type()
		- :nth-last-of-type()
		- :only-child()
		- :only-of-type()
		- :first-child()
		- :first-of-type()
		- :last-child()
		- :last-of-type()
		- :not()
		- :empty
		- :checked
		- :enabled
		- :disabled
		
		
	Non-Standard Support Selectors (many inspired by the jQuery library and other libraries):
		- [attributeName!=value] the attribute is not equal to the value
		- contains(x) 	(a text node that is an immediate child of the element contains the text "x")
		- :even			(alias to :nth-child(even))
		- :odd			(alias to :nth-child(odd))
		- :first		(alias to :first-child)
		- :last			(alias to :last-child)
		- :only			(alias to :only-child)
		- :nth(X)		(alias to :nth-child(X) where X is a single number)
		- :eq(X)		(alias to :nth-child(X) where X is a single number)
		- :gt(X)		(alias to :nth-child(n+X) where X is a single number)
		- :lt(X)		(alias to :nth-child(-n+X) where X is a single number)
		- :visible		(Element does not have a display value of "none" or a visibility value of "hidden" and is not an input of type "hidden")
		- :hidden		(Element has a display value of "none" or a visibility value of "hidden" or is an input of type "hidden")
		- :selected		(an option element is selected)
		
	
	notes:
		- an un-parsable selector will return [] (or be ignored if multiple selectors)
		- This function is extended into the global namespace 
	
*/
	$css:function(cssStr){
		var _t=this;
		if(_t.nodeName==undefined||_t==window){_t=document;}
		var groups=adlib.CSS.helper.parse(cssStr);
		var allnodes=groups.map(function(grp){
			var currentNodes=new Collection([_t]);
			if(grp.count()==1){
				adlib.CSS.store.counter++;
				if(grp.tag){
					return _t.$t(grp.tag[1]);
				}
				else if(grp.id){					
					var i=_t.$(grp.id[1]);
					return i?new Collection(i):new Collection();
				}
			}			
			return adlib.CSS.helper.doCSSGroup(grp,currentNodes);
		});		
		var tmp=new Collection();
		tmp=tmp.concat.apply(tmp,allnodes);
		tmp.removeDuplicates();
		adlib.CSS.store.reset();
		return tmp;
	},
	
/*
	function: first
	returns the first child node of the current element that is an element.  Equivalent to the CSS :first-child
	
	parameters:
		(String) nType - (optional) an Element type (case insensitive).  Makes this equivalent to the CSS :first-of-type
	
	returns:
		(Node) the first element child node or null
		
	note:
		this function is not global
*/
	first:function(nType){
		return adlib.CSS.helper._getSingleChild(this,nType,false);
	},
/*
	function: last
	returns the last child node of the current element that is an element. Equivalent to the CSS :last-child 
	
	parameters:
		(String) nType - (optional) an Element type (case insensitive).  Makes this equivalent to the CSS :last-of-type
		
	returns:
		(Node) the last element child node or null
		
	note:
		this function is not global
*/
	last:function(nType){
		return adlib.CSS.helper._getSingleChild(this,nType,true);
	},
/*
	function: nth
	returns the nth children of the current node
	
	parameters:
		(String) factor - a nth argument (see the CSS3 standard for valid argument types).  Equivalent to the CSS :nth-child
		(String) nType - (optional) an Element type (case insensitive).  Makes this equivalent to the CSS :nth-of-type
		
	returns:
		(Collection) a <Collection> of the matched child elements or null
		
	note:
		this function is not global
*/
	nth:function(factor,nType){	
		return	adlib.CSS.helper.getNth(this,factor,false,nType);
	},
/*
	function: nthLast
	returns the nth children of the current node (searching from end to start)
	
	parameters:
		(String) factor - a nth argument (see the CSS3 standard for valid argument types).  Equivalent to the CSS :nth-last-child
		(String) nType - (optional) an Element type (case insensitive).  Makes this equivalent to the CSS :nth-last-of-type
		
	returns:
		(Collection) a <Collection> of the matched child elements or null
		
	note:
		this function is not global
*/
	nthLast:function(factor,nType){
		return	adlib.CSS.helper.getNth(this,factor,true,nType);
	},
	
/*
	function: $parent
	gets the parent node of the current node
	
	returns:
		(Node) the (extended) parent node of the current element 
	
	note:
		this function is not global
*/
	$parent:function(){		
		DOM.extend(this.parentNode);
		return this.parentNode;
	},
/* 
	function: $next
	gets the next element node sibling of the current node (ignoring text nodes)
	
	parameters:
		(Boolean) all - (optional) whether to get all sibling nodes after the current node
		(String) nType - (optional) an Element type (case insensitive). This restricts  the search to only elements of this node name
	
	returns:
		(Node/Collection) a single node (or <Collection> if all is true) or null 
	
	note:
		this function is not global
*/
	$next:function(all,nType){
		return (all?this.$parent().childrenAfter(this,nType):adlib.CSS.helper._getSibling(this,'next',nType));
	},	
/* 
	function: $previous
	gets the previous element node sibling of the current node (ignoring text nodes)
	
	parameters:
		(Boolean) all - (optional) whether to get all sibling nodes after the current node
		(String) nType - (optional) an Element type (case insensitive). This restricts  the search to only elements of this node name
	
	returns:
		(Node/Collection) a single node (or <Collection> if all is true) or null 
	
	note:
		this function is not global
*/
	$previous:function(all,nType){
		return (all?this.$parent().childrenBefore(this,nType):adlib.CSS.helper._getSibling(this,'previous',nType));
	},
/* 
	function: onlyChild
	returns whether the current node is the only Element child node of it's parent
	
	parameters:
		(Boolean) ofType - (optional) whether to search if the node is the only of its type instead
	
	returns:
		(Boolean) true if the element is the only child
	
	note:
		this function is not global
*/
	onlyChild:function(ofType){
		if(this==document){
			return true;
		}
		return (this.$parent().childElements((ofType?this._nn():null)).length==1);
	},
/* 
	function: isEmpty
	returns whether the current node is "empty" based on the W3C CSS3 definition <http://www.w3.org/TR/css3-selectors/#empty-pseudo>
	
	returns:
		(Boolean) true if the element has no children
	
	note:
		this function is not global
*/
	isEmpty:function(){
		if(this.childNodes.length==0){
			return true;
		}
		else{
			var c=this.childNodes;
			for(var x=0; x<c.length; x++){
				var t=c[x].nodeType;
				if(t==Node.ELEMENT_NODE||(t>=Node.TEXT_NODE&&t<=Node.ENTITY_NODE&&c[x].nodeValue.length>0)){
					return false;
				}
			}
		}
		return true;
		
	},
/* 
	function: childElements
	collects all element nodes that are children of the current element
	
	parameters:
		(String) nType - (optional) an Element type (case insensitive). This restricts  the search to only elements of this node name
	
	returns:
		(Collection) a <Collection> of all child elements of the current node
	
	note:
		this function is not global
*/
	childElements:function(nType){
		var ret=new Collection();
		var t=(nType&&(isString(nType)))?nType.toLowerCase():null;
		for(var x=0,r=this.childNodes; x<r.length; x++){
			if(r[x].nodeType==1){				
				if((!t)||(t==r[x]._nn())){
					ret.push(r[x]);
				}
			}
		}
		DOM.extend(ret);
		return ret;	
	},
	
/* 
	function: childrenBefore
	collects all element nodes that are children of the current element and come before the indicated position
	
	parameters:
		(Number/Node) end - a number or node to indicate where in the list of child nodes stop (not included in the return)
		(String) nType - (optional) an Element type (case insensitive). This restricts  the search to only elements of this node name
	
	returns:
		(Collection) a <Collection> of all child elements of the current node
	
	note:
		this function is not global
*/
	childrenBefore:function(end,nType){
		return adlib.CSS.helper._childrenAround(this,end,true,nType);
	},
/* 
	function: childrenAfter
	collects all element nodes that are children of the current element and come after the indicated position
	
	parameters:
		(Number/Node) end - a number or node to indicate where in the list of child nodes should start (not included in the return)
		(String) nType - (optional) an Element type (case insensitive). This restricts  the search to only elements of this node name
	
	returns:
		(Collection) a <Collection> of all child elements of the current node
	
	note:
		this function is not global
*/
	childrenAfter:function(start,nType){
		return adlib.CSS.helper._childrenAround(this,start,false,nType);
	},
/* 
	function: childAt
	returns the childNode[x] where only element nodes are counted
	
	parameters:
		(Number) pos - a number to indicate which child node should be returned
		(Boolean) last - (optional) whether the count should start from the end of children and count backwards (default=false)
		(String) nType - (optional) an Element type (case insensitive). This restricts  the search to only elements of this node name
	
	returns:
		(Node) the child element at the indicated position or null
	
	note:
		this function is not global
*/
	childAt:function(pos,last,nType){
		if(last==null){
			last=false;
		}			
		var r=this.childElements(nType);
		if(last){
			r.reverse();
		}
		return r[pos]?r[pos]:null;
	},
/* 
	function: _nn
	returns the node's nodeName converted to all lowercase
	
	returns:
		(String) the elements nodeName property converted to all lowercase
	
	note:
		this function is not global
*/
	_nn:function(){
		return this.nodeName.toLowerCase();
	}	
	
});




/*****************	end DOM Methods *********************/




DOM.custom={};
/******************************************************
	Script: DOM Extending functions
	
	About:
	Functionality to make functions which extend DOM elements
*********************************************************/

/*
	Class: DOM.make
*/
DOM.make={
	globalStyleFunction:function(names){
		if(!isArray(names)){
			names=[names];
		}
		names.forEach(function(v){
			window[v]=function(){
					var parms=args2array(arguments);
					var name=new Collection($(parms.shift()));
					if(!isDefined(name)){
						return;
					}
					var r=name[v].apply(name,parms);					
					if(r!=void 0){
						return r.length>1?r:r[0];
					}
				};
				Collection.prototype[v]=function(){
					var a=arguments;
					var r=this.map(function(node){
						if(isCollectable(node)){
							return node[v].apply(node,a);
						}
						else{
							return void 0;
						}
					});
					var q=r.some(function(val){
						return val!=void 0;
					});
					if(q){
						return r;
					}	
				};	
		});
	},
/* 
	Static: DOM.make.elementFunction
	takes a function and extends it to all DOM elements and to <Collection>s
	
	Parameters:
		(Hash/Object) funcs - a <Hash> of the name (as key) of the function on the element and the function itself (as value)
		(Boolean) overwrite - whether to overwrite any existing functions (default=false)
	returns:
		nothing
*/
	elementFunction:function(funcs,overwrite){
		var o=isDefined(overwrite)?overwrite:false;
		if(!isHash(funcs)){
			funcs=new Hash(funcs);
		}
		// extend DOM.custom
		funcs.forEach(function(v,k){
			adlib.cleanseNodeStack.push(k);
			Class.method(DOM.custom,k,v,o);
		});		
		// extend Collection
		DOM.make.collectionFunction(funcs.keys(),o);
		Class.inherits(document,DOM.custom);
		if(typeof Element!="undefined"){
			Class.inherits(Element.prototype,DOM.custom,o);			
		}
		else{
			var e=$t('*');
			for(var x=0; x<e.length; x++){
				Class.inherits(e[x],DOM.custom,o);
			}
		}		
	},
/* 
	Static: DOM.make.elementStyleShortcut
	creates getter and setter shortcuts to style properties for all elements/<collection>s and in the global space 
	
	Parameters:
		(Hash/Object) styles - a <Hash> of the last portion of the name (as key) of the style to be reference and the actual name of the style (as value)
		
	returns:
		nothing
		
	Example:
		var myStyles=new Hash({"Color":"color","BGImage":"background-image"});
		
		DOM.make.elementStyleShortCut(myStyles);
		
		will create
		
		- $('myNode').setColor(color value)
		- $('myNode').getColor()
		- $('myNode').setBGImage(background-image value)
		- $('myNode').getBGImage()
		- $t('div').setColor(color value)
		- $t('div').getColor()
		- $t('div').setBGImage(background-image value)
		- $t('div').getBGImage()
		- setColor(<idRefArray>, Color value)
		- getColor(<idRefArray>)
		- setBGImage(<idRefArray>, background-image value)
		- getBGImage(<idRefArray>)
		
*/
	elementStyleShortcut:function(styles){
		var p=new Hash();
		if(!isHash(styles)){
			styles=new Hash(styles);
		}
		styles.forEach(function(v,k){
			if(isString(v)){
				p['set'+k]=function(val){
					this.setStyle(v,val);
				};
				p['get'+k]=function(def){
					return this.getStyle(v,def);
				};
			}
			else{
				p[k]=v;
			}
		});
		DOM.make.elementFunction(p);
		DOM.make.globalStyleFunction(p.keys());
	},
	globalSearchFunction:function(names){
		if(!isArray(names)){
			names=[names];
		}
		names.forEach(function(v){
			adlib.cleanseNodeStack.push(v);
			window[v]=function(){
				var parms=args2array(arguments);
				return document[v].apply(document,parms);
			};
		});
	},
	
	collectionFunction:function(names,overwrite){
		var o=isDefined(overwrite)?overwrite:false;
		if(!isArray(names)){
			names=[names];
		}		
		names.forEach(function(v){
			Class.method(Collection.prototype,v,function(){
				var a=arguments;
				var ret=[];
				this.forEach(function(val){
					if(isCollectable($(val))){
						var q=val[v].apply(val,a);
						if(q!=void 0){
							ret.push(q);
						}
					}
				});
				if(ret.length>0){
					var p=new Collection();
					var c=ret.every(function(node){
						p=p.concat(node);
						return (node==null||isCollectable(node)||isCollection(node));
					});
					if(c){
						ret=p;
					}
				}
				if(isCollection(ret)||ret.length>0){
					return ret;
				}
			},o);
		});
	}
};


/******************************************************
	Script: Element Style and Class functions
	
	About:
	Element extensions for manipulating styles and classes.
	
	Usage:
		$('nodeID').function();
		
	Note:
	All functions are also in the global space with an additional 1st parameter of an <idRefArray>.  If run from the global space,
	the function will be run on each element passed in the first parameter.  E.x. setStyle(['myId1','myId2'],'color','#f00');
	
*********************************************************/
adlib.DOM.styles={
	getDim:function(el,wh,fDim){
		var dim=((wh=="w")?"Width":"Height");
		var resetDisplay=false;
		if(el.getStyle('display')=='none'){
			el.toggleOn();
			resetDisplay=true;
		}
		var r=el["offset"+dim];
		if(!fDim){
			var dirs=((wh=="w")?["Left","Right"]:["Top","Bottom"]);
			var bl = parseInt(el.getStyle("border"+dirs[0]+"Width"));
			var br = parseInt(el.getStyle("border"+dirs[1]+"Width"));
			var pl = parseInt(el.getStyle("padding"+dirs[0]));
			var pr = parseInt(el.getStyle("padding"+dirs[1]));
			bl = (isNaN(bl)?0:bl);
			br = (isNaN(br)?0:br);
			pl = (isNaN(pl)?0:pl);
			pr = (isNaN(pr)?0:pr);	
			r=r - bl - br - pl - pr;
		}		
		if(resetDisplay){
			el.toggleOff();
		}	
		return r;
	},
	getLoc:function(el,xy,cont){
		var cord=((xy=="x")?"Left":"Top");
		var par=el;
		var ofp=0;
		do{
			ofp+=par["offset"+cord]||0;
			par=par.offsetParent;
		}while(par);
		if(cont){
			var bord = parseInt(el.getStyle("border"+cord+"Width"));
			var pad = parseInt(el.getStyle("padding"+cord));
			bord = (isNaN(bord)?0:bord);
			pad = (isNaN(pad)?0:pad);
			ofp+=pad+bord;
		}
		return ofp;
	}
};

DOM.styles={
/******************************************************
	Group: Style Setting
	Functions to set the style of an element
*********************************************************/
/*
	Function: setStyle
	Sets a single style of an Element
	
	parameters:
		(String) styleName - css notation (eg: border-width) or  js notation  (eg: borderWidth)
		(String) val - the new value to be applied
	
	returns:
		nothing

*/
	setStyle:function(styleName, val){
		styleName=styleName.camelize();
		if(styleName=="opacity"){
			this.setOpacity(val);
		}
		else{
			if($if(styleName,'==','||',"width","height","top","left")&&isNumber(val)){
		 		val=val+'px';
			}
			else if(styleName.contains('Image')&&!styleName.startsWith('url(')){
				val='url('+val+')';
			}
			this.style[styleName]=val;
		}
	},

/*
	Function: applyStyle
	Takes a string of css declarations ("color:#000; font-size:11px; display:block") and applies <setStyle>
	for each item.
	
	parameters:
		(String/Hash) sText - the CSS declaration to be applied or a <Hash> of CSS style/value pairs
		
	returns:
		nothing
*/
	applyStyle:function(sText){
		var stls=(isHash(sText))?sText:sText.toHash([';',':']);
		stls.forEach(function(val,key){
			this.setStyle(key,val);
		},this);
	},
/*
	Function: setOpacity
	Sets the opacity value of an element.
	
	parameters:
		(String/Float) val - the value to set the opacity to.  Can be in percentages (e.g. 50 ... no % sign) or decimal (e.g. 0.5).  Will convert to browser specific notation.
		
	returns:
		nothing
*/
	setOpacity:function(val){	
		val=parseFloat(val);
		// handle IE
		if(isDefined(this.filters)){
			if(val<=1){
				val*=100;
			}
			var tmp="DXImageTransform.Microsoft.Alpha";
			if(this.filters[tmp]!=null){
				this.filters.item(tmp).opacity=val;
			}	
			else{
				this.style.filter+="progid:"+tmp+"(opacity="+val+")";
			}
		}else{	
			var stl=Object.selectProperty(this.style,"opacity","MozOpacity","KhtmlOpacity")||'opacity';
			if(val>1){
				val/=100;
			}
			this.style[stl]=val;
		}			
	},
	/*
		Function: setWidth
		Sets the width value of an element.
		
		parameters:
			(String/Integert) val - the value to set the width to. 
			
		returns:
			nothing
	*/
	setWidth:function(v){
		this.setStyle('width',v);
	},
	/*
		Function: setHeight
		Sets the height value of an element.
		
		parameters:
			(String/Integert) val - the value to set the height to. 
			
		returns:
			nothing
	*/
	setHeight:function(v){
		this.setStyle('height',v);
	},
	/*
		Function: setTop
		Sets the top value of an element.
		
		parameters:
			(String/Integert) val - the value to set the top to. 
			
		returns:
			nothing
	*/
	setTop:function(v){
		this.setStyle('top',v);
	},
	/*
		Function: setLeft
		Sets the left value of an element.
		
		parameters:
			(String/Integert) val - the value to set the left to. 
			
		returns:
			nothing
	*/
	setLeft:function(v){
		this.setStyle('left',v);
	},

	
/*******************end  style setting functions *********************/


/******************************************************
	Group: Style Getting 
	Functions to set the style of an element.  
	
	Note:
		For all "getters", if an array is passed in as the "name" parameter, an array of the return type is returned
*********************************************************/

/* 
	function: getStyle
	Retrieves the calculated value for a style
	
	parameters:
		(String) styleName - the style to be returned
		(String/Number) def - (optional) the default value to be returned if the style does not exist
	
	returns:
		(String/Number) the calculated value for styleName or the default value (if present) or the default value or null
*/
	getStyle:function(styleName, def){
		styleName=styleName.camelize();
		var tmp = def||null;
		if(styleName=="opacity"){
			this.getOpacity();
		}
		else if(styleName=="width"){
			this.getWidth();
		}
		else if(styleName=="height"){
			return this.getHeight();
		}
		else{
			if(document.defaultView&&document.defaultView.getComputedStyle(this,null)
				&&document.defaultView.getComputedStyle(this,null)[styleName]){
				return document.defaultView.getComputedStyle(this,null)[styleName];	
			}
			else if(this.currentStyle&&this.currentStyle[styleName]){
				return this.currentStyle[styleName];
			}
			else if(this.style[styleName]){
				return this.style[styleName];
			}
		}		
		return tmp;
	},

/*
	Function: getOpacity
	returns the current opacity value of an object.  Use this instead of getStyle!  
	
	returns:
		(Integer) the current value of the opacity.  *Return values are always returned as a percentage!* e.g. a W3C value of 0.5 will return 50, an IE value of 50 will return 50.
	
	
*/
	getOpacity:function(){
		if(isDefined(this.filters)){
			if(this.filters["DXImageTransform.Microsoft.Alpha"]!=null){
				return this.filters["DXImageTransform.Microsoft.Alpha"].opacity;
			}
			return 100;
		}
		else{
			var val=100;
			var stl=Object.selectProperty(this.style,"opacity","MozOpacity","KhtmlOpacity")||'opacity';
			var tmp=parseFloat(this.style[stl]);
			if(isNumber(tmp)){
				val=tmp*100;
			}
			return val;
		}
		return null;
	},
	
/*
	Function: getDefaultDisplay
	returns the default display type for an element based on its nodeName 
	
	returns:
		(String) the default display type for the element (e.g. "block" or "inline")	
	
*/
	getDefaultDisplay:function(){
		var n=this._nn();
		if(adlib.CSS.tableElements[n]){
			return adlib.CSS.tableElements[n];
		}
		if(adlib.CSS.blockElements.indexOf(n)>-1){
			return "block";
		}
		if(adlib.CSS.inlineElements.indexOf(n)>-1){
			return "inline";
		}
		return "block";		
	},

/******************************************************
	Group: Size and Position
	Functions to set the style of an element
*********************************************************/
/* 
	function: getWidth
	gets the width of the content portion of the element
	
	returns:
		(Integer) the current value of the width
	
*/
	getWidth:function(){
		return adlib.DOM.styles.getDim(this,'w',false);
	},
/* 
	function: getHeight
	gets the height of the content portion of the element
	
	returns:
		(Integer) the current value of the height
	
*/
	getHeight:function(){
		return adlib.DOM.styles.getDim(this,'h',false);
	},
/* 
	function: getFullWidth
	gets the full width of the element (content + padding + borders)
	
	returns:
		(Integer) the current value of the width + padding + borders
	
*/
	getFullWidth:function(){
		return adlib.DOM.styles.getDim(this,'w',true);
	},
/* 
	function: getFullHeight
	gets the full height of the element (content + padding + borders)
	
	returns:
		(Integer) the current value of the Height + padding + borders
*/
	getFullHeight:function(){
		return adlib.DOM.styles.getDim(this,'h',true);
	},
	

/* 
	function: getTop
	gets the calculated top property of an Element
	
	returns:
		(Integer) the current value of the the elements top coordinate
*/
	getTop:function(){
		return adlib.DOM.styles.getLoc(this,'y',false);
	},
/* 
	function: getLeft
	gets the calculated top property of an Element
	
	returns:
		(Integer) the calculated value of the elements left coordinate
*/
	getLeft:function(){
		return adlib.DOM.styles.getLoc(this,'x',false);
	},
/* 
	function: getContentTop
	gets the calculated top property of an Element

	returns:
		(Integer) the numeric value of the top position (y coord) of the content portion of an Element
*/
	getContentTop:function(){
		return adlib.DOM.styles.getLoc(this,'y',true);
	},
/* 
	function: getContentLeft
	gets the calculated left position (x coord) of the content portion of an Element
	
	returns:
		(Integer) the numeric value of the left position (x coord) of the content portion of an Element
*/
	getContentLeft:function(name){
		return adlib.DOM.styles.getLoc(this,'x',true);
	},
	
/*
	Function: moveObjTo
	Moves an element to a new top/left position.
	
	Parameters:
		(Number) x - the left value
		(Number) y - the top value
		
	returns:
		nothing
*/
	moveObjTo:function(x, y){
		this.setStyle('left',x);
		this.setStyle('top',y);
	},

/*********End Object size and location functions*******************/


/******************************************************
	Group: Class Manipulation
	Functions to set the style of an element
*********************************************************/
/*
	function: setClass
	Sets the full className attribute of an element
	
	parameters:
		(String) clsName - the new value for the class
	
	returns:
		nothing
*/
	setClass:function(clsName){
		this.className = clsName;
	},
/*
	function: getClassName
	gets the full className attribute of an element
	
	returns:
		(String) the className property
*/
	getClassName:function(){
		return this.className;
	},

/*
	function: addClass
	adds a class to an element (if it doesn't already have the class)
	
	parameters:
		(String) clsName - the new value for the class
	
	returns:
		nothing
*/
	addClass:function(clsName){
		if(!this.hasClass(clsName)){
			this.className+=' '+clsName;
		}
	},

/*
	function: removeClass
	removes all instances of a class from the element
	
	parameters:
		(String) clsName - the new value for the class
	
	returns:
		nothing
*/
	removeClass:function(clsName){	
		var cls=this.className;
		if(cls.contains(clsName,true)){
			var h=cls.strip().toHash(' ');	
			if(isString(h)){
				cls="";
			}
			else{
				h.cut(clsName);
				cls=h.keys().join(' ');
			}
			this.className = cls;
		}
		
	},

/*
	function: replaceClass
	replaces (and removes duplicates) a class with another class
	
	parameters:
		(String) remCls - the class to be replaced
		(String) addCls - the new class
	
	returns:
		Nothing
*/
	replaceClass:function(remCls, addCls){
		var cls=this.className.strip();
		var h=cls.toHash(' ');
		if(isString(h)){
			if(h==remCls){
				cls=addCls;
			}
		}
		else{
			if(h.hasKey(remCls)){
				delete h[remCls];
				var k=h.keys();
				k.push(addCls);
				cls= k.join(' ');
			}
		}
		this.className=cls;	
	},

/*
	function: rAddClass
	Removes all instances of a class and adds a new class.  The new class is added regardless of whether the old exists. (Replace OR Add)
	
	parameters:
		(String) remCls - the class to be removed
		(String) addCls - the new class
	
	returns:
		nothing
*/
	rAddClass:function(remCls,addCls){
		var cls=this.className.strip();
		var h=cls.toHash(' ');
		if(isString(h)){
			cls=((h==remCls)?addCls:h+' '+addCls);
		}
		else{
			if(h.hasKey(remCls)){	
				delete h[remCls];
			}
			var k=h.keys();
			k.push(addCls);
			cls=k.join(' ');
		}
		this.className=cls;
	},

/*
	function: hasClass
	checks if an element currently has a specified class
	
	parameters:
		(String) clsName - the class to check for
	
	returns:
		(Boolean) true/false
*/
	hasClass:function(clsName){
		return this.className.contains(clsName,true);
	},
/*********end class manipulation functions*******************/


/******************************************************
	Group: InnerHTML
	Functions to set the style of an element
*********************************************************/
/*
	Function: setHTML
	sets the "innerHTML" property of an element
	
	parameters:
		(String) content - the string to set the content of the element to
	
	returns:
		nothing
*/
	setHTML:function(content){
		this.innerHTML = content;
		this.$t('*');
	},

/*
	Function: getHTML
	gets the "innerHTML" property of an element
	
	returns:
		(String) string representation of the elements contents
*/
	getHTML:function(){
		return this.innerHTML.replace(/_isExtended\="true"/g,'');
	},
/*********end innerHTML functions*******************/


/******************************************************
	Group: display toggling
	Functions to to toggle the display of an element on and off
*********************************************************/

/*
	Function: toggle
	toggles the display of an element to the opposite of it's current state.  (remembers default display value if not "none")
	
	returns:
		nothing
*/
	toggle:function(){
		var d=this.getStyle("display");
		if(d=="none"){
			if(this._oldDisplay != null){
				this.style.display=this._oldDisplay;
				this._oldDisplay = null;
			}
			else{
				this.style.display=this.getDefaultDisplay();
			}
		}
		else{
			this._oldDisplay=d;
			this.style.display="none";
		}
	},	
/*
	Function: toggleOn
	toggles the display of an element on if it is currently display:none;  (remembers default display value if not "none")
	
	returns:
		nothing
*/
	toggleOn:function (){
		if(this.getStyle("display")=="none"){
			if(this._oldDisplay != null){
				this.style.display=this._oldDisplay;
				this._oldDisplay = null;
			}
			else{
				this.style.display=this.getDefaultDisplay();
			}
		}		
	},	
/*
	Function: toggleOff
	toggles the display of an element off if it is currently displayed (remembers default display value if not "none")
	
	returns:
		nothing
*/
	toggleOff:function(){
		var d=this.getStyle("display");
		if(d!='none'){		
			this._oldDisplay=d;
			this.style.display="none";
		}
	},

/*
	Function: show
	Sets the visibility property to "visible"
	
	returns:
		nothing
*/
	show:function(){
		this.style.visibility='visible';
	},
/*
	Function: hide
	Sets the visibility property to "hidden"
	
	returns:
		nothing
*/
	hide:function(){
		this.style.visibility='hidden';
	}
	
};






/******************************************************
	Script: Number
	
	About:
	Extensions to the core Number object
	
	Extends: 
		- Number.prototype
		
*********************************************************/
Class.inherits(Number.prototype,{
/*
	function: toHex
	converts the number to base16
	
	parameters:
		(Integer) precision - the minimum number of characters to return
	
	returns:
		(String) A string of the base16 representation of the number
*/
	toHex:function(precision){
		var p=precision||null;
		var rt=this.toString(16);
		if(p&&(rt.length<p)){
			rt=rt.pad(arguments[0]-rt.length,'l','0');
		}
		return rt;
	}

}); 
/*****************	End Number *********************/


/******************************************************
	File: Basic Utilities
	
	About:
	Simple utility functions, shortcuts etc
*********************************************************/
/*
	alias: dwl
	Shortcut to document.writeln
*/
function dwl(str){
	document.writeln(str);
};



/*
	function: $if
	checks one value against multiple values with a single operator
	
	parameters:
		(Any) testValue - the var/value to be checked
		(String) operator - the equivalency operator to use. Must be quoted. (e.g. '!=' or '>=')
		(String) type - '&&' or '||' (default='&&' if omitted)
		(Any) value1(,...valueN) - any additional number of arguments of values to check the testValue against
	
	returns:
		(Boolean) true if the testValue meets all conditions
		
	example:
>
>		$if(myVar,'!=','&&','value1','value2','value3')
>		
		
		is equivalent of
>		
>		if((myVar!='value1')&&(myVar!='value2')&&(myVar!='value3'))
>
*/
function $if(testValue,operator,type){
	var i=testValue;
	var tpe=((type=='&&')||(type=='||'))?type:'&&';
	var start=((type=='&&')||(type=='||'))?3:2;
	var ret=true;
	if(isString(i)){
		i='\''+i+'\'';
	}
	for(var x=start; x<arguments.length; x++){
		var v=arguments[x];
		if(isString(v)){
			v='\''+v+'\'';
		}
		if(tpe=='&&'){			
			if(!(eval(i+operator+v))){
				return false;
			}
			else{
				ret=true;
			}
		}
		else{
			if((eval(i+operator+v))){
				return true;
			}
			else{
				ret=false;
			}
		}
	}
	return ret;
};		
/************************* end basic utilities **************************/

/******************************************************
	Script: Events
	
	About:
	An Event management tool for adding and removing events ("onClick" etc) to HTML elements across browser types.  
	
	Events are managed by creating a "Queue" of functions that are to be run when the event is fired 

	Supports:
		- artificial events
		- user defined events (e.g. "onFoo")
		- does NOT support artificial Mouse Events
	
	Differences from w3c rules:
		- functions are run in the order they are called
		- the same function can be added multiple times
		- function scope can be maintained
		- additional parameters can be passed to the function	
		- a queue can be added to multiple elements
	
		
	functions in a queue should accept the following parameters:
		- event
		- element
		- additional parameters as passed in when the function is added (see <functionItem>
		
	
	Class: Events
*********************************************************/

var Events={
	items:new Hash(),
	assignedEvents:new Hash(),
	customIE:new Hash()
};


Class.inherits(Events,{	
/*
	static: add
	Creates an event Queue. *note* this does not assign the queue to an element (see <attach>)
	
	parameters:
		(String) id - An ID to be used to reference the queue (*not* an html element id)
		(String) eventName - the name of the event the queue is tied to without the "on" prefix.  e.g. to add as an "onclick" event, the eventName would be "click"
		(functionItem)(,...argN) - any number of additional arguments to <inQ>
	
	returns:
		nothing
	
	note:
		Do *NOT* put a hyphen in the id, it fails
	
*/
	add:function(id,eventName){
		this.items[id]=new Events.Event(eventName);
		for(var x=2; x<arguments.length; x++){
			this.inQ(id,arguments[x]);
		}
	},
/*
	static: remove
	Deletes an event queue and <detaches> the event queue from all elements it has been assigned to.
	
	parameters:
		(String) id - the reference ID for the queue (see <add>)
		
	returns:
		nothing
	
*/
	remove:function(id){
		var _t=this;
		var k=this.assignedEvents.filterKeys(function(val){
			return (val.eventId==id);
		});		
		k.forEach(function(val){
			_t.detach(val);
		});
		delete this.items[id];			
	},
/*
	static: inQ
	adds a <functionItem> to a queue.
	
	parameters:
		(String) id - the ID of the queue that the items should be added
		(functionItem)(,...argN) - any number of additional arguments to run when the event fires
		
	returns:
		nothing
	
*/
	inQ:function(id){
		for(var x=1; x<arguments.length;x++){
			var i=new Object();
			if(isFunction(arguments[x])){
				i.funct=arguments[x];
				i.obj=window;
				i.parameters=[];
			}
			else{
				i.funct=arguments[x].f||_Empty;
				i.obj=arguments[x].o||window;
				i.parameters=arguments[x].p||[];			
			}
			this.items[id].queue.push(i);
		}
	},
/*
	static: deQ
	removes a function call from a queue
	
	parameters:
		(String) id - the ID of the queue that the items should be added
		(Function) funct - the function to be removed
		(Boolean) all - remove all instances of that function in the queue or just the first(default=true)
		
	returns:
		nothing
	
*/
	deQ:function(id,funct,all){
		var a=isDefined(all)?all:true;
		for(var x=0; x<this.items[id].queue.length; x++){
			if(this.items[id].queue[x].funct==funct||String(this.items[id].queue[x].funct)==String(funct)){
				delete this.items[id].queue[x];
				if(!a){		
					this.items[id].queue.normalize();
					return ;
				}
			}
		}		
		this.items[id].queue.normalize();
	},
/*
	static: clone
	takes an existing queue and clones it with a new name and event (does not <attach> the queue to an element)
	
	parameters:
		(String) newName - the name of the new queue to be created
		(String) oldName - the name of the queue to be cloned
		(String) newEventName - the event the new queue should be assigned to	
		
	returns:
		nothing
*/
	clone:function(newName,oldName,newEventName){
		var ev=newEventName||this.items[oldName].eventType;
		this.add(newName,ev);
		this.items[newName].queue=this.items[oldName].queue.clone();
	},
/*
	static: attach
	attaches a queue to an element's event handler
	
	Note:
		- this can be run from an element as $('myId').attach(id,capture,cancel,stop); 
	
	parameters:
		(idRefArray) elem - the target element
		(String) id - the id of the event queue
		(Boolean) capture - (optional) whether to enable event capturing (see: w3c addEventListener() and MS setCapture()) (default=false) 
		(Boolean) cancel - (optional) whether to cancel the event (equivelent to returning false, see w3c event.preventDefault() and MS event.returnValue) (default=false) 
		(Boolean) stop - (optional) whether to stop event bubbling (see w3c event.stopPropagation() and MS event.cancelBubble) (default=false)
	
	Returns:
		(Integer) the event identifier (used, not required, in detaching the event)
	
*/
	attach:function(elem,id,capture,cancel,stop){
		if(!isCollection(elem)){
			elem=new Collection($(elem));
		}
		var timeRand=[];				
		elem.forEach(function(node,k){
			timeRand[k]=Math.floor(Math.random()*new Date().getTime());	
			var ev=this.makeAssignedEventObject(node,id,timeRand[k],capture,cancel,stop);
			if(document.addEventListener){// if w3c
				node.addEventListener(this.items[id].eventType,ev.run, ev._capture);
			}
			else{// assume IE
				if(ev._capture){
					node.setCapture();
				}
				node.attachEvent("on"+this.items[id].eventType,ev.run);
				if(node["on"+this.items[id].eventType]===undefined){
					if(!Object.hasProperty(this.customIE,this.items[id].eventType,node._uniqueId)){
						Object.setChild(this.customIE,[this.items[id].eventType,node._uniqueId],[timeRand[k]],"Hash");
					}
					else{
						this.customIE[this.items[id].eventType][node._uniqueId].push(timeRand[k]);
					}
				}
			}
		},this);
		return (timeRand.length==1)?timeRand[0]:timeRand;
	},
/*
	static: detach
	removes a queue from an elements event handler
	
	Note:
		- this can be run from an element as $('myId').detach(ID); where ID can be parameter type 1 ID or parameter type 2 eventType
	
	parameters (type 1):
		(Integer/Array) id - an event identifier(s) returned from <attach>
		
	parameters (type 2):
		(idRefArray) element - the element(s) that the event(s) should be removed from
		(String/Array) eventType - the name (or array of names of the event to remove ("click" for onclick) or '*' to remove all or the name of the event queue that was added via <Events.attach> (e.g. "myButtonClickEvent")
	
	returns:
		nothing
		

					
*/
	detach:function(){
		var i=[];
		if(arguments.length==1){
			i=i.concat(arguments[0]);
		}			
		else{
			var el=isArray(arguments[0])?arguments[0]:[arguments[0]];
			var t=isArray(arguments[1])?arguments[1]:[arguments[1]];
			i=this.assignedEvents.filterKeys(function(val,key){
				return ((el.indexOf(val.element)!=-1)&&((t.indexOf(Events.items[val.eventId].eventType)!=-1)||(t.indexOf('*')!=-1)||(t.indexOf(val.eventId)!=-1)))
			});
		}	
		i.forEach(function(v){
			if(document.removeEventListener){//if w3c
				this.assignedEvents[v].element.removeEventListener(this.items[this.assignedEvents[v].eventId].eventType,this.assignedEvents[v].run,this.assignedEvents[v]._capture);	
			}
			else{// assume IE					
				if(this.assignedEvents[v]._capture){
					this.assignedEvents[v].element.releaseCapture();
				}
				if(this.assignedEvents[v].element["on"+this.items[this.assignedEvents[v].eventId].eventType]===null){
					this.assignedEvents[v].element.detachEvent("on"+this.items[this.assignedEvents[v].eventId].eventType,this.assignedEvents[v].run);	
				}
				else{
					var ae=this.assignedEvents[v];
					if(Object.hasProperty(this.customIE,this.items[ae.eventId].eventType,ae.element._uniqueId)){
						this.customIE[this.items[ae.eventId].eventType][ae.element._uniqueId]=this.customIE[this.items[ae.eventId].eventType][ae.element._uniqueId].filter(function(val){
							return val!=v;
						});
					}
				}
			}			
			this.assignedEvents[v]=void 0;
			delete this.assignedEvents[v];
		},this);
			
	},	
/*
	static: cleanUp
	removes all assigned event Queue's from the document or removes all events from child nodes of a node
	
	Note:
		- this can be run from an element as $('myId').cleanUp();
		- This is automatically run on window.unload
	
	parameters:
		(idRefArray) node - (optional) a node from which all events on child nodes should be removed (document is treated the same as no parameter)
	
	returns:
		nothing
		
	
	
*/
	cleanUp:function(nod){
		if(!nod){
			nod=[document];
		}
		if(!isArray(nod)){
			nod=[nod];
		}
		var _t=this;
		nod.forEach(function(nd){			
			if(isDefined(nd)&&(isNode($(nd)))&&($(nd)!=document)){
				_t.assignedEvents.forEach(function(val,key){
					if(DOM.isDescendantOf(val.element,nd)){
						_t.detach(key);
					}
				});
			}
			else{
				_t.assignedEvents.forEach(function(val,key){
					_t.detach(key);
				});
			}
		});
	},

	makeAssignedEventObject:function(elem,id,t,capture,cancel,stop){	
		this.assignedEvents[t]={
				element:$(elem), 
				eventId:id,
				_capture:isBoolean(capture)?capture:false,
				_cancel:isBoolean(cancel)?cancel:false,
				_stop:isBoolean(stop)?stop:false
			};
		this.assignedEvents[t].run=new Function("e",
			"Events.items."+id+".run(e,Events.assignedEvents["+t+"]);"
		);
		return this.assignedEvents[t];
	},
/*
	static: stop
	stops event propogation (see w3c event.stopPropagation() and MS event.cancelBubble)
	
	parameters:
		(Object) e - an event object
	
	returns:
		nothing
*/
	stop:function(e){//stopBubble
		if(e.stopPropagation){
			e.stopPropagation();
		}
		else{
			e.cancelBubble=true;
		}
	},
/*
	static: cancel
	cancels the events default behavior (equivelent to returning false, see w3c event.preventDefault() and MS event.returnValue)
	
	parameters:
		(Object) e - an event object
	
	returns:
		nothing
*/
	cancel:function(e){//preventDefault
		if(e.preventDefault){
			e.preventDefault();
		}
		else{
			e.returnValue=false;
		}
	},
/*
	static: kill
	runs <stop> and <cancel> on an event
	
	parameters:
		(Object) e - an event object
	
	returns:
		nothing
*/
	kill:function(e){
		Events.stop(e); 
		Events.cancel(e);
	},
/*
	static: send
	Creates and fires an artificial event on an element. 
	
	parameters:
		(String) etype - the type of event to be created
		(idRefArray) el - the element to fire the event on
		(Object) props - (optional) an object whose properties the event should inherit
		(Boolean) cancelable - (optional) whether the event is cancelable (default=true)
		(Boolean) bubbles - (optional) whether the event should bubble (default=true)
		
	Known issues:
		- It does NOT support firing Mouse Events (including click).
		- Safari does not bubble fake events
*/
	send:function(etype,el,props,cancelable,bubbles){
		var elems=$(el);
		if(!isArray(elems)){
			elems=[elems];
		}
		var e=null;
		var c=isBoolean(cancelable)?cancelable:true;
		var b=isBoolean(bubbles)?bubbles:true;		
		elems.forEach(function(obj){
			if(document.createEvent){
				var e=null;
				try{
					e=document.createEvent("Events");
					e.initEvent(etype,b,c);
				}
				catch(err){
					e=document.createEvent("UIEvents");
					e.initEvent(etype,b,c,window,1);
				}
				if(props){
					Class.inherits(e,props);
				}
				obj.dispatchEvent(e);
			}else{
				e=document.createEventObject();
				if(props){
					Class.inherits(e,props);
				}
				if(obj["on"+etype]!==undefined){
					obj.fireEvent("on"+etype,e);
				}
				else{					
					var i;	
					e.type=etype;
					if(this.customIE[etype][obj._uniqueId]){
						this.customIE[etype][obj._uniqueId].forEach(function(v){							
							this.assignedEvents[v].run(e);
						},this);
						var o=obj.$parent();
						if(b&&o&&(!e.returnValue||!c)){							
							if(o!=document&&o!=window&&o!=document.body){
								this.send(etype,o,props,c,b);
							}
						}
					}
				}
			}		
		},this);
	}	
	
});


Events.Event=Class.create({
	init:function(eventName){
		this.eventType=eventName;
		this.queue=new Array();
	},
	run:function(e,ae){
		//fix for safari botching custom events
		if(e.type!=this.eventType||ae==null){
			return;
		}
		if(ae._stop){
			Events.stop(e);
		}
		if(ae._cancel){
			Events.cancel(e);
		}
		if(bis.ie){
			if(!this.baseObject){
				this.baseObject=(bis.smode)?document.body:document.documentElement;
			}
			e.target=e.srcElement;
			e.relatedTarget=(e.fromElement)?e.fromElement:e.toElement;		
			if(this.baseObject){
				e.pageX=e.clientX+this.baseObject.scrollLeft;
				e.pageY=e.clientY+this.baseObject.scrollTop;
			}
		}
		this.queue.forEach(function(val){
			var p=val.parameters.clone();
			p.unshift(ae.element);
			p.unshift(e);
			val.funct.apply(val.obj,p);
		});
	},
	baseObject:(bis.smode)?document.body:document.documentElement
});


/* 
	Section: Constants
	constants which can be used in scripts to normalize event codes
	
	Const: Events.Button
	- Events.Button.none
	- Events.Button.left
	- Events.Button.middle
	- Events.Button.right
*/
Events.Button={
	left:(bis.ie)?1:0,
	none:(bis.ie)?0:null,
	right:2,
	middle:(bis.ie)?4:1
};

/*
	Section: Pre-Defined Event Queues

	var: DOMLoad
	pre-defined Event Queue for "DOMContentLoaded". Use <Events.inQ> ('DOMLoad',....) to add scripts
*/
Events.add('DOMLoad','DOMContentLoaded');
/*
	var: BodyUnLoad
	pre-defined Event Queue for "window.onunload". Use <Events.inQ> ('BodyUnLoad',....) to add scripts. <cleanUp> automatically runs.
*/
Events.add('BodyUnLoad','unload',{f:Events.cleanUp,o:Events});
/*
	var: BodyLoad
	pre-defined Event Queue for "window.load". Use <Events.inQ> ('BodyLoad',....) to add scripts.
*/
Events.add('BodyLoad','load');

/* 
	var: clickAutoStop
	pre-defined prevents default behavior on 'click' events
*/
Events.add('clickAutoStop','click',function(ev){Events.cancel(ev)});

/* 
	var: submitAutoStop
	pre-defined prevents default behavior on 'submit' events
*/
Events.clone('submitAutoStop','clickAutoStop','submit');



if(bis.safari||(bis.opera&&bis.v<9)){
	(function(){
		if(/loaded|complete/.test(document.readyState)){
			Events.send('DOMContentLoaded',document);
			return;
		}
		else{
			setTimeout(arguments.callee,1);
		}
	})();
}
if(bis.ie){
	Events.startSize=0;
	Events.DOMLoaded=false;
	adlib.testDOMLoad=function(force){
		if(!Events.DOMLoaded){
			if(((Events.startSize!=0)&&(document.body.innerHTML.length==Events.startSize))||(force)){
				Events.DOMLoaded=true;
				Events.send('DOMContentLoaded',document);
			}
			else{
				Events.startSize=document.body.innerHTML.length;
				setTimeout(adlib.testDOMLoad,25);
			}
		}
	};		
	document.write('<scr'+'ipt defer src="//:" onreadystatechange="(function(el){if(el.readyState === \'complete\'){el.parentNode.removeChild(el); adlib.testDOMLoad();}})(this)"></sc'+'ript>');
	Events.inQ('BodyLoad',function(){adlib.testDOMLoad(true)});
}


DOM.events={
	attach:function(eventName,capture,cancel,stop){
		return Events.attach(this,eventName,capture,cancel,stop);
	},
	detach:function(eventId){
		if(isNumber(eventId)){
			Events.detach(eventId);
		}
		else{
			Events.detach(this,eventId);
		}
	},
	cleanUp:function(){
		Events.cleanUp(this);
	},
	runBehavior:function(a){
		Behaviors.run(a,this);
	}
};



/******************************************************
	Script: Behaviors
	
	Class: Behavior	
	
	requires:
		- Events
*********************************************************/
var Behavior=Class.create({
/*
	constructor: init
	creates a new Behavior object
	
	Parameters:
		(Object) o - An object of the properties for the Behavior
		
	Object Properties:
		(String/Collection) css - a css string or Collection of nodes
		(Object) events - (optional) an object with the event ids as property names and either *true* or an *array* of the capture/cancel/stop paramters as values (see <Events.add>)
		(Array) exec - (optional) an array of functions to run when the behavior is executed.  Each function should take a single parameter of the Node to run the function on.
	
	Example:
>			{
>				"css":cssString,
>		 		 "events":{"eventId":[capture,cancel,stop],"eventId":true}
>	 			 "exec":[function1,.....functionN]
>			}
		
*/

	init:function(o){
		this.css=o.css;
		this.events=new Hash();
		this.runnow=new Array();
		if(arguments[0].events){
			this.events.paste(o.events);
		}
		if(o.exec){
			if(isArray(o.exec)){
				this.runnow=o.exec;
			}
			else if(isFunction(o.exec)){
				this.runnow.push(o.exec);
			}
		}
	},
/*
	function: exec
	Executes the behavior
	
	parameters:
		(idRefArray) targ - (optional) a element or array of elements at which point the <$css> search should start.  If omitted, defaults to document. Parameter is ignored if the css property of the behavior is an array of nodes instead of a css string.
	
	returns:
		nothing
*/
	exec:function(targ){
		var _t=this;
		targ=targ||document;
		this.nodes=new Collection();
		if(!isCollection(targ)){
			targ=new Collection($(targ));
		}
		if(isCollection(this.css)){
			this.nodes=this.css;
		}else{
			this.nodes=targ.$css(this.css);
		}
		this.nodes.forEach(function(nd){
			_t.runnow.forEach(function(f){
				f(nd);
			});
			_t.events.forEach(function(val,key){
				var a=[];
				if(isArray(val)){
					a=val.clone();
				}
				a.unshift(key);
				a.unshift(nd);
				Events.attach.apply(Events,a);
			});			
		});	
	},
/*
	function: setCss
	sets the CSS value of the behavior
	
	parameters:
		(String) css - a css string or an array of nodes
		
	returns:
		nothing
*/
	setCss:function(css){
		
		this.css=css;
	}
});

/*
	Class: Behaviors
	a static object for Behavior manipulation
*/
var Behaviors={

/*
	Static: run
	 Executes one or more <Behavior>s 
	 
	 Note:
		- this can be run from an element as $('myId').runBehavior(behavior); 
	
	parameters:
		(Array/Hash/Behavior) behavior - an array or hash of or single <Behavior>s (if passed in from an event, it will ignore the event properties)
		(idRefArray) targ - (optional) a element or array of elements at which point the <$css> search should start.  If omitted, defaults to document. 
		
	returns:
		nothing
	
*/
	run:function(a,targ){
		var x=((arguments.length>2)?arguments[2]:a);
		var y=((arguments.length>2)?((arguments[3])?arguments[3]:document):((targ)?targ:document));	
		if(!isArray(x)){
			x=[x];
		}	
		x.forEach(function(v){
			v.exec(y);
		});
	},
	
	runAtLoad:function(){
		if(arguments[2]=="DOM"){
			Behaviors.run(Behaviors.DOMLoadItems);
		}
		else if(arguments[2]=="Body"){
			Behaviors.run(Behaviors.BodyLoadItems);
		}
	},
/*
	Static: addToDOMLoad
	Pushes a single (or array of) behavior(s) into the DOMLoad stack
	
	parameters:
		(Behavior/Array) behavior1(,....behaviorN) - any number of parameters, each should be either a behavior or an array of behaviors
		
	returns:
		nothing
	
*/
	addToDOMLoad:function(){
		Behaviors.DOMLoadItems=Behaviors.DOMLoadItems.concat.apply(Behaviors.DOMLoadItems,arguments);
	},
/*
	Static: addToBodyLoad
	Pushes a single (or array of) behavior(s) into the DOMLoad stack
	
	parameters:
		(Behavior/Array) behavior1(,....behaviorN) - any number of parameters, each should be either a behavior or an array of behaviors
		
	returns:
		nothing
	
*/
	addToBodyLoad:function(){
		Behaviors.BodyLoadItems=Behaviors.BodyLoadItems.concat.apply(Behaviors.BodyLoadItems,arguments);
	},
/*
	Section: Pre-Defined Behavior Queues
	
	Var: DOMLoadItems
	An array for behaviors to be run in the DOMLoad event 
	
	See: <Behaviors.addToDOMLoad>
*/
	DOMLoadItems:new Array(),
/*
	Var: BodyLoadItems
	An array for behaviors to be run in the body onLoad event. 
	
	See: <Behaviors.addToBodyLoad>
*/
	BodyLoadItems:new Array()
};






adlib.cleanseStack=["DOM","Events","fx","behaviors","widgets","Class","Iterator"];
adlib.cleanseNodeStack=[];
adlib.cleanse=function(){
	if(typeof Element!="undefined"){
		var nds=document.getElementsByTagName('*');
		for(var x=0; x<nds.length; x++){
			for(var y=0; y<adlib.cleanseNodeStack.length; y++){
				nds[x][adlib.cleanseNodeStack[y]]=null;
			}
		}
	}
	for(var x=0; x<adlib.cleanseStack.length; x++){
		window[adlib.cleanseStack[x]]=null;
	};
	adlib=null;
};

$(document);
/**** init events ******/

Events.attach(document,'DOMLoad');
Events.attach(window,'BodyLoad');
Events.attach(window,'BodyUnLoad');

/*** init Element inheritance ****/
if(typeof Element!="undefined"){
	Class.inherits(Element.prototype,DOM.styles);
	Class.inherits(Element.prototype,DOM.search);
	Class.inherits(Element.prototype,DOM.events);
}

/**** init DOM shortcuts *****/
DOM.make.globalStyleFunction((new Hash(DOM.styles)).keys());
DOM.make.globalSearchFunction(['$css','$t','$c','$a']);
DOM.make.collectionFunction((new Hash(DOM.search)).keys());
DOM.make.collectionFunction((new Hash(DOM.events)).keys());
Events.inQ('DOMLoad',function(){if(isDefined(window["Logs"])){Logs.addLog('Timeline',['DOMLoad','Start'])}});
Events.inQ('BodyLoad',function(){if(isDefined(window["Logs"])){Logs.addLog('Timeline',['BodyLoad','Start'])}});
Events.inQ('BodyLoad',function(){Events.inQ('BodyUnLoad',adlib.cleanse);});

/*** init behaviors ***/
Events.inQ('DOMLoad',{f:Behaviors.runAtLoad,p:["DOM"]});
Events.inQ('BodyLoad',{f:Behaviors.runAtLoad,p:["Body"]});

/******************************************************
	Script: Logging
	
	About:
	Logging utility 
*********************************************************/


/*
	Class: Logs
	Logging Object
	
*/
var Logs=new Object();
Class.inherits(Logs,{
	init:function(){
		this.types=new Hash();
		this.stylesheet=null;
	},
/*
	function: addLogType
	Adds a logging category (type).  To add a log, a log type must first be added
	
	parameters:
		(String) typeName - The category to be added (standard js var rules apply)
		(Boolean) status - whether the log type is enabled
		(Boolean) pop - whether an alert message is generated when a log is added (good for error debugging)
		(Array) headers - the headers/labels for the log type when it is output.
	
	returns:
		nothing
		
	Note:
		A timestamp is automatically added to all logs.  A "Time" header is also automatically added to the headers array.
*/
	
	addLogType: function(typeName,status,pop,headers){
		this.types[typeName] = new LogType(status,pop,headers);
	},
/* 
	function: enableLogType
	turns logging on for a specified log type
	
	parameters:
		(String) typeName - the category(type) that should be enabled
	
	returns:
		nothing
*/
	enableLogType: function(typeName){
		if(isDefined(this.types[typeName])){
			this.types[typeName].active = true;
		}
	},	
/* 
	function: enableAllLogs
	turns logging on for all log types
	
	returns:
		nothing

*/
	enableAllLogs: function(){
		this.types.forEach(function(val){
			val.active=true;
		});
	},
/* 
	function: disableLogType
	turns logging off for a specified log type
	
	parameters:
		(String) typeName - the category(type) that should be enabled
		
	returns:
		nothing
*/
	disableLogType: function(typeName){
		if(isDefined(this.types[typeName])){
			this.types[typeName].active = false;
		}	
	},
/* 
	function: disableAllLogs
	turns logging off for all log types
	
	returns:
		nothing

*/
	disableAllLogs: function(){
		this.types.forEach(function(val){
			val.active=false;
		});
	},
/* 
	function: addLog
	adds a log entry
	
	parameters:
		(String) logType - the category(type) that the entry should go under
		(Array) logItem - an array of the items to be logged.  Should match the order of the headers entered when <addLogType> for the type was added.  Timestamp is automatically added.
		
	returns:
		nothing
*/
	addLog:	function(logType,logItem){
		if(isDefined(this.types[logType])){
			if(this.types[logType].active){
				logItem.push((new Date).getTime());
				this.types[logType].items.push(logItem);
			}
			if(this.types[logType].alerts){
				var msg='';
				logItem.forEach(function(itm,cnt,tpe){
					msg+=this[cnt]+': '+itm+_NL;
				},this.types[logType].properties);
				alert(logType+_NL+msg);				
			}
		}
	},
/* 
	function: enableAlert
	turns on alert messages for a log type
	
	parameters:
		(String) typeName - the category(type) that should be enabled
		
	returns:
		nothing
*/
	enableAlert:function(typeName){
		if(isDefined(this.types[typeName])){
			this.types[typeName].alerts=true;
		}
	},
/* 
	function: enableAllAlerts
	turns on alert messages for all log types
	
	returns:
		nothing

*/
	enableAllAlerts: function(){
		this.types.forEach(function(val){
			val.alerts=true;
		});
	},
/* 
	function: disableAlert
	turns off alert messages for a log type
	
	parameters:
		(String) typeName - the category(type) that should be disabled
	
	returns:
		nothing
*/
	disableAlert:function(typeName){
		if(isDefined(this.types[typeName])){
			this.types[typeName].alerts=false;
		}
	},
/* 
	function: disableAllAlerts
	turns off alert messages for all log types
	
	returns:
		nothing

*/
	disableAllAlerts: function(){
		this.types.forEach(function(val){
			val.alerts=false;
		});
	},
/* 
	function: clearLogs
	Clears the log entries for a specified type
	
	parameters:
		(String) typeName - the category(type) that should be cleared
		
	returns:
		nothing
*/
	clearLogs:function(typeName){
		if(isDefined(this.types[typeName])){
			this.types[typeName].items.clear();
		}
	},
/* 
	function: clearAllLogs
	Clears all logs
		
	returns:
		nothing
*/
	clearAllLogs:function(){
		this.types.forEach(function(v,k){
			v.items.clear();
		});
	},
/* 
	function: setStyleSheet
	sets the URL for a stylesheet to use in <showLogs>
	
	parameters:
		(String) url - the url of the stylesheet
	
	returns:
		nothing
*/
	setStyleSheet: function(url){
		this.stylesheet=url;
	},
	getStyleSheet: function(){
		return ((this.stylesheet)?'<link rel="stylesheet" href="'+this.stylesheet+'" type="text/css" />':'');
	},
/* 
	function: getLogs
	Retrieves all log entries for a single log type
	
	parameters:
		(String) typeName - the category(type) that should be enabled
		
	returns:
		(String) an HTML string of all of the log entries (table) with a header of the type name
*/
	getLogs: function(typeName){
		var outp='';
		if(this.types[typeName].active){
			outp+='<h1>'+typeName+'</h1>'+_NL+'<table cellpadding="5" cellspacing="0" border="1">'+_NL;
			outp+='<tr>'+_NL;
			var odd=true;
			this.types[typeName].properties.forEach(function(val,key){
				outp+='<th class="'+(odd?'odd':'even')+'TH">'+val+'</th>'+_NL;
				odd=!odd;
			});
			outp+='<th class="'+(odd?'odd':'even')+'TH">Time Difference (in ms)</th>'+_NL;
			outp+='</tr>'+_NL;		
			odd=true;
			var lastVal=0;
			var tDif=0;
			this.types[typeName].items.forEach(function(val,key){
				outp+='<tr class="'+(odd?'odd':'even')+'TR">'+_NL;
				odd= !odd;
				var odd2=true;
				val.forEach(function(v,k){
					outp+='<td class="'+(odd2?'odd':'even')+'TD">'+v+'</td>'+_NL;
					odd2= !odd2;
				});
				tDif=(key==0)?0:(val[val.length-1]-lastVal);
				lastVal=val[val.length-1];
				outp+='<td class="'+(odd2?'odd':'even')+'TD">'+tDif+'</td>'+_NL;					
				outp+='</tr>'+_NL;
			});			
			if(this.types[typeName].items.length>0){
				outp+='<tr>'+_NL;
				outp+='<td colspan="'+(this.types[typeName].items[0].length+1)+'" class="total"><span>Total Time:</span> '+(this.types[typeName].items[this.types[typeName].items.length-1][this.types[typeName].items[0].length-1]-this.types[typeName].items[0][this.types[typeName].items[0].length-1])+'</td>'+_NL;
				outp+='</tr>'+_NL;		
			}
			outp+='</table>'+_NL;
		}
		return outp;
	},
/* 
	function: getAllLogs
	Generates HTML of all log types.  Drop down selection displays each individual category.
	
	returns:
		(String) HTML string of all logs
*/
	getAllLogs:function(){
		var ctId='logContainer-';
		var scpt="<scr"+"ipt>"+_NL;
		scpt+="function setLogSelection(t){"+_NL;
		scpt+="var sel=document.logView.selection;"+_NL;
		scpt+="var val=sel.options[sel.selectedIndex].value;"+_NL;
		scpt+="var hd=document.getElementById('logView-Current');"+_NL;
		scpt+="if(val!='null'){document.getElementById(val).style.display='block';}"+_NL;
		scpt+="if(hd.value!='null'){document.getElementById(hd.value).style.display='none';}"+_NL;
		scpt+="hd.value=val;"+_NL;			
		scpt+="}</sc"+"ript>"+_NL;			
		var cont='';
		var fm='<form name="logView">'+_NL;
		var selection='<select name="selection" onchange="setLogSelection(this)"><option value="null" selected="selected"></option>'+_NL;
		var tr='<input type="hidden" name="logView-Current" id="logView-Current" value="null" />';	
		this.types.forEach(function(v,k){
			if(this.types[k].active){
				selection+='<option value="'+ctId+k+'">'+k+'</option>'+_NL;
				cont+='<div id="'+ctId+k+'" style="display:none">'+this.getLogs(k)+'</div>'+_NL;
			}
		},this);
		cont=scpt+fm+tr+selection+'</select></form>'+cont;
		return cont;
	},
/* 
	function: showLogs
	Creates a pop-up window with logs.  If stylesheet is set, then that is added to the window.
	
	parameters:
		(String) typeName - (optional) the category(type) that should be displayed.  If omitted all logs are shown.
	
	returns:
		nothing
*/
	showLogs: function(typeName){
		var cont=this.getStyleSheet()+(isValidText(typeName)?this.getLogs(typeName):this.getAllLogs());
		var w=window.open('://','logConsole','width=400,height=400,resizable,scrollbars,status,top=0,left=0');
		function setLogContent(){
			if(w.document){
				var d=w.document;
				d.open();
				d.write(cont);
				d.close();
			}
			else{
				setTimeout(arguments.callee,10);
			}
		};
		setLogContent();
	}
		
								
});

var LogType=Class.create();
LogType.prototype={
	init:function(active,pop,titles){
		this.active=active;
		this.alerts=pop;
		this.properties=titles;
		this.properties.push('Timestamp');
		this.items=[];
	}
};	

Logs.init();

/*
	section: Functions
*/

/*
	Alias: Log
	alias to <Logs.addLog>
*/
function Log(lType,parms){
	Logs.addLog(lType,parms);
};

/******************end logging**********************/

Logs.addLogType('Timeline',true,false,['Section','Operation']);
adlib.cleanseStack.push("Logs");

/******************************************************
	Script: Cookies
	
	About:
	A class for reading/writing cookies
	
	Class: Cookies	
	
*********************************************************/


var Cookies=Class.create();
Cookies.prototype={
	init:function(){
		this.name='test';
		this.delimiters=[];
		this.readList=[];
		this.cookieValue=null;
		this.path=null;
		this.expires=null;
		this.domain=null;
	},
/*
	function: addReadableCookie
	if readable cookies are set, then only those cookies within the list are read into the object. This allows you
	to selectively ignore extra cookies that you don't care about (session ID cookies etc)
	
	parameters:
		(String/Array) cookieName1(,...cookieNameN) - any number of cookie names or arrays of cookie names to be added to the readable list
	
	returns:
		nothing
*/
	addReadableCookie:function(){
		this.readList.concat.apply(this.readList,arguments);
	},
/*
	function: setDomain
	sets the generic domain for all cookies being set (unless overridden at write time by the function doing the writing).  If not set, then no domain is added (browser default behavior).
	
	parameters:
		(String) domain - the domain to be used
	
	returns:
		nothing
*/
	setDomain: function(domain){
		this.domain=domain;
	},
/*
	function: setPath
	sets the generic path for all cookies being set (unless overridden at write time by the function doing the writing).  If not set, then no path is added (browser default behavior).
	
	parameters:
		(String) path - the path to be used	
	
	returns:
		nothing
*/
	setPath: function(path){
		this.path=path;
	},
/*
	function: setExpires
	sets the generic expires for all cookies being set (unless overridden at write time by the function doing the writing).  If not set, then no expires is set (browser default behavior, session).
	
	parameters:
		(String/Date) expi - a Date object or GMT String
	
	returns:
		nothing
*/
	setExpires: function(expi){
		this.expires=((expi instanceof Date)?expi.toGMTString():expi);
	},
/*
	function: setDelimiters
	sets the set of delimiters to be used to split a single cookie into multiple pieces (see <String.multiSplit>).
	
	parameters:
		(String) arg1(,...argN) - any number of delimiters to use to split the cookie value
		
	returns:
		nothing
*/
	setDelimiters:function(){
		this.delimiters.clear();
		for(var x=0; x<arguments.length; x++){
			this.delimiters[x]=arguments[x];
		}
	},
/* 
	function: getRawCookie
	gets the raw (decoded but not split) value of a cookie
	
	parameters:
		(String) cookieName - (optional)the name of the cookie being requested.  If omitted, all cookies are returned
		
	returns:
		(String) if cookieName, a string with the raw value of the cookie
		
		 or
		 
		(Hash) otherwise, a <Hash> of all cookies raw format (indexed by name)
*/
	getRawCookie:function(cookieName){
		var tmp=decodeURIComponent(document.cookie);
		var ck=null;
		if(isValidText(tmp)){			
			ck=tmp.toHash([';','=']);
		}
		else{
			ck=new Hash();
		}
		if(isValidText(cookieName)){
			return ck[cookieName];		
		}
		else{
			return ck;
		}
	},
/* 
	function: getAllCookies
	gets all cookies and splits them into nested <Hashes> if delimiters are set
	
	returns:
		(Hash) a <Hash> of all cookies split by set delimiters, indexed by cookie name
*/
	getAllCookies:function(){
		var cokys=this.getRawCookie();
		var _t=this;
		var gList=cokys.filterKeys(function(val,key){
				return ((_t.readList.length>0)?_t.readList.contains(key):true);
			});
		cokys=cokys.cut.apply(cokys,gList);
		if(this.delimiters.length>0){
			cokys.run(function(val,key){
				this[key]=val.multiSplit(_t.delimiters.clone());
			});
		}
		return cokys;
	},
/* 
	function: setCookie
	sets a cookie to a specified value (or builds it from the existing structure)
	
	parameters:
		(String) cookieName - the name of the cookie to set
		(String) value - (optional) the value to set the cookie to (builds the cookie from existing data if omitted)
		(String) expires - (optional) the expiration date for the cookie (uses the global if omitted, see <setExpires>).
		(String) domain - (optional) the domain for the cookie (uses the global if omitted, see <setDomain>).
		(String) path - (optional) the path for the cookie (uses the global if omitted, see <setPath>).
		
	returns:
		nothing
*/
	setCookie:function(cookieName,value,expires,domain,path){
		var cstr=cookieName+"="+(isValidText(value)?value:this.buildCookie(cookieName));
		var expire='';
		if(isValidText(expires)){
			expire='; expires='+expires;
		}
		else if(expires instanceof Date){
			expire='; expires='+expires.toGMTString();
		}
		else if((expires==false)||(expires=="")){
			expire="";
		}
		else if(this.expires){
			expire='; expires='+this.expires;
		}		
		var	dmn=((isValidText(domain))?"; domain="+domain:(isValidText(this.domain)?"; domain="+this.domain:''));
		var pth=((isValidText(path))?"; path="+path:(isValidText(this.path)?"; path="+this.path:''));
		cstr+=expire+dmn+pth;
		document.cookie=cstr;			
		this.load();
	},
/* 
	function: setCookieItem
	sets a specific piece of a cookie to a specified value (assumes the cookie is split beyond the raw format).
	
	parameters:
		(Array) cookieNodes - an array of the object heirarchy to set (see <Object.setChild>)
		(String) value - the value to set the cookie part to
		(String) expires - (optional) the expiration date for the cookie (uses the global if omitted, see <setExpires>).
		(String) domain - (optional) the domain for the cookie (uses the global if omitted, see <setDomain>).
		(String) path - (optional) the path for the cookie (uses the global if omitted, see <setPath>).
		
	returns:
		nothing
*/
	setCookieItem:function(cookieNodes,value,expires,domain,path){
		Object.setChild(this.cookieValue,cookieNodes,value,'Hash');	
		var cstr=this.buildCookie(cookieNodes[0]);
		this.setCookie(cookieNodes[0],cstr,expires,domain,path);
	},
	
	buildCookie:function(cookieName){
		return encodeURIComponent(this.cookieValue[cookieName].toString(this.delimiters.clone()));
	},	
/* 
	function: deleteCookieItem
	removes a specific piece of a cookie (assumes the cookie is split beyond the raw format).
	
	parameters:
		(Array) cookieNodes - an array of the object heirarchy to set (see <Object.deleteChild>)
		(String) expires - (optional) the expiration date for the cookie (uses the global if omitted, see <setExpires>).
		(String) domain - (optional) the domain for the cookie (uses the global if omitted, see <setDomain>).
		(String) path - (optional) the path for the cookie (uses the global if omitted, see <setPath>).
		
	returns:
		nothing
*/	
	deleteCookieItem:function(cookieNodes,expires,domain,path){
		Object.deleteChild(this.cookieValue,cookieNodes);
		var str=this.buildCookie(cookieNodes[0]);
		this.setCookie(cookieNodes[0],str,expires,domain,path);		
	},
/* 
	function: deleteCookie
	deletes a cookie (sets the expires to a past date)
	
	parameters:
		(String) cookieName - The name of the cookie to be deleted
		(String) domain - (optional) the domain of the cookie
		(String) path - (optional) the path of the cookie
		
	returns:
		nothing
*/	
	deleteCookie:function(cookieName,domain,path){
		var dte=new Date();
		dte.setFullYear(dte.getFullYear()-1);
		var	dmn=((isValidText(domain))?"; domain="+domain:(isValidText(this.domain)?"; domain="+this.domain:''));
		var pth=((isValidText(path))?"; path="+path:(isValidText(this.path)?"; path="+this.path:''));
		document.cookie=cookieName+"=false; expires="+dte.toGMTString()+dmn+pth;
		this.load();
	},
/*
	function: load
	Loads the cookies (all or read list) into a hash (this.cookieValue)
	
	returns:
		nothing
*/
	load:function(){
		this.cookieValue=this.getAllCookies();
	}
};


/***********************End Cookie Functions****************/

/*
	File: Popups
	Sets a behavior on all links which have a "popup" attribute to override the click and open a new window.  
	
	
	Instructions:
	- Add the <a> as normal.
	- set the "href" to the location for the popup window
	- set the "target" to a desired target window ("_blank" is not needed, only named targets)
	- add an attribute of popup="[window.open options string]".  By default all options are set to yes, size and location options are set to null (browser defaults).
	- a function can be set to pre-process the attributes by added an option of "pre=functionName" to the popup options.  The function should take 2 parameters 1) the href of the anchor, 2) a hash of the new window options and return the href (Hash is passed in by reference)
	- a function can be set to post-process the new window by adding an option of "post=functionName" to the popup options. The function should take the window object returned by window.open().
	- A behavior will run on DOMLoad to attach a click to each <a>
	- to run manually see <runManual>
	
	
*/

/* 
	Class: widgets.Popups
*/
widgets.Popups=new Object();

Class.inherits(widgets.Popups,{
/*
	var: defaultCss
	the default CSS string that the behavior runs on
*/
	defaultCss:'a[popup]',
	behaviorDefinition:new Behavior({
		css:'a[popup]',
		events:{'WidgetPopupClicks':[false,true,true]}
	}),
/* 
	function: doPopUpWindow
	the function that runs when a popup link is clicked
	
	returns:
		nothing
*/
	doPopupWindow:function(ev,el){
		var d=new Hash({width:null,height:null,left:null,top:null,location:"yes",menubar:"yes",resizable:"yes",scrollbars:"yes",status:"yes",toolbar:"yes"});
		var opts=el.getAttribute('popup').multiSplit([',','=']);
		var funcs=opts.cut("pre","post");
		opts.forEach(function(val,key){
			if(!isValidText(val)){
				if(!d[key]){
					d.cut(key);
				}
				else{
					d[key]="yes";
				}		
			}
			else{
				d[key]=val;
			}
		});
		var t=el.getAttribute('target');
		var r=el.getAttribute('href');
		if(t=='_blank'){
			t=null;
		}
		if(funcs["pre"]){
			href=eval(funcs["pre"]+"(r,d)");
		}
		var h=window.open(r,t,d);
		if(funcs["post"]){
			eval(funcs["post"]+"(h)");
		}
	},
/* 
	function: runManual
	a function to manually run the behavior (it is automatically run on DOMLoad)
	
	parameters:
		(String) css - (optional) a css string to prepend to <defaultCss> to narrow the scope of the dom to search (such as the ID for a container with new content)
	
	returns:
		nothing
*/
	runManual:function(css){
		if(isValidText(css)){
			this.behaviorDefinition.setCss(css+' '+this.defaultCss);
		}
		Behaviors.run([this.behaviorDefinition]);
		this.behaviorDefinition.setCss(this.defaultCss);		
	}
});

Events.add('WidgetPopupClicks','click');
Events.inQ('WidgetPopupClicks',widgets.Popups.doPopupWindow);

Behaviors.addToDOMLoad(widgets.Popups.behaviorDefinition);

/*
	File: tabs.js
	
	About:
	Scans the DOM at page load for tab groups and assigns event handlers to automate tabbed widgets.
	
	Requires:
		- core files
		- events
		- behaviors
		
	Notes:
		- Tabs can be images or text or a combination
		- Anchors on tabs will automatically be turned off (accessible)
		- by default <widgets.tabs.setup> is run onDOMContentLoaded
				
	Default HTML Example:
>		<div class="tab-block" >
>			<ul class="tabs">
>				<li id="tab-myTab1" class="tab"><img src="/images/myTabImage1_on.gif" alt="some alt" class="tab-img"/></li>
>				<li id="tab-myTab2" class="tab"><a href="/index.html">My Linked Tab</a></li>
>				<li id="tab-myTab3" class="tab">Non-Image-Tab</li>
>			</ul>
>			<div class="tab-contents">
>				<div id="tab-content-myTab1" class="tab-content">Content 1 here</div>
>				<div id="tab-content-myTab2" class="tab-content">Content 2 here</div>
>				<div id="tab-content-myTab3" class="tab-content">Content 3 here</div>
>			</div>
>		</div>
	
	
*/


/*
	Class: widgets.tabs
	The generic class for setting up tabs.  Tabbing functions have default settings for various things.  Each default setting can be
	overridden for specific tab-groups.
	
*/		
		
widgets.tabs={
/*
	Var: group_css
	the default class name for the outermost container of the tabs.	
*/
	group_css:"div.tab-block",
/*
	Var: defaultTabs
	An array of tab that should be turned "on" when the page loads (by tab ID).  If no ID for a group is specified, the first one will be turned on.
	To add a default tab, use widgets.tabs.defaultTabs.push(id) or similar.
*/
	defaultTabs:[],
/*
	Var: tabGroupDefaults
	A <Hash> of tab-groups to which the default functions should be overridden.  To override the default functions for a group, add an ID
	to the group.  Then add a function with the same name as any function in the <defaultSettings> object.  The key should be the group node ID.
*/
	tabGroupDefaults:new Hash(),
	tabSets:[],
/*
	function: tabImgOn
	The default function which changes the state of a tab image when it is turned on.  (default replaces "_off" with "_on");
	All images within the tab that have a class of "tab-img" are toggled.  All others are ignored.
	
	returns:
		nothing
*/
	tabImgOn:function(img){
		img.setAttribute('src',img.getAttribute('src').replace(/_off_/i,"_on_"));
	},
/*
	function: tabImgOff
	The default function which changes the state of a tab image when it is turned off.  (default replaces "_on" with "_off");
	All images within the tab that have a class of "tab-img" are toggled.  All others are ignored.
	
	returns:
		nothing
*/
	tabImgOff:function(img){
		img.setAttribute('src',img.getAttribute('src').replace(/_on_/i,"_off_"));
	},
/*
	function: tab2Content
	The default function which matches a tab ID to its corresponding content section ID.  (default is tab-myID to tab-content-myID)
	
	returns:
		nothing
*/
	tab2Content:function(name){
		return name.replace(/-/,'-content-');
	},
/*
	function: content2Tab
	The default function which matches a content ID to its corresponding tab ID.  (default is tab-content-myID to tab-myID)
	
	returns:
		nothing
*/
	content2Tab:function(name){
		return name.replace(/-content-/,'-');
	},
/*
	Object: defaultSettings
	The default functions that are used to setup and execute the tab functionality.  To override the defaults, see <tabGroupDefaults>
	
	returns:
		nothing
*/
	defaultSettings:{
/*
	function: defaultSettings.getCSS
	sets the css strings for finding each tab and each tab-content section within a tab-block
	
	returns:
		nothing
*/		
		getCSS:function(){
			this.tab_css="ul.tabs li.tab";
			this.content_css="div.tab-contents div.tab-content";
		},
/*
	function: defaultSettings.getTabEvents
	creates the events that should be assigned to each tab (click, tabOn, tabOff). Click and On should be the same (loading a default tab will generate a tabOn event)
	
	returns:
		(Array) the event names to be assigned to the tabs [click,tabOn,tabOff]
*/	
		getTabEvents:function(id,tabOn,tabOff,obj){
			Events.add(id+'tabOnClick','click',{f:tabOn,o:obj});
			Events.clone(id+'tabOn',id+'tabOnClick','tabOn');
			Events.add(id+'tabOff','tabOff',{f:tabOff,o:obj});
			return [id+'tabOnClick',id+'tabOn',id+'tabOff'];
		},
/*
	function: defaultSettings.getContentEvents
	creates the events that should be assigned to each content section (on, off).
	
	returns:
		(Array) the event names to be assigned to the content sections [on,off]
*/	
		getContentEvents:function(id,contentOn,contentOff,obj){
			Events.add(id+'contentOn','tabContentOn',{f:contentOn,o:obj});
			Events.add(id+'contentOff','tabContentOff',{f:contentOff,o:obj});
			return [id+'contentOn',id+'contentOff'];
		},
/*
	function: defaultSettings.getContentEventFunctions
	the on/off functions for the tab-content blocks (default=toggle)
	
	returns:
		nothing
*/	
		getContentEventFunctions:function(){	
			this.contentOn=function(ev,el){
				el.toggleOn();		
			};
			this.contentOff=function(ev,el){
				el.toggleOff();
			};
		},
/*
	function: defaultSettings.getTabEventFunctions
	sets functions for handling the tabOn and tabOff events.
	
	returns:
		nothing
*/	
		getTabEventFunctions:function(){	
			this.tabOff=function(ev,el){
				el.rAddClass('on','off');
				el.$css('img.tab-img').forEach(function(val){
					widgets.tabs.tabImgOff(val);
				});
				Events.send('tabContentOff',$(widgets.tabs.tab2Content(el.getAttribute('id'))));
			};
			this.tabOn=function(ev,el){	
				this.currentTab=el;
				el.rAddClass('off','on');
				el.$css('.tab-img').forEach(function(val){
					widgets.tabs.tabImgOn(val);
				});
				Events.send('tabContentOn',$(widgets.tabs.tab2Content(el.getAttribute('id'))));
				this.tabs.forEach(function(val){
					if(val!=el){
						Events.send('tabOff',val);
					}
				});	
			};
		}
	},
/*
	Function: setup
	scans the DOM for tab-groups and generates/assigns all events for them.
	
	parameters:
		(idRef) nd - the node from which to start the setup (document by default)
		
	returns:
		nothing
*/
	setup:function(nd){
		var n=nd||document;
		n.$css(widgets.tabs.group_css).forEach(function(val){
			widgets.tabs.tabSets.push(new widgets.tabs.tabGroup(val,val.getAttribute('id')));
		});
	}
};



/*
	Class: widgets.tabs.tabGroup
	
*/		
widgets.tabs.tabGroup=Class.create();

Class.inherits(widgets.tabs.tabGroup.prototype,{
	init:function(groupNode,groupId){
		var _th=this;
		this.node=groupNode;
		this.id=groupId||('tabGroup'+Math.floor(Math.random()*1000000));
		this.tabEvents=[];
		this.contentEvents=[];		
		Class.inherits(this,widgets.tabs.defaultSettings,true);
		if(widgets.tabs.tabGroupDefaults.hasKey(this.id)){
			Class.inherits(this,widgets.tabs.tabGroupDefaults[this.id],true);
		}
		this.getCSS();
		this.getTabEventFunctions();
		this.getContentEventFunctions();
		this.tabs=groupNode.$css(this.tab_css);
		this.contents=groupNode.$css(this.content_css);
		this.currentTab=this.tabs[0];
		
		this.tabs.forEach(function(v){
			var ancs=v.$t('a');
			ancs.forEach(function(va){
				Events.attach(va,'clickAutoStop');
			});		
			widgets.tabs.defaultTabs.forEach(function(val){
				if((v.getAttribute('id')==val)||(v==val)){
					_th.currentTab=v;
				}
			});
		});
		this.tabEventList=this.getTabEvents(this.id,this.tabOn,this.tabOff,this);
		this.contentEventList=this.getContentEvents(this.id,this.contentOn,this.contentOff,this);
		this.assignTabEvents.apply(this,this.tabEventList);
		this.assignContentEvents.apply(this,this.contentEventList);
		Events.send('tabOn',this.currentTab);
	},
	assignTabEvents:function(){
		var _th=this;
		var args=args2array(arguments);
		this.tabs.forEach(function(val){
			var tmp=[];
			args.forEach(function(v,k){
				tmp[k]=Events.attach(val,v,false,true,false);
			});
			_th.tabEvents.push({id:val.getAttribute('id'),
				evIds:tmp
			});
		});
	},
	assignContentEvents:function(){
		var _th=this;
		var args=args2array(arguments);
		this.contents.forEach(function(val){
			var tmp=[];
			args.forEach(function(v,k){
				tmp[k]=Events.attach(val,v);
			});
			_th.contentEvents.push({id:val.getAttribute('id'),
				evIds:tmp
			});
		});
	},
	clearEvents:function(){
		this.tabEvents.forEach(function(val){
			Events.detach(val);
		});
		this.contentEvents.forEach(function(val){
			Events.detach(val);
		});		
	},
	addTab:function(){
	},
	removeTab:function(){
	}
	
	
});

Events.inQ('DOMLoad',function(e,n){widgets.tabs.setup(n)});


