/*
Script name: DC_form.js

Author: Marcus Kielly
Date: 23 March 2009
Company: Deckchair

Description:
this script creates/appends the DC object with a suite of functions to
automate form validation and display

Method:
Add the relevante class name (space delimited) to apply validation

available tests:

required: req
alphabet: alpha
numeric: num (integers only, no floating point)
alphanumeric: alphanum
first name: firstname
surname: surname
full name: fullname
email: email
telephone: phone
date: date
postcode: postcode
zipcode: zipcode
checkbox: checkreq
*/



(function(){
    //the DC Namespace
	//this technique (and library elements) is derived from:
	//'AdvancED DOM scripting' by Jeffrey Sambells, Aaron Gustafson
    //declare a new object called DC in the windows object
    if(!window['DC']) {window['DC'] = {};
        }


// TESTFORMINPUT
//------------------------------------------------------------------------------
// Description:
//-------------
// Iterates through classnames of array elements, and applies filter accordingly


function testFormInput(currentInput){
        var inputReport = [];

//test CHECK GROUPS.................................................................
        if(currentInput instanceof Array){
            var sum=0;
            for(var c=0;c < currentInput.length; c++){
                if(currentInput[c].checked){
                    sum++;
                    }
                }
            if(sum==0){
              inputReport = [currentInput,'chkgroup']
              return inputReport;
              }
            }

        else{
        var inputClass=currentInput.className;
        var inputClasses=inputClass.split(" ");

        for(var i=0;i < inputClasses.length; i++){
//test REQUIRED.................................................................
        if ( inputClasses[i]=="req" )
            {
                if ( !currentInput.value )
        			{
                    inputReport = [currentInput,'required'];
                    return inputReport;
                    }
            }
        else if (currentInput.value!=""){
//test ALPHABET....................................................................
		if ( inputClasses[i]=="alpha" )
            {
                if ( !validateAlpha(currentInput.value) )
        			{
                    inputReport = [currentInput,'alpha'];
                    return inputReport;
        			}
            }

//test NUMERIC (INTEGER)....................................................................
		else if ( inputClasses[i]=="numint" )
            {
                if ( !validateNumInt(currentInput.value) )
        			{
                    inputReport = [currentInput,'numint'];
                    return inputReport;
        			}
            }

//test NUMERIC (DECIMAL)....................................................................
		else if ( inputClasses[i]=="numdec" )
            {
                if ( !validateNumDec(currentInput.value) )
        			{
                    inputReport = [currentInput,'numdec'];
                    return inputReport;
        			}
            }

//test ALPHANUMERIC....................................................................
		else if ( inputClasses[i]=="alphanum" )
            {
                if ( !validateAlphanum(currentInput.value) )
        			{
                    inputReport = [currentInput,'alphanum'];
                    return inputReport;
        			}
            }

//test NAME....................................................................
		else if ( inputClasses[i]=="firstname" )
            {
                if ( !validateFirstname(currentInput.value) )
        			{
                    inputReport = [currentInput,'firstname'];
                    return inputReport;
        			}
            }

//test NAME....................................................................
		else if ( inputClasses[i]=="surname" )
            {
                if ( !validateSurname(currentInput.value) )
        			{
                    inputReport = [currentInput,'surname'];
                    return inputReport;
        			}
            }

//test FULLNAME....................................................................
		else if ( inputClasses[i]=="fullname" )
            {
                if ( !validateFullname(currentInput.value) )
        			{
                    inputReport = [currentInput,'fullname'];
                    return inputReport;
        			}
            }

//test EMAIL....................................................................
		else if ( inputClasses[i]=="email" )
            {
                if ( !validateEmail(currentInput.value) )
        			{
                    inputReport = [currentInput,'email'];
                    return inputReport;
        			}
            }


//TEST TELEPHONE................................................................
		else if ( inputClasses[i]=="telephone" )
            {
                if ( !validateTelephone(currentInput.value) )
        			{
                    inputReport = [currentInput,'telephone'];
                    return inputReport;
        			}
            }


//TEST POSTCODE................................................................
		else if ( inputClasses[i]=="postcode" )
            {
                if ( !validatePostCode(currentInput.value) )
        			{
                    inputReport = [currentInput,'postcode'];
                    return inputReport;
        			}
            }

//TEST POSTCODE................................................................
		else if ( inputClasses[i]=="zipcode" )
            {
                if ( !validatePostCode(currentInput.value) )
        			{
                    inputReport = [currentInput,'zipcode'];
                    return inputReport;
        			}
            }


//TEST DATE.....................................................................
		else if ( inputClasses[i]=="date" )
            {
                if ( !validateDate(currentInput.value) )
        			{
                    inputReport = [currentInput,'date'];
                    return inputReport;
        			}
            }

//TEST CHECKBOX.................................................................

// THIS NEEDS TO BE MODIFIED TO APPLY TO ANY CHECK REQUIREMENT (i.e: not always privacy)

		else if ( inputClasses[i]=="checkreq" )
            {
            if (!currentInput.checked)
                {
                inputReport = [currentInput,'checkreq'];
                return inputReport;
                }
            }
/*
//TEST CHECKBOX GROUP...........................................................
		else if ( inputClasses[i]=="checkgrp" )
            {

            if (!currentInput.checked)
                {
                inputReport = [currentInput,''];
                return inputReport;
                }
            }

*/
           }
        }
    }
}

//create the interface
window['DC']['testFormInput'] = testFormInput;


//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------



//FILTERING PATTERNS.............................................................

function validateAlpha(value)
{
	var filter = /^[a-zA-Z]*$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}




function validateNumInt(value)
{
	var filter = /^[0-9]*$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}


function validateNumDec(value)
{
	var filter = /^[0-9]+\.?[0-9]*$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}


function validateAlphanum(value)
{
	var filter = /^[a-zA-Z0-9]*$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}

function validateFirstname(value)
{
	var filter = /^[a-zA-Z]*$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}

function validateSurname(value)
{
	var filter = /^[a-zA-Z \.\-]*$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}


function validateFullname(value)
{
	var filter = /^([a-zA-Z]+[.-]?[a-zA-Z])+(\s?[a-zA-Z]+[.-]?[a-zA-Z]+)+$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}


function validateDate(value)
{
	var filter = /^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/;

	if ( !filter.test(value) )
	{
		return false;
	}

	return true;
}



function validateEmail(value)
{
	var filter = /^.+@.+\..{2,3}$/;

	if ( !filter.test(value) )
	{
		return false;
	}
	return true;
}

function validatePostCode(value)
{
	var filter = /^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/;

	if ( !filter.test(value) )
	{
		return false;
	}
	return true;
}





function validateTelephone(value)
{
	var filter = /^([0-9 ]{7,})$/;

	if ( !filter.test(value) )
	{
		return false;
	}
	return true;
}

/*

function validateZipCode(value)
{
	var filter = /^(?<Zip>\d{5})-(?<Sub>\d{4})$/;

	if ( !filter.test(value) )
	{
		return false;
	}
	return true;
}

function validateKeyword(value)
{
	return true;
	var filter = /([ \.]+)/;

	if ( !filter.test(value) )
	{
		return true;
	}
	return false;
}



function validateJpg(value)
{
	var filter = /(jpg|jpeg)/;

	if ( value == "" )
	{
		return true;
	}

	if ( filter.test(value.toLowerCase()) )
	{
		return true;
	}

	return false;

}
*/

//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------








/*
FORMERRORDISPLAY()
------------------------------------------------------------------------------
Description:
-------------
Iterates through classnames of array elements, and applies filter accordingly



RELATIONSHIP
- FUNCTION: ERRMSGGEN()

NOTES FOR DEVELOPMENT:
- the script could automate containment of its target element
- e.g: wrap the current element within a container
- or use sibling methods to insert the error box into the correct
- location

- could be extended to supply additional display methods
- by changing the displayMethod variable
- e.g: Modal, Label
- this suggests prototypal development

- this function is currently tailored to display all a forms
- values at one location in the document, and is not suited
- to an event triggered by a "change"

TESTING REQUIRED
- must implement object tests to test for browser capabilities
- cross browser tests (IE, Firefox, Opera, Safari)


PARAMETERS
-------------------------------------------------------------------------------------------------
formElem: target form reference
msgArrayParsed: array of pre-generated text messages
displayMethod: to permit multiple display methods
errorClass: class to apply to error message container
inputClass: class to apply to inputs with errors

N.B: It is ideal if the form is contained within it's own element
i.e: a fieldset/div
*/



function formErrorDisplay(formElem,msgArrayParsed,displayMethod,errorClass,inputClass){

        var formParent = formElem.parentNode;
        var formInputs = [];
        var allFormInputs = [];
        var inputs = formElem.getElementsByTagName("input");
        var text = formElem.getElementsByTagName("textarea");


    if(inputs.length>0){
        for(var j=0; j < inputs.length; j++){
            formInputs.push(inputs[j]);
            }
        }

    if(text.length>0){
        for(var k=0; k < text.length; k++){
            formInputs.push(text[k]);
            }
        }

        var inputClass = " " + inputClass;
//-------------------------------------------------------------
//DOM DISPLAY ROUTINE
//-------------------------------------------------------------
    if (displayMethod == "dom"){


        // FLUSH PREVIOUS ERROR DISPLAY
        //------------------------------------------------------------------------------------------
        // check DOM for error box existence and remove it
        var divCheck = document.getElementsByTagName("div");

        for (var i=0;i<divCheck.length;i++){
                // Check to see if a div exists with the "errors" class
                // and also check to see if it is not a child of the same parent node
                if (divCheck[i].className == errorClass){
                    var errorNode = divCheck[i];
                    errorNode.parentNode.removeChild(errorNode);
                    }
                }

        var subCheck = document.getElementsByTagName("h4");
        for (var i=0;i<subCheck.length;i++){
                // Check to see if a div exists with the "errors" class
                // and also check to see if it is not a child of the same parent node
                if (subCheck[i].className == "errsub"){
                    var subNode = subCheck[i];
                    subNode.parentNode.removeChild(subNode);
                    }
                }

         // check DOM for input styling
        var allInputs = document.getElementsByTagName("input");
        var allText = document.getElementsByTagName("textarea");

        if(allInputs.length>0){
            for(var j=0; j < allInputs.length; j++){
                allFormInputs.push(allInputs[j]);
                }
            }

        if(allText.length>0){
            for(var k=0; k < allText.length; k++){
                allFormInputs.push(allText[k]);
                }
            }


         for(var j=0;j < allFormInputs.length;j++){
                    // acquire the current class names
                    var currentInputClass = allFormInputs[j].className;

                    // replace any instance of the error class name
                    // with an empty string
                    // (removes any PHP generated classnames too...)
                    allFormInputs[j].className = currentInputClass.replace(inputClass,"");
                    }


        if(msgArrayParsed.length>0){
            // CREATE NEW DISPLAY ELEMENTS
            //------------------------------------------------------------------------------------------

            // CREATE ERROR CONTAINER
            var errorBox = document.createElement("div");
            errorBox.className=errorClass;

             // CREATE ERROR LIST CONTAINER
            var errorUl = document.createElement("ul");

            // CREATE ERROR LIST TITLE
            var errorTitle = document.createElement("h3");
            var errorTitleTxt = document.createTextNode("There have been some errors with the form - see below:");
            errorTitle.appendChild(errorTitleTxt);

            // CREATE ERROR SUBTITLE
            /*
            var errorSubTitle = document.createElement("h4");
            errorSubTitle.className="errsub";
            var errorSubTitleTxt = document.createTextNode("There have been some errors with the form - see above:");
            errorSubTitle.appendChild(errorSubTitleTxt);
            */

            // PROCESS ERROR MESSAGES
            // process error messages into text nodes and apply styles to inputs
            for(i=0; i < msgArrayParsed.length;i++){
                 // create list item
                 var errListItem = document.createElement("li");

                 // create the text
                 var txtNode = document.createTextNode(msgArrayParsed[i][1]);
                 // append text to list item
                 errListItem.appendChild(txtNode);

                 // attach to the list container
                 errorUl.appendChild(errListItem);

                // APPLY INPUT CLASS
                for(var k=0;k < formInputs.length;k++){
                        if(formInputs[k].getAttribute("title")==msgArrayParsed[i][0]){
                            formInputs[k].className += inputClass;
                            }
                        }
                 }

                 // append errorList to the errorBox
                 errorBox.appendChild(errorTitle);
                 errorBox.appendChild(errorUl);

                 // INSERT CONTAINER INTO DOCUMENT
                 formParent.insertBefore(errorBox,formElem);

                // INSERT SUBTITLE INTO DOCUMENT
                //formParent.appendChild(errorSubTitle);

                // INSERT SUBTITLE INTO FORM FIELDSET
                //formElem.appendChild(errorSubTitle);
                }
        }

    else

        {

          /*
          -------------------------------------------------------------
          DEFAULT ALERT DISPLAY ROUTINE
          -------------------------------------------------------------

           else if(display == "alert"){
                       msg="";
           for(i=0;i<errorText.length;i++){

                       //create list item
                       msg += errorText[i] + "\n";
                       }
             alert(msg);
             return false;
             }

               return false;
          }//end of form submission test
          */
        }


    }



//create the interface
window['DC']['formErrorDisplay'] = formErrorDisplay;

//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------







/*
ERRMSGGEN()
--------------------------------------------------------------------------
Description:
generates an array of strings to be inserted into the DOM via FORMERRORDISPLAY()

RELATIONSHIPS:
- FUNCTION: testFormInput() : recieves array from
- FUNCTION: formErrorDisplay() : sends array to
*/


function errMsgGen(msgArray){
    // initialise variables
    var msgParsedArray = new Array();
    var msg = "";

    for(var i=0;i<msgArray.length;i++){

        if(msgArray[i][0] instanceof Array)
            {
            var errorInput = msgArray[i][0][0];
            var inputTitle=DC.capitalise(msgArray[i][0][0].name.split("_",1)[0]);
            var errorCase="group";
            }
        else
            {
            //extract input name
            //NOTE - test for presence of "_" character
            var errorInput = msgArray[i][0];
            var inputTitle=DC.capitalise(msgArray[i][0]);
            var errorCase=msgArray[i][1];
            }
        if(errorCase=="required")
            {
            msg =  inputTitle + " is required";
            }
        else
            {
            switch(errorCase){

                case "alpha":
                msg = inputTitle + " must use alphabetic characters only";
                break;

                case "numint":
                msg = inputTitle + " must use numbers only";
                break;

                case "numdec":
                msg = inputTitle + " must be a number, optionally decimal";
                break;

                case "alphanum":
                msg = inputTitle + " can only use a combination of letters and numbers";
                break;

                case "firstname":
                msg = inputTitle + " can only consist of letters";
                break;

                case "surname":
                msg = inputTitle + " can only consist of letters with optional hyphens, fullstops or spaces";
                break;

                case "fullname":
                msg = inputTitle + " can only consist of letters with optional hyphens, fullstops or spaces";
                break;

                case "email":
                msg = "You have entered an invalid email address.";
                break;

                case "group":
                msg = "You need to pick one item from the " + inputTitle + " group.";
                break;

                case "telephone":
                msg = "You have entered an invalid phone number. Only use numbers or spaces";
                break;

                case "postcode":
                msg = "You have entered an invalid postcode";
                break;

                case "checkreq":
                msg = "You need to tick the " + inputTitle + " box";
                break;
                }
            }
        var inputMsgPair=[errorInput,msg];
        msgParsedArray.push(inputMsgPair);
        }
    return msgParsedArray;
    }

//create the interface
window['DC']['errMsgGen'] = errMsgGen;

//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------




/*
CHECK FORM VALUES()
--------------------------------------------------------------------------
Description:
sends the form isolated by SUBMIT FORM to testFormInput() for validation
and return error array to callee

RELATIONSHIPS:
- FUNCTION: testFormInput() : sends element array to
- FUNCTION: submitFormGrp: called by
*/

function checkFormValuesGrp(currentForm)
    {
    //initialise error array
    var errorArray=[];
    var inputArray = [];
    var chkHoldingArray = [];
    var chkArray = [];
    var n = 0;
    var lastn = 0;

    // CONFIGURE FILTERS TO ALLOW PRIVACY CHECK
    //acquire references to all inputs for current form
    var formInputs = currentForm.getElementsByTagName("input");
    var formTextarea = currentForm.getElementsByTagName("textarea");

    var chkGrpClassRegex = new RegExp("(^|\\s)" + "checkgrp" + "(\\s|$)");


    if(formInputs.length>0){

// FILTER INPUT TYPES
//------------------------------------------------------------------------------
        for(var j=0; j < formInputs.length; j++){

// FILTER CHECKBOXES
//------------------------------------------------------------------------------

            //filter checkboxes
            if(formInputs[j].type=="checkbox" && (chkGrpClassRegex.test(formInputs[j].className))){
                chkHoldingArray.push(formInputs[j]);
                }
// FILTER TEXT INPUTS
//------------------------------------------------------------------------------
            if((formInputs[j].type=="text")||(formInputs[j].type=="checkbox" && (!chkGrpClassRegex.test(formInputs[j].className)))){
                inputArray.push(formInputs[j]);
                }
        }

// FILTER CHECKBOXES
//------------------------------------------------------------------------------
        if(chkHoldingArray.length>0){
            for(var c=0; c < chkHoldingArray.length; c++){
                 //count out the names
                 //alert((c+1)+":"+chkHoldingArray.length);

                 if(c+1==chkHoldingArray.length){
                    inputArray.push(chkArray);
                    }

                if(chkArray.length>0){

                    lastn = n-1;//current count - 1, to access last entry

                    var oldName = chkArray[lastn].name.split('_',1)[0];
                    var thisName = chkHoldingArray[c].name.split('_',1)[0];

                    if(oldName !== thisName){
                        inputArray.push(chkArray);

                        //reset variables
                        chkArray = [];
                        n=0;

                        chkArray.push(chkHoldingArray[c]);
                        n++;
                        }

                    else{
                        //if group name is constant, add to current array
                        chkArray.push(chkHoldingArray[c]);
                        n++;
                        }

                    }//main loop

                 else
                    {//start the collection loop
                    chkArray.push(chkHoldingArray[c]);
                    n++;
                    }
                }//end chkHoldingArray for loop
            }//end of chkHoldingArray filtering

// FILTER TEXTAREAS
//------------------------------------------------------------------------------
        if(formTextarea.length>0){
            for(var k=0; k < formTextarea.length; k++){
                inputArray.push(formTextarea[k]);
                }
            }
        }

        //SEND INPUTS TO TESTFORMINPUT
        if(inputArray.length>0){
            //iterate through the inputs and validate in the testFormInput function
            for ( var i=0; i < inputArray.length; i++ ){
                //create an array of all elements returned from testFormInput
                errorArray.push(DC.testFormInput(inputArray[i]));
                }
            return errorArray;
            }
    }

//create the interface
window['DC']['checkFormValuesGrp'] = checkFormValuesGrp;

//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------









/*
SUBMIT FORM()
--------------------------------------------------------------------------
Description:
Handles the form events and passes data between the various core functions

RELATIONSHIPS:

- FUNCTION: checkFormValues()
- sends current form to checkform, which retrieves an array of errors

- FUNCTION: formErrorDisplay()
- sends array to form formErrorDisplay() (in formErrorDisplay.js) for styling
*/

function submitFormGrp(currentForm,displayMethod)
{

var errorArray=checkFormValuesGrp(currentForm);

var msgArray = [];
var inputMsg = [];


for (var i=0;i < errorArray.length;i++){
    //only report on those elements that return errors

    var errorDefined=(typeof(errorArray[i]) == "undefined")?  false: true;
    if(errorDefined){

        //extract title from the returned elements
        if(errorArray[i][0].title){
        var inputTitle = errorArray[i][0].title;
        }
        else{
        var inputTitle = errorArray[i][0];
        }

        //extract error type
        var inputErrorType = errorArray[i][1];
        inputMsg=[inputTitle,inputErrorType];
        msgArray.push(inputMsg);
        }
    //retrieve messages from msgArrayParsedFunction
    var msgArrayParsed = DC.errMsgGen(msgArray);
    }
    //pass results to formErrorDisplay
    DC.formErrorDisplay(currentForm,msgArrayParsed,displayMethod,"errors","err");

if (msgArray.length>0){
    return false;
    }
else
    {
    return true;
    }
}

//create the interface
window['DC']['submitFormGrp'] = submitFormGrp;

//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------





/*
PREPFORMS()
--------------------------------------------------------------------------
Description:
This script automatically locates all forms in a document and
applies an event triggered function (submitForm) to them

RELATIONSHIPS:

- FUNCTION: submitForm()

- sends current form to DC.submitForm(), which then handles interchange of data
- between core functions

to separate bulk checks from individual checks
*/

function prepForms() {
    forms=document.getElementsByTagName("form");
    for(var i=0;i<forms.length;i++){
        forms[i].onsubmit= function(){return DC.submitFormGrp(this,"dom")};
        }
    }

//create the interface
window['DC']['prepForms'] = prepForms;
//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------




})();