/*
Script: kap_validation.js
    KickApps Validation Functions.

General Information:
    Company - <http://www.kickapps.com>
    Created - Wednesday, April 04, 2007 | 1:17 PM

Author:
    Oliver Nassar - onassar@kickapps.com

Editing:
    onassar - Wednesday, April 04, 2007 | 5:15 PM
    onassar - Wednesday, April 05, 2007 | 8:57 AM
    onassar - Friday, June 08, 2007 	| 7:16 PM
    onassar - Sunday, June 10, 2007 	| 6:48 PM

Notes:
    - After you have made changes and are done with the file, please sign in the editing field above (even with our SVN, this is the best to track back changes)
    - Some functions (eg. kap_alert(), kap_validate()) were NOT made object oriented, although it is ideal
    - The reasoning for this was the doubt that, as proper coding standards dictate, seperate class .js files would be included per page load (where neccessary mind you)
    - Flat out, the original developer of this class didn't want to deal with the debate over bandwidth and server processing, and therefore used procedural coding for the validation process
    - I don't *ever* want to hear how the alert or validation functions should have been made into objects
    - kap_mootools.js *dependent*
*/

    // variable to store the length of the error slide (in milliseconds)
    var kap_errorSlideLength         =    200;

/*
Function: kap_alert
    Alerts out an inline alert (not popup) in a specific defined area of the browser.

Arguments:
    type - the type of error it is (e.g. error/tip).
    message - the error message that should be 'alerted'.
    span - the span where to this alert should be written.
    options - (*optional*) the options object for handling the alert's look and behaviour.

Notes:
    - This function will work only once per instance, meaning that if there is an alert on the page, it will not spit out another until it has timed out
    - kap_dhtmlFunctions.js *dependent* when slide is *not* used

Options Object Properties:
    onStart - function to execute at the start of the alert
    onComplete - function to execute after the alert has completed
    backgroundColor - the background color of the alert (over riding the default css)
    icon - the full url of the icon that should be used for the alert (over riding the default css)
    border - the border in full (e.g. 1px solid black) to be used (over riding the default css)
    display - whether the error should be displayed or not (over riding the default css)
    padding - the padding of the error message (over riding the default css)
    margin - the margin of the error message (over riding the default css)
    fontSize - the font size of the text (over riding the default css)
    fontWeight - the weight of the text (over riding the default css)
    color - the color of the test (over riding the default css)
    timeout - whether the error message should timeout (defaults to false; over riding the default css)
    timeoutCount - how long the error message should wait before timing out (defaults to 3 seconds; in seconds; over riding the default css)
    slide - whether or not the error should use a slide animation

Example:
    (start code)
    // sample alert
    kap_alert
    (
        // what type of alert (tip or error)
        'Error',
        // what the error message is
        'test error message',
        // the span to write the error to
        'error_message_span_id',
        // options to modify the look/behaviour
        {onStart:function(){alert('test on start function');},timeout:true,timeoutCount:5,slide:true}
    );
    (end code)
*/

    // class that creates new alert objects
    function kap_alert(type,message,span,options)
    {
        // if the error span isn't empty (already have an error there)
        if(document.getElementById(span).childNodes.length>0)
        {
            // end the function
            return;
        }

        // if a options object wasn't sent in, create one (empty) to supress any potential errors
        var options           =    !options ? new Object() : options;

        // create a variable that stores any procedures for the object (as an array)
        var objectProc        =    new Array();

        // if an onstart function is specified
        if(options.onStart)
        {
            // run it (since function sent in, create a new instance of one, and evaluate it)
            eval(new options.onStart);
        }

        // turn the span to a block
        document.getElementById(span).style.display          =    'block';
        // create the error
        document.getElementById(span).appendChild(document.createTextNode(message));

        // get any styles set through the options object
        var backgroundColor    =    !options.backgroundColor    ?    ''       :    options.backgroundColor;
        var icon               =    !options.icon               ?    ''       :    options.icons;
        var border             =    !options.border             ?    ''       :    options.border;
        var display            =    !options.display            ?    ''       :    options.display;
        var padding            =    !options.padding            ?    ''       :    options.padding;
        var margin             =    !options.margin             ?    ''       :    options.margin;
        var fontSize           =    !options.fontSize           ?    ''       :    options.margin;
        var fontWeight         =    !options.fontWeight         ?    ''       :    options.fontWeight;
        var color              =    !options.color              ?    ''       :    options.color;
        // get any other options set through the options object
        var timeout            =    !options.timeout            ?    false    :    options.timeout;
        var timeoutCount       =    !options.timeoutCount       ?    3        :    options.timeoutCount;

        // set the new options, if they were specified
        if(border!='')
        {
            document.getElementById(span).style.border        =    border;
        }
        if(padding!='')
        {
            document.getElementById(span).style.padding       =    padding;
        }
        if(margin!='')
        {
            document.getElementById(span).style.margin        =    margin;
        }
        if(fontSize!='')
        {
            document.getElementById(span).style.fontSize      =    fontSize;
        }
        if(fontWeight!='')
        {
            document.getElementById(span).style.fontWeight    =    fontWeight;
        }
        if(color!='')
        {
            document.getElementById(span).style.color         =    color
        }
        if(backgroundColor!='' && icon!='')
        {
            document.getElementById(span).style.background            =    backgroundColor+' url('+icon+') no-repeat scroll 5px 3px';
        }
        else if(backgroundColor!='' && icon=='')
        {
            document.getElementById(span).style.backgroundColor       =    backgroundColor
        }
        else if(backgroundColor=='' && icon!='')
        {
            document.getElementById(span).style.backgroundImage       =    'url('+icon+')';
            document.getElementById(span).style.backgroundRepeat      =    'no-repeat';
            document.getElementById(span).style.backgroundPosition    =    'scroll 5px 3px';
        }

        // if an effect was specified
        if(options.slide)
        {
            // create an instance of the sliding effect for the error
            var errorSlideFX = new Fx.Slide(span,{duration: kap_errorSlideLength}).hide();
            // show it
            errorSlideFX.slideIn();
        }

        // if a timeout was specified
        if(timeout)
        {
            // set a timeout for the error message (to disappear)
            setTimeout
            (
                // anonymous function to handle timeout
                function()
                {
                    // if an effect was specified
                    if(options.slide)
                    {
                        // show it
                        errorSlideFX.slideOut();
                        // clear the slide wrappers from the page
                        setTimeout
                        (
                            function ()
                            {
                                // record the position of the error span by the next sibling of the animation parent div
                                siblingElement            =    document.getElementById(span).parentNode.nextSibling;
                                // recreate the error span
                                newErrorSpan              =    document.createElement('span');
                                newErrorSpan.id           =    document.getElementById(span).id;
                                newErrorSpan.className    =    'ka_alert'+type;
                                // remove the span and it's animation parent
                                document.getElementById(span).parentNode.parentNode.removeChild(document.getElementById(span).parentNode);
                                // insert the new span
                                siblingElement.parentNode.insertBefore(newErrorSpan,siblingElement);

                                // if a complete function was specified
                                if(options.onComplete)
                                {
                                    // run it (since function sent in, create a new instance of one, and evaluate it)
                                    eval(new options.onComplete);
                                }
                            }
                            ,200// since the slide will take exactly 200
                        );
                    }
                    // otherwise no effect
                    else
                    {
                        // clear the contents and class
						kap_showHideToggle ( { toggleIds: new Array ( span ) } );
                        document.getElementById(span).removeChild(document.getElementById(span).childNodes[0]);

                        // if a timeout function was specified
                        if(options.onComplete)
                        {
                            // run it (since function sent in, create a new instance of one, and evaluate it)
                            eval(new options.onComplete);
                        }
                    }
                },
                // run function after the number of user selected seconds (in milliseconds)
                timeoutCount*1000
            );
        }
        // otherwise no timeout was specified
        else
        {
            // if an end function was specified
            if(options.onComplete)
            {
                // run it (since function sent in, create a new instance of one, and evaluate it)
                eval(new options.onComplete);
            }
        }
    }

/*
Function: kap_alertConfirm
    Creates an alert to confirm some action; currently a javascript popup

Arguments:
    type - the type of alert it is (e.g. default/inline [inline not yet implemented]).
    method - the method to which the confirmation will work (see method Options for more details)
    message - the alert message that should be shown (normally a question).
    options - (*optional*) the options object for handling the behaviour of the alert confirmation.

Method options:
    'return' - a return a value of true or false will be returned by the call
    'anonymous' - functions will be sent in through the options object (via the onTrue and onFalse options object properties) which will be executed respectively

Notes:
    - This function currently only implements the javascript method for confirmation. It will be extended to allow for inline.
    - At this point, the options object will be extended to allow for similar properties and attributes as kap_alert

Options Object Properties:
    onTrue - function to execute if the confirmation evaluates to true (aka. yes in most cases)
    onFalse - function to execute if the confirmation evaluates to false (aka. no in most cases)

Example:
    (start code)
    // sample alert
    kap_alertConfirm
    (
        // what type of alert confirmation
        'default',
        // whether true/false should be returned, or the function should perform any actions anonymously
        'anonymous',
        // what the message is
        'Are you sure you want to delete this message?',
        // options to modify the ehaviour
        {
            onTrue:     function(){alert('you clicked not to delete this link');},
            onFalse:    function(){alert('you clicked not to delete this link');}
        }
    );
    (end code)
*/

    // function to facilitate some sort of confirmation process
    function kap_alertConfirm(type,method,message,options)
    {
        // if no options are specified create an empty object
        var options           =    !options ? new Object() : options;

        // if it's set to be a default confirmation
        if(type=='default')
        {
            // show the confirmation
            var confirmValue    =    confirm(message);
            // if a return value is specified
            if(method=='return')
            {
                // return the value
                return confirmValue;
            }
            // otherwise if the confirmation should be handled anonymously
            else if(method=='anonymous')
            {
                // if an onTrue function is specified and the confirmation evaluated to true
                if(options.onTrue && confirmValue)
                {
                    // run the anonymous function
                    eval(new options.onTrue);
                }
                // otherwise if the onFalse function is specified and the confirmation evaluated to false
                else if(options.onFalse && !confirmValue)
                {
                    // run the anonymous function
                    eval(new options.onFalse);
                }
                // end the function
                return;
            }
        }
    }

/*
Function: kap_submitForm
    Submits a form and redirects any validation that it may require. Additionally redirects any error message.

Arguments:
    formId - the id of the form that is to be submitted.
    options - (*optional*) options for the forms submit process, including fields to validate, how to display alerts, etc.

Notes:
    Handles only the actual submit process; outsources the validation to another function, which handles any error messages

Options Object Properties:
    onStart - function to execute at the start of the submit process
    onComplete - function to execute after the submit process has completed (right before the page redirects)
    validate - whether or not the form ought to validate it's data
    validateOptions - the options for the validation process (see kap_validate for options)

Example:
    (start code)
    // attatch an event handler to the post button
    $('ka_msgb_postFormButton').addEvent
    (
        // what type of event handler it is (eg. click, mouseover, etc.)
        'click',
        // the function to execute when it's clicked
        function()
        {
            // submit form function
            kap_submitForm
            (
                // id of the form
                'ka_msgb_newForm',

                // submit options
                {
                    // whether to validate
                    validate:        true,

                    // the validation options
                    validateOptions:
                    {
                        // the object of fields to validate (and their specific options)
                        fields:
                        {
                            // the id of the field to validate
                            'subject':
                            // first field as an object
                            {
                                // what the field's type is (eg. input, textarea, select, radio button, checkbox, file)
                                type:            'input',
                                // the minimum length of the field
                                minLength:       3,
                                // the error message when conditions are not met
                                errorMessage:    'The subject needs to be between 3 and 100 letters long.'
                            }
                        },
                        // the span to show errors
                        errorSpan:       'ka_msgb_loginError',
                        // the error options
                        errorOptions:
                        {
                            // whether the alert should timeout
                            timeout:true,
                            // how long it should wait before timing out
                            timeoutCount:4,
                            // whether the any error should use the slide property
                            slide:true
                        }
                    }
                }
            )
        }
    );
    (end code)
*/

    // function to handle a form being submitted
    function kap_submitForm(formId,options)
    {
        // if an options object wasn't sent in, create one (empty) to supress any potential errors
        var options             =    !options ? new Object() : options;

        // if an onStart function is specified
        if(options.onStart)
        {
            // run it (since function sent in, create a new instance of one, and evaluate it)
            eval(new options.onStart);
        }

        // variable to store whether the form ought to be submitted (true by default)
        var validationStatus    =    !options.validate ? { submit: true } : kap_validate( options.validateOptions );

        // if the form ought to be submitted
        if(validationStatus.submit)
        {
			// ***TEMP*** options.validateOptions.successSupress.constructor.toString().toLowerCase().indexOf('boolean')
        	// if the form should not be submitted on success (specifically stipulated)
        	if(typeof options.validateOptions.successSupress=='undefined' || options.validateOptions.successSupress===false)
        	{
	            // submit it (framework is weird[?]; 10 millisecond timeout)
	            setTimeout(function() { document.getElementById(formId).submit(); },0);
        	}

            // if a onComplete function is specified
            if(options.onComplete)
            {
                // run it (since function sent in, create a new instance of one, and evaluate it)
                eval(new options.onComplete);
            }
        }
        // otherwise it should not be submitted, as there was an error
        else
        {
            // alert the proper error message
            kap_alert('Error',validationStatus.errorMessage,options.validateOptions.errorSpan,options.validateOptions.errorOptions);

            // if a onComplete function is specified
            if(options.validateOptions.onComplete)
            {
                // if a timeout is occuring
                if(options.validateOptions.errorOptions.timeout)
                {
                    // run the function after the proper time (since function sent in, create a new instance of one, and evaluate it)
                    setTimeout
                    (
                        // anonymous function
                        function()
                        {
                            // evaluate it
                            eval(new options.validateOptions.onComplete);
                        },
                        // the lenght of the timeout plus the length of the slide plus one (in milliseconds)
                        (options.validateOptions.errorOptions.timeoutCount*1000)+kap_errorSlideLength+1
                    );
                }
                // otherwise just run it
                else
                {
                    // evaluate it (since function sent in, create a new instance of one, and evaluate it)
                    eval(new options.validateOptions.onComplete);
                }
            }

            ///////////////////////////////////////////////////////////////////////////////
            //  Only type:'input' has been implemented so far, do for all other types    //
            //  Allowable types:  input, textarea, select, radio, checkbox, file         //
            ///////////////////////////////////////////////////////////////////////////////

			// error handling since not all inputs (e.g. tinyMCE) don't accept focus calls (or select calls)
			try {
	            // focus on the field (returned from kap_validaite)
	            document.getElementById(validationStatus.errorFieldId).focus();
	            // select it
	            document.getElementById(validationStatus.errorFieldId).select();
	        }
	        catch(e) {
	        }
        }
        // 
        return;
    }

/*
Function: kap_validate
    Validates a form's data through specified options.

Arguments:
    options - options for the forms submit process, including fields to validate, how to display alerts, etc.

Notes:
    Handles only the actual submit process; outsources the validation to another function, which handles any error messages

Options Object Properties:
    fields - a fields object specifying those which ought to be validated
    errorSpan - the span where to any possible errors ought to be written to
    errorOptions - (*optional*) the options for any error message (see ..... DO ......)

Fields Object Properties:
    id - the id of the field
    type - the type of field (eg. input, textarea, select, radio, checkbox, file)
    errorMessage - the error message that should be shown when some condition is not met
    minLength - (*optional*) the minimum length of the value of the field
    maxLength - (*optional*) the maximum length of the value of the field

Returns:
    An object containing whether it was successful (submit), otherwise the error message (errorMessage) and which field violated (errorFieldId)

Example:
    (start code)
    // validate some form, returning back an object with it's status, etc.
    kap_validate
    (
        // the validation options
        validateOptions:
        {
            // the object of fields to validate (and their specific options)
            fields:
            {
                // the id of the field to validate
                'subject':
                // first field as an object
                {
                    // what the field's type is (eg. input, textarea, select, radio button, checkbox, file)
                    type:            'input',
                    // the minimum length of the field
                    minLength:       3,
                    // the maximum length of the field
                    maxLength:       100,
                    // the error message when conditions are not met
                    errorMessage:    'The subject needs to be between 3 and 100 letters long.'
                }
            },
            // the span to show errors
            errorSpan:       'ka_msgb_loginError',
            // the error options
            errorOptions:
            {
                // whether the alert should timeout
                timeout:true,
                // how long it should wait before timing out
                timeoutCount:4,
                // whether the any error should use the slide property
                slide:true
            }
        }
    );
    (end code)
*/

    // function to faciliate the validation of a form's data
    function kap_validate(options)
    {
        // variables to store the error message and violating field
        var errorMessage = '',errorFieldId;

        // loop through the fields
        for(x in options.fields)
        {
            ///////////////////////////////////////////////////////////
            //  Implemented: 'input', 'textarea', 'tinyMCE'		 	 //
            //  Possible:  	 'select', radio button, checkbox, file  //
            ///////////////////////////////////////////////////////////

            // if an empty check is required
            if
            (
                options.fields[x].isEmpty &&
                (options.fields[x].type.indexOf('input')!=-1 && document.getElementById(x).value=='')// input field
            )
            {
                // record what the error message is
                errorMessage   =   options.fields[x].errorMessage;
            }
            // if a minimum length is required
            if
            (
/*alert(tinyMCE.getInstanceById(x).getContent().replace(/(<([^>]+)>)/ig,'').length) && */
                options.fields[x].minLength &&
                (options.fields[x].type.indexOf('input')!=-1 && document.getElementById(x).value.length<options.fields[x].minLength) ||// input field
                (options.fields[x].type.indexOf('tinyMCE')!=-1 && tinyMCE.getInstanceById(x).getContent().replace(/(<([^>]+)>)/ig,'').length<options.fields[x].minLength) ||// tinyMCE
                // ENHANCEMENT: focus on textareaMCE
                (options.fields[x].type.indexOf('textarea')!=-1 && document.getElementById(x).value.length<options.fields[x].minLength)// textarea
            )
            {
                // record what the error message is
                errorMessage   =   options.fields[x].errorMessage;
            }
            // if a maximum length is required
            if
            (
                options.fields[x].maxLength &&
                (options.fields[x].type.indexOf('input')!=-1 && document.getElementById(x).value.length>options.fields[x].maxLength)// input field
            )
            {
                // record what the error message is
                errorMessage   =   options.fields[x].errorMessage;
            }
            // if an error message has been defined
            if(errorMessage!='')
            {
	            // break the loop
	            break;
	        }
        }

        // return the proper information
        return {submit:(errorMessage==options.fields[x].errorMessage ? false : true),errorMessage:errorMessage,errorFieldId:x};
    }