﻿// **************************************************************************************************************************
// Form validation class
// --------------------------------------------------------------------------------------------------------------------------
// - showToolTips                  : bool   - can tool tips be showed?
// - toolTipElementId              : string - ID of central element of tool tip pupup
// - toolTipTextElementId          : string - ID of text element of tool tip popup
// - toolTipElementHeight          : int    - height of central element of tool tip popup
// **************************************************************************************************************************
function FormValidator(showToolTips, toolTipElementId, toolTipTextElementId, toolTipElementHeight)
{
    // -------------------------------------------------------------------
    // OPTIONS - filling from constructor, default values
    // -------------------------------------------------------------------
    this.options = 
    {
        remoteElementsIDs : new Array(),
        items : new Array(),
        
        showToolTips : true,
        toolTipElementId : "formValidatorToolTipElement",
        toolTipTextElementId : "formValidatorToolTipElementText",
        toolTipElementHeight : 84,
        lastToolTipItemIndex : -1,
        actualToolTipItemIndex : -1,
        hideRemoteElements : false,
        errorsSummaryElementId : "formValidatorErrorsSummary",
        errorsSummaryHeader : ""
    };
    
    this.options.showToolTips = (showToolTips != null ? showToolTips : this.options.showToolTips);
    this.options.toolTipElementId = (toolTipElementId != null ? toolTipElementId : this.options.toolTipElementId);
    this.options.toolTipTextElementId = (toolTipTextElementId != null ? toolTipTextElementId : this.options.toolTipTextElementId);
    this.options.toolTipElementHeight = (toolTipElementHeight != null ? toolTipElementHeight : this.options.toolTipElementHeight);
    
    // -------------------------------------------------------------------
    // FUNCTIONALITY FOR TOOLTIPS AND VALIDATION
    // -------------------------------------------------------------------
    
    var lastToolTipItemId = null;
    var actualToolTipItemId = null;
    
    this.itemFocused = itemFocused;
    this.itemLostFocus = itemLostFocus;
    this.itemContentChanged = itemContentChanged;
    this.showToolTipMessage = showToolTipMessage;
    this.checkAllItems = checkAllItems;
    this.isValid = isValid;
    this.workoutRemoteElements = workoutRemoteElements;
    
    // Method for work with objects after focus on some form input item.
    function itemFocused(formValidatorInstance, itemIndex)
    {
        // setting indexes
        formValidatorInstance.options.lastToolTipItemIndex = formValidatorInstance.options.actualToolTipItemIndex;
        formValidatorInstance.options.actualToolTipItemIndex = itemIndex;
        
        // validate lastly active element
        if (formValidatorInstance.options.lastToolTipItemIndex > -1)
        {
            formValidatorInstance.options.items[formValidatorInstance.options.lastToolTipItemIndex].checkValue();
            formValidatorInstance.workoutRemoteElements();
        }
            
        if (formValidatorInstance.options.showToolTips)
        {
            formValidatorInstance.showToolTipMessage();
        }
    }
    
    // Method which executes when form element losts focus.
    function itemLostFocus(formValidatorInstance, itemIndex)
    {
        formValidatorInstance.options.items[itemIndex].checkValue();
        formValidatorInstance.workoutRemoteElements();
        hideElement(formValidatorInstance.options.toolTipElementId);
    }
    
    // Method which executes when form element has changed content.
    function itemContentChanged(formValidatorInstance, itemIndex)
    {
        //formValidatorInstance.options.items[itemIndex].checkValue();
        formValidatorInstance.options.items[itemIndex].checkValue();
        formValidatorInstance.workoutRemoteElements();
    }
    
    // Shows tool tip message.
    function showToolTipMessage()
    {
        var index = this.options.actualToolTipItemIndex;
        if (this.options.items[index].options.toolTipMessage != "")
        {
            var input = this.options.items[index].objects.input;
            var toolTipObj = document.getElementById(this.options.toolTipElementId);
            var toolTipTextObj = document.getElementById(this.options.toolTipTextElementId);
            
            toolTipObj.style.left = (getOffsetLeft(input) + 10) + "px";
            toolTipObj.style.top = (getOffsetTop(input) - this.options.toolTipElementHeight + 2) + "px";
            toolTipObj.style.display = "block";
            toolTipTextObj.innerHTML = this.options.items[index].options.toolTipMessage;
        }
    }
    
    // Validity checking of all items.
    function checkAllItems()
    {
        if (this.options.items.length > 0)
        {
            for (var i = 0; i < this.options.items.length; i++)
            {
                this.options.items[i].checkValue();
            }
        }
        workoutRemoteElements();
    }
    
    // Returns true if all form items has valid content.
    function isValid()
    {
        var valid = true;
        if (this.options.items.length > 0)
        {
            for (var i = 0; i < this.options.items.length; i++)
            {
                if (!(this.options.items[i].isValid()))
                    valid = false;
            }
        }
        return valid;
    }
    
    // Settings of remote controls according to form validity.
    function workoutRemoteElements()
    {
        if (this.options.remoteElementsIDs.length > 0)
        {
            var isFormValid = this.isValid();
            for (var i = 0; i < this.options.remoteElementsIDs.length; i++)
            {
                if (document.getElementById(this.options.remoteElementsIDs[i]) != null)
                {
                    document.getElementById(this.options.remoteElementsIDs[i]).disabled = (!isFormValid ? "disabled" : "");
                    if (this.options.hideRemoteElements)
                        document.getElementById(this.options.remoteElementsIDs[i]).style.display = (!isFormValid ? "none" : "block");
                }
            }
        }
        
        var errorsSummaryObj = document.getElementById(this.options.errorsSummaryElementId);
        var errorsSummaryMessage = "";
        if (errorsSummaryObj != null)
            errorsSummaryObj.style.display = "none";
        
        if (this.options.items.length > 0)
        {
            for (var i = 0; i < this.options.items.length; i++)
            {
                this.options.items[i].checkValue();
                if (this.options.items[i].currentErrorMessage != "")
                    errorsSummaryMessage += removeHTMLTags(this.options.items[i].currentErrorMessage) +"<br />";
            }
        }
        
        if ((errorsSummaryMessage != "") && (errorsSummaryObj != null))
        {
            errorsSummaryObj.innerHTML = 
                (this.options.errorsSummaryHeader ? "<strong>"+ this.options.errorsSummaryHeader +"</strong><br /><br />" : "") + 
                errorsSummaryMessage;
            errorsSummaryObj.style.display = "block";
        }
    }
    
    // -------------------------------------------------------------------
    // FUNCTIONS - for form validator items
    // -------------------------------------------------------------------
    
    this.getItems = getItems;
    this.setItems = setItems;
    this.clearItems = clearItems;
    this.addItem = addItem;
    this.removeItem = removeItem;
    
    // Gets array of all form items.
    function getItems()
    {
        return this.options.items;
    }
    
    // Sets array of all form items.
    function setItems(itemsArray)
    {
        this.options.items = itemsArray;
    }
    
    // Clears array of all form items.
    function clearItems()
    {
        this.options.items = new Array();
    }
    
    // Adds new form item.
    function addItem(formValidatorInstance, elementId, toolTipMessage, elementValidClass, elementNonValidClass, compulsory, compulsoryMessage, 
        compulsoryMessageElementId, compulsoryImageElementId, validator, validatorMessage, 
        validatorMessageElementId, validatorRegex)
    {
        if (document.getElementById(elementId) == null)
        {
            return -1;
        }
    
        var newItem = new FormValidatorItem(elementId, elementValidClass, elementNonValidClass, compulsory, compulsoryMessage, 
            compulsoryMessageElementId, compulsoryImageElementId, validator, validatorMessage, 
            validatorMessageElementId, validatorRegex, toolTipMessage);
            
        // assignment of event methods
        var newItemArrayIndex = this.options.items.length;
        newItem.objects.input.onfocus   = function() { itemFocused(formValidatorInstance, newItemArrayIndex); };
        newItem.objects.input.onblur    = function() { itemLostFocus(formValidatorInstance, newItemArrayIndex); };
        newItem.objects.input.onchange  = function() { itemContentChanged(formValidatorInstance, newItemArrayIndex); };
        newItem.objects.input.onkeyup   = function() { itemContentChanged(formValidatorInstance, newItemArrayIndex); };
        
        this.options.items[this.options.items.length] = newItem;
    }
    
    // Removes form item by its central element id.
    function removeItem(elementId)
    {
        if (this.options.remoteElementsIDs.length > 0)
        {
            var itemIndex = -1;
            for (var i = 0; i < this.options.items.length; i++)
            {
                if (this.options.items[i].options.elementId == elementId)
                {
                    itemIndex = i;
                    break;
                }
            }
            if ((itemIndex > -1) && (this.options.items.length > itemIndex + 1))
            {
                for (var i = itemIndex; i < this.options.items.length; i++)
                {
                    this.options.items[i] = this.options.items[i + 1];
                }
                
            }
            if (this.options.items.length >= itemIndex + 1)
                this.options.items[this.options.items.length - 1] = null;
        }
    }
    
    // -------------------------------------------------------------------
    // FUNCTIONS - for Remote elements IDs
    // -------------------------------------------------------------------
    
    this.getRemoteElementsIDs = getRemoteElementsIDs;
    this.setRemoteElementsIDs = setRemoteElementsIDs;
    this.clearRemoteElementsIDs = clearRemoteElementsIDs;
    this.addRemoteElementId = addRemoteElementId;
    this.removeRemoteElementId = removeRemoteElementId;
    
    // Gets array of all remote elements ids.
    function getRemoteElementsIDs()
    {
        return this.options.remoteElementsIDs;
    }
    
    // Sets array of all remote elements ids.
    function setRemoteElementsIDs(elementsIDs)
    {
        this.options.remoteElementsIDs = elementsIDs;
    }
    
    // Clears array of all remote elements ids.
    function clearRemoteElementsIDs()
    {
        this.options.remoteElementsIDs = new Array();
    }
    
    // Adds new remote element id.
    function addRemoteElementId(elementId)
    {
        this.options.remoteElementsIDs[this.options.remoteElementsIDs.length] = elementId;
        this.workoutRemoteElements();
    }
    
    // Removes remote element's id.
    function removeRemoteElementId(elementId)
    {
        if (this.options.remoteElementsIDs.length > 0)
        {
            var itemIndex = -1;
            for (var i = 0; i < this.options.remoteElementsIDs.length; i++)
            {
                if (this.options.remoteElementsIDs[i] == elementId)
                {
                    itemIndex = i;
                    break;
                }
            }
            if ((itemIndex > -1) && (this.options.remoteElementsIDs.length > itemIndex + 1))
            {
                for (var i = itemIndex; i < this.options.remoteElementsIDs.length; i++)
                {
                    this.options.remoteElementsIDs[i] = this.options.remoteElementsIDs[i + 1];
                }
                
            }
            if (this.options.remoteElementsIDs.length >= itemIndex + 1)
                this.options.remoteElementsIDs[this.options.remoteElementsIDs.length - 1] = null;
        }
    }
    
}

// **************************************************************************************************************************
// Popup validation message
// **************************************************************************************************************************

// **************************************************************************************************************************
// FormValidatorItem - object for one form item with all settings
// --------------------------------------------------------------------------------------------------------------------------
// - elementId                  : string - ID of input element for validation
// - elementValidClass          : string - CSS class name for valid input element
// - elementNonValidClass       : string - CSS class for non valid input element
// - compulsory                 : bool   - indicates, if content of input element must be filled in
// - compulsoryMessage          : string - compulsory error message
// - compulsoryMessageElementId : string - ID of element for view of compulsory error message
// - validator                  : bool   - text format validator switch
// - validatorMessage           : string - validator error message
// - validatorMessageElementId  : string - ID of element for view of validator error message
// - validatorRegex             : regex  - regular expression for validation
// - toolTipMessage             : string - help tool tip message text
// **************************************************************************************************************************
function FormValidatorItem(elementId, elementValidClass, elementNonValidClass, compulsory, compulsoryMessage, 
    compulsoryMessageElementId, compulsoryImageElementId, validator, validatorMessage, 
    validatorMessageElementId, validatorRegex, toolTipMessage) 
{
    // -------------------------------------------------------------------
    // OPTIONS - filling from constructor, default values
    // -------------------------------------------------------------------
    this.options = 
    {
        elementId : null,
        elementValidClass : "",
        elementNonValidClass : "",
        compulsory : false,
        compulsoryMessage : "Položka musí být vyplněna.",
        compulsoryMessageElementId : "",
        compulsoryImageElementId : "",
        validator : false,
        validatorMessage : "",
        validatorMessageElementId : "",
        validatorRegex : "",
        toolTipMessage : ""
    };
    
    this.options.elementId = (elementId != null ? elementId : this.options.elementId);
    this.options.elementValidClass = (elementValidClass != null ? elementValidClass : this.options.elementValidClass);
    this.options.elementNonValidClass = (elementNonValidClass != null ? elementNonValidClass : this.options.elementNonValidClass);
    this.options.compulsory = (compulsory != null ? compulsory : this.options.compulsory);
    this.options.compulsoryMessage = (compulsoryMessage != null ? compulsoryMessage : this.options.compulsoryMessage);
    this.options.compulsoryMessageElementId = (compulsoryMessageElementId != null ? compulsoryMessageElementId : this.options.compulsoryMessageElementId);
    this.options.compulsoryImageElementId = (compulsoryImageElementId != null ? compulsoryImageElementId : this.options.compulsoryImageElementId);
    this.options.validator = (validator != null ? validator : this.options.validator);
    this.options.validatorMessage = (validatorMessage != null ? validatorMessage : this.options.validatorMessage);
    this.options.validatorMessageElementId = (validatorMessageElementId != null ? validatorMessageElementId : this.options.validatorMessageElementId);
    this.options.validatorRegex = (validatorRegex != null ? validatorRegex : this.options.validatorRegex);
    this.options.toolTipMessage = (toolTipMessage != null ? toolTipMessage : this.options.toolTipMessage);
    
    // -------------------------------------------------------------------
    // OBJECTS - references for set objects
    // -------------------------------------------------------------------
    this.objects = 
    {
        input : null,
        compulsoryMessageElement : null,
        compulsoryImageElement : null,
        validatorMessageElement : null
    };
    
    this.objects.input = (this.options.elementId != null ? document.getElementById(elementId) : this.objects.input);
    this.objects.compulsoryMessageElement = (this.options.compulsoryMessageElementId != null ? document.getElementById(compulsoryMessageElementId) : this.objects.compulsoryMessageElement);
    this.objects.compulsoryImageElement = (this.options.compulsoryImageElementId != null ? document.getElementById(compulsoryImageElementId) : this.objects.compulsoryImageElement);
    this.objects.validatorMessageElement = (this.options.validatorMessageElementId != null ? document.getElementById(validatorMessageElementId) : this.objects.validatorMessageElement);
    
    // -------------------------------------------------------------------
    // Public property for presentation of actual error message of the item
    // -------------------------------------------------------------------
    this.currentErrorMessage = "";
    
    // -------------------------------------------------------------------
    // FUNCTIONS - for working with events
    // -------------------------------------------------------------------
    
    this.checkValue = checkValue;
    
    // Checking of validity and according to that showing
    // error messages.
    function checkValue()
    {
        this.currentErrorMessage = "";
        if (!this.isValid())
        {
            // non valid settings
            
            if ((this.objects.input != null) && ((this.options.elementValidClass != "") && (this.options.elementNonValidClass != "")))
            {
                this.objects.input.className = this.options.elementNonValidClass;
            }
            
            // compulsory
            if ((!this.isFilled()) && this.options.compulsory)
            {
                // if value is not filled and value is compulsory
                if (this.objects.compulsoryMessageElement != null)
                {
                    this.objects.compulsoryMessageElement.innerHTML = this.options.compulsoryMessage;
                    this.objects.compulsoryMessageElement.style.display = "block";
                    this.currentErrorMessage = this.options.compulsoryMessage;
                }
                if (this.objects.compulsoryImageElement != null)
                {
                    this.objects.compulsoryMessageElement.innerHTML = this.options.compulsoryMessage;
                    this.objects.compulsoryImageElement.style.display = "block";
                    this.currentErrorMessage = this.options.compulsoryMessage;
                }
                
                // if object has to be filled, we do not show validity error
                if (this.objects.validatorMessageElement != null)
                {
                    this.objects.validatorMessageElement.style.display = "none";
                }
            }
            else
            {
                // if value is not compulsory or when is compulsory and is filled
                if (this.objects.compulsoryMessageElement != null)
                {
                    this.objects.compulsoryMessageElement.style.display = "none";
                }
                if (this.objects.compulsoryImageElement != null)
                {
                    this.objects.compulsoryImageElement.style.display = "none";
                }
                
                // validity - if objects is valid by compulsority then it is not valid by format
                if (this.objects.validatorMessageElement != null)
                {
                    this.objects.validatorMessageElement.innerHTML = this.options.validatorMessage;
                    this.objects.validatorMessageElement.style.display = "block";
                    this.currentErrorMessage = this.options.validatorMessage;
                }
            }
        }
        else
        {
            // valid settings
            
            if ((this.objects.input != null) && ((this.options.elementValidClass != "") && (this.options.elementNonValidClass != "")))
            {
                this.objects.input.className = this.options.elementValidClass;
            }
            if (this.objects.compulsoryMessageElement != null)
            {
                this.objects.compulsoryMessageElement.style.display = "none";
            }
            if (this.objects.compulsoryImageElement != null)
            {
                this.objects.compulsoryImageElement.style.display = "none";
            }
            if (this.objects.validatorMessageElement != null)
            {
                this.objects.validatorMessageElement.style.display = "none";
            }
        }
    }
    
    // -------------------------------------------------------------------
    // FUNCTIONS - for getting info about object
    // -------------------------------------------------------------------
    
    this.isValid = isValid;
    this.isFilled = isFilled;
    
    // If this must be valid, controls if it is valid and if it is compulsory,
    // controls if item has all properties ok.
    function isValid()
    {
        if (this.objects.input != null)
        {
            // check of validation
            var validationOk = false;
            if (this.options.validator)
            {
                try
                {
                    var regex = new RegExp(this.options.validatorRegex);
                    var match = regex.exec(this.objects.input.value);
                    if (match == null)
                    {
                        validationOk = false;
                    } else {
                        validationOk = true;
                    }
                }
                catch (error)
                {
                    validationOk = false;
                }
            }
            else
            {
                validationOk = true;
            }
            // check of compulsory value
            if (this.options.compulsory)
            {
                if (this.isFilled())
                {
                    return validationOk;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return validationOk;
            }
        }
        else
        {
            return false;
        }
    }
    
    // Controls if item is filled.
    function isFilled()
    {
        if (this.objects.input != null)
        {
            return (this.objects.input.value.length > 0);
        }
        else
        {
            return false;
        }
    }
    
}

// **************************************************************************************************************************
// Regural expressions for values validation
// --------------------------------------------------------------------------------------------------------------------------
// Meaning of included expressions:
// ________________________________
// Email          : value@value.val
// PhoneEN        : 000-000-0000
// Phone          : +000 123 456 789   OR   +000123456789   OR   123456789   OR   123 456 789
// PhoneL         : +000 123 456 789   OR   +000123456789
// PhoneS         : 123 456 789   OR   123456789
// PhoneNoSpaces  : +000123456789   OR   123456789
// PhoneNoSpacesL : +000123456789
// PhoneNoSpacesS : 123456789
// ZIP            : 123 45
// **************************************************************************************************************************
var ValidationExpressions =
{
    Email          : /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/,
    Phone          : /^((\+\d{3}([ ]{0,1})){0,1})(\d{3})([ ]{0,1})(\d{3})([ ]{0,1})(\d{3})$/,
    PhoneL         : /^\+(\d{3})([ ]{0,1})(\d{3})([ ]{0,1})(\d{3})([ ]{0,1})(\d{3})$/,
    PhoneS         : /^(\d{3})([ ]{0,1})(\d{3})([ ]{0,1})(\d{3})$/,
    PhoneNoSpaces  : /^((\+\d{3}){0,1})(\d{3})(\d{3})(\d{3})$/,
    PhoneNoSpacesL : /^\+(\d{3})(\d{3})(\d{3})(\d{3})$/,
    PhoneNoSpacesS : /^(\d{3})(\d{3})(\d{3})$/,
    PhoneEN        : /^\d{3}\-\d{3}\-\d{4}$/,
    ZIP            : /^(\d{3})([ ]{1})(\d{2})$/
};

// **************************************************************************************************************************
// Global functions
// **************************************************************************************************************************

// Hides HTML element by its ID.
function hideElement(elementId)
{
    document.getElementById(elementId).style.display = "none";
}

// Shows HTML element by its ID.
function showElementAsBlock(elementId)
{
    document.getElementById(elementId).style.display = "block";
}

// Shows HTML element by its ID.
function showElementAsInline(elementId)
{
    document.getElementById(elementId).style.display = "inline";
}

// Geting offset top position of object to left top window corner.
function getOffsetTop(object)
{
    var offTop = object.offsetTop;
    if (object.offsetParent != null)
    {
        offTop += getOffsetTop(object.offsetParent);
    }
    return offTop;
}

// Geting offset left position of object to left top window corner.
function getOffsetLeft(object)
{
    var offLeft = object.offsetLeft;
    if (object.offsetParent != null)
    {
        offLeft += getOffsetLeft(object.offsetParent);
    }
    return offLeft;
}