/**
 * @author Sztraczinszki István
 * version 0.1
 */
SForm = function(){



    this.fields = [], this.onBeforeSubmit = null, this.config = {
    
		showRequiredOnLabelBefore: true, // az input elemet megelőző label classt is requiredre állítja, ha be van kapcsolva
        showRequiredOnFields: true, // az input mező classt requiredre állítja ha nincs kitöltve	
        setMaxLengthOnFields: true, // rápakolja a beviteli mezőkre maxlengthet ha be van kapcsolva
        autoFocus: true, // ha be van kapcsolva akkor inicializáláskor a form első mezőjére teszi a focust	
        validateRealTime: true, // események után ellenőrzi az adott mezőt
        validateAllRealTime: true, // események után az ÖSSZES mezőt ellenőrzi, CSAK akkor kell bekapcsolni ha két mező között feltételes, vagy egyezőségi ellenrzés van
        createErrorLabels: true, // egy label elemet készít az input elem után és abba írja a hibaüzenetet
        errorLabelElement: "div", // ha createErrorLabels = true, akkor ez határozza meg, hogy milyen elemet kreálunk, lehet pl. div, p, span, label
        //createErrorLabelEffect: "slideDown", // error label létrehozásánál valamilyen effekt, alapból: false, lehetséges: fadeIn
        //createErrorLabelEffectParam: "fast", // az effekt paramétere
        //destroyErrorLabelEffect: "slideUp",
        //destroyErrorLabelEffectParam: "fast",
        AlertOnFormIsNotValid: true,
		msgFormIsNotValid: "Az űrlapon a mezők megfelelő kitöltése szükséges!",
        debug: true
    
    
    }, this.classNames = {
        required: "required",
        warning: "warning",
        errorlabel: "error"
    
    }, this.formats = {
        email: {
            reg: /^[0-9a-z\.-_]+@([0-9a-z-]+\.)+[a-z]{2,4}$/i,
            message: "A mezőbe érvényes email címet kell írnia!"
        },
        numeric: {
            reg: /^[0-9\. ]+$/i,
            message: "A mező értéke csak szám lehet!"
        },
		lettersonly: {
			reg: /^[a-z]+$/i,
			message: "A mező értéke csak betűket tartalmazhat!"
		},
		alphanumeric: {
			reg: /^\w+$/i,
			message: "A mező csak alfanumerikus értéket tartalmazhat!"
		},
		alphanumeric_strict: {
			reg: /^[a-z0-9_.-]+$/i,
			message: "A mező csak az angol abc kis és nagybetűit, valamint számokat tartalmazhat!"
		},
		alphanumeric_strict_small: {
			reg: /^[a-z0-9_.-]+$/,
			message: "A mező csak az angol abc kis betűit és számokat tartalmazhat!"
		},
		shopname: {
			reg: /^[a-z0-9]+$/,
			message: "A mező csak az angol abc kis betűit és számokat tartalmazhat!"
		},
        integer: {
            reg: /^[0-9\-]+$/i,
            message: "A mező értéke csak egész szám lehet!"
        },
        integer_positive: {
            reg: /^[0-9]+$/i,
            message: "A mező értéke csak pozitív egész szám lehet!"
        },
        phone: {
            reg: /^[a-zA-Z0-9\ \-\+\/\#\*\(\)]{4,40}$/,
            message: "A mezőbe érvényes telefonszámot kell írnia!"
        }
    }
    
    /*
     * Lehetőség van saját formátum hozzáadására a js alapkód módosítása nélkül, projektspecifikusan
     */
    this.addFormat = function(format, reg, message){
        this.formats.format.reg = reg;
        this.formats.format.message = message;
    }
    
    /*
     * Az adott fieldhez tartozó input objektum (el), típusát adja vissza validálási szempontból
     */
    this.getElementType = function(elType){
        t = this;
        elType = elType.toLowerCase();
        if (elType == "text" || elType == "textarea" || elType == "password") {
            return "simple";
        }
        if (elType == "select-one") {
            return "select";
        }
        if (elType == "checkbox" || elType == "radio") {
            return "complex";
        }
    },    
	
	/*
     * Adott mező ellenőrzése kötelezőségre vonatkoztatva
     */
    this.checkRequired = function(field){
        var isRequiredError = false;
        
        // ha feltételes kötelezőségről van szó
        if (typeof(field.required) == "function") {
            isRequiredError = field.required.call();
            if (!isRequiredError) 
                isRequiredError = false;
        }
        
        if (typeof(field.required) == "boolean") {
            // text, password, textarea
            if (this.getElementType(field.elType) == "simple") {
                if (!$(field.el).val()) {
                    isRequiredError = true;
                }
            }
            // select
            else 
                if (this.getElementType(field.elType) == "select") {
                    if (!$(field.el).val() || field.el.value == 0) {
                        isRequiredError = true;
                    }
                }
            // radio, chekbox
			else 
				if (this.getElementType(field.elType) == "complex") {
					field.isChecked = false;
					for (var i = 0; i < field.el.length; i++) 
					{
                       	if (field.el[i].checked) field.isChecked = true;
					}
					if (!field.isChecked) 
					{
						isRequiredError = true;
					}
				}
        }
        
  
        // class hozzáadása vagy elvétele
        if (isRequiredError == true) {
            if (this.config.showRequiredOnLabelBefore) {
				$(field.el).parents("tr").find("label").addClass(this.classNames.required);
			}
            if (this.config.showRequiredOnFields) {
				$(field.el).addClass(this.classNames.required);
			}
            return false;
        }
        else 
		{
            if (this.config.showRequiredOnLabelBefore) {
				$(field.el).parents("tr").find("label").removeClass(this.classNames.required);
			}
            if (this.config.showRequiredOnFields) {
				$(field.el).removeClass(this.classNames.required);
			}
            return true;
        }
    }
    
    /*
     * Formátumellenőrzés
     */
    this.checkFormat = function(value, format){
        var reg = eval("this.formats." + format).reg;
        var result = reg.test(value);
        return result;
    }
    
    /*
     * A konkrét hibaüzenet string összeállítása, ellenőrzi hogy van-e megadott (custom) hibaüzenet az adott mezőhöz
     */
    this.createErrorMessage = function(fieldName, criteria, warning){
        t = this;
        var errorMessage = new String();
        var customMessages;
        
        //van-e testreszabott hibaüzenet
        if (customMessages) {
            if (customMessages = eval("this.messages." + fieldName)) {
                if (eval("customMessages." + criteria)) 
                    errorMessage = eval("customMessages." + criteria);
            }
        }
        // ha nincs
        else 
            if (errorMessage == "") {
                errorMessage = warning;
            }
        return errorMessage;
    }
    
    /*
     * Miután az egeret warningos input felé visszük, megjelenik a hibaüzenet, itt állutjuk össze hogy mi
     */
    this.createErrorTitle = function(field, warnings){
        t = this;
        var errorMessage = new String();
        var customMessages;
  
        // formátumhoz kapcsolódó hibaüzenet
        if (eval("this.formats." + warnings[0])) {
            return this.createErrorMessage(field.name, "format", eval("this.formats." + warnings[0]).message);
        }
  
        // egyéb megkötésekhez kapcsolódó hibaüzenetek
        switch (warnings[0]) {
            case 'minLength':
                return this.createErrorMessage(field.name, "minLength", "A mező értékének minimális hossza: " + field.minLength + " karakter!");
            case 'maxLength':
                return this.createErrorMessage(field.name, "maxLength", "A mező értékének maximális hossza: " + field.maxLength + " karakter!");
            case 'rangeLength':
                return this.createErrorMessage(field.name, "rangeLength", "A mező min. hossza: " + field.rangeLength[0] + ", max. hossza: " + field.rangeLength[1] + " karakter");
            case 'minValue':
                return this.createErrorMessage(field.name, "minValue", "A mező értékének minimális értéke: " + field.minValue);
            case 'maxValue':
                return this.createErrorMessage(field.name, "maxValue", "A mező értékének maximális értéke: " + field.maxValue);
            case 'rangeValue':
                return this.createErrorMessage(field.name, "rangeValue", "A mező értékének " + field.rangeValue[0] + " és " + field.rangeValue[1] + " között kell lenni");
            case 'equalTo':
                return this.createErrorMessage(field.name, "equalTo", "A mező értéke nem egyezik a megfelelő mező értékével!");
            case 'minChecked':
                return this.createErrorMessage(field.name, "minChecked", "Minimum ki kell választania: " + field.minChecked + " opciót!");
            case 'maxChecked':
                return this.createErrorMessage(field.name, "maxChecked", "Maximum " + field.minChecked + " opciót választhat ki!");
            case 'rangeChecked':
                return this.createErrorMessage(field.name, "rangeChecked", "Minimum " + field.rangeChecked[0] + ", maximum " + field.rangeChecked[1] + " opciót válasszon ki!");
			case 'remote':
                return this.createErrorMessage(field.name, "remote", "Foglalt!");
        }
    },    
	
	/*
     * A dom-ban az input elem(ek) után kreál egy elemet, ahová a hibaüzenetet írjuk
     */
    this.createErrorLabel = function(field, message){
         t = this;
        // megvizsgáljuk van-e már error label
        var n = $("." + this.classNames.errorlabel, $(field.el).parents("td")).length;
        // ha nincs akkor legeneráljuk
        if (n == 0) {
            var errorLabel = document.createElement(this.config.errorLabelElement);
            errorLabel.className = this.classNames.errorlabel;
            errorLabel.innerHTML = message;
            errorLabel.style.display = 'none';
            $(field.el).parents("td").append(errorLabel);
			// beállított effektel megjelenítjük
            switch (this.config.createErrorLabelEffect) {
                case "fadeIn":
                    $(errorLabel).fadeIn(this.config.createErrorLabelEffectParam);
                    break;
                case "slideDown":
                    $(errorLabel).slideDown(this.config.createErrorLabelEffectParam);
                    break;
                case false:
                default:
                    $(errorLabel).show();
                    break;
            }
        }
		// ha van akkor a tartalmát cseréljük
		else {
			$("." + this.classNames.errorlabel, $(field.el).parents("td")).html( message );
		}
		
        
    }, 

	/*
     * A hibaüzenetet befoglaló elem eltávolítása
     */
	this.destroyErrorLabel = function(field){
        //var errorLabel = $("."+this.classNames.errorlabel, $(field.el).parents("td") )[0];
        /*switch ( this.config.destroyErrorLabelEffect ) {
         case "fadeOut" : $("."+this.classNames.errorlabel, $(field.el).parents("td") ).fadeOut( this.config.destroyErrorLabelEffectParam ); break;
         case "slideUp" : $("."+this.classNames.errorlabel, $(field.el).parents("td") ).slideUp( this.config.destroyErrorLabelEffectParam ); break;
         }*/
        $("." + this.classNames.errorlabel, $(field.el).parents("td")).remove();
        
    },    
	
	/*
     * Egy adott field ellenőrzése ajax kérés alapján
     * field -> objektumon belül a field
     */
	this.checkRemoteFields = function(field, actualField) {
		var warnings = [];
		if (field.remote) {
			var _root = this;
			var result = null;
			// ajax eredmény lekérése
			$.ajaxSetup({
			 	async: false
			});
			$.post(field.remote, field.name+"="+field.el.value, function(response) {
				if (response=='true') {
		
				}
				else {
					warnings.push('remote');
				}
				 // warning class ráadása illetve levétele
				if (warnings.length) {
					isValid = false;
					$(field.el).addClass(_root.classNames.warning);
					var errorMessage = _root.createErrorTitle(field, warnings)
					field.el.title = errorMessage;
					if (_root.config.createErrorLabels) 
					{
						_root.createErrorLabel(field, errorMessage);
					}
					field.el.style.cursor = "help";
					result = false;
				}
				else {
					$(field.el).removeClass(_root.classNames.warning);
					$(field.el).removeAttr('title');
					if (_root.config.createErrorLabels) {
						_root.destroyErrorLabel(field);
					}
					field.el.style.cursor = "auto";
					result = true;
				}
			});
			$.ajaxSetup({
			 	async: true
			});
			return result;
		}
		

	},

	
	/*
     * Egy adott field ellenőrzése
     * field -> objektumon belül a field
     * actualField -> az az elem melyen éppen esemény hajtódik végre
     */
    this.checkField = function(field, actualField, isFinalValidation) {
        var isValid = true;
        var warnings = [];
        if (field.required) 
		{
            //console.log(field.el.id);
            isValid = this.checkRequired(field)
        }
        // "szimpla" beviteli mezőkre alkalmazható ellenőrzések
        if (this.getElementType(field.elType) == "simple") {
            // minLength 
            if (field.minLength) {
                if (field.el.value && field.el.value.length < field.minLength) {
                    warnings.push('minLength');
                }
            }
            
            // maxLength 
            if (field.maxLength) {
                if (field.el.value && field.el.value.length > field.maxLength) {
                    warnings.push('maxLength');
                }
            }
            
            // rangeLength
            if (field.rangeLength) {
                if (field.el.value && (field.el.value.length < field.rangeLength[0] || field.el.value.length > field.rangeLength[1])) {
                    warnings.push('rangeLength');
                }
            }
            
            // minValue
            if (field.el.value && field.minValue) {
                if (field.el.value < field.minValue) {
                    warnings.push('minValue');
                }
            }
            
            // maxValue
            if (field.el.value && field.maxValue) {
                if (field.el.value > field.maxValue) {
                    warnings.push('maxValue');
                }
            }
            
            // rangeValue
            if (field.el.value && field.rangeValue) {
                if (field.el.value < field.rangeValue[0] || field.el.value > field.rangeValue[1]) {
                    warnings.push('rangeValue');
                }
            }
            
            // equalTo
            if ($(field.equalTo).val()) {
                if (field.el.value != $(field.equalTo).val()) {
                    warnings.push('equalTo');
                }
            }
            
       
            
            // format
            if (field.format) {
                if (field.el.value && !this.checkFormat(field.el.value, field.format)) {
                    warnings.push(field.format);
                }
            }
            
            // warning class ráadása illetve levétele
            if (warnings.length) {
                isValid = false;
                $(field.el).addClass(this.classNames.warning);
                var errorMessage = this.createErrorTitle(field, warnings)
                $(field.el).attr('title', errorMessage);
                if (this.config.createErrorLabels) 
				{
                    this.createErrorLabel(field, errorMessage);
                }
                field.el.style.cursor = "help";
            }
            
            else {
                $(field.el).removeClass(this.classNames.warning);
                $(field.el).removeAttr('title');
                if (this.config.createErrorLabels) {
                    this.destroyErrorLabel(field);
                }
				field.el.style.cursor = "auto";
            }
        }
        
        // "komplex mezők ellenőrzése"
        if (this.getElementType(field.elType) == "complex") {
            var checked = $("input:checked", $(field.el).parent()).length
            if (checked && field.minChecked) {
                if (checked < field.minChecked) {
                    warnings.push('minChecked');
                }
            }

            if (checked && field.maxChecked) {
                if (checked > field.maxChecked) {
                    warnings.push('maxChecked');
                }
            }
            
            if (checked && field.rangeChecked) {
                if (checked < field.rangeChecked[0] || checked > field.rangeChecked[1]) {
                    warnings.push('rangeChecked');
                }
            }
            
            // warning class ráadása illetve levétele
            if (warnings.length) {
                isValid = false;
                var errorMessage = this.createErrorTitle(field, warnings);
                if (this.config.createErrorLabels) {
                    this.createErrorLabel(field, errorMessage);
                }
            }
            else {
                if (this.config.createErrorLabels) {
                    this.destroyErrorLabel(field);
                }
            }
        }
        return isValid;
    },   
	

	
	/*
     * Összes field ellenőrzése
     */
    this.checkAll = function(actualField){
        var isSendable = true;
		var isFinalValidation = actualField ?  false : true;
        for (var i in this.fields) {
            var temp = this.checkField(this.fields[i], actualField, isFinalValidation);
            if (temp == false) isSendable = false;
        }

		// ha minden rendben van és finalValidation, akkor ellenőrizzük a remote fieldeket
		if (isSendable==true && isFinalValidation==true) {
			for (var i in this.fields) {
				if (this.fields[i].remote) {
					var temp = this.checkRemoteFields( this.fields[i], actualField );
					//console.log('tmp='+temp);
					if (temp == false) isSendable = false;
				}
			}
		}
        return isSendable;
    },    
	
	/*
     * Megfelelő input elem hozzárendelése a fields objektum-tömb adott eleméhez
     */
    this.bindFieldElement = function(id, field){
        var element = null;
        element = $("#" + id).get(0);
        if (element) {
            field.elType = element.type.toLowerCase();
        }
        else {
            element = $("input[@name=" + id + "]");
        	if (element.length > 0) {
				field.elType = element.get(0).type.toLowerCase();
			}
			else 
			{
				element = null;
			}
        }
        return element;
    },    
	
	/*
     * Az ellenőrizni kívánt mezők inicializációja
     * - Input elemek hozzárendelése a fields objektumtömbhöz
     * - Parent objektumok (fieldek) átadása az alá tartozó input elemeknek
     * - Eseménykezelők hozzárendelése az input elemekhez
     */
    this.initFields = function(){
        for (var i in this.fields) {
            this.fields[i].parent = this;
            this.fields[i].el = this.bindFieldElement(i, this.fields[i]);
            
			// ha a fieldsben definiált elem nem létezik akkor...
			if (!this.fields[i].el) {
			 	if (this.config.debug) console.log(i + ' - Nem létező mező');
				delete this.fields[i];
				continue;
			}
		
			this.fields[i].el.parent = this.fields[i];
            this.fields[i].name = i;

            // mezőkhöz csatolt eseménykezelés
            if (this.config.validateRealTime == true) {
                // ha radio vagy checkbox
                if (this.getElementType(this.fields[i].elType) == "complex") {
                    for (var j = 0; j < this.fields[i].el.length; j++) {
                        this.fields[i].el[j].parent = this.fields[i];
                        this.fields[i].el[j].onclick = function(){
                            if (this.parent.parent.config.validateAllRealTime == true) 
                                this.parent.parent.checkAll(this);
                            else 
                                this.checkField(this.parent, this);
                        }
                    }
                }
                
                // text, password, textarea
                else 
                    if (this.getElementType(this.fields[i].elType) == "simple") {
                        this.fields[i].el.onblur = this.fields[i].el.onkeyup = function(){
                            if (this.parent.parent.config.validateAllRealTime == true) 
                                this.parent.parent.checkAll(this);
                            else 
                                this.checkField(this.parent, this);
                        }
                    }
               // select 
               else 
					if (this.getElementType(this.fields[i].elType) == "select") {
						this.fields[i].el.onchange = function(){
							if (this.parent.parent.config.validateAllRealTime == true) 
								this.parent.parent.checkAll(this);
							else 
								this.checkField(this.parent, this);
                            }
                        }
            }
            
            // ha be van kapcsolva a megfelelő config, akkor beállítja az inputra a maxlengthet
            if (this.getElementType(this.fields[i].elType) == "simple") {
                if (this.config.setMaxLengthOnFields && this.fields[i].maxLength) 
				{
                    this.fields[i].el.setAttribute('maxlength', this.fields[i].maxLength);
                }
                if (this.config.setMaxLengthOnFields && this.fields[i].rangeLength) 
				{
                    this.fields[i].el.setAttribute('maxlength', this.fields[i].rangeLength[1]);
                }
            }
        }
    },    
	
	/*
     * Kívülről meghívható függvény, visszaadja hogy a form valid-e
     */
    this.isValid = function(){
        return this.checkAll();
    }
    
    this.isFieldValid = function(field){
        return this.checkField(field); 
    }
    
    this.check = function(){
        this.checkAll();
    }

    /*
     * Sform inicializálása, id = ellenőrizni kívánt form id-ja
     */
    this.init = function(id){
        // űrlap beállításai
        this.form = $("#" + id).get(0);
        this.form.parent = this;
        $(this.form).bind('reset', function(e){
            var _self = this;
            setTimeout(function(){
                _self.parent.checkAll();
            }, 10)
        });
        this.form.onsubmit = function(){
			var res = this.parent.checkAll();
            
			if (!res) {
                if (this.parent.config.AlertOnFormIsNotValid) alert(this.parent.config.msgFormIsNotValid);
            }
			return res;
			
        }
        // mezők beállításai
        this.initFields();
        // ellenőrzés a generálás után azonnal
        this.checkAll();
    }
}

