//Object that adds additional formatting to form input.
//Only supports North American Number Planning Phone Numbers and US/Candian Postal Codes
//To add support for other inputs add the class name as a key and the function as the value to the class2format object.
//Closely related to the formFormatterAttacher
function FormFormatter() {

    //PROPERTIES
    var class2PresentationFormat = new Object();  //format to display to the user on change of a input box
    var class2SubmitFormat = new Object();  //format to sumbit to the server

    //Symbolic Constants for phoneNumbers;
    var EXTENSION_SYMBOL = "x"; //symbol to use if there is an extension

    var onInputChange = function(e) {
        var input = null;
        if(e.srcElement) {
            input = e.srcElement;
        } else {
            input = this;
        }

        if(class2PresentationFormat[input.className] != undefined && class2PresentationFormat[input.className] != null) {
            input.value = class2PresentationFormat[input.className](input.value);   //Convert User Entered String to a formatted string.
        }
    };

    //Only works for North American Number Planning (NANP) numbers (U.S., Canada, and most of the Carribean Nations)
    //NOTE WELL: Has not been tested on international numbers.
    class2PresentationFormat["js_phoneNumber"] = function(preformattedStr) {

        //SYMBOLIC CONSTANTS
        //Regular Expressions
        var NANP = new RegExp("(1)?"            //TRUNK
                            + "\\D*"
                            + "([2-9][0-8]\\d)" //NPA (Area Code) NOTE WELL we are expecting an area code
                            + "\\D*"
                            + "([2-9]\\d{2})"   //NXX
                            + "\\D*"
                            + "(\\d{4})"        //Station Code
                            + "\\D*"
                            + "(\\d*)");        //Extension

        //Exception to the Regular Expression above
        //These may be matched by the regular expression above but they are in fact not valid phone numbers
        var NOT_NXX = new RegExp("[2-9]11"); //nxx - can not be [2-9]11
        var NOT_NXX_STATION = new RegExp("555\D*01\\d{2}"); //5550100 - 5550199 reserved for fictional use

        var TRUNK_GROUP = 1;        //indicies into the array returned by NANP.exec
        var NPA_GROUP = 2;          //If the RegEx changes above these will have to change
        var NXX_GROUP = 3;          //as each set of () defines a group
        var STATION_GROUP = 4;
        var EXTENSION_GROUP = 5;

        //CODE
        var matches = NANP.exec(preformattedStr);
        if(matches == null) {
            return preformattedStr; //don't do any formatting we didn't detect a valid number
        }

        //else
        var trunk = matches[TRUNK_GROUP];
        var npa = matches[NPA_GROUP];
        var nxx = matches[NXX_GROUP];
        var station = matches[STATION_GROUP];
        var extension = matches[EXTENSION_GROUP];

        //Test if we got a invalid number
        if(NOT_NXX.test(nxx) || NOT_NXX_STATION.test(nxx+station)) {
            return preformattedStr; //don't do any formatting we didn't detect a valid number
        }

        return (trunk? trunk + "-" : "") + npa + "-" + nxx + "-" + station + (extension? " " + EXTENSION_SYMBOL + extension : "");  //return formatted string
    }

    //Only design to work with US and Candian postal code.
    class2PresentationFormat["js_postalCode"] = function(preformattedStr) {
        //SYMBOLIC CONSTANTS
        var US_ZIP_CODE_REGEX = new RegExp("(\\d{5})\\D*(\\d{4})?");
        var CANDIAN_POSTAL_CODE_REGEX = new RegExp("([a-zA-Z]\\d[a-zA-Z])\\D*(\\d[a-zA-Z]\\d)");

        var US_5_DIGIT_GROUP = 1;
        var US_4_DIGIT_GROUP = 2;

        var CA_GROUP_1 = 1;
        var CA_GROUP_2 = 2;

        //CODE
        var matches = US_ZIP_CODE_REGEX.exec(preformattedStr); //Try to match the US zip code
        if(matches != null) {   //we've found US zip code
            var grp1 = matches[US_5_DIGIT_GROUP];
            var grp2 = matches[US_4_DIGIT_GROUP];

            var reformattedZipCode = grp1 + (grp2 ? "-" + grp2 : "");   //format the zip code
            return reformattedZipCode; //done reformatting
        }

        matches = CANDIAN_POSTAL_CODE_REGEX.exec(preformattedStr);
        if(matches != null) { //we've found  Canadian postal code
            var grp1 = matches[CA_GROUP_1];
            var grp2 = matches[CA_GROUP_2];

            var reformattedPostalCode = grp1.toUpperCase() + " " + grp2.toUpperCase();  //format the postal code
            return reformattedPostalCode; //done reformatting
        }

        return preformattedStr; //Didn't match either the candian or US postal code don't apply formatting
    }

    //FUNCTIONS

    //Attaches the FormFormatter to a form given an id
    //This causes the FormFormatter to move through the nodes of the form and addEventListeners to inputs with recognized class names
    this.attach = function(form) {
    	$('input.js_phoneNumber').change(
    		function(evt) {
    			var curVal = $(this).val();
    			$(this).val(class2PresentationFormat['js_phoneNumber'](curVal));
    		}
    	);
    	
    	$('input.js_postalCode').change(
    		function(evt) {
    			var curVal = $(this).val();
    			$(this).val(class2PresentationFormat['js_postalCode'](curVal));
    		}
    	);
    };
}

//Attaches a Form Formatter to every form on the page.
//See FormFormatter for details.
function formFormatterAttacher() {
    var forms = document.forms; //get the forms in the page
    for(var i = 0; i < forms.length; ++i) { //attach the a FormFormatter to each form
        (new FormFormatter()).attach(forms[i]);
    }
}


