function formify(element)
{
    element = '#'+ element;
    var setting = formifySettings(element);
    $xo(element).prepend('<input type="hidden" name="hnypt1" ignore>').append('<input type="hidden" name="hnypt2" ignore>');
    
    // Adds unique formatting based on settings.
    $xo(element +'.formify input,'+ element +'.formify select,'+ element +'.formify textarea').each(function()
    {
        var fieldSetting = formifyFieldSettings(this);
		if (fieldSetting.required == 'true')
        {
            $xo(this).before('<div class="requiredFieldHolder"><div class="requiredField"></div></div>');
        }
        if (fieldSetting.ignore == 'true')
        {
            $xo(this).addClass('ignoreField');
        }
    });
	
	$xo(element +' button[type="reset"]').click(function()
    {
        $xo(element +'.formify input,'+ element +'.formify select,'+ element +'.formify textarea').each(function()
        {
            $xo(this).val(function(){ return this.defaultValue; }).removeClass('invalid');
            $xo('error-single[data="'+ $xo(this).attr('name') +'"]').empty();
        });
    });
    
    $xo(element +' button[type="submit"]').click(function()
    {
        var errorMsg = [];
        var isSpam = false;
        $xo(element +' error').empty();
        
        $xo(element +'.formify input,'+ element +'.formify select,'+ element +'.formify textarea').each(function()
        {
            var fieldSetting = formifyFieldSettings(this);
            $xo(this).focus().blur();
            
            if ($xo(this).hasClass('invalid'))
            {
                var inputTitle = $xo(this).attr('title');
				var inputName = $xo(this).attr('name');
                if ($xo(element +' error-single[data="'+ inputName +'"]').length == 0 && $xo(element +' error').length == 0 && fieldSetting.error != 'false')
                {
                    errorMsg.push({ title:inputTitle, error:fieldSetting.error });
                }
            }
            if (setting.prevent != 'false')
            {
                for (e=0;e<=setting.prevent.length-1;e++)
                {
                    if ($xo(this).val().indexOf(setting.prevent[e]) > -1)
                    {
						isSpam = true;
                    }
                }
            }
        });
        if (setting.submissions != false)
        {
			if (readCookie('submissions'))
            {
                var submissions = readCookie('submissions');
                if (submissions < setting.submissions)
                {
                    submissions++;
                    createCookie('submissions',submissions,1);
                }
                else
                {
					isSpam = true;
                }
            }
            else
            {
                createCookie('submissions',1,1);
            }
        }
        if ($xo(element +' .invalid').length == 0 && $xo(element +' input[name="hnypt1"]').val() == '' && $xo(element +' input[name="hnypt2"]').val() == '' && isSpam == false)
        {
			if (setting.action != 'false')
            {
                if ($xo(element +' form').length == 0)
                {
                    var enctype = '';
                    if (setting.enctype != 'false')
                    {
                        enctype = 'enctype="'+ setting.enctype +'"';
                    }
                    $xo(element +' .ignoreField').remove();
                    $xo(element).wrapInner('<form action="'+ setting.action +'" method="'+ setting.method +'" '+ enctype +'></form>');
                }
            }
            if (setting.message != 'false')
            {
                if ($xo(element +' message').length > 0)
                {
                    $xo(element +' message').html(setting.message);
                }
                else
                {
                    alert(setting.message);
                }
            }
            $xo(element +' button[type="submit"]').attr('passed','true');
        }
        else
        {
			if ($xo(element +' .invalid').length > 0)
            {
				if (errorMsg.length > 0)
                {
                    var alertMsg = '';
                    for (e=0;e<=errorMsg.length-1;e++)
                    {
                        if (errorMsg[e]['title'] != undefined)
                        {
                            alertMsg += ' - '+ errorMsg[e]['title'] +': '+ errorMsg[e]['error'] + '\n';
                        }
                        else
                        {
                            alertMsg += ' - '+ errorMsg[e]['error'] + '\n';
                        }
                    }
                    alert('VERIFICATION ISSUES\n\n'+ alertMsg);
                }
                $xo(element +' button[type="submit"]').attr('passed','false');
            }
            else if (setting.message != 'false')
            {
                if ($xo(element +' message').length > 0)
                {
                    $xo(element +' message').html(setting.message);
                    $xo(element +' button[type="submit"]').attr('passed','true');
                }
                else
                {
                    alert(setting.message);
                    location.reload(true);
                } 
            }
            else if (isSpam)
            {
                location.reload(true);
            }
            return false;
        }
    });
    
    $xo(element +'.formify input,'+ element +'.formify select,'+ element +'.formify textarea').focus(function(char)
    {
        var fieldSetting = formifyFieldSettings(this);
        
        if (fieldSetting.format != 'false')
        {
            formifyFormatter(this,element,char);
        }
        
        $xo(this).on('keydown',function(character)
        {
            var fieldVal = $xo(this).val();
            var keycode = character.which ? character.which : character.keyCode;
            var keyChar = convertKeycode(keycode);
            
            var hotkeys = checkForHotKeys(character,keycode,this,element);
            if (hotkeys == true)
            {
                $xo(this).off('keyup');
				checkValidStatus(this,element);
                return true;
            }
            else
            {
                $xo(this).off('keyup').on('keyup',function(character)
                {
					if (fieldSetting.format != 'false')
                    {
                        formifyFormatter(this,element,character);
                    }
					else
					{
						checkValidStatus(this,element);
					}
                });
            }
            // Check maximum character count
            if (fieldSetting.max != 'false')
            {
                var valCount = $xo(this).val().length;
                var number = parseInt(fieldSetting.max);
                if (valCount >= number && keycode != 37 && keycode != 39)
                {
                    return false;
                }
            }
            if (fieldSetting.format != 'false')
            {
                formifyFormatter(this,element,character);
            }
            formifyInputBlocker(this,element,character);
        });
        
        $xo(this).off('blur change').on('blur change',function(character)
        {
            if (fieldSetting.format != 'false')
            {
                formifyFormatter(this,element,character);
            }
            else
            {
                formifyInputBlocker(this,element,character);
            }
			checkValidStatus(this,element);
        });
    });
}
function checkValidStatus(field,element)
{
	var fieldSetting = formifyFieldSettings(field);
	var fieldVal = $xo(field).val();
	var invalidCount = 0;
	// Check minimum required character count
	if (fieldSetting.min != 'false' && fieldSetting.required == 'true')
	{
		var valCount = $xo(field).val().length;
		var number = parseInt(fieldSetting.min);
		if (valCount < number || fieldVal == '')
		{
			invalidCount++;
		}
	}
	// Check required characters
	if (fieldSetting.require != 'false' && fieldSetting.required == 'true')
	{
		var reqOpts = fieldSetting.require.split('||');
		var reqOptsCount = reqOpts.length;
		for (e=0;e<reqOptsCount;e++)
		{
			if (fieldVal.indexOf(reqOpts[e]) == -1)
			{
				invalidCount++;
			}
		}
	}
	// Check if field is empty
	if (fieldVal == '' && fieldSetting.required == 'true')
	{
		invalidCount++;
	}
	
	if (invalidCount == 0)
	{
		invalidStatus(false,field,element);
	}
	else
	{
		invalidStatus(true,field,element);
	}
}
function formifyInputBlocker(field,element,character,exceptions)
{
    var fieldSetting = formifyFieldSettings(field);
    var fieldVal = $xo(field).val();
    var keycode = character.which ? character.which : character.keyCode;
    var keyChar = convertKeycode(keycode);
    
    if (character.type == 'keyup' || character.type == 'blur')
    {
        // Check for blocked character groups
        if (fieldSetting.block != 'false')
        {
            if (fieldSetting.block.indexOf('123~') > -1)
            {
                fieldSetting.block = fieldSetting.block.replace('123~','1||2||3||4||5||6||7||8||9||0')
            }
            if (fieldSetting.block.indexOf('abc~') > -1)
            {
                fieldSetting.block = fieldSetting.block.replace('abc~','a||b||c||d||e||f||g||h||i||j||k||l||m||n||o||p||q||r||s||t||u||v||w||x||y||z')
            }
            var blockedChars = fieldSetting.block.split('||');
            var blockedCharsCount = blockedChars.length;

            for (e=0;e<blockedCharsCount;e++)
            {
                if (fieldVal.indexOf(blockedChars[e]) > -1)
                {
                    fieldVal = fieldVal.replace(blockedChars[e],'');
                }
            }
            $xo(field).val(fieldVal);
        }

        // Check for disallowed characters
        if (fieldSetting.only != 'false')
        {
            if (fieldSetting.only.indexOf('123~') > -1)
            {
                fieldSetting.only = fieldSetting.only.replace('123~','1234567890')
            }
            if (fieldSetting.only.indexOf('abc~') > -1)
            {
                fieldSetting.only = fieldSetting.only.replace('abc~','abcdefghijklmnopqrstuvwxyz')
            }
            if (exceptions)
            {
                var exceptionChars = '';
                for (e=0;e<=exceptions.length-1;e++)
                {
                    exceptionChars = exceptionChars +'||'+ exceptions[e]['character'];
                }
                fieldSetting.only = fieldSetting.only + exceptionChars;
            }
            var allowedChars = fieldSetting.only.replace(/\||/g,'');
            var valCount = fieldVal.length;
            var currentVal = fieldVal;
            for (e=0;e<valCount;e++)
            {
                if (allowedChars.indexOf(fieldVal.substring(e,e+1)) == -1)
                {
                    currentVal = currentVal.replace(fieldVal.substring(e,e+1),'');
                }
            }
            $xo(field).val(currentVal);
        }
    }
    else if (character.type == 'keydown')
    {
        // Check for blocked characters
        if (fieldSetting.block != 'false')
        {
            if (fieldSetting.block.indexOf('123~') > -1)
            {
                fieldSetting.block = fieldSetting.block.replace('123~','1||2||3||4||5||6||7||8||9||0')
            }
            if (fieldSetting.block.indexOf('abc~') > -1)
            {
                fieldSetting.block = fieldSetting.block.replace('abc~','a||b||c||d||e||f||g||h||i||j||k||l||m||n||o||p||q||r||s||t||u||v||w||x||y||z||A||B||C||D||E||F||G||H||I||J||K||L||M||N||O||P||Q||R||S||T||U||V||W||X||Y||Z')
            }
            var blockedChars = fieldSetting.block.split('||');
            var blockedCharsCount = blockedChars.length;
            for (e=0;e<blockedCharsCount;e++)
            {

                if (blockedChars[e].length == 1 && blockedChars[e] == keyChar)
                {
                    return false;
                }
            }
        }

        // Check for Allowed characters
        if (fieldSetting.only != 'false')
        {
            if (fieldSetting.only.indexOf('123~') > -1)
            {
                fieldSetting.only = fieldSetting.only.replace('123~','1||2||3||4||5||6||7||8||9||0');
            }
            if (fieldSetting.only.indexOf('abc~') > -1)
            {
                fieldSetting.only = fieldSetting.only.replace('abc~','a||b||c||d||e||f||g||h||i||j||k||l||m||n||o||p||q||r||s||t||u||v||w||x||y||z||A||B||C||D||E||F||G||H||I||J||K||L||M||N||O||P||Q||R||S||T||U||V||W||X||Y||Z');
            }

            var allowedChars = fieldSetting.only.split('||');
            var allowedCharsCount = allowedChars.length;
            for (e=0;e<allowedCharsCount;e++)
            {
                if (allowedChars[e] == keyChar)
                {
                    return true;
                }
            }
            return false;
        }
    }

}
function formifyFormatter(field,element,character)
{
    var fieldSetting = formifyFieldSettings(field);
    var keycode = character.which ? character.which : character.keyCode;
    var hotkeys = checkForHotKeys(character,keycode,field,element);

    if (keycode != 37 && keycode != 39 && keycode != 8 && $xo(field).val() != '' && (character.type == 'keyup' || character.type == 'blur'))
    {
        // get positioning of custom formatting
        var formatArr = [];
        for (e=0;e<fieldSetting.format.length;e++)
        {
            if (fieldSetting.format.substring(e,e+1) != 't' && fieldSetting.format.substring(e,e+1) != 'n' && fieldSetting.format.substring(e,e+1) != 'x')
            {
                formatArr.push({
                    position: e,
                    character: fieldSetting.format.substring(e,e+1)
                });
            }
            // Removes custom formatting
            $xo(field).val($xo(field).val().replace(fieldSetting.format.substring(e,e+1),''));
        }
        formifyInputBlocker(field,element,character);
        
        //var cursorPos = getCaretPosition(field);
        var fieldVal = $xo(field).val();
        var enteredValue = fieldVal;
        var formatCharNum = -1;

        for (i=0;i<formatArr.length;i++)
        {
            var fieldValCount = fieldVal.length;
            if (fieldValCount + formatCharNum + 1 >= formatArr[i].position)
            {
                enteredValue = [enteredValue.slice(0,formatArr[i].position),formatArr[i].character, enteredValue.slice(formatArr[i].position)].join('');
                formatCharNum++;
                
                //if (cursorPos >= formatArr[i].position)
                //{
                   // cursorPos++;
                //}
            }
        } 
        $xo(field).val(enteredValue).change();
		checkValidStatus(field,element);
        //setCaretToPos($xo(field)[0],cursorPos);
    }
}
function setCaretToPos(input,pos)
{
    setSelectionRange(input,pos,pos);
}
function getCaretPosition(ctrl)
{
    var CaretPos = 0;
    if (document.selection)
    {
        ctrl.focus ();
        var Sel = document.selection.createRange ();
        Sel.moveStart ('character', -ctrl.value.length);
        CaretPos = Sel.text.length;
    }
    else if (ctrl.selectionStart || ctrl.selectionStart == '0')
    {
        CaretPos = ctrl.selectionStart;
    }
    return (CaretPos);
}
function getSelectionText()
{
    var text = "";
    var activeEl = document.activeElement;
    var activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null;
    
    if ((activeElTagName == "textarea" || activeElTagName == "input") && /^(?:text|search|password|tel|url)$/i.test(activeEl.type) && (typeof activeEl.selectionStart == "number"))
    {
        text = activeEl.value.slice(activeEl.selectionStart, activeEl.selectionEnd);
    }
    else if (window.getSelection())
    {
        text = window.getSelection().toString();
    }
    else if(document.getSelection)
    {
        text = document.getSelection().toString();
    }
    else
    {
        text = document.selection.createRange().text;
    }
    return text;
}
function setSelectionRange(input,selectionStart,selectionEnd)
{
    if (input.setSelectionRange)
    {
        input.focus();
        input.setSelectionRange(selectionStart, selectionEnd);
    }
    else if (input.createTextRange)
    {
        var range = input.createTextRange();
        range.collapse(true);
        range.moveEnd('character', selectionEnd);
        range.moveStart('character', selectionStart);
        range.select();
    }
}
function checkForHotKeys(character,keycode,field,element)
{
	// Allow Select All
    if ((character.metaKey || character.ctrlKey) && keycode == 65)
    {
        return true;
    }
    // Allow Copy
    if ((character.metaKey || character.ctrlKey) && keycode == 67)
    {
        return true;
    }
    // Allow Paste
    if ((character.metaKey || character.ctrlKey) && keycode == 86)
    {
        return true;
    }
    // Allow Cut
    if ((character.metaKey || character.ctrlKey) && keycode == 88)
    {
        return true;
    }
	// Allow Tab
    if (keycode == 9)
    {
        return true;
    }
    // Allow Delete
    if (keycode == 8)
    {
        return true;
    }
}
function convertKeycode(character)
{
    var keyChar = String.fromCharCode(character).toLowerCase();
    return keyChar;
}
function formifySettings(element)
{
    var actionParam     = $xo(element).attr('action');
    var methodParam     = $xo(element).attr('method');
    var enctypeParam    = $xo(element).attr('enctype');
    var preventParam    = $xo(element).attr('prevent');
    var submissionsParam= $xo(element).attr('submissions');
    var messageParam    = $xo(element).attr('message');
    var sanitizeParam   = $xo(element).attr('sanitize');
    

    if (isEmpty(actionParam) == 'true')
    {
        actionParam = 'false';
    }
    if (isEmpty(methodParam) == 'true')
    {
        methodParam = 'post';
    }
    if (isEmpty(enctypeParam) == 'true')
    {
        enctypeParam = 'false';
    }
    if (isEmpty(preventParam) == 'true')
    {
        preventParam = 'false';
    }
    else if (preventParam.indexOf('||') > -1)
    {
        preventParam = preventParam.split('||');
    }
    else
    {
        preventParam = [preventParam];
    }
    if (isEmpty(submissionsParam) == 'true')
    {
        submissionsParam = 'false';
    }
    if (isEmpty(messageParam) == 'true')
    {
        messageParam = 'false';
    }
    if (isEmpty(sanitizeParam) == 'true')
    {
        sanitizeParam = 'false';
    }
    else
    {
        sanitizeParam = 'true';
    }

    var setting =
    {
        action: actionParam, 
        method: methodParam,
        enctype: enctypeParam,
        prevent: preventParam,
        submissions: submissionsParam,
        message: messageParam,
        sanitize: sanitizeParam
    }
    return setting;
}
function formifyFieldSettings(element)
{
    var formatParam = $xo(element).attr('format');
    var requireParam = $xo(element).attr('require');
    var onlyParam = $xo(element).attr('only');
    var blockParam = $xo(element).attr('block');
    var errorParam = $xo(element).attr('error');
    var maxParam = $xo(element).attr('max');
    var minParam = $xo(element).attr('min');
    var requiredParam = $xo(element).attr('required');
    var ignoreParam = $xo(element).attr('ignore');
	
    if (isEmpty(formatParam) == 'true')
    {
        formatParam = 'false';
    }
    if (isEmpty(requireParam) == 'true')
    {
        requireParam = 'false';
    }
    if (isEmpty(onlyParam) == 'true')
    {
        onlyParam = 'false';
    }
    if (isEmpty(blockParam) == 'true')
    {
        blockParam = 'false';
    }
    if (isEmpty(errorParam) == 'true')
    {
        errorParam = 'false';
    }
    if (isEmpty(maxParam) == 'true')
    {
        maxParam = 'false';
    }
    if (isEmpty(minParam) == 'true')
    {
        minParam = 'false';
    }
    
    if (isEmpty(requiredParam) == 'true')
    {
        requiredParam = 'false';
    }
    else
    {
        requiredParam = 'true';
    }
    if (isEmpty(ignoreParam) == 'true')
    {
        ignoreParam = 'false';
    }
    else
    {
        ignoreParam = 'true';
    }
    
    var setting =
    {
        format: formatParam, 
        require: requireParam,
        only: onlyParam,
        block: blockParam,
        error: errorParam,
        max: maxParam,
        min: minParam,
        required: requiredParam,
        ignore: ignoreParam,
    }
    return setting;
	
    function isEmpty(data)
	{
		if (data === undefined || data === null || data === false)
		{
			return 'true';
		}
		else
		{
			return 'false';
		}
	}
}

function invalidStatus(status,field,element)
{
    var fieldSetting = formifyFieldSettings(field);
    
    if (status == true)
    {
        $xo(field).addClass('invalid');

        if (fieldSetting.error != 'false') // Checks to see if it has an error message
        {
            var inputName = $xo(field).attr('name');
            if ($xo(element +' error-single[data="'+ inputName +'"]').length != 0) // Check if location has been specified for error message, otherwise place it into an variable that can then be placed in an alert box.
            {
                $xo(element +' error-single[data="'+ inputName +'"]').html(fieldSetting.error);
            }
            else if ($xo(element +' error').length != 0)
            {
                $xo(element +' error').append('<error-single data="'+ $xo(field).attr('name') +'">'+ fieldSetting.error +'</error-single>');   
            }
        }
    }
    else
    {
		$xo(field).removeClass('invalid');

        if (fieldSetting.error != 'false')
        {
            $xo('error-single[data="'+ $xo(field).attr('name') +'"]').empty();
        }
    }
}
