/*
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 = [];
        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....................................................................
		else if ( inputClasses[i]=="num" )
            {
                if ( !validateNumeric(currentInput.value) )
        			{
                    inputReport = [currentInput,'numeric'];
                    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]=="phone" )
            {
                if ( !validatePhone(currentInput.value) )
        			{
                    inputReport = [currentInput,'phone'];
                    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;
                }
            }

//TEST RADIO GROUP..............................................................
          else if ( inputClasses[i]=="check" )
            {

            if (!currentInput.checked)
                {
                inputReport = [currentInput,'privacy'];
                return inputReport;
                }
            }



//TEST SELECT...................................................................
	if ( document.getElementsByTagName("select").length > 0 )
      	{
      		chkSelect = document.getElementsByTagName("select");

      		for ( var i=0; i<chkSelect.length; i++ )
      		{
      			if ( /req$/.test(chkSelect[i].className) )
      			{
      				if ( !chkSelect[i].value )
      				{
      				 return currentInput;
      				}
      			}
      		}
      	}

*/
        }
        }
}

//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 validateNumeric(value)
{
	var filter = /^[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 validateZipCode(value)
{
	var filter = /^(?<Zip>\d{5})-(?<Sub>\d{4})$/;

	if ( !filter.test(value) )
	{
		return false;
	}
	return true;
}
*/

function validatePhone(value)
{
	var filter = /^([0-9 ]{7,})$/;

	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);
                }
        }
    }
/*
-------------------------------------------------------------
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++){

        //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 "numeric":
                msg = inputTitle + " must use numeric characters only";
                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 "phone":
                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 "zipcode":
                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 checkFormValuesGrp(currentForm)
    {
    //initialise error array
    var errorArray=[];
    var inputArray = [];
    var textArray = [];

    //acquire references to all inputs for current form
    var currentFormInputs = currentForm.getElementsByTagName("input");
    var currentFormText = currentForm.getElementsByTagName("textarea");

    if(currentFormInputs.length>0){
        for(var j=0; j < currentFormInputs.length; j++){
            inputArray.push(currentFormInputs[j]);
            }
        }

    if(currentFormText.length>0){
        for(var k=0; k < currentFormText.length; k++){
            inputArray.push(currentFormText[k]);
            }
        }

    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)
{

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
        var inputTitle = errorArray[i][0].getAttribute("title");

        //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,"dom","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)};
        }
    }

//create the interface
window['DC']['prepForms'] = prepForms;
//FUNCTION END
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------




})();
