(function($){

//--------------------------------plugin equalTo------------------------------//
// Description: Determines the equality of 2 jQuery objects.
// PARAMETERS:
//		obj;     //The jQuery object being compared
// Postcondition: Returns boolean true if objects are equal. False if not.
//----------------------------------------------------------------------------//
jQuery.fn.equalTo = function(obj)
{
	isEqual = !jQuery(this).not( jQuery(obj) ).length;
	return isEqual;
};


 
})(this.jQuery);


/*
 * jQuery Selectbox plugin 0.1.2
 *
 * Copyright 2011, Dimitar Ivanov (http://www.bulgaria-web-developers.com/projects/javascript/selectbox/)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 * 
 * Date: Fri Mar 18 22:28:49 2011 +0200
 */
(function($,undefined){
var dropDownImg = "http://www.teachertrainingbooks.com/images/dropdown.png";

var PROP_NAME="selectbox",FALSE=false,TRUE=true;function Selectbox(){this._state=[];this._defaults={classHolder:"sbHolder",classHolderDisabled:"sbHolderDisabled",classSelector:"sbSelector",classOptions:"sbOptions",classToggleOpen:"sbToggleOpen",classToggle:"sbToggle",effect:"slide",onChange:null,onOpen:null,onClose:null}}$.extend(Selectbox.prototype,{_isOpenSelectbox:function(target){if(!target){return FALSE}var inst=this._getInst(target);return inst.isOpen},_isDisabledSelectbox:function(target){if(!target){return FALSE}var inst=this._getInst(target);return inst.isDisabled},_attachSelectbox:function(target,settings){if(this._getInst(target)){return FALSE}var $target=$(target),self=this,inst=self._newInst($target),sbHolder,sbSelector,sbToggle,sbOptions,s=FALSE,opts=$target.find("option"),olen=opts.length;$target.attr("sb",inst.uid);$.extend(inst.settings,self._defaults,settings);self._state[inst.uid]=FALSE;$target.hide();function closeOthers(){var key,uid=this.attr("id").split("-")[1];for(key in self._state){if(key!==uid){if(self._state.hasOwnProperty(key)){if($(":input[sb='"+key+"']")[0]){self._closeSelectbox($(":input[sb='"+key+"']")[0])}}}}}sbHolder=$("<div>",{id:"sbHolder-"+inst.uid,"class":inst.settings.classHolder});sbSelector=$("<a>",{id:"sbSelector-"+inst.uid,href:"#","class":inst.settings.classSelector,click:function(e){e.preventDefault();closeOthers.apply($(this),[]);var uid=$(this).attr("id").split("-")[1];if(self._state[uid]){self._closeSelectbox(target)}else{self._openSelectbox(target)}}});sbToggle=$("<a>",{id:"sbToggle-"+inst.uid,href:"#","class":inst.settings.classToggle,click:function(e){e.preventDefault();closeOthers.apply($(this),[]);var uid=$(this).attr("id").split("-")[1];if(self._state[uid]){self._closeSelectbox(target)}else{self._openSelectbox(target)}}});sbToggle.appendTo(sbHolder);sbOptions=$("<ul>",{id:"sbOptions-"+inst.uid,"class":inst.settings.classOptions,css:{display:"none"}});opts.each(function(i){var that=$(this),li=$("<li>");if(that.is(":selected")){sbSelector.html("<span>" + that.text() + "</span>");s=TRUE}if(i===olen-1){li.addClass("last")}$("<a>",{href:"#"+that.val(),rel:that.val(),text:that.text(),click:function(e){e.preventDefault();var t=sbToggle,uid=t.attr("id").split("-")[1];self._changeSelectbox(target,$(this).attr("rel"),$(this).text());self._closeSelectbox(target)}}).appendTo(li);li.appendTo(sbOptions)});if(!s){sbSelector.text(opts.first().text())}$.data(target,PROP_NAME,inst);sbSelector.appendTo(sbHolder);sbOptions.appendTo(sbHolder);sbHolder.insertAfter($target)},_detachSelectbox:function(target){var inst=this._getInst(target);if(!inst){return FALSE}$("#sbHolder-"+inst.uid).remove();$.data(target,PROP_NAME,null);$(target).show()},_changeSelectbox:function(target,value,text){var inst=this._getInst(target),onChange=this._get(inst,"onChange");if(text.length > 15){$("#sbSelector-"+inst.uid).html("<span>" + text.substr(0,23) + "..." + "</span>");}else{$("#sbSelector-"+inst.uid).html("<span>" + text + "</span>");}$(target).children("option[value='"+value+"']").attr("selected",TRUE);if(onChange){onChange.apply((inst.input?inst.input[0]:null),[value,inst])}else{if(inst.input){inst.input.trigger("change")}}},_enableSelectbox:function(target){var inst=this._getInst(target);if(!inst||!inst.isDisabled){return FALSE}$("#sbHolder-"+inst.uid).removeClass(inst.settings.classHolderDisabled);inst.isDisabled=FALSE;$.data(target,PROP_NAME,inst)},_disableSelectbox:function(target){var inst=this._getInst(target);if(!inst||inst.isDisabled){return FALSE}$("#sbHolder-"+inst.uid).addClass(inst.settings.classHolderDisabled);inst.isDisabled=TRUE;$.data(target,PROP_NAME,inst)},_optionSelectbox:function(target,name,value){var inst=this._getInst(target);if(!inst){return FALSE}inst[name]=value;$.data(target,PROP_NAME,inst)},_openSelectbox:function(target){var inst=this._getInst(target);if(!inst||inst.isOpen||inst.isDisabled){return }$(".sbHolder").not("#sbHolder-" + inst.uid).css("z-index", 1);$("#sbHolder-"+inst.uid).css("z-index", "100");var el=$("#sbOptions-"+inst.uid),viewportHeight=parseInt($(window).height(),10),offset=$("#sbHolder-"+inst.uid).offset(),scrollTop=$(window).scrollTop(),height=el.prev().height(),diff=viewportHeight-(offset.top-scrollTop)-height/2,onOpen=this._get(inst,"onOpen");inst.settings.effect==="fade"?el.fadeIn():el.css("z-index", "100").slideDown();$("#sbToggle-"+inst.uid).addClass(inst.settings.classToggleOpen);this._state[inst.uid]=TRUE;inst.isOpen=TRUE;if(onOpen){onOpen.apply((inst.input?inst.input[0]:null),[inst])}$.data(target,PROP_NAME,inst)},_closeSelectbox:function(target){var inst=this._getInst(target);if(!inst||!inst.isOpen){return }var onClose=this._get(inst,"onClose");$("#sbOptions-"+inst.uid).fadeOut();$("#sbSelector-"+inst.uid+":not(:has(img))").append('<img src="' + dropDownImg + '" alt="Drop Down">');$("#sbOptions-"+inst.uid).slideUp();$("#sbToggle-"+inst.uid).removeClass(inst.settings.classToggleOpen);this._state[inst.uid]=FALSE;inst.isOpen=FALSE;if(onClose){onClose.apply((inst.input?inst.input[0]:null),[inst])}$.data(target,PROP_NAME,inst)},_newInst:function(target){var id=target[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:id,input:target,uid:Math.floor(Math.random()*99999999),isOpen:FALSE,isDisabled:FALSE,settings:{}}},_getInst:function(target){try{return $.data(target,PROP_NAME)}catch(err){throw"Missing instance data for this selectbox"}},_get:function(inst,name){return inst.settings[name]!==undefined?inst.settings[name]:this._defaults[name]}});$.fn.selectbox=function(options){var otherArgs=Array.prototype.slice.call(arguments,1);if(typeof options=="string"&&options=="isDisabled"){return $.selectbox["-"+options+"Selectbox"].apply($.selectbox,[this[0]].concat(otherArgs))}if(options=="option"&&arguments.length==2&&typeof arguments[1]=="string"){return $.selectbox["-"+options+"Selectbox"].apply($.selectbox,[this[0]].concat(otherArgs))}return this.each(function(){typeof options=="string"?$.selectbox["-"+options+"Selectbox"].apply($.selectbox,[this].concat(otherArgs)):$.selectbox._attachSelectbox(this,options)})};$.selectbox=new Selectbox();$.selectbox.version="0.1.2"})(jQuery);

//-------------------------------Function isset-------------------------------//
// Purpose: Determines if a variable has been set/initialized
// PARAMETERS:
// 		Var: A variable of any type.
// Postcondition: Returns boolean true if the variable is set. False otherwise.
//----------------------------------------------------------------------------//
function isset(Var)
{
	return !(typeof Var == 'undefined' || Var === null  || Var === "");
}

/************************************************************/
//Class Regex
//Purpose: Static entity for evaluating common regex patterns.
//
//Note the use of anonymous functions around the pattern and
//descrption properties to keep them private.
/************************************************************/
var Regex = {}; //Object instead of function for static effect

//Define regex patterns.
Regex.pattern = function(){
    return {
    int: "\\d+",
    float: "\\d*\\.\\d+",
    mailstrings: "(content\\-type|mime\\-version|multipart\\/mixed|Content\\-Transfer\\-Encoding|bcc|cc|to|headers):",
    email: "[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,4}",
    html: "<([a-zA-Z][a-zA-Z0-9]*)\\b[^>]*>.*?<\\/\\1>",
    url: "([-a-z0-9+.]*(:|:\\/\\/))?([\\w_-]+\\.)+[a-zA-Z]{2,}[-%\\$_.+!*'(),;\\/?:@=&\\w#]*",
    zip: "^((\\d{5}-\\d{4})|(\\d{5})|([A-Z]\\d[A-Z]\\s\\d[A-Z]\\d))",
    alpha: "[a-zA-Z]+",
    num: "\\d+",
    bbcode: "\\[([a-zA-Z][a-zA-Z0-9]*)\\b[^\\]]*\\].*?\\[\\/\\1\\]",
    usphone: "(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\d{4})\\s*(([xX]|[eE][xX][tT])[-.:]?\\s*(\\d+))*",
    usaddress: "\\d+\\s[-\\w.,\\s#:]+",
    fullname: "[a-zA-Z]+\\s+([-a-zA-Z.'\\s]|[0-9](nd|rd|th))+",
    name: "[-a-zA-Z.'\\s]+",
    lastname: "([-a-zA-Z.'\\s]|[0-9](nd|rd|th))+"
    };
};

//Define the descriptions as they will appear in emails or response text
Regex.description = function() {    
    return {
    int: "Integer",
    float: "Float",
    mailstrings: "Mail Strings",
    email: "Email",
    html: "HTML",
    url: "URL",
    zip: "Zip Code",
    alpha: "Alphabetic Character",
    num: "Number",
    bbcode: "BB Code",
    usphone: "Phone",
    usaddress: "Address",
    name: "Name",
    fullname: "Name",
    lastname: "Last Name",
    message: "Message"
    };
};

Regex.example = function() {
    return {
    name: "John Doe",
    usphone: "903-555-5555",
    email: "myemail@gmail.com",
    html: "<b>HTML</b>",
    url: "www.vertstudios.com",
    zip: "75701",
    alpha: "abcdefg",
    num: "99095",
    bbcode: "[B]BBCODE[/B]",
    usaddress: "1800 East Barbara Street",
    fullname: "John Doe",
    lastname: "Doe 2nd",
    int: "111",
    float: "111.50",
    mailstrings: "to:bcc:",
    message: "No HTML or BB Code."
    };
};

//Returns the match result for an exact match
Regex.is = function(type, val)
{
    //Make sure the type is formatted properly
    type = Regex.getType(type);		
    
    pattern = Regex.pattern()[type];
    
    //Create regular expression object
    var re = new RegExp("^" + pattern + "$");
    
    return re.test(val);
        
};

    //Returns the logical negation of Regex.is
    Regex.isNot = function(type,val)
    {
        return !Regex.is(type,val);
    };

//Returns the match result for a match contained anywhere in the string
Regex.has = function(type, val)
{
    //Make sure the type is formatted properly
    type = Regex.getType(type);		
    
    pattern = Regex.pattern()[type];
    
    //Create regular expression object
    var re = new RegExp(pattern);
    
    return re.test(val);        
};

    //Returns the logical negation of Regex.has
    Regex.hasNot = function(type,val)
    {
        return !Regex.has(type,val);
    };

//Returns the match result for match options contained anywhere in the string
Regex.hasAny = function(types, val)
{
    //Parse the string passed in for types
    types = Regex.getArray(types);
    
    //Assume none are found
    var flag = false; 
    
    for(var i=0; i<types.length; i++)
    {		
            //Make sure the type is formatted properly
            type = Regex.getType(types[i]);
            
            
            if(Regex.has(type,val))
            {
                    flag = true;
            }		
    }
    
    return flag;
};

    //Returns the logical negation of Regex.hasAny
    Regex.hasNone = function(types, val)
    {
        return !Regex.hasAny(types,val);
    };
//Get the text description of a validation type
Regex.getDescription = function(type)
{
    //Make sure the type is formatted properly. Return the description.
    type = Regex.getType(type);
    
    return Regex.description()[type];
};

//Get the text example of a validation type
Regex.getExample = function(type)
{
    //Make sure the type is formatted properly. Return the description.
    type = Regex.getType(type);
    
    return Regex.example()[type];
};
        
//Transforms a comma delimited string into an array
Regex.getArray = function(str)
{
    //Get rid of blank spaces
    str = str.replace(" ", "");
    
    return str.split(",");		
};

//Get the type from a string that may contain integers, or may not be
//lowercase.
Regex.getType = function(str)
{
    return str.toLowerCase().replace(/[^a-z]+/,"");    
};



/************************************************************/
//Class GetSet
//Purpose: Creates dynamic getters and setters
/************************************************************/

var GetSet = {};

//=========================================================//
//Public Method override
//Purpose: Override default values through iteration
//Parameters:
//  obj: The object whose default values will be overridden
//Postcondition: options Object is altered
//=========================================================//
GetSet.override = function(options, defaults)
{
    //Store this scope
    var $this = options;
    
    
    for (var i in defaults)
    {
        if(!($this[i]))
        {
            $this[i] = defaults[i];
        }        
    }
};

//=========================================================//
//Public Method gettters
//Purpose: Dynamically creates accessor methods(getters)
//Parameters: 
//  scope: The scope in which the accessor methods will be
//         applied
//  prefix: Goes before the property. i.e. (get)Name
//  camel: whether to induce camel case
//  obj: Accessors
//Postcondition: scope has been altered to include
//accessor methods
//=========================================================//
GetSet.getters = function(options)
{   
    //Over-ride default values
    var defaults =
    {
        prefix: "get",
        camel: true
    };
    
    //Override defaults values
    GetSet.override(options, defaults);
    
    //If prefix is set to 'none', force blank. A blank string as a parameter
    //evaluates to null for some reason.
    options.prefix = (options.prefix === "none") ? "" : options.prefix;
    
    //Iterate through the properties of the object
    var str;
    for ( var i in options.obj )
    {
        //If camel case is enabled and no blank prefix
        if(options.camel && options.prefix !== "")
        {
            str = i.charAt(0).toUpperCase() + i.substr(1);
        }
        else
        {
            str = i;
        }
        (function(i)
        {
                // Dynamically create an accessor method
                options.scope[ options.prefix + str ] = function()
                {
                        return options.obj[i];
                };  
            })(i);
    }
};

//=========================================================//
//Public Method setters
//Purpose: Dynamically creates muator methods(setters)
//Parameters: 
//  scope: The scope in which the mutator methods will be
//         applied
//  prefix: Goes before the property. i.e. (set)Name
//  camel: whether to induce camel case
//  obj: The object that will have mutators
//Postcondition: scope has been altered to include mutator
//methods
//=========================================================//
GetSet.setters = function(options)
{
    //Over-ride default values
    var defaults =
    {
        prefix: "set",
        camel: true
    };
    
    //Override defaults values
    GetSet.override(options, defaults);
    
    //If prefix is set to 'none', force blank. A blank string as a parameter
    //evaluates to null for some reason.
    options.prefix = (options.prefix === "none") ? "" : options.prefix;    
    
    //Iterate through the properties of the object
    var str;
    for ( var i in options.obj )
    {
        //If camel case is enabled and no blank prefix
        if(options.camel && options.prefix !== "")
        {
            str = i.charAt(0).toUpperCase() + i.substr(1);
        }
        else
        {
            str = i;
        }
        (function(i)
        {
                // Dynamically create an accessor method
                options.scope[ options.prefix + str ] = function(val)
                {
                       options.obj[i] = val;
                };  
            })(i);
    }
};

/************************************************************/
//Class POST
//Purpose: Easily communicate with server via POST
//Parameters:
//  url: The URL of the PHP file that will send a response
/************************************************************/
function POST(url, callback)
{            
    //Initialize empty string for field/value pairs
    var str = "";
        
    //=========================================================//
    //Public Method set
    //Purpose: Set's the value of a field
    //Parameters: 
    //  field: The field corresponding to the value. (Name, phone)
    //  val: The new value associated with the field
    //Postcondition: str is altered
    //=========================================================//
    this.set = function(field, val)
    {        
        //Check to see if this field already exists
        var re = new RegExp(field + "=[a-zA-Z0-9%+.!$()_+*'-]*");        
        var match = re.exec(str);
        
        //If this field is set, overwrite the value
        if(match)
        {            
            str = str.replace(match, field + "=" + val);
        }
        
        //If not, append the string with the value
        else
        {
            //Check if an & is at the end of str.
            //If not, add one if this isn't the beginning of the string.            
            if( (str.length > 0) && (str.substr(-1) != "&") )
            {
                str += "&";
            }
            
            str += (field + "=" + val);
        }
    };
    
    //=========================================================//
    //Public callback
    //Purpose: Set callback function
    //Parameters:
    //  func: The new callback function
    //Postcondition: callback is altered
    //=========================================================//
    this.callback = function(func)
    {
        callback = func;
    };
    
    //=========================================================//
    //Public serialize
    //Purpose: Serves as a wrapper for jQuery serialize. 
    //Parameters:
    //  form: jQuery selector of the form to be serialized.
    //Postcondition: str is appended with seralized form sring
    //=========================================================//
    this.serialize = function(form)
    {
        str += ($(form).serialize());
    };
    
    //=========================================================//
    //Method getResponse
    //Purpose: Get a string response from the server
    //Postcondition: Returns a string
    //=========================================================//
    this.getResponse = function()
    {                  
        //Make sure appropriate values are set
        if( isset(url) && isset(str) && isset(callback) )
        {
            jQuery.ajax(
            {
                type: "POST",
                url: url,
                data: str,
                success: function(msg)
                {
                    callback(msg);
                },
                error: function(msg)
                {
                    callback(msg);
                }
            });            
        }
    };
}

/************************************************************/
//Class Form
//Purpose: Form validation and AJAX
//Dependencies: POST, Regex
//Parameters:
// id: The jQuery Object representing the form
// URL: The URL of the server file
// hasAny: Any form values that will be subject to a hasAny
//         test should go here. Takes the form
//         {id, types}
// hasNone: Same concept as hasAny, but for a hasNone test.
// invalidClass: The invalid class that will be added to form items
//               That do not pass form validation.
// requiredClass:The class associated with required input fields
/************************************************************/
function Form(id,options)
{
    //------------------------------------------------------------
    // Set default property values
    //------------------------------------------------------------
    
    //Get default form
    id = (typeof id === 'undefined') ? $("form") : id;
    
    //Store a relatively global scope for convenience
    var form = this;
    
    //Initialize default values
    var defaults = {
        id: $(id),
        URL: $(id).attr("action"),
        hasAny: null,
	hasNone: null,
        invalidClass: "invalid",
        requiredClass: "required",
        inputs: $(id).find('input[type="text"], textarea')
    };	settings = jQuery.extend(defaults,options);
    
    //Get setters and methods for the settings object.
    GetSet.getters({obj: settings, scope: form, prefix: "none"});
    GetSet.setters({obj: settings, scope: form});
     
    //------------------------------------------------------------
    //Define variables/functions for adding an AJAX string
    //------------------------------------------------------------
    var addAJAX = false;
    
    //=========================================================//
    //Pulic Method addAjax
    //Purpose: Concatenates a serialized post variable AJAX
    //Postcondition: var addAjax is set to true
    //=========================================================//
    form.addAJAX = function()
    {
        addAJAX = true;
    };
    
    //Allow user to force the form to invalid
    var forceInvalid = false;
    
    //=========================================================//
    //Public forceInvalid
    //Purpose: Allows user to force the form into an invalid state
    //Postcondition: forceInvalid altered
    //=========================================================//
    this.forceInvalid = function()
    {
        forceInvalid = true;
    };
    
    //=========================================================//
    //Public Method valid
    //Purpose: Validate a single item
    //Parameters:
    //  obj: The jQuery object of the item being tested
    //Postcondition: InvalidClass added to input if invalid
    //=========================================================//
    form.valid = function(obj)
    {
        //Get form attributes
        var value = $(obj).val();       
        var name = $(obj).attr('name'); 
        var valid;
        
        //Boolean: If the input is required
        var isRequired = $(obj).hasClass(settings.requiredClass);
        
        //If the user has requested the form be forced invalid, return false.
        if(forceInvalid)
        {
            forceInvalid = false;
            return false;
        }
        
        //ZOMG RECURSIVE POLYMORPHISM!
        if(typeof obj === 'undefined')
        {	    
            //Invalid counter
            var invalidCount = 0;	    
            
            $(settings.inputs).each(function()	       
            {
               if(form.invalid($(this)))
               {
                    invalidCount++;
               }
            });
            
            //Returning the negation of invalidCount causes a valid form
            //to return true
            return !invalidCount;
        }
        
        else
        {   
            //If blank and required, return false. If blank and not required,
            //return true
            if(!value)
            {
                if(isRequired)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
            
            //------------------------------------------------------------
            //If the value is not blank, proceed with these processes
            //------------------------------------------------------------
            
            //Check to see if this item is included in the "HasAny" pseudo-array
            var hasAnyTypes = false;
            if(isset(settings.hasAny))
            {	
                for(var i=0; i<settings.hasAny.length; i++)
                {
                    if($(obj).equalTo($(settings.hasAny[i].id)))
                    {
                        //store the types
                        hasAnyTypes = settings.hasAny[i].types;
                    }            
                }
            }
            
            //Repeat the process for the HasNone psuedo-array
            var hasNoneTypes = false;
            if(isset(settings.hasNone))
            {
                for(var i=0; i<settings.hasNone.length; i++)
                {
                    if($(obj).equalTo($(settings.hasNone[i].id)))
                    {
                        //store the types
                        hasNoneTypes = settings.hasNone[i].types;
                    }            
                }
            }	
            
            //Handle specific validation criteria
            if(hasAnyTypes)
            {
                valid = Regex.hasAny(hasAnyTypes, value);
            }
            else if(hasNoneTypes)
            {
                valid = Regex.hasNone(hasNoneTypes, value);
            }
            else
            {
                valid = Regex.is(name, value);
            }
            
            return valid;
        }
    };
    
    //Create a copy of .valid for lingistic convenience. Some people may
    //prefer to call form.validate(input) for a specific item since the
    //verb "validate" implies an action.
    form.validate = form.valid;
    
    //Return the logical negation of form.validate. Also returns the jq objects
    //That correlate to it.
    form.invalid = function(obj)
    {
        if(typeof obj === 'undefined')
        {
            //Will hold the jquery object with the invalid items
            var invalidObj = $();
            $(settings.inputs).each(function()
            {
                if( form.invalid($(this)) )
                {
                    invalidObj = $(invalidObj).add($(this));	    
                }
            });	    
            return invalidObj;
        }
        else
        {
            return !form.validate(obj);
        }
    };
    
    //=========================================================//
    //Public Method clear
    //Purpose: Clears the form of all input values
    //Postcondition: Form is cleared
    //=========================================================//
    form.clear = function()
    {
        jQuery(settings.id).find(':input')
                    .not(':button, :submit, :reset, :hidden')
                    .val('').removeAttr('checked')
                    .removeAttr('selected')
                    .removeClass(settings.invalidClass);
        jQuery(settings.id).find('textarea').val('');                                
    };    

        //------------------------------------------------------------
        //Define the potential messages back from the server
        //------------------------------------------------------------
        var Server = {};
        Server.successResponse = "thanks";
        Server.invalidResponse = "invalid";
        Server.errorResponse = "error";
        
        GetSet.getters({obj: Server, scope:form});
        GetSet.setters({obj: Server, scope:form});
	
        //-----------------------------------------------------------
        // Define Callback object to hold private callback methods 
        //-----------------------------------------------------------
        var Callback = {};
	
        Callback.success = function()
        {
            $("#confirmation").html("<h4>Your message was sent successfully.</h4>");
            form.clear();
        };
        
        Callback.serverInvalid = function()
        {
            $("#confirmation").html("<h4>Message not sent. Please verify your information.</h4>");            
        };
        
        Callback.clientInvalid = function(invalid)
        {
            $("#confirmation").html("<h4>Please verify your information.</h4>");
            $(invalid).addClass(settings.invalidClass);
        };
        
        Callback.error = function(response)
        {
            $("#confirmation").html("<h4>Server Error. Please try again.</h4>");            
        };
        
        Callback.send = function()
        {
           $("#confirmation").html("<h4>Sending...</h4>"); 
        };
        
        //Function that decides which callback to execute based on server response.
        Callback.doCallback = function(response)
        {
            if(response == Server.successResponse)
            {
                Callback.success();
            }
            
            else if(response == Server.invalidResponse)
            {
                Callback.serverInvalid();
            }
            
            else
            {
                Callback.error();                
            }            
        };
        
        //Define mutator methods for Callback. onSuccess, onInvalid
        GetSet.setters({obj: Callback, scope: form, prefix: "on"});
        
    //=========================================================//
    //Public Method mail
    //Purpose: Attempt to send mail and get response from server
    //Postcondition: Calls Callback.doCallback
    //=========================================================//
    form.mail = function()
    {	
        var post = new POST(settings.URL, function(response)
        {
            Callback.doCallback(response);
        });
        
        //Serialize the form
        post.serialize(settings.id);
        
        //If developer requests to add AJAX value, append it to POST string
        if(addAJAX)
        {
            post.set("AJAX", "true");
        }
        
        post.getResponse();
    };
    
    //=========================================================//
    //Public Method quickform
    //Purpose: Provide an easy form send for lazy designers that
    //         probably shouldn't be using jQuery. :P #owned
    //Postcondition: Invalid class added or form is mailed with
    //               the appropriate callback message.
    //=========================================================//
    form.quickform = function()
    {
        $(settings.id).submit(function()
        {
            //If the form is valid, mail it. If not, add the
            //invalid class to all invalid items.
            if(form.valid())
            {
                Callback.send();
                
                //Add an AJAX post variable
                form.addAJAX();
                
                //Display a sending indicator
                form.mail();
            }
            else
            {
                Callback.clientInvalid($(form.invalid()));
            }
            
            return false; //Override default form behavior
        });
    };
}



// usage: log('inside coolFunc',this,arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
  log.history = log.history || [];   // store logs to an array for reference
  log.history.push(arguments);
  if(this.console){
    console.log( Array.prototype.slice.call(arguments) );
  }
};



// catch all document.write() calls
(function(doc){
  var write = doc.write;
  doc.write = function(q){ 
    log('document.write(): ',arguments); 
    if (/docwriteregexwhitelist/.test(q)) write.apply(doc,arguments);  
  };
})(document);



