 
/* 
 * Toolbelt Javascript Library 
  
 * http://tbelt 
 * Created by Stephen Rushing, eSiteful 
 * Compiled Thu 08/20/2009, 15:31:10.64 
 */ 
  
var debugMode = 1;

//jquery 1.3.2 is a requirement for this library
var j = jQuery.noConflict();


if (!Array.indexOf) {
    Array.prototype.indexOf = function(arr){
        for (var i = 0; i < this.length; i++) {
            if (this[i] == arr) {
                return i;
            }
        }
        return -1;
    }
}

if (!Array.indexesOf) {
    Array.prototype.indexesOf = function(arr){
        var idxs = [];
		for (var i = 0; i < this.length; i++) {
            if (this[i] == arr) {
               idxs.push(i);
            }
        }
        return idxs;
    }
}

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to){
	var rest = this.slice((to || from) + 1 || this.length);
	this.length = from < 0 ? this.length + from : from;
	return this.push.apply(this, rest);
};




/* STRING EXTENSIONS */
String.prototype.trim = function(){
    return this.replace(/^\s+|\s+$/g, '');
}

String.prototype.toTitleCase = function(){
    var regex = /(^[a-z]|\b[a-z])/g;
    var returnString = this.replace(regex, makeTitle);
    function makeTitle(){
        return arguments[0].toUpperCase();
    }
    return returnString;
}

String.prototype.setCharAt = function(index,chr) {
	if(index > this.length-1) return str;
	return this.substr(0,index) + chr + this.substr(index+1);
}


if (document.__defineGetter__ && !HTMLElement.outerHTML) {
    HTMLElement.prototype.__defineGetter__("outerHTML", function(){
        var parent = this.parentNode;
        var span = document.createElement("span");
        span.appendChild(this);
        var HTML = span.innerHTML;
        if(parent!=null)parent.appendChild(this);
        delete span;
        return HTML;
    });
}





function trace(msg, priority){
	//console.log(!console);
	if (!window["console"]) {
    	tbelt.createConsole();
	}
	if (priority == null) {
        priority = 1;
    }
    if (debugMode >= priority) {        
        console.log(msg);
    }
}

j(function(){
	j().mousemove(function(evt){
		window.mouseX = evt.pageX;
		window.mouseY = evt.pageY;
	});	
})



/**
 * tbelt is a base class made available for extending.
 * @version 1 alpha
 * @author Stephen Rushing, eSiteful
 * @class tbelt
 * @constructor
 */

function tbelt(){
	var I = this;
}

/**
 * typeOf
 * @method typeOf
 * @param {Object} obj The object whose type you want to evaluate.
 * @return The specific type of an object.
 */
tbelt.typeOf = tbelt.prototype.typeOf = function(obj){		
	//define the types
	var types = {
        Null: function(o){
            return o === null;
        },
        Undefined: function(o){
            return o === undefined;
        },
        nt: function(o){
            return (o === null || o === undefined);
        },
        Function: function(o){
			return (typeof(o) === 'function') ? constr.match(/function/i) !== null : false;
        },
        String: function(o){
            return (typeof(o) === 'string') ? true : (typeof(o) === 'object') ? constr.match(/string/i) !== null : false;
        },
        Array: function(o){
            return (typeof(o) === 'object') ? constr.match(/array/i) !== null : false;
        },
        Boolean: function(o){
            return (typeof(o) === 'boolean') ? true : (typeof(o) === 'object') ? constr.match(/boolean/i) !== null : false;
        },
        Date: function(o){
            return (typeof(o) === 'date') ? true : (typeof(o) === 'object') ? constr.match(/date/i) !== null : false;
        },
        HTML: function(o){
            return (typeof(o) === 'object') ? constr.match(/html/i) !== null : false;
        },
        Number: function(o){
            return (typeof(o) === 'number') ? true : (typeof(o) === 'object') ? constr.match(/number/i) !== null : false;
        },        
        RegExp: function(o){
            //trace(constr + (typeof(o) === 'object' || typeof(o) === 'function') + (constr.match(/regexp/i)!=null));
			return (typeof(o) === 'object' || typeof(o) === 'function') ? (constr.match(/regexp/i)) !== null : false;
        },
		Window: function(o){
             if(typeof(o) === 'object' &&o.self !=null && o.status!=null){
			 	return true;
			 }
			 else{
			 	return false;
			 }
        },
        Object: function(o){
            return (typeof(o) === 'object') ? constr.match(/object/i) !== null : false;
        }
    }
	//figure out the type
	var constr = (obj && obj.constructor)?obj.constructor.toString():"null";
	for (var t in types) {       
		//trace(t + " : " +types[t](obj));
		if (types[t](obj)) {
			return t.toLowerCase();
		}
		
    }   
	
}




tbelt.createConsole=function(){
	window["console"] = {
        log: function(msg){
            //alert console.log messages if console.log does not exist
            if (debugMode > 0){
				//alert(msg);
				if (document.getElementById("consoleLog")) {					
					document.getElementById("consoleLog").value += "\n" + msg;					
					document.getElementById("consoleLog").scrollTop = document.getElementById("consoleLog").scrollHeight - document.getElementById("consoleLog").clientHeight;
				}
			}
              
        }
    };
	if (!document.getElementById("consoleLog") && debugMode>1) {
		j(document).ready(function(){
			j("body").append("<textarea id='consoleLog' style='width:99%;height:200px;position:absolute;bottom:0;left:0;'></textarea><div style='height:200px;'></div>");
		});
	}
}







	


/*
 * http://tbelt.stephenrushing.com/library/string/
 */
tbelt.string = {}


/*
 * http://tbelt.stephenrushing.com/library/string/#mesh
 */
 tbelt.string.mesh = function(str, vars){
	var returnStr = str;
	for(var v=0; v<vars.length; v++){
		var regex = new RegExp("\\{\\s*?\\$"+vars[v].name+"\\s*?\\}","g");
		returnStr = returnStr.replace(regex, vars[v].value);
	}
	return returnStr;
 }
 
/*
 * http://tbelt.stephenrushing.com/library/string/#multiply
 */
tbelt.string.multiply = function(str, times){    
    if (times <= 0) return str;
    var returnStr = str;
    while (times > 0) {
        returnStr += str;
        times--;
    };
    return returnStr;
}

/*
 * http://tbelt.stephenrushing.com/library/string/#translate
 */
/**
 * string.translate is similar to the XSLT translate function. 
 * @param {Object} str Original string to translate.
 * @param {Object} fromKey Original translation key (eg. 'abc').
 * @param {Object} toKey Desired translation key (eg. 'cab').
 * @return The str parameter with all characters replaced based on index comparisons of fromKey and toKey.
 */
tbelt.string.translate = function(str, fromKey, toKey){
	var translated = str;
	//console.log(returnStr.replace(/(.)/g, "[$1]"));
	for(var f=0; f<str.length; f++){
		var tmpIdx = fromKey.indexOf(str[f]);
		if(tmpIdx>-1){
			//console.log(returnStr[f] + " : " +toKey[tmpIdx]);
			translated = translated.setCharAt(f, toKey.charAt(tmpIdx));
		}
	}
	return translated;
}	



/*
 * http://tbelt.stephenrushing.com/library/array/
 */
tbelt.array = tbelt.prototype.array = function(){}

/*
 * http://tbelt.stephenrushing.com/library/array/#clean
 */
tbelt.array.clean = tbelt.array.prototype.clean = function(dirtyArray){
	var returnArray = [];
	for(var a=0;a<dirtyArray.length; a++){
		if(dirtyArray[a]!=null && dirtyArray[a]!=undefined && dirtyArray[a].toString().trim()!=""){
			returnArray.push(dirtyArray[a]);
		}
	}
	return returnArray;	
}


/*
 * http://tbelt.stephenrushing.com/library/array/#search
 */
tbelt.array.search = tbelt.array.prototype.search = function(searchFor, arrayToSearch) {
	var returnArray = [];
	for (var f = 0; f < arrayToSearch.length; f++) {
		//trace(arrayToSearch[f] + " : " + tbelt.typeOf(arrayToSearch[f]) + " : " + searchFor);
		if (tbelt.typeOf(arrayToSearch[f])== "regexp" && arrayToSearch[f].test(searchFor)) {			
			returnArray.push(searchFor);
		}else if(arrayToSearch[f]==searchFor){
			returnArray.push(searchFor);
		}
	}
	return (returnArray.length>0)?returnArray:false;
}


/*
 * http://tbelt.stephenrushing.com/library/array/#searchObjects
 */
tbelt.array.searchObjects = tbelt.array.prototype.searchObjects = function(objQuery, objArray, excludeObjects){
	var returnList = [];
	function isMatch(obj){		
		for (var prop in objQuery) {			
			if (obj[prop] != objQuery[prop]) return false;
			//trace(obj[prop] + " = " + objQuery[prop]);			
		}
		return true;
	}

	for (var obj = 0; obj < objArray.length; obj++) {
		if(!excludeObjects || excludeObjects.indexOf(objArray[obj])==-1){
			if(isMatch(objArray[obj]))
				returnList.push(objArray[obj]);
		}
	}
	
	return returnList;
}


/**
 * util
 */
tbelt.util = {}




tbelt.util.Meshtml=function(opts){
	
	var I=this;
	
	// ---- CONSTRUCTOR --------------------------	
	I.defaults = {
		elements:".mesh",
		base:"<div class='mesh-wrapper'>{$mesh}</div>",
		vars:[
			{name:"mesh", value:"each.html()"},
			{name:"var2", value:"each.find('h1')"},
			{name:"var3", value:"outerHTML(each.find('p'))"}],
		replaceMode:true,
		autoMesh:true
	};

	j.extend(I, I.defaults, opts);
	
	j(document).ready(function(){I.init();});
	//I.init();

	return I;
}

tbelt.util.Meshtml.prototype={
	events:{
		EACH_START:"Meshtml.eachStart",
		EACH_FINISH:"Meshtml.eachFinish",
		INIT:"Meshtml.init",
		MESH:"Meshtml.mesh"
	},
	init:function(){
		var I = this;
		I.elements = j(I.elements);

		j(I).trigger(I.events.INIT);
		if(I.autoMesh) I.mesh();
	},
	mesh:function(){
		var I = this;
		var outerHTML = I._outerHTML;
		//trace(I.elements);
		//loop through the elements
		I.elements.each(function(i, e){
			var meshEl = j(e);
			if(!meshEl.data("meshed")){
				j(I).trigger(I.events.EACH_START, i);
				
				//meshEl.find("script").prependTo("body");
				
				var meshedMarkup = I.base;				
				var tmpVar;
				var meshVars = [];
				for(var v=0;v<I.vars.length;v++){
					meshVars.push(j.extend({},I.vars[v]));
					var ivar = I.vars[v];
					var mvar = meshVars[v];
					//trace(mvar);
					//console.log(v);
					if(typeof(mvar.value)=="string" && (/^[a-zA-Z]*\.|\(/).test(mvar.value)){
						mvar.value = mvar.value.replace(/each\./g,"meshEl.");
						//trace(mvar.value);
						mvar.value = eval(mvar.value);
					}else if(mvar.value!=null){
						//mvar.value = mvar.value;
					}else{
						mvar.value = "";
					}
					//meshedMarkup = meshedMarkup.replace("{$"+I.vars[m].name+"}", tmpVar);	
					//trace(mvar);
					
				}
				meshedMarkup = tbelt.string.mesh(meshedMarkup, meshVars);
				
				//if replace is false, insert the html inside of the current element				
				if(!I.replaceMode){
					meshEl.html(meshedMarkup);
				}else{
					//replace by default
					meshEl.before(meshedMarkup);
					meshEl.remove();
				}
				
				meshEl.data("meshed",true);
				j(I).trigger(I.events.EACH_FINISH, i);
			}
		
		});
		
		j(I).trigger(I.events.MESH);
	},
	_outerHTML:function(elements){
		elements = j(elements);
		var html = "";
		elements.each(function(i, e){
			html += e.outerHTML;
		});
		return html;
	}
}


tbelt.util.Timer = function(opts){
	var I = this;
	I.defaults = {		
		interval:250,
		duration:0,
		loop:false,
		onInterval:function(){return true;},
		onFinish:function(){},
		onError:function(){}
	};
	j.extend(I, I.defaults, opts);
	
	var timerInterval;
	
	I.reset=function(){	
		I.stop();
		I.time=0;
	}
	
	I.stop=function(){
		clearInterval(timerInterval);	
		j(I).trigger("stopped",I);
	}
	
	I.start=function(){
		timerInterval = setInterval(function(){
			if(I.onInterval()){
				I.force();
			}else if(I.duration>0 && I.time >= I.duration){
				I.fail();
			}
			I.time+=I.interval;
			j(I).trigger("interval",I);
			
		}, I.interval);
		j(I).trigger("started",I);
	}
	
	I.fail=function(){
		I.stop();		
		I.onError(I);
		j(I).trigger("failed",I);
	}
	
	I.force=function(){
		I.stop();		
		I.onFinish(I);
		j(I).trigger("finished",I);
		if(I.loop) I.start();
	}
	
	I.reset();
	I.start();
}


/**
 * url contains all Array specific functions.
 */
tbelt.url = {};





tbelt.url.relative = function(fullPath, root, guess){
	var returnPath="";	
	if(root && fullPath.indexOf(root)){
		returnPath = fullPath.replace(root,"");
	}else if(fullPath.indexOf(window.location.hostname)>-1){
		returnPath = fullPath.replace(window.location.protocol+"//"+window.location.host,"");
	}else if(guess){
		returnPath = fullPath.replace(/\/{2,5}/,"/").split("/");
		returnPath = returnPath.slice(2,returnPath.length).join("/");
	}
	return returnPath;	
}




/**
 * url.queryString returns the value of a url query string.
 * @param {String} qsName Name of the query string to return.
 * @return Specified query string via qsName parameter, or the entire URL query string (if qsName is empty).
 */
tbelt.url.queryString = function(qsName, qString){
	if(qString==null) qString=window.location.href;
	var returnQS = (/\?(.[^\#]*)\#?/).exec(qString);	
	if(returnQS && returnQS.length>0){
		returnQS = returnQS[1];
		if(qsName && qsName.length>0){
			var regex = new RegExp("&?"+qsName+"=(.[^&$]*)")
			returnQS = regex.exec(returnQS);
			if(returnQS && returnQS.length>0){
				returnQS = returnQS[1];
			}else{
				returnQS = "";
			}
		}
	}else{
		returnQS="";
	}
	//trace(returnQS);
	return unescape(returnQS);
}


//returns rigidity, the levels at which they match
tbelt.url.compare = function(suspectURL, compareURL, ignoreProtocol, ignoreQueryStrings, ignoreAnchors){
	var regex, hashes, queryString;
	var suspectQS="", compareQS="";
	var patterns=[], replacements=[];
	//trace(suspectURL + " : " +compareURL);
	//get and sort query strings according to compareURL
	regex = /\?.*$/;
	suspectQS = suspectURL.match(regex);
	compareQS = compareURL.match(regex);	
	if (compareQS && compareQS.length > 0 && suspectQS && suspectQS.length > 0){
		suspectURL = suspectURL.replace(regex, "");
		compareURL = compareURL.replace(regex, "");
		//remove ? from query strings
		regex = /(^\?)/;
		suspectQS = suspectQS[0].replace(regex,"");
		compareQS = compareQS[0].replace(regex,"");
		//replace slashes in query string values
		regex = /\//g;
		suspectQS = suspectQS.replace(regex, "%2F");
		compareQS = compareQS.replace(regex, "%2F");
		//split the query string values
		suspectQS = suspectQS.split("&");
		compareQS = compareQS.split("&");
		
		suspectQS = suspectQS.sort(function(a,b){
			//trace(a + " : " + compareQS.indexOf(a));
			//trace(b + " : " + compareQS.indexOf(b));
			var aPos = compareQS.indexOf(a);
			var bPos = compareQS.indexOf(b);
			if(aPos==-1) return 1;
			else
			return compareQS.indexOf(a) - compareQS.indexOf(b);
		});		
		suspectURL += "/"+suspectQS.join("/");
		compareURL += "/"+compareQS.join("/");
	}
	//ignore protocol (default)
	if(ignoreProtocol!=false){
		patterns.push(/^(http|https|file|www):/i);
		replacements.push("");	
	}
	//remove anchors if specified
	if(ignoreAnchors){		
		patterns.push(/\#.*$/);
		replacements.push("");
	}
	//remove query strings if specified
	if(ignoreQueryStrings){		
		patterns.push(/\?.*$/);
		replacements.push("");
	}		
	//replace hash with slash
	patterns.push(/#/);
	replacements.push("/");	
	//replace double slashes
	patterns.push(/(\/\/)/g);
	replacements.push("/");
	//remove leading and trailing slashes
	patterns.push(/(^\/|\/$)/g);
	replacements.push("");
	
	//apply patterns & replacements to urls
	for(var p=0;p<patterns.length;p++){
		suspectURL = suspectURL.replace(patterns[p],replacements[p]);
		compareURL = compareURL.replace(patterns[p],replacements[p]);
		//trace("\t\t"+suspectURL + " : " +compareURL);
	}	
	
	//split the urls into an array for comparison
	regex = /\//;		
	suspectURL = suspectURL.split(regex);
	compareURL = compareURL.split(regex);	
	
	//remove empty items from urls
	suspectURL = tbelt.array.clean(suspectURL);
	compareURL = tbelt.array.clean(compareURL);
	
	//compare and return rigidity	
	var returnRigidity=0;
	for(var r=0;r<compareURL.length;r++){
		//trace("\t\t"+suspectURL[r] + " : " +compareURL[r]);
		if(suspectURL[r]==compareURL[r]){
			returnRigidity=r+1;
		}
	}
	//trace(returnRigidity + " : " +suspectURL + " : " +compareURL);
	return returnRigidity;
	
}



/**
 * Highlighter
 * @member tbelt.url
 * @constructor
 * @param {Object} opts Options for Highlighter.
 * @return The Highlighter object.
 */

tbelt.url.Highlighter = function(opts){
	var I = this;

	I.options = j.extend({}, I.defaults, opts);	
	
	if(opts!=null && opts.links!=null){ 
		j(document).ready(function(){
			I.init()
		});
	}
	
	return I;
	
}
tbelt.url.Highlighter.prototype = {
	defaults:{
		links:j(""),
		manualMatches:[],
		url:window.location.href,
		minRigidity:0,
		alterElements: [{element:"each", className:"active"}],
		defaultPages: [/(default|index)\.[a-z]{2,5}/i],
		ignoreAnchors:false,
		ignoreQueryStrings:false,
		ignoreProtocols:true,
		autoHighlight:true,
		useLastBestMatch:false
	},
	options:null,
	bestMatch:null,
	matches:null,
	/*
		PUBLIC FUNCTIONS
	*/
	//init()
	init:function(){
		var I=this;
		if(I.options.autoHighlight){
			I.highlight();
		}else{
			I.setMatches();
		}
		j(I).trigger("Highlighter.init", I);
	},
	//getMatches(rigidity)
	getMatches:function(rigidity){				
		var I=this;
		if(rigidity==null) return I.matches;
		return tbelt.array.searchObjects({rigidity:rigidity}, I.matches);
	},
	//setMatches()
	setMatches:function(){
		var I=this;
		var links = j(I.options.links);
		I.matches = [];
		//convert rigidity to array if necessary
		if(typeof(I.options.rigidity)=="number"){
			I.options.rigidity=[I.options.rigidity];	
		}
		//convert alterElements to array if necessary
		if(I.options.alterElements.element!=null){
			I.options.alterElements = [I.options.alterElements];
		}
		var compareURL = I._cleanURL(I.options.url);
		var bestRigidity=0;
		links.each(function(i, link){
			var tmpURL = I._cleanURL(link.href);
			
			//check manual matches first
			function manualMatchIdx(url){
				for(var mm=0; mm < I.options.manualMatches.length; mm++){
					var manMatch=I.options.manualMatches[mm][0];
					
					if((tbelt.typeOf(manMatch)=="regexp" && (url).match(manMatch)) || url.indexOf(manMatch)>-1){
						trace(manMatch + " : " + url);
						return mm;
					}
				}
				return -1;		
			}
			var mmidx=manualMatchIdx(link.href);
			if(mmidx>-1){				
			
					var manMatchList = I.options.manualMatches[mmidx];					
				
					for(var mm=1; mm < manMatchList.length; mm++){
						var manMatch=manMatchList[mm];
						
						if((tbelt.typeOf(manMatch)=="regexp" && (I.options.url).match(manMatch)) || I.options.url.indexOf(manMatch)>-1){
							trace("\t"+manMatch + " : " + I.options.url);
							var match={
								link:link,
								rigidity:I.options.url.split(/\/|\?|&/).length
							};
							I.bestMatch=match;
							I.matches.push(match);
							//trace(match);
							return;
						}
					}

			}
			
			
			//compare urls			
			var rigidity = tbelt.url.compare(compareURL, tmpURL, I.options.ignoreProtocols, I.options.ignoreAnchors, I.options.ignoreQueryStrings);
			if(rigidity>I.options.minRigidity){				
				var match = {
					link: link,
					rigidity: rigidity
				};
				//set best match
				if(I.matches.length==0 || (I.options.useLastBestMatch && I.matches[I.matches.length-1].rigidity<=rigidity)
					|| I.bestMatch.rigidity < match.rigidity){
					I.bestMatch = match;
					//trace(I.bestMatch.link);
				} 
				//add to matches
				I.matches.push(match);	
			}
		});
				
	},
	//highlight(rigidity)
	highlight:function(rigidity){
		var I=this;
		I.setMatches(); 
		var toHighlight=[I.bestMatch];
		if(rigidity!=null && rigidity>0){
			toHighlight = I.getMatches(rigidity);
		}
		for(var th=0; th<toHighlight.length; th++){
			for(var ae=0; ae<I.options.alterElements.length; ae++){			
				try{
					//trace(I.options.alterElements[ae]);
					var alterElement = eval(I.options.alterElements[ae].element.replace(/each/,"j(toHighlight[th].link)"));								
					alterElement.addClass(I.options.alterElements[ae].className);
					//trace(alterElement[0]);
				}catch(err){
					//trace(err);
				}
			}
		}
		j(I).trigger("Highlighter.highlight", toHighlight);		
	},
	/*
		PRIVATE FUNCTIONS
	*/
	_cleanURL:function (dirtyURL){
		var I=this;
		var returnURL=dirtyURL;
		var regex;
		//remove default pages
		for(var dp=0;dp<I.options.defaultPages.length;dp++){			
			var dpType = tbelt.typeOf(I.options.defaultPages[dp]);
			if(dpType=="string"){
				returnURL = returnURL.replace(I.options.defaultPages[dp], "");
			}else if(dpType=="regexp"){
				returnURL = returnURL.replace(I.options.defaultPages[dp], "");
			}
		}	
		//trace(returnURL);
		return returnURL;
	}
};

tbelt.ui = {}

//contains all of the tbelt.ui.Overlays instances
tbelt.ui.overlays=[];


tbelt.ui.animateScroll = function(scrollTo, duration, easing, callback){
	if(duration==null) speed=500;
	if(easing==null) easing="swing";

	function getTop(pos){
		if(typeof(pos)=="number") return pos;
		if(pos.offset()) return pos.offset().top;
		if(pos.nodeName) return j(pos).offset().top;
		if(pos.top) return getTop(pos.top);
		return j("html").scrollTop();		
	}
	function getLeft(pos){
		if (pos.left) {
			if(typeof(pos)=="number") return pos;
			if(pos.offset()) return pos.offset().top;
			if(pos.nodeName) return j(pos).offset().left;
		}
		return j("html").scrollLeft();
	}
	
	j('html').animate({scrollTop: getTop(scrollTo)}, duration, easing, callback);
}

//http://www.learningjquery.com/2007/10/improved-animated-scrolling-script-for-same-page-links
/*
 * 
 * why it's better: 
 * One particular improvement is that I'm only cleaning the current page's path once, whereas Karl Swedberg's is running it through the function with each comparison. Another more debateable improvement is that the target scroll position is calculated when the link is clicked, rather than all at once. This is a definite improvement where you have alot of Javascript content initialization on a page.
 */
tbelt.ui.animateAnchors = function(duration, easing, ignoreSelector){
	j(function(){
		function cleanPath(dirty){
			return dirty.replace(/(index|default)\..[a-zA-Z]*$/,"").replace(/\/{2,5}/g,"/").replace(/(^\/)|(\/$)/g,"");	
		}
		var pagePath = cleanPath(window.location.pathname);
		//assign actions to all anchor links on the page
		if(ignoreSelector==null)ignoreSelector="nothing"
		j("a[href*='#']").not(ignoreSelector).each(function(i,a){
			if(cleanPath(a.pathname)==pagePath){
				j(a).click(function(evt){
					evt.preventDefault();
					tbelt.ui.animateScroll(j("a[name='"+a.hash.replace(/^#/,"")+"']"), duration, easing, function(){window.location.hash=a.hash;});					
				});
			}
		});
		//check for current anchor link and go to it
		if(window.location.hash.length>0){
			tbelt.ui.animateScroll(j("a[name='"+window.location.hash.replace(/^#/,"")+"']"), duration, easing);
		}
	});	
}


tbelt.ui.allowsFixed = function(){
	//if browser is ie6 or ie and there is no doctype
	if(!j.boxModel || j.browser.msie && j.browser.version.indexOf("6.")>-1){
		return false;
	}
	return true;
}



tbelt.ui.cacheImages = function(pathsArray){
	if(pathsArray && pathsArray.length>0){
		for(var i=0; i<pathsArray.length; i++){
			var img = new Image();
			img.src = pathsArray[i];
		}
	}
}


tbelt.ui.ratioResize = function(oldDims, newDims, round){
	if(newDims.height!=null && !isNaN(newDims.height)){
		newDims.width = (newDims.height/oldDims.height) * oldDims.width;
	}else if(newDims.width!=null && !isNaN(newDims.width)){
		newDims.height = (newDims.width/oldDims.width) * oldDims.height;
	}trace(newDims);
	if(round!=false){
		newDims.width = Math.round(newDims.width);
		newDims.height = Math.round(newDims.height);
	}	
	return newDims;
}

tbelt.ui.relativePosition = function(anchor, posEl, relativity, fixed, outside){
	relativity = j.extend({x:"center", y:"center"}, relativity);
	if(anchor.top==null || tbelt.typeOf(anchor)=="window") anchor=j(anchor);	
	
	var returnPos = {top:null,left:null};
	
	var posItemDims={height:null, width:null};
	if(posEl.height != null && posEl.width !=null && typeof(posEl.height)!="function"){
		posItemDims=posEl;
	}else{
		posEl = j(posEl);
		posItemDims.height=posEl.outerHeight();
		posItemDims.width=posEl.outerWidth();		
	}
	
	//trace(anchor.top);
	var anchorDims = {height:0,width:0,top:0,left:0};
	
	if(tbelt.typeOf(anchor[0])!="window" && anchor.top==null){
		anchorDims.top = anchor.offset().top;
		anchorDims.left = anchor.offset().left;
		anchorDims.height = anchor.outerHeight();
		anchorDims.width = anchor.outerWidth();
	}else if(anchor.top==null){
		anchorDims.top = anchor.scrollTop();
		anchorDims.left = anchor.scrollLeft();
		anchorDims.height = anchor.height();
		anchorDims.width = anchor.width();
	}else{
		anchorDims.top = anchor.top;
		anchorDims.left = anchor.left;
	}
	
	//vertical position
	if(relativity.y=="center"){
		returnPos.top = anchorDims.top+(anchorDims.height/2)-(posItemDims.height/2);
	}else if(relativity.y=="bottom" && anchorDims.height>0 || anchorDims.height==0 && relativity.y=="top"){
		returnPos.top = (anchorDims.top+anchorDims.height)-posItemDims.height;
		if(outside&&anchorDims.height>0) returnPos.top+=posItemDims.height;
	}else if(relativity.y=="top" && anchorDims.height>0 || anchorDims.height==0 && relativity.y=="bottom"){
		returnPos.top = (anchorDims.top);
		if(outside&&anchorDims.height>0) returnPos.top-=posItemDims.height;
	}

	//horizontal position
	if(relativity.x=="center"){
		returnPos.left = anchorDims.left+(anchorDims.width/2)-(posItemDims.width/2);
	}else if(relativity.x=="right" && anchorDims.width>0 || anchorDims.width==0 && relativity.x=="left"){
		returnPos.left = anchorDims.left+(anchorDims.width-posItemDims.width);
		if(outside&&anchorDims.width>0) returnPos.left+=posItemDims.width;
	}else if(relativity.x=="left" && anchorDims.width>0 || anchorDims.width==0 && relativity.x=="right"){
		returnPos.left=anchorDims.left;
		if(outside&&anchorDims.width>0) returnPos.left-=posItemDims.width;
	}
	
	
	//get fixed position if true
	if(fixed){
		returnPos.top-=j(window).scrollTop();
		returnPos.left-=j(window).scrollLeft();
	}
	
	//trace(returnPos);
	return returnPos;
}


/*
tbelt.ui.rolloverImages = tbelt.ui.prototype.rolloverImages = function(){
	j(function(){
		j("img[rolloverstate]").each(function(i, img){
			
		});
	});	
}*/
/**
 * Popup makes browser and in-page popups more simple.
 * @member tbelt.
 * @constructor
 * @param {Object} opts Options for Popup.
 * @return The Popup object.
 */


tbelt.ui.Popup = function(opts){
	var I = this;
	I._opts=opts;
	I.options = j.extend(true, {}, I.defaults, opts);
	
	I.ID = "P"+ Math.floor(Math.random()*50000);


	j(document).ready(function(){I.init()});
	
	return I;
}

tbelt.ui.Popup.prototype = {
	defaults:{
		image:"",
		html:"",
		element: "",
		ajax:{url:"", data:"", method:"get"},        
		position: {y:"center", x:"center", animate:null},
		overlay: {element:"", opacity:.7, color:"", showSpeed:300, hideSpeed:150},
		zIndex: 2000,
		easyClose: true,
		autoLoad:false,        
		displayOnLoad: true
	},
	options:null,
	html:null,
	content:null,
	overlay:null,
	loaded:false,
	ID:null,
	isOpen:false,
	/* 
		Popup.init() 
	*/
	init:function(){
		var I=this;
		//create overlay element if it doesn't already exist
		I.html="";
		I.loaded=false;
		
		if(I.options.overlay.element.length==0){
			if(j("div.popup-overlay").length==0) 
				j("body").append("<div class='popup-overlay' style='display:none;height:100%;width:100%;'></div>");
			I.options.overlay.element = j("div.popup-overlay");
		}else if(I.options.overlay.element.length==0){
			I.options.overlay.element = j(I.options.overlay.element.selector);
		}
		I.overlay = I.options.overlay.element;

		j(I).trigger("Popup.init",I);
		
		
		//use an in-page element (no loading needed)
		if(I.options.html.length>0){			
			I.html=I.options.html;
			I.content=j(I.html);
			I._setLoaded(true);
		}else if(I.options.element.length>0){			
			I.content = j(I.options.element);
			I.html = I.content[0].outerHTML;
			I.content = I.content.replaceWith("");			
			//make sure it doesn't auto display on accident
			if(!I.options.autoLoad && I._opts.displayOnLoad!=true){
				I.options.displayOnLoad=false;
			}			
			
			I._setLoaded(true);
		}
		//if the content needs loading...
		else if(I.options.autoLoad){
			I.load();
		}
	},
	/* 
		Popup.load() 
	*/
	load:function(){
		var I=this;
		I._setLoaded(false);
		//load an image
		if(I.options.image.length>0){
			var img = new Image();
			img.src=I.options.image;
			
			var imgLoader = new tbelt.util.Timer({
				interval:200,
				duration:0,
				onInterval:function(){
					return img.complete;
				},
				onFinish:function(){
					var div = document.createElement("div");
					div.id=I.ID;
					div.appendChild(img);
					I.content = j(div);
					I.content.selector = "div#"+I.ID;
					I.content.css({"float":"left"});
					I.html = div.outerHTML;	
					
					I._setLoaded(true);
					
				},
				onError:function(){}
			});
			
		}
		//load the ajax call
		else if(I.options.ajax.url.length>0){
			var ajaxOpts = j.extend({
				error:function(evt){},
				success:function(data){
					var div = document.createElement("div");
					div.id=I.ID;
					div.innerHTML = data;
					I.content = j(div);
					I.content.css({"float":"left"});
					//I.content.width(I.content.outerWidth());
					//I.content.height(I.content.outerHeight());					
					I.html = div.innerHTML;
					
					I._setLoaded(true);
				}
			}, I.options.ajax);
			
			//make the call
			j.ajax(ajaxOpts);			
			
		}
		
	},
	/* 
		Popup.close() 
	*/
	close:function(isConfirm){
		var I=this;
		
		if(isConfirm!=true) isConfirm=false;
		//hide the content
		I.content.css({display:"none"});

		//hide the overlay
		I.overlay.animate({opacity:0}, I.options.overlay.hideSpeed,function(){
			I.overlay.css({display:"none"});
			I._toggleStubbornElements(true);
			I.content = I.content.replaceWith("");
		});
		
		I.isOpen=false;
		
		I.overlay.unbind("click");		
		
		j(I).trigger("Popup.close", isConfirm);

	},
	/* 
		Popup.open() 
	*/
	open:function(evt){		
		var I=this;
		
		if(!I.loaded){
			I.load();
		}else{
			I.content = j(unescape(I.html)).appendTo("body");
			I.content.css({zIndex:I.options.zIndex+1});
			//prep the popup content
			I._prepContent();
			
			//take the focus off of the clicked item
			if(evt!=null && evt.target!=null)
				j(evt.target).blur();
			
			//hide stubborns
			I._toggleStubbornElements(false);
			
			//show the content and overlay	
			I.overlay.css({opacity:0, display:"block"}).animate({opacity:I.options.overlay.opacity}, I.options.overlay.showSpeed);
			I.content.css({display:"block"});		
			
			//make it easy to close
			if(I.options.easyClose)	I.overlay.bind("click", function(){I.close()});
			
			//bind events while it is open
			j(window).bind("keydown", function(evt){
				if(I.isOpen) I._keyPress(evt);									 
			});				
			
			j(I).trigger("Popup.open");
			I.position();
			I.isOpen=true;
		}
		
		
	},
	/* 
		Popup.position() 
	*/
	position:function(posOpts){
		var I=this;
		posOpts = j.extend({}, I.options.position, posOpts);
		
		var contentStyles = {position:"fixed", top:0, left:0, zIndex:(I.options.zIndex+1)};
		if(posOpts.height!=null) contentStyles.height=posOpts.height;
		if(posOpts.width!=null) contentStyles.width=posOpts.width;
		
		var overlayStyles = {position:"fixed", top:0, left:0, backgroundColor:I.options.overlay.color, zIndex:I.options.zIndex};
	
		I.options.overlay.element.css(overlayStyles);
		
		if(I.content.length>0){
			if(posOpts.height != null && posOpts.width != null){
				var contentPos = tbelt.ui.relativePosition(window, posOpts, {y:I.options.position.y, x:I.options.position.x}, true);
			}else{			
				var contentPos = tbelt.ui.relativePosition(window, I.content, {y:I.options.position.y, x:I.options.position.x}, true);
			}
			if(contentPos.top.toString()!="NaN") contentStyles.top=contentPos.top;
			if(contentPos.left.toString()!="NaN") contentStyles.left=contentPos.left;
		}
		
		
		//ie6 doesn't like fixed positions
		if(!tbelt.ui.allowsFixed()){
			contentStyles.position="absolute";
			overlayStyles.position="absolute";
			contentStyles.top += j(window).scrollTop();
			overlayStyles.top += j(window).scrollTop();
			overlayStyles.height = j(window).height();
			j(window).bind("scroll", function(){
				if(I.isOpen) I.position();
			});
		}
		
		if(!posOpts.animate){
			I.content.css(contentStyles);			
		}else{			
			I.content.css({position:contentStyles.position, zIndex:contentStyles.zIndex});
			delete contentStyles.position;
			delete contentStyles.zIndex;
			I.content.animate(contentStyles, posOpts.animate.duration, posOpts.animate.easing, posOpts.animate.callback);
		}
		I.overlay.css(overlayStyles);
		//trace(contentStyles)
		
		j(I).trigger("Popup.position", I);
	},
	/* 
		Popup._setLoaded() 
	*/
	_setLoaded:function(setTo){
		var I = this;
		if(setTo!=false){
			I.loaded=true;
			j(I).trigger("Popup.load", I);
			if(I.options.displayOnLoad)I.open();
		}else{
			I.loaded=false;
		}
	},
	/* 
		Popup._keyPress() 
	*/
	_keyPress:function(evt){
		var I=this;
		//if esc
		if(evt.keyCode==27){
			I.close(false);
		}
		//if enter
		else if(evt.keyCode==13){
			I.close(true);
		}
	},
	/* 
		Popup._prepContent() 
	*/
	_prepContent:function(){		
		var I=this;
		//set the close button actions
		I.content.find("a[href*='Popup.close(']").each(function(i, a){				
			j(a).attr("popupconfirm", (a.href.indexOf("Popup.close(true)")>-1)?true:false);
			a.href="javascript:void(0)";				
			j(a).click(function(evt){
				var toConfirm = (j(evt.target).attr("popupconfirm")=="true")?true:false;
				I.close(toConfirm);
			})				
		});			
	},
	/* 
		Popup._toggleStubbornElements() 
	*/
	_toggleStubbornElements:function(toShow){
		var I=this;
		j("embed,object,select").not(I.content.find("embed,object,select")).each(function(i,n){	
			if(!toShow){
				j(n).css("visibility","hidden");
			}else{
				j(n).css("visibility","visible");
			}	
		});			
	}
	
}
	
	







/**
 * Poptip
 * @member tbelt.
 * @constructor
 * @param {Object} opts Options for Poptip.
 * @return The Poptip object.
 */
tbelt.ui.Poptip = function(opts){
	var I = this;	

	I.options = j.extend(true, {}, I.defaults, opts);

	j(function(){I.init()});	
	
	return I;
	
}


tbelt.ui.Poptip.prototype={
	/*
		PUBLIC PROPERTIES
	*/
	defaults:{
		trigger:"",
		tip:"",
		position:{y:"top", x:"right"},
		positionTo:"mouse",
		positionOffset:{y:0,x:0},
		positionOutside:true,
		positionAbsolute:true,
		followMouse:true,
		hideOnScroll:true,
		hideWait:250,
		showWait:100,
		showFunction:function(tip){
			tip.css({display:"block"});
		},
		hideFunction:function(tip){
			tip.css({display:"none"});
		}
	},
	options:null,
	trigger:null,
	tip:null,
	visible:false,	
	/*
		PRIVATE PROPERTIES
	*/
	_hideTimeout:null,
	_showTimeout:null,
	/*
		PUBLIC FUNCTIONS
	*/
	//init()
	init:function(){
		var I=this;
		I.tip = j(I.options.tip);		

		I.trigger = j(I.options.trigger);
		
		j([I.trigger[0],I.tip[0]]).bind("mouseenter", function(evt){			
			clearTimeout(I._hideTimeout);
			if (evt.target == I.trigger[0]) {
				showTimeout = setTimeout(function(){				
					I.position();
					I.show();
					
					if (I.options.positionTo=="mouse" && I.options.followMouse) I.trigger.bind("mousemove", function(){I.position()});					
					
				}, I.options.showWait);
			}			
		});
		
		
		j([I.trigger[0],I.tip[0]]).bind("mouseleave", function(evt){
			clearTimeout(I._showTimeout);
			I._hideTimeout = setTimeout(function(){
				
				I.hide();	
			}, I.options.hideWait);
		});
		if(I.options.hideOnScroll){
			j(window).scroll(function(evt){
				if(I.visible) I.hide(true);
			});
		}
	
		I.hide();
	},
	//position()
	position:function(){
		var I=this;
		var xPos = mouseX;
		var yPos = mouseY;
		
		var anchor;
		if (I.options.positionTo == "trigger") {
			anchor = I.trigger;
		} else if (I.options.positionTo=="mouse") {
			anchor = {
				top: mouseY,
				left: mouseX
			};
		} else {
			anchor = j(I.options.positionTo);
			if(anchor.length==0){
				I.options.positionTo="trigger";
				I.position();
				return null;				
			}
		}
		
		var newPos;		
		if(!tbelt.ui.allowsFixed() || I.options.positionAbsolute){
			newPos = tbelt.ui.relativePosition(anchor, I.tip, {y:I.options.position.y, x:I.options.position.x}, false, I.options.positionOutside);
			newPos.position="absolute";
		}else{
			newPos = tbelt.ui.relativePosition(anchor, I.tip, {y:I.options.position.y, x:I.options.position.x}, true, I.options.positionOutside);
			newPos.position="fixed";
		}		
		
		if(typeof(I.options.position.y)=="number") newPos.top=I.options.position.y;
		if(typeof(I.options.position.x)=="number") newPos.left=I.options.x;
		newPos.top+=I.options.positionOffset.y;
		newPos.left+=I.options.positionOffset.x;
		
		I.tip.css(newPos);
		j(I).trigger("Poptip.position", I);
		return newPos;
	},
	//show()
	show:function(instant){
		var I=this;
		if(!instant){
			I.options.showFunction(I.tip);
		}else{
			I.tip.css({display:"block"});
		}
		I.visible=true;
		j(I).trigger("Poptip.show", I);
	},
	//hide()
	hide:function(instant){
		var I=this;
		if(!instant){
			I.options.hideFunction(I.tip);
		}else{
			I.tip.css({display:"none"});
		}
		I.trigger.unbind("mousemove");
		I.visible=false;
		j(I).trigger("Poptip.hide", I);
	}
	/*
		PRIVATE FUNCTIONS
	*/
};


tbelt.ui.Tabset = function(opts){
	var I = this;
	I.options = j.extend({}, I.defaults, opts);
	
	j(document).ready(function(){I.init();});
}

tbelt.ui.Tabset.prototype = {
	/*
		PUBLIC PROPERTIES
	*/
	defaults:{
		tabs:"",
		contents:"",
		activeClass:"active",
		autoDisplay:0,
		hide:function(paneToHide){
			paneToHide.css({display:"none"});	
		},
		show:function(paneToShow){paneToShow.fadeIn(300);}
	},
	options:null,
	tabs:null,
	contents:null,
	activeTabset:null,
	/*
		PUBLIC METHOD
	*/
	init:function(){
		var I = this;
		I.tabs = j(I.options.tabs);
		I.contents = j(I.options.contents);
		
		var invalids = [];
		I.tabs.each(function(i,n){			
			var tmpTab = j(n);
			var tmpTabLink = tmpTab.find("a[href^=#]");	
			if (tmpTabLink.length>0 && tmpTabLink.attr("href")) {
				tmpTabLink.click(function(evt){
					evt.preventDefault();
					I.showTabset(tmpTabLink.attr("href"));
				});
			}else{
				invalids.push(i);
			}
			
		});	
		I.tabs = I.tabs.filter(function(i){
			return (invalids.indexOf(i)==-1);						   
		});	
		I.hideContents(true);
		//set the first tab as active
		if(I.options.autoDisplay>=0){
			if(I.options.autoDisplay < I.tabs.length){
				j(I.tabs[I.options.autoDisplay]).find("a[href!='']").trigger("click");
			}else{
				j(I.tabs[I.tabs.length-1]).find("a[href!='']").trigger("click");
			}
		}
		

	},
	hideContents:function(instant){
		var I = this;
		I.tabs.each(function(i, n){
			var tabset = I.getTabset(j(n).find("a[href*='#']").attr("href"));
			if(tabset!=null){			
				if (tabset.content.css("display") != "none" && (!I.activeTabset || tabset.content[0]!=I.activeTabset.content[0])) {				
					if(instant!=true)
						I.options.hide(tabset.content);
					else
						tabset.content.css({display:"none"});			
				}
			}
		});
	},
	deactivateButtons:function(){
		var I = this;
		I.tabs.removeClass("active");
	},
	showTabset:function(tabAnchor){
		var I = this;
		tabAnchor = tabAnchor.replace(/^\#/, "");
		var tabset = I.getTabset(tabAnchor);
		if(tabset!=null){
			if(!I.activeTabset || I.activeTabset.content[0]!=tabset.content[0]){				
				I.options.show(tabset.content);
				I.deactivateButtons();
				tabset.tab.addClass(I.options.activeClass);
				I.activeTabset=tabset;
				I.hideContents();
			}
		}
	},
	getTabset:function(tabAnchor){
		var I = this;
		tabAnchor = tabAnchor.replace(/^\#/, "");
		var tabsetContent, tabsetTab;
		for(var c=0; c<I.contents.length; c++){
			if(j(I.contents[c]).find("a[name='"+tabAnchor+"']").length>0){					
				tabsetContent=I.contents[c];
				break;
			}				
		}		
		for(var t=0; t<I.tabs.length; t++){
			if(j(I.tabs[t]).find("a[href='#"+tabAnchor+"']").length>0){					
				tabsetTab=I.tabs[t];
				break;
			}				
		}	
		//trace("tab: "+tabsetTab + ", content: "+tabsetContent);
		if(tabsetTab && tabsetContent)
			return {tab:j(tabsetTab), content:j(tabsetContent)};
		else
			return null;
	}
}

//debugMode=2;

tbelt.ui.SlotWheel = (function(j){

	function SlotWheel(opts){
		var I = this;
		j.extend(I, I.defaults, opts);
		
		j(document).ready(function(){I.init()});
	}
	
	SlotWheel.prototype={
		
		slots:null, //jQuery selector/HTML element array
		slotsWrapper:"<div class='slots-wrapper'></div>", //HTML string/jQuery selector/HTML element
		slotsWindow:null, //jQuery selector/HTML element		
		currentIndex:0,
		isHorizontal:true,
		scrollSpeed:500,
		scrollEasing:"swing",
		scrollCallback:function(idx,position){},
		
		init:function(){
			var I = this;
			I.slots = j(I.slots);
			I.slotsWindow=j(I.slotsWindow);
			
			I.slotsWrapper = j(I.slotsWrapper);
			I.slotsWrapper.css((I.isHorizontal)?"width":"height", I._getSlotsDim());
			
			I.slots.wrapAll(I.slotsWrapper);			
			I.slotsWrapper = I.slotsWindow.find(">*:eq(0)");
		
			I.showSlot(0);
		},
		
		showSlot:function(idx, position){
			//trace("showSlot("+idx+")");
			var I = this;
			I.currentIndex=idx;
			function getPosition(){
				position = I._getSlotsDim(idx)*-1;
				return position;
			}
			var animateParms = {};
			if(I.isHorizontal){
				animateParms.marginLeft=getPosition();
			}else{
				animateParms.marginTop=getPosition();
			}
			I.slotsWrapper.stop(true);
			I.slotsWrapper.animate(animateParms, I.scrollSpeed, I.scrollEasing, function(){I.scrollCallback(idx,position)});
		},
		
		next:function(){
			var I = this;
			var nextIdx = I.currentIndex+1;
			I.showSlot((nextIdx<I.slots.length) ? nextIdx : 0);
		},
		
		previous:function(){
			var I = this;
			var prevIdx = I.currentIndex-1;
			I.showSlot((prevIdx>-1) ? prevIdx : I.slots.length-1);
		},
		
		_getSlotsDim:function(maxIndex){
			var I = this;
			var dim=0;
			
			for(var s=0; (s<I.slots.length); s++){
				if(s==maxIndex)break;
				var slot = j(I.slots[s]);
				if(I.isHorizontal){
					dim+=slot.outerWidth("px"); //seems like it auto calculates margin with "px" as parameter
				}else{
					dim+=slot.outerHeight("px");
				}
				
			}
			
			return dim;
		}
	};
	
	return SlotWheel;

})(jQuery);/*
 * http://tbelt.stephenrushing.com/library/form/
 */
tbelt.form = tbelt.prototype.form = function(){}


/*
 * http://tbelt.stephenrushing.com/library/form/#toQueryString
 */
tbelt.form.toQueryString = tbelt.form.prototype.toQueryString = function(formToConvert, escapeResult){
	if(formToConvert.nodeName)	formToConvert = j(formToConvert);
	if(escapeResult==null) escapeResult = true;
	var queryString="";
	
	j(formToConvert).find("*").each(function(i,field){
		var nodeName = field.nodeName.toLowerCase();
		if(nodeName=="input" && field.type=="text" ||
			nodeName=="input" && field.type=="hidden" ||
			nodeName=="input" && field.type=="radio" && field.checked ||
			nodeName=="input" && field.type=="checkbox" && field.checked ||
			nodeName=="select" ||
			nodeName=="textarea"){
			queryString += ((queryString.length==0)?"?":"&")+j(field).attr("name") +"="+ ((escapeResult)?escape(j(field).val()):j(field).val());			
		}		
	});
	
	return queryString;	
}


tbelt.form.validateField=function(field, expression, required){
	var isValid = true;	
	var checkValue = "";
	field = j(field);
	//trace(field.attr("name") + " : " +field.attr("type"));
	if(field.attr("type")=="radio" || field.attr("type")=="checkbox"){
		var activeInputs = j("input[name='"+field.attr("name")+"']:checked");
		activeInputs.each(function(i, n){
			checkValue += (i==0)?j(n).val():","+j(n).val();
		});
	}else{
		checkValue = field.val();
	}
	
	//if not required and no value
	if(!required && checkValue.length==0) return true;
	else if(required && checkValue.length==0) return false;
	
	//check the input value against the expression 
	if(tbelt.typeOf(expression)=="string" && expression.length>0){
		if(expression=="creditcard"){
			isValid = tbelt.form.isValCreditCard(checkValue);
		}else if(expression=="date"){
			isValid = tbelt.form.isValDate(checkValue);
		}else if(expression=="email"){
			isValid = tbelt.form.isValEmail(checkValue);
		}else if(expression=="phone"){
			isValid = tbelt.form.isValPhone(checkValue);
		}else if(expression=="url"){
			isValid = tbelt.form.isValUrl(checkValue);
		}else{
			isValid = (checkValue==expression);
		}
	}else if(tbelt.typeOf(expression)=="regexp"){
		isValid = expression.test(checkValue);
		//trace(expression.toString() + " : " + checkValue + " : " +isValid);
	}else if(typeof(expression)=="function"){
		isValid = expression(field, required);
	}
	
	//trace(field[0]);
	//trace("\t"+isValid + " : " +checkValue);
	return isValid;
}

tbelt.form.isValEmail = function(emailStr){
	var regex = /^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+([;.](([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+)*$/i;
	return regex.test(emailStr);
}


tbelt.form.isValCreditCard = function(ccStr){
	var validFormats = [/(^(4|5)\d{3}-?\d{4}-?\d{4}-?\d{4}|(4|5)\d{15})|(^(6011)-?\d{4}-?\d{4}-?\d{4}|(6011)-?\d{12})|(^((3\d{3}))-\d{6}-\d{5}|^((3\d{14})))/,
	/^((4\d{3})|(5[1-5]\d{2})|(6011))-?\d{4}-?\d{4}-?\d{4}|3[4,7]\d{13}$/];
	return tbelt.array.search(ccStr, validFormats);	
}

tbelt.form.isValDate = function(dateStr){
	//Basic check for format validity
	var validFormats = [(/^\d{2}\/\d{2}\/\d{4}$/), (/^\d{1}\/\d{2}\/\d{4}$/), (/^\d{1}\/\d{1}\/\d{4}$/), (/^\d{2}\/\d{1}\/\d{4}$/)];
	//var isValid = this.isInList(dateStr, validFormats);
	var isValid = tbelt.array.search(dateStr, validFormats);
	if(!isValid)return false;	

	//Detailed check for valid date ranges
	var monthfield=dateStr.split("/")[0]
	var dayfield=dateStr.split("/")[1]
	var yearfield=dateStr.split("/")[2]
	var dayobj = new Date(yearfield, monthfield-1, dayfield)
	if ((dayobj.getMonth()+1!=monthfield)||(dayobj.getDate()!=dayfield)||(dayobj.getFullYear()!=yearfield)){
		return false;			
	}else{
		return true;
	}
	
}


tbelt.form.isValPhone = function(phoneStr){
	var validFormats = [/^([\(]{1}[0-9]{3}[\)]{1}[\.| |\-]{0,1}|^[0-9]{3}[\.|\-| ]?)?[0-9]{3}(\.|\-| )?[0-9]{4}$/];	
	return tbelt.array.search(phoneStr, validFormats);	
}

tbelt.form.isValUrl = function(ccStr){
	var validFormats = [/(http|https:\/\/)?([a-z0-9][^\.]*)\.([a-z0-9][^\.]*)/i];
	return tbelt.array.search(ccStr, validFormats);	
}


tbelt.form.Checkbox = (function(j){

	function Checkbox(opts){
		var I = this;
		I.options = j.extend(true, I, opts);
		
		j(document).ready(function(){I.init()});
	}
	
	Checkbox.prototype={
		options:null,
		elementClasses:{base:"checkbox", hover:"checkbox-hover", checked:"checkbox-checked", disabled:"checkbox-disabled"},
		element:null, //jQuery selector/HTML elment
		//skin:"<span class='checkbox'></span>", //html string
		skin:false,
		label:false, //jQuery selector
		labelClasses:{base:"label", hover:"label-hover", checked:"label-checked", disabled:"label-disabled", error:"label-error"},

		init:function(){
			var I = this;
			
			I.element=j(I.element);
			if(I.element.length>1) I.element=j(I.element[0]);
			
			if(I.element.data("tbelt.form.Checkbox")!=null){
				I = I.element.data("tbelt.form.Checkbox");
			}else{
				I.element.data("tbelt.form.Checkbox", I);
			}
				
			//try to find label if none is specified
			I.label=j(I.label);
			if(I.label[0]==document) I.label=false;
			
			if((!I.label ||I.label.length==0) && I.element[0].id){
				var lbl = j("label[for='"+I.element[0].id+"']");
				if(lbl.length>0) I.label = lbl;					
				else I.label = false;
			}
			if(I.label!=false){ 
				I.label.data("tbelt.form.Checkbox", I);
				I.label.addClass(I.labelClasses.base);
				I.label.click(onLabelClick);
				I.label.hover(onLabelHover, onLabelHover);
			}
			
			if(I.skin!=false){
				I.skin = j(I.skin);
				I.skin.data("tbelt.form.Checkbox", I);
				I.element.css({display:"none"});
				I.element.before(I.skin);	
				
				I.element.click(function(evt){
					if(I.element[0].checked){
						I.click(false);
					}else{
						I.click(true);
					}		
				});
				
				I.skin.click(function(evt){
					I.element.click();
				});			
				
				I.label.click(function(evt){
					evt.preventDefault();
					I.element.click();
				});
				
				j([I.label[0],I.skin[0]]).hover(function(evt){
					I.skin.addClass(I.elementClasses.hover);
				}, function(evt){
					I.skin.removeClass(I.elementClasses.hover);
				});
				
				if(I.element[0].checked) I.checked(true);
			}else{
				I.element.addClass(I.elementClasses.base);
			}
		},
		checked:function(isChecked){
			var I = this;
			//trace("checked("+isChecked);

			if(isChecked==true){
				if(I.skin!=false) I.skin.addClass(I.elementClasses.checked);
				else{
					I.element[0].checked=true;
					I.element.addClass(I.elementClasses.checked);
				}
				if(I.label!=null) I.label.addClass(I.labelClasses.checked);
			}else if(isChecked==false){
				if(I.skin!=false) I.skin.removeClass(I.elementClasses.checked);
				else{
					I.element[0].checked=false;
					I.element.removeClass(I.elementClasses.checked);
				}
				if(I.label!=null) I.removeClass.addClass(I.labelClasses.checked);
			}else{
				isChecked = I.element[0].checked;
			}
			
			return isChecked;
		},
		click:function(isChecked){
			var I = this;
			//trace("click("+isChecked);
			if(isChecked==null){
				I.element.click();
				return;
			}
			I.checked(isChecked);
			j(I).trigger("Checkbox.click", isChecked);
			return isChecked;
		},
		
		hover:function(isHover){
			var I = this;
			
			if(isHover==true){
				if(I.skin!=false){
					I.skin.addClass(I.elementClasses.hover);
				}else{
					I.element.addClass(I.elementClasses.hover);
				}
				if(I.label!=null) I.label.addClass(I.labelClasses.hover);
			}else{
				if(I.skin!=false){
					I.skin.removeClass(I.elementClasses.hover);
				}else{
					I.element.removeClass(I.elementClasses.hover);
				}
				if(I.label!=null) I.label.removeClass(I.labelClasses.hover);
				isHover=false;
			}
			j(I).trigger("Checkbox.hover", isHover);
			return isHover;
		},
		
		disabled:function(toDisable){
			var I = this;
			if(toDisable!=null){
				if(toDisable){
					I.element.attr("disabled","disabled");
					if(I.skin!=false){
						I.skin.addClass(I.elementClasses.disabled);
					}else{
						I.element.addClass(I.elementClasses.disabled);
					}
					I.label.addClass(I.labelClasses.disabled);
				}else{
					I.element.removeAttr("disabled");
					if(I.skin!=false){
						I.skin.removeClass(I.elementClasses.disabled);
					}else{
						I.element.removeClass(I.elementClasses.disabled);
					}
					if(I.label!=null) I.label.removeClass(I.labelClasses.disabled);
				}
			}else{
				toDisable = (I.element.attr("disabled"))? true : false;
			}
			j(I).trigger("Checkbox.disabled", toDisable);
			return toDisable;
		},
		
		validate:function(){
			var I = this;
			var isValid = tbelt.form.validateField(I.element, I.criteria, I.required);
			if(isValid){
				I.element.removeClass(I.elementClasses.error);
				I.label.removeClass(I.labelClasses.error);
			}else{
				I.element.addClass(I.elementClasses.error);
				I.label.addClass(I.labelClasses.error);
			}
			j(I).trigger("Checkbox.validate", isValid);
			return isValid;
		}
		
		
		
	};
	
	
	function onInputClick(evt){
		var I = j(evt.target).data("tbelt.form.Checkbox");
		if(!I) return;	
		I.click(evt);
	}
	
	
	function onInputHover(evt){
		var I = j(evt.target).data("tbelt.form.Checkbox");
		if(!I) return;	
		if(evt.type=="mouseenter"){
			I.hover(true);
		}else{
			I.hover(false);
		}
	}
	
	function onLabelHover(evt){		
		var target = j(evt.target);
		var I = j(evt.target).data("tbelt.form.Checkbox");
		if(!I) I=target.closest("label").data("tbelt.form.Checkbox"); 
		if(!I) return;	
		if(evt.type=="mouseenter"){
			if(!I.disabled())I.hover(true);
		}else{
			if(!I.disabled())I.hover(false);
		}
		
	}
	
	
	function onLabelClick(evt){		
		evt.preventDefault();
		var target = j(evt.target);		
		var I = j(evt.target).data("tbelt.form.Checkbox");		
		if(!I) I=target.closest("label").data("tbelt.form.Checkbox"); 
		if(!I) return;	
		if(!I.disabled())I.element.click();
	}
	
	
	return Checkbox;

})(jQuery);






tbelt.form.RadioButton = (function(j){

	function RadioButton(opts){
	
		tbelt.form.Checkbox.apply(this, arguments);
		//j(document).ready(function(){I.init()});
	}
	
	j.extend(RadioButton.prototype, tbelt.form.Checkbox.prototype,
	{
		elementClasses:{base:"radio", hover:"radio-hover", checked:"radio-checked", disabled:"radio-disabled"}		
	});
	
	
	return RadioButton;

})(jQuery);
/*
tbelt.form.Checkbox = (function(){
	var Checkbox=function(){
	
	}
	return Checkbox;
})();
*/


tbelt.form.OptionsGroup = (function(j){

	function OptionsGroup(opts){
		var I = this;
		I.options = j.extend(true, I, opts);
		
		j(document).ready(function(){I.init()});
	}
	
	OptionsGroup.prototype={
		elements:null, //jQuery selector/HTML elment
		optionParams:{}, //parameters to be passed to each tbelt.form.Checkbox/RadioButton instance
		group:[],
		mode:"checkbox", //or radio
		required:false,
		criteria:/.*/,
		errorMessage:"",
		label:null, //jQuery selector
		labelClasses:{base:"label", hover:"label-hover", focus:"label-focus", disabled:"label-disabled", error:"label-error"},		
		
		init:function(){
			var I = this;
			
			I.elements = j(I.elements);	
			if(I.elements[0]==document)return;			
			I.mode = I.elements[0].type.toLowerCase();
			//trace(I.mode);
			//try to get label automatically if none is specified
			I.label=j(I.label);
			if(I.label[0]==document && I.elements[0].name){
				var lbl = j("label[for='"+I.elements[0].name+"']");
				if(lbl.length>0) I.label = lbl;
			}
			I.label.addClass(I.labelClasses.base);
			
			for(var e=0; e<I.elements.length; e++){
				var el = j(I.elements[e]);
				var option;
				I.optionParams.element=el;
				if(I.mode=="checkbox"){
					option = el.data("tbelt.form.Checkbox");
					if(option==null){
						option = new tbelt.form.Checkbox(I.optionParams);
					}					
				}else if(I.mode=="radio"){
					option = el.data("tbelt.form.RadioButton");
					if(option==null){
						option = new tbelt.form.RadioButton(I.optionParams);
					}
				}
				el.data("tbelt.form.OptionsGroup",I);
				I.group.push(option);
			}
			
		},
		
		validate:function(){
			var I = this;
			var isValid = tbelt.form.validateField(I.elements[0], I.criteria, I.required);
			if(isValid){				
				I.label.removeClass(I.labelClasses.error);
			}else{
				I.label.addClass(I.labelClasses.error);
			}
			j(I).trigger("Select.validate", isValid);
			return isValid;
		}
		
		
		
	};
	
	
	return OptionsGroup;

})(jQuery);


tbelt.form.TextField = (function(j){

	function TextField(opts){
		var I = this;
		var criteria = opts.criteria || I.criteria;
		I.opts=opts;
		j.extend(true, I, I.opts);
		I.criteria=criteria;
		j(document).ready(function(){I.init()});
	}
	
	TextField.prototype={
		opts:null,
		elementClasses:{base:"textfield", hover:"textfield-hover", focus:"textfield-focus", disabled:"textfield-disabled", error:"textfield-error"},
		element:null, //jQuery selector/HTML elment
		//skin:"<span></span>", //html string
		skin:false,
		label:false, //jQuery selector
		labelClasses:{base:"label", hover:"label-hover", focus:"label-focus", disabled:"label-disabled", error:"label-error"},
		required:false,
		errorMessage:"",
		criteria:/.*/, //regexp, string, function
		init:function(){
			var I = this;
			
			I.element=j(I.element);		
			var original = I.element.data("tbelt.form.TextField");	
			if(original!=null){				
				//j.extend(original, I.opts);
				//I=original;
				//original.init();				
				//return;
			}else{
				I.element.data("tbelt.form.TextField", I);				
			}
			
			//try to find label if none is specified
			I.label=j(I.label);
			if(I.label[0]==document) I.label=false;
			
			if((!I.label ||I.label.length==0) && I.element[0].id){
				var lbl = j("label[for='"+I.element[0].id+"']");
				if(lbl.length>0) I.label = lbl;					
				else I.label = false;
			}
			if(I.label!=false){ 
				I.label.data("tbelt.form.TextField", I);
				I.label.addClass(I.labelClasses.base);
			}
			
			
			if(original==null){
				I.element.hover(onInputHover, onInputHover);			
				I.element.focus(onInputFocus);
				I.element.blur(onInputBlur);
				if(I.label!=false){
					I.label.click(onLabelClick);
					I.label.hover(onLabelHover,onLabelHover);
				}
			}
			if(I.skin!=false && !I.element.data("skinned")){
				I.skin = j(I.skin);
				I.skin.addClass(I.elementClasses.base);
				I.element.wrap(I.skin);	
				I.skin = I.element.closest("."+I.elementClasses.base);
				I.element.data("skinned",true);
			}else{
				I.element.addClass(I.elementClasses.base);
				
				
			}
			
			if(I.element.attr("disabled")) I.disabled(true);
		},
		focus:function(evt){
			var I = this;
			if(I.skin!=false){
				I.skin.addClass(I.elementClasses.focus);	
				
			}else{
				I.element.addClass(I.elementClasses.focus);					
			}	
			if(I.label!=false) I.label.addClass(I.labelClasses.focus);				
			j(I).trigger("TextField.focus");
		},
		blur:function(evt){
			var I = this;
			if(evt==null){
				I.element.blur();
				return;
			}
			if(I.skin!=false){
				I.skin.removeClass(I.elementClasses.hover);
				I.skin.removeClass(I.elementClasses.focus);
			}else{
				I.element.removeClass(I.elementClasses.hover);
				I.element.removeClass(I.elementClasses.focus);
			}
			if(I.label!=false){
				I.label.removeClass(I.labelClasses.hover);	
				I.label.removeClass(I.labelClasses.focus);
			}
			j(I).trigger("TextField.blur");
		},
		
		disabled:function(toDisable){
			var I = this;
			if(toDisable!=null){
				if(toDisable){
					I.element.attr("disabled","disabled");
					if(I.skin!=false){
						I.skin.addClass(I.elementClasses.disabled);
					}else{
						I.element.addClass(I.elementClasses.disabled);
					}
					if(I.label!=false) I.label.addClass(I.labelClasses.disabled);
				}else{
					I.element.removeAttr("disabled");
					if(I.skin!=false){
						I.skin.removeClass(I.elementClasses.disabled);
					}else{
						I.element.removeClass(I.elementClasses.disabled);
					}
					if(I.label!=false) I.label.removeClass(I.labelClasses.disabled);
				}
			}else{
				toDisable = (I.element.attr("disabled"))? true : false;
			}
			j(I).trigger("TextField.disabled", toDisable);
			return toDisable;
		},
		
		hover:function(isHover){
			var I = this;
			
			if(isHover==true){
				if(I.skin!=false){
					I.skin.addClass(I.elementClasses.hover);
				}else{
					I.element.addClass(I.elementClasses.hover);
				}
				if(I.label!=false) I.label.addClass(I.labelClasses.hover);
			}else{
				if(I.skin!=false){
					I.skin.removeClass(I.elementClasses.hover);
				}else{
					I.element.removeClass(I.elementClasses.hover);
				}
				if(I.label!=false) I.label.removeClass(I.labelClasses.hover);
				isHover=false;
			}
			j(I).trigger("TextField.hover", isHover);
			return isHover;
		},
		
		validate:function(){
			var I = this;
			var isValid = tbelt.form.validateField(I.element, I.criteria, I.required);
			if(isValid){
				I.element.removeClass(I.elementClasses.error);
				if(I.label!=false) I.label.removeClass(I.labelClasses.error);
			}else{
				I.element.addClass(I.elementClasses.error);
				if(I.label!=false) I.label.addClass(I.labelClasses.error);
			}
			j(I).trigger("TextField.validate", isValid);
			return isValid;
		}
		
		
	};
	
	
	
	function onInputFocus(evt){
		var I = j(evt.target).data("tbelt.form.TextField");
		if(!I) return;	
		I.focus(evt);
	}
	
	function onInputBlur(evt){
		var I = j(evt.target).data("tbelt.form.TextField");
		if(!I) return;	
		I.blur(evt);
	}
	
	function onInputHover(evt){
		var I = j(evt.target).data("tbelt.form.TextField");
		if(!I) return;	
		if(evt.type=="mouseenter"){
			I.hover(true);
		}else{
			I.hover(false);
		}
	}
	
	function onLabelHover(evt){		
		var target = j(evt.target);
		var I = j(evt.target).data("tbelt.form.TextField");
		if(!I) I=target.closest("label").data("tbelt.form.TextField"); 
		if(!I) return;	
		if(evt.type=="mouseenter"){
			if(!I.disabled())I.hover(true);
		}else{
			if(!I.disabled())I.hover(false);
		}
		
	}
	
	
	function onLabelClick(evt){		
		evt.preventDefault();
		var target = j(evt.target);		
		var I = j(evt.target).data("tbelt.form.TextField");		
		if(!I) I=target.closest("label").data("tbelt.form.TextField"); 
		if(!I) return;	
		if(!I.disabled())I.element.focus();
	}
	
	
	return TextField;

})(jQuery);




tbelt.form.Select = (function(j){

	function Select(opts){
		var I = this;
		var criteria = opts.criteria || I.criteria;
		j.extend(true, I, opts);
		I.criteria=criteria;
		j(document).ready(function(){I.init()});
	}
	
	Select.prototype={
		elementClasses:{base:"select", hover:"select-hover", focus:"select-focus", disabled:"select-disabled", error:"select-error"},
		element:null, //jQuery selector/HTML elment
		//skin:"<span class='textfield'></span>", //html string
		skin:false,
		label:false, //jQuery selector
		labelClasses:{base:"label", hover:"label-hover", focus:"label-focus", disabled:"label-disabled", error:"label-error"},
		required:false,
		criteria:/.*/, //regexp, string, function
		initValidate:false,
		errorMessage:"",
		init:function(){
			var I = this;
			
			I.element=j(I.element);			
			if(I.element.data("tbelt.form.Select")!=null){
				I = I.element.data("tbelt.form.Select");
				return;
			}else{
				I.element.data("tbelt.form.Select", I);				
			}
			if(I.element.attr("disabled")) I.disabled();
			
			//try to find label if none is specified
			I.label=j(I.label);
			if(I.label[0]==document) I.label=false;
			
			if((!I.label ||I.label.length==0) && I.element[0].id){
				var lbl = j("label[for='"+I.element[0].id+"']");
				if(lbl.length>0) I.label = lbl;					
				else I.label = false;
			}
			if(I.label!=false){ 
				I.label.data("tbelt.form.Select", I);
				I.label.addClass(I.labelClasses.base);
				I.label.click(onLabelClick);
				I.label.hover(onLabelHover, onLabelHover);
			}
			
			
			I.element.hover(onInputHover, onInputHover);
			I.element.focus(onInputFocus);
			I.element.blur(onInputBlur);
			
			if(I.skin!=false){
				I.skin = j(I.skin);
				I.skin.addClass(I.elementClasses.base);
				I.element.wrap(I.skin);	
				I.skin = I.element.closest("."+I.elementClasses.base);
				
				I.skin.click(function(evt){
					I.element.click();
				});							
				
				//I.skin.hover(function(evt){I.hover(true);}, function(evt){I.hover(false)});
				
			}else{
				I.element.addClass(I.elementClasses.base);
				
				
			}
			
			if(I.initValidate){
				I.validate();
			}
		},
		focus:function(evt){
			var I = this;
			if(evt==null){
				I.element.focus();
				return;
			}
			if(I.skin!=false){
				I.skin.addClass(I.elementClasses.focus);	
			}else{
				I.element.addClass(I.elementClasses.focus);	
			}	
			if(I.label!=false) I.label.addClass(I.labelClasses.focus);			
			j(I).trigger("TextField.focus");
		},
		blur:function(evt){
			var I = this;
			if(evt==null){
				I.element.blur();
				return;
			}
			if(I.skin!=false){
				I.skin.removeClass(I.elementClasses.hover);
				I.skin.removeClass(I.elementClasses.focus);
			}else{
				I.element.removeClass(I.elementClasses.hover);
				I.element.removeClass(I.elementClasses.focus);
			}
			if(I.label!=false) I.label.removeClass(I.labelClasses.focus);	
			j(I).trigger("TextField.blur");
		},
		
		disabled:function(toDisable){
			var I = this;
			if(toDisable!=null){
				if(toDisable){
					I.element.attr("disabled","disabled");
					if(I.skin!=false){
						I.skin.addClass(I.elementClasses.disabled);
					}else{
						I.element.addClass(I.elementClasses.disabled);
					}
					if(I.label!=false) I.label.addClass(I.labelClasses.disabled);	
				}else{
					I.element.removeAttr("disabled");
					if(I.skin!=false){
						I.skin.removeClass(I.elementClasses.disabled);
					}else{
						I.element.removeClass(I.elementClasses.disabled);
					}
					if(I.label!=false) I.label.removeClass(I.labelClasses.disabled);	
				}
			}else{
				toDisable = (I.element.attr("disabled"))? true : false;
			}
			j(I).trigger("TextField.disabled");
			return toDisable;
		},
		
		hover:function(isHover){
			var I = this;
			
			if(isHover==true){
				if(I.skin!=false){
					I.skin.addClass(I.elementClasses.hover);
				}else{
					I.element.addClass(I.elementClasses.hover);
				}
				if(I.label!=false) I.label.addClass(I.labelClasses.hover);	
			}else{
				if(I.skin!=false){
					I.skin.removeClass(I.elementClasses.hover);
				}else{
					I.element.removeClass(I.elementClasses.hover);
				}
				if(I.label!=false) I.label.removeClass(I.labelClasses.hover);	
				isHover=false;
			}
			return isHover;
		},
		
		validate:function(){
			var I = this;
			var isValid = tbelt.form.validateField(I.element, I.criteria, I.required);
			if(isValid){
				if(I.skin!=false){
					I.skin.removeClass(I.elementClasses.error);
				}else{
					I.element.removeClass(I.elementClasses.error);
				}
				if(I.label!=false) I.label.removeClass(I.labelClasses.error);
			}else{
				if(I.skin!=false){
					I.skin.addClass(I.elementClasses.error);
				}else{
					I.element.addClass(I.elementClasses.error);
				}
				if(I.label!=false) I.label.addClass(I.labelClasses.error);
			}
			j(I).trigger("Select.validate", isValid);
			return isValid;
		}
		
		
	};
	
	
	function onInputFocus(evt){
		var I = j(evt.target).data("tbelt.form.Select");
		if(!I) return;	
		I.focus(evt);
	}
	
	function onInputBlur(evt){
		var I = j(evt.target).data("tbelt.form.Select");
		if(!I) return;	
		I.blur(evt);
	}
	
	function onInputHover(evt){
		var I = j(evt.target).data("tbelt.form.Select");
		if(!I) return;	
		if(evt.type=="mouseenter"){
			I.hover(true);
		}else{
			I.hover(false);
		}
	}
	
	function onLabelHover(evt){		
		var target = j(evt.target);
		var I = j(evt.target).data("tbelt.form.Select");
		if(!I) I=target.closest("label").data("tbelt.form.Select"); 
		if(!I) return;		
		if(evt.type=="mouseenter"){
			if(!I.disabled())I.hover(true);
		}else{
			if(!I.disabled())I.hover(false);
		}
		
	}
	
	function onLabelClick(evt){		
		evt.preventDefault();
		var target = j(evt.target);
		var I = j(evt.target).data("tbelt.form.Select");
		if(!I) return;	
		if(!I) I=target.closest("label").data("tbelt.form.Select"); 		
		if(!I.disabled())I.element.focus();
	}
	
	
	return Select;

})(jQuery);tbelt.form.Handler = (function(j){

	function Handler(opts){
		var I = this;
		I.opts = opts;
		j.extend(true, I, I.opts);

		j(document).ready(function(){I.init()});
	}
	
	Handler.prototype={
		opts:null,
		form:null, //jQuery Selector or HTML Element
		fields:[],
		elementClasses:{base:null, error:null}, //{base:"input", error:""}
		labelClasses:{base:null, error:null},
		errorMessageContainer:null, //jQuery Selector or HTML Element
		onError:function(handler, errorMsg){},
		onSuccess:function(handler){},
		
		init:function(){
			var I = this;
			I.form=j(I.form);
			I.fields=[];
			
			I.errorMessageContainer=j(I.errorMessageContainer);
			if(I.errorMessageContainer[0]!=document) I.errorMessageContainer.css({display:"none"});
			
			I.form.data("tbelt.form.Handler", I);
			
			for(var f=0; f<I.opts.fields.length; f++){
				I.addField(I.opts.fields[f]);
			}
			
			if(I.form[0].nodeName.toLowerCase()=="form"){
				I.form.submit(function(evt){
					if(!I.validate()) evt.preventDefault();					
				});				
			}
			
			j(I).trigger("Handler.init");
		},
		
		addField:function(newField){
			var I = this;
			//if a tbelt.form.Checkbox/TextField/Select etc object is NOT being directly passed
			if(newField.validate==null){
				var el = j(newField.element||newField.elements);
				if(el.length==0) return;
				
				var nodeName = el[0].nodeName.toLowerCase();
				if(nodeName=="input"){
					var nodeType = el[0].type.toLowerCase();
					if(nodeType=="text"){
						var existingObj = el.data("tbelt.form.TextField");
						if(!existingObj) newField = new tbelt.form.TextField(newField);
						else newField = existingObj;
					}else if(nodeType=="checkbox" || nodeType=="radio"){
						var existingObj = el.data("tbelt.form.OptionsGroup");
						if(!existingObj) newField = new tbelt.form.OptionsGroup(newField);
						else newField = existingObj;
					}
					
				}else if(nodeName=="select"){
					var existingObj = el.data("tbelt.form.Select");
					if(!existingObj) newField = new tbelt.form.Select(newField);
					else newField = existingObj;
				
				}else if(nodeName=="textarea"){
					var existingObj = el.data("tbelt.form.TextField");
					if(!existingObj) newField = new tbelt.form.TextField(newField);
					else newField = existingObj;
				}
			
			}
			//override base/error classes
			for(prop in I.elementClasses)
				if(I.elementClasses[prop]==null) delete I.elementClasses[prop];
			j.extend(newField.elementClasses, I.elementClasses);
			for(prop in I.labelClasses)
				if(I.labelClasses[prop]==null) delete I.labelClasses[prop];
			j.extend(newField.labelClasses, I.labelClasses);
			I.fields.push(newField);
			
			j(I).trigger("Handler.addField", newField);
			return newField;
		},
		
		validate:function(){
			var I = this;
			var isValid=true;
			var errorMsg="";
			for(var f=0; f<I.fields.length; f++){
				var field = I.fields[f];
				var tmpValid = field.validate();
				if(!tmpValid) {
					isValid=false;
					errorMsg += "<li>"+field.errorMessage+"</li>";
				}
			}
			
			if(isValid) I.onSuccess(I);
			else{
				if(I.errorMessageContainer[0]!=document){
					I.errorMessageContainer.css({display:""});
					I.errorMessageContainer.html("<ul>"+errorMsg+"</ul>");
				}
				I.onError(I, errorMsg);
			}
			j(I).trigger("Handler.validate", isValid, errorMsg);
			return isValid;
		}
	}
	
	return Handler;
	
})(jQuery);

/*
tbelt.form.Handler = tbelt.prototype.form.Handler = function(opts){
	var I = this;
	I.defaults = {
		form:"",
		fields:[], // see I.addField for acceptable field properties
		fieldClassDefault:"",
		fieldClassError:"input-error",
		labelClassDefault:"",
		labelClassError:"",
		errorMsgContainer:"",
		error:function(handler){handler.options.errorMsgContainer.css({display:"block"});handler.options.errorMsgContainer.html(handler.errorMsg);},
		success:function(handler){handler.options.form.submit();}	
	};		
	I.options = j.extend({}, I.defaults, opts);	
	I.form = j("");
	
	
	//get a field by a property
	I.getField=function(fieldProp, byProp){
		if (byProp == null) {
			byProp = "name";
		}
		var objQuery = new Object();
		objQuery[byProp]=fieldProp;
		var field = tbelt.queryList(I.options.fields, objQuery);
		return tbelt.typeOf(field)=="array" && field.length>1 ? field : field[0]; 
	}
	
	
	
	//add a field to the handler
	I.addField=function(field){
		
		//sync field with field template object
		field = j.extend({name:"", 
			expression:null, 
			msg:"", 
			required:false, 
			initValue:null, 
			defaultValue:"", 
			revertTrigger:"blur"
		}, field);
		field.element = I.form.find("[name='"+field.name+"']");	
		if (field.element.length > 0) {
			if (field.element.attr("type") != "radio" && field.element.attr("type") != "checkbox") {			
				//set field revert properties/actions
				if (field.initValue == null) {
					field.initValue = field.element.val();
				}else{
					field.element.val(field.initValue);
				}
				field.element.attr("initValue", field.initValue);
				field.element.focus(function(evt){
					if (j(evt.target).val() == j(evt.target).attr("initValue")) 
						j(evt.target).val("");
				});
				if (field.revertTrigger == "blur") {
					field.element.blur(function(evt){
						if (j(evt.target).val() == "") 
							j(evt.target).val(j(evt.target).attr("initValue"));
					});
				}
			}
			//add the field to the fields array
			I.options.fields.push(field);
		}
		
	}


	//validate a single field
	I.validateField=function(field){
		var fieldIsValid = true;	
		var checkValue = "";
		//trace(field.element.attr("name") + " : " +field.element.attr("type"));
		if(field.element.attr("type")=="radio" || field.element.attr("type")=="checkbox"){
			var activeInputs = j("input[name='"+field.element.attr("name")+"']:checked");
			activeInputs.each(function(i, n){
				checkValue += (i==0)?j(n).val():","+j(n).val();
			});
		}else{
			checkValue = field.element.val();			
		}
		
		if(checkValue==field.initValue || checkValue=="" || checkValue==null){
			if(checkValue!=field.initValue) field.element.val(checkValue);
			checkValue=(field.defaultValue && field.defaultValue.length>0)?field.defaultValue:"";

			if(!field.required) return true;
		}	
		
		//first check the input value against the expression 
		if(tbelt.typeOf(field.expression)=="string" && field.expression.length>0){
			if(field.expression=="creditcard"){
				fieldIsValid = I.isCreditCard(checkValue);
			}else if(field.expression=="date"){
				fieldIsValid = I.isDate(checkValue);
			}else if(field.expression=="email"){
				fieldIsValid = I.isEmail(checkValue);
			}else if(field.expression=="phone"){
				fieldIsValid = I.isPhone(checkValue);
			}else{
				fieldIsValid = (checkValue==field.expression);
			}
		}else if(tbelt.typeOf(field.expression)=="regexp"){
			fieldIsValid = field.expression.test(checkValue);
			//trace(field.expression.toString() + " : " + checkValue + " : " +fieldIsValid);
		}else if(tbelt.typeOf(field.expression)=="function"){
			fieldIsValid = field.expression(field.element, field);
		}
		//if it's not required and is empty
		
		//if it's required....
		if(field.required && fieldIsValid){
			if(field.element.attr("type")=="checkbox" || field.element.attr("type")=="radio"){
				fieldIsValid = (field.element[0].checked)?true:false;
			}else{
				fieldIsValid = (checkValue.length>0)?true:false;
			}
		}
		
		var fieldLabel = I.form.find("label[for='"+field.element.attr("id")+"']");
		
		if(!fieldIsValid){
			//toggle field and label classes
			field.element.addClass(I.options.fieldClassError);
			field.element.removeClass(I.options.fieldClassDefault);
			fieldLabel.addClass(I.options.labelClassError);
			fieldLabel.removeClass(I.options.labelClassDefault);
			
			if(field.msg && field.msg.length>0) I.errorMsg +="<li>"+field.msg+"</li>";
			
			isValid=false;
		}else{
			//toggle field and label classes
			field.element.removeClass(I.options.fieldClassError);
			field.element.addClass(I.options.fieldClassDefault);					
			fieldLabel.removeClass(I.options.labelClassError);
			fieldLabel.addClass(I.options.labelClassDefault);
		}
		//trace(field.name + " : " +tbelt.typeOf(field.expression) + " : " + fieldIsValid);
		
		
		return fieldIsValid;
	}
	
	I.toQueryString=function(escapeResult){		
		return tbelt.form.toQueryString(I.form, escapeResult);
	}
	

	
	//validate the entire form
	I.validate = function(){
		
		var isValid = true;
		I.errorMsg = "";
		//check all the fields in options.fields
		for(var f=0; f < I.options.fields.length; f++){			
			var fieldIsValid = I.validateField(I.options.fields[f]);
			if(fieldIsValid==false){
				isValid=false;
			}
			//console.log(I.options.fields[f].name + " : " +fieldIsValid);
		}		
		//a lightweight check for bots
		if(I.checkpointField.length==0 || I.checkpointField.val().length>0){
			isValid=false;
		}
		//final actions based on isValid
		if(isValid){
			I.checkpointField.remove();
			I.options.success(I);
		}else{
			I.errorMsg = "<ul>"+I.errorMsg+"</ul>";
			I.options.error(I);
			
		}	
		return isValid;
	}
	
	I.submit=function(){
		I.form.submit();			
	}
	
	I.reset=function(){
		I.form[0].reset();	
		
		//clear the error message
		I.errorMsg="";
		I.options.errorMsgContainer.html("").css({display:"none"});
		
		//clear the field error classes	
		if(I.options.fieldClassError.length>0){
			var errorFields = I.form.find("."+I.options.fieldClassError);
			if(errorFields.length>0){
				errorFields.removeClass(I.options.fieldClassError);
			}
		}
		//clear the label error classes
		if(I.options.labelClassError.length>0){
			var errorLabels = I.form.find("."+I.options.labelClassError);
			if(errorLabels.length>0){
				errorLabels.removeClass(I.options.labelClassError);
			}	
		}		
	}
	
	I.init=function(){
		I.form = j(I.options.form);
		if(I.options.errorMsgContainer.length>0)
			I.options.errorMsgContainer = j(I.options.errorMsgContainer);
		else
			I.options.errorMsgContainer = j("nothing");
			
		I.reset();
		
		I.form.append("<input type='hidden' name='checkpoint' value=''/>");
		I.checkpointField = I.form.find("input[name='checkpoint']");
		I.errorMsg="";	
		
		//create a copy of options.fields and run all through I.addField
		var fieldsCopy = I.options.fields;
		I.options.fields=[];
		for(var f=0; f<fieldsCopy.length; f++){
			I.addField(fieldsCopy[f]);		
		}
		
	}
	
	j(document).ready(I.init);
	
	
	return I;
}

tbelt.form.Handler.prototype.isEmail = function(emailStr){
	var regex = /^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+([;.](([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+)*$/i;
	return regex.test(emailStr);
}


tbelt.form.Handler.prototype.isCreditCard = function(ccStr){
	var validFormats = [/(^(4|5)\d{3}-?\d{4}-?\d{4}-?\d{4}|(4|5)\d{15})|(^(6011)-?\d{4}-?\d{4}-?\d{4}|(6011)-?\d{12})|(^((3\d{3}))-\d{6}-\d{5}|^((3\d{14})))/,
	/^((4\d{3})|(5[1-5]\d{2})|(6011))-?\d{4}-?\d{4}-?\d{4}|3[4,7]\d{13}$/];
	return tbelt.array.search(ccStr, validFormats);	
}

tbelt.form.Handler.prototype.isDate = function(dateStr){
	//Basic check for format validity
	var validFormats = [(/^\d{2}\/\d{2}\/\d{4}$/), (/^\d{1}\/\d{2}\/\d{4}$/), (/^\d{1}\/\d{1}\/\d{4}$/), (/^\d{2}\/\d{1}\/\d{4}$/)];
	//var isValid = this.isInList(dateStr, validFormats);
	var isValid = tbelt.array.search(dateStr, validFormats);
	if(!isValid)return false;	

	//Detailed check for valid date ranges
	var monthfield=dateStr.split("/")[0]
	var dayfield=dateStr.split("/")[1]
	var yearfield=dateStr.split("/")[2]
	var dayobj = new Date(yearfield, monthfield-1, dayfield)
	if ((dayobj.getMonth()+1!=monthfield)||(dayobj.getDate()!=dayfield)||(dayobj.getFullYear()!=yearfield)){
		return false;			
	}else{
		return true;
	}
	
}


tbelt.form.Handler.prototype.isPhone = function(phoneStr){
	var validFormats = [/^([\(]{1}[0-9]{3}[\)]{1}[\.| |\-]{0,1}|^[0-9]{3}[\.|\-| ]?)?[0-9]{3}(\.|\-| )?[0-9]{4}$/];	
	return tbelt.array.search(phoneStr, validFormats);	
}
*/