function checker() {
	
	if (typeof checker._initialized == "undefined") {

		this.fail_handler; // callback function for when a test fails
		this.fail_object; // arbitrary object to pass to fail callback

		this.success_handler; // callback function for when test succeeds
		this.success_object; // arbitrary object to pass to success callback

		this.current; // position of current test
		this.proceed;

		checker.prototype.build = function() {
			this.clear();
		};

		// return false if any test in series fails
		checker.prototype.run = function() {
		
			var isPassed = true;
	
			for ( var i in this.tests ) {

				if ( this.proceed ) {
					this.current = i;
					if( !(this.results[i] = this.tests[i]( this.elements[i] ) ) ) {
						if ( this.fail_handler ) { // use fail handler provided by user
							this.fail_handler( this.fail_object );
						}
						isPassed = false;
					} else {
						if ( this.success_handler ) { // use success handler provided by user
							this.success_handler( this.success_object );
						}
					}

				}
			}

			this.clear();
			return isPassed;
		};

		checker.prototype.stopTests = function() {
			this.proceed = false;
		};

		// get current element
		checker.prototype.getElement = function() {
			return this.elements[this.current];
		};

		// get current error
		checker.prototype.getError = function() {
			return this.errors[this.current];
		}; 

		checker.prototype.getResults = function() {
			return this.results;
		};

		// set callback for failed test.
		checker.prototype.setOnFail = function( f, obj ) {
			this.fail_handler = f;
			this.fail_object = obj;
		};

		// set callback for passed test.
		checker.prototype.setOnSuccess = function( f, obj ) {
			this.success_handler = f;
			this.success_object = obj;
		};

		// TODO think more on this
		checker.prototype.addCustomTest = function( fn, obj ) {
		};

		checker.prototype.addTest = function( elem, test, error ) {
			if ( typeof( elem ) != "object" ) {
				elem = document.getElementById( elem );
			}

			if ( elem ) {
				// add the element
				this.elements.push( elem );

				// add the error
				this.errors.push( error );

				// set the corresponding test
				switch( test ) {
				case "is_populated": 		this.tests.push( this.is_populated ); 		break;
				case "is_selected":			this.tests.push( this.is_selected );		break;
				case "is_valid_url":		this.tests.push( this.is_valid_url );		break;
				case "is_valid_url_simple":		this.tests.push( this.is_valid_url_simple );		break;
				case "is_valid_email":		this.tests.push( this.is_valid_email );		break;
				case "is_checked":			this.tests.push( this.is_checked );			break;
				case "is_valid_zip":		this.tests.push( this.is_valid_zip );		break;
				case "is_contain_alpha":	this.tests.push( this.is_contain_alpha );	break;
				case "is_contain_numeric":	this.tests.push( this.is_contain_numeric );	break;
				case "is_currency":			this.tests.push( this.is_currency );		break;
				case "is_numeric":			this.tests.push( this.is_numeric );			break;
				case "is_integer":			this.tests.push( this.is_integer );			break;
				}
			}
		};

		checker.prototype.clear = function() {
			this.tests = [];
			this.results = [];
			this.errors = [];
			this.elements = [];
			this.current = 0;
			this.proceed = true;
		};

		// check if an html input field is populated
		checker.prototype.is_populated = function( elem ) {
			if ( typeof( elem ) != "object" ) elem = document.getElementById( elem );
			var isPassed = true;
			var pop_value = elem.value;
			pop_value = trim(pop_value);
			if ( !pop_value.length ) isPassed = false;
			return isPassed;
		};

		// check if an option is selected with an html select box
		checker.prototype.is_selected = function( elem ) {
			if ( typeof( elem ) != "object" ) elem = document.getElementById( elem );
			var isPassed = true;
			if ( !elem.selectedIndex ) isPassed = false;
			return isPassed;
		};

		checker.prototype.is_numeric = function( input ) {

			if ( typeof( input ) == "object" ) input = input.value;
			var isPassed = ( (input - 0) == input ) && (input.length > 0 ) && ( ( input - 0 ) > 0 );
			
			if ( isPassed == "NaN" ) isPassed = false;
			return isPassed;
		}


		checker.prototype.isDigit = function( c ) {
			return ((c >= "0") && (c <= "9"))
		}

		checker.prototype.is_integer = function( s ) {

			if ( typeof( s ) == "object" ) s = s.value;

			for ( var i = 0; i < s.length; i++) {
				var c = s.charAt(i);

				if ( !( ( ( c >= "0" ) && ( c <= "9") ) ) ) return false;
			}
			return true;
		}

		checker.prototype.is_empty = function(s) {
			return ((s == null) || (s.length == 0))
		}

		checker.prototype.is_currency = function( str ) {
			if ( typeof( str ) == "object" ) str = str.value;
      str = trim(str);
			reCurrency = /^[0-9]+\.[0-9]{2}$/;
			return reCurrency.test( str );
		};

		checker.prototype.is_valid_url = function( url ) {
			if ( typeof( url ) == "object" ) url = url.value;
      url.value = trim(url);
			var reUrl = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
			return reUrl.test( url );
		};

		checker.prototype.is_valid_email = function( email ) {
			if ( typeof( email ) == "object" ) email = email.value;
			email = trim(email);
			var reEmail = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
			return reEmail.test( email );
		};

		checker.prototype.is_valid_zip = function( zip ) {
			if ( typeof( zip ) == "object" ) zip = zip.value;
      zip = trim(zip);
			var reZip = /^\d{5}?$/;
			return reZip.test( zip );
		};

		checker.prototype.is_contain_numeric = function( str ) {
			if ( typeof( str ) == "object" ) str = str.value;
      str = trim(str);
			reNumeric = /\d/;
			return reNumeric.test( str );
		};

		checker.prototype.is_contain_alpha = function( str ) {
			if ( typeof( str ) == "object" ) str = str.value;
      str = trim(str);
			var reAlha = /[a-zA-Z]/;
			return reAlpha.test( str );
		};

		checker.prototype.is_checked = function( elem ) {
			if ( typeof( elem ) == "string" ) elem = document.getElementById( elem );
			return elem.checked;
		};

		checker._initialized = true;
	}

	this.build();	
}
