function NAForm(name) {
    this.name = name;
    this.elements = [];
    this.errors = [];
    //set this callback if you want your own method to handle errors
    this.onErrorCallback = null;
    this.onSuccessCallback = null;
}

/*
Sets error callback to handle errors
callback function will get:
1. this
2. array of bad input element id
3. error messages in selected language
*/
NAForm.prototype.setErrorCallback = function(f) {
    this.onErrorCallback = f;
}

/*
Sets form success callback
To handle form submition without NAForm object, return false
from success callback.
*/
NAForm.prototype.setSuccessCallback = function(f) {
    this.onSuccessCallback = f;
}

/*
validate all elements in form
*/
NAForm.prototype.validate = function() {
    this.errors = [];
    var ids = [];
    var errFocus = false;
    //remove error class
    $("#naform-" + this.name + " .naform-input-error").removeClass("naform-input-error");
    for (var i=0; i<this.elements.length; i++) {
        var cur = this.elements[i];
        if (!this.validateField(cur)) {
            var $elm = $("#" + cur.id);
            if (!errFocus) {
                $elm.focus();
                errFocus = true;
            }
            
            $elm.addClass("naform-input-error");
            this.errors.push(cur.errMsg);
            ids.push(cur.id);
        }
    }
    
    if (this.errors.length) {
        if (this.onErrorCallback != null) {
            //call
            this.onErrorCallback(this, ids, this.errors);
        }
        else {
            //show alert box - default
            if (this.errors[0].length) {
                alert(this.errors[0]);
            }
        }
        
        return false;
    }
    
    if (this.onSuccessCallback != null) {
        return this.onSuccessCallback();
    }
    
    return true;
}

NAForm.prototype.normalizeNames = function() {
    var name = this.name;
    var len = name.length + 1;
    var $frm = $("#naform-" + name);
    $frm.find("input,textarea,select").each(function() {
        var fieldName = $(this).attr("name");
        if (fieldName.indexOf(name + "_", 0) == 0) {
            //prefix found, replace
            fieldName = fieldName.substring(len);
            $(this).attr("name", fieldName);
        }
    });
}

NAForm.prototype.submit = function() {
    if (this.validate()) {
        //change names to their originals
        this.normalizeNames();
        $("#naform-" + this.name).submit();
        return true;
    }
    
    return false;
}

NAForm.prototype.validateField = function(obj) {
    var $elm = $("#" + obj.id);
    var val = $elm.val();
    if ("text" == obj.type) {
        if (obj.required && val.length == 0)
            return false;
            
        if (val.length > obj.maxChars) {
            //trim
            $elm.val(val.substr(0, obj.maxChars));
        }
        
        //any case is now valid
        return true;
    }
    else if ("date" == obj.type) {
        if (val.length == 0 && !obj.required)
            return true;
        
        if (obj.required && val.length == 0)
            return false;
            
        return true;
    }
    else if ("select" == obj.type) {
        //nothing to validate
        return true;
    }
    else if ("checkbox" == obj.type) {
        if (obj.required && !$elm.is(":checked"))
            return false;
        
        return true;
    }
    else if ("email" == obj.type) {
        if (!obj.required && val.length == 0)
            return true;
        
        //validate email even if not required but entered!
        return this.validateEmail(val);
    }
}

NAForm.prototype.validateEmail = function (str) {
    //copied from NA scripts
	///// FUNCTION FOR VALIDATING EMAIL ADDRESS
	var at="@"
	var dot="."
	var lat=str.indexOf(at)
	var lstr=str.length
	var ldot=str.indexOf(dot)

	if (str.indexOf(at)==-1){
		return false
	} else if (str.indexOf(at)==-1 || str.indexOf(at)==0 || str.indexOf(at)==lstr){
		return false
	} else 	if (str.indexOf(dot)==-1 || str.indexOf(dot)==0 || str.indexOf(dot)==lstr){
		return false
	} else  if (str.indexOf(at,(lat+1))!=-1){
		return false
	} else 	 if (str.substring(lat-1,lat)==dot || str.substring(lat+1,lat+2)==dot){
		return false
	} else  if (str.indexOf(dot,(lat+2))==-1){
		return false
	} else if (str.indexOf(" ")!=-1){
		return false
	} else {
		return true
	}
}

NAForm.prototype.addInput = function(id, name, req, val, n, err) {
    var obj = {
        type : "text",
        name : name,
        id : id,
        required : (req) ? true : false,
        validated : (val) ? true : false,
        maxChars : n,
        errMsg : err
    };
    
    this.elements.push(obj);
}

NAForm.prototype.addDate = function(id, name, req, val, err) {
    var obj = {
        type : "date",
        name : name,
        id : id,
        required : (req) ? true : false,
        validated : (val) ? true : false,
        errMsg : err
    };
    
    //create calendar
    new NACalendar(obj.id);
    
    this.elements.push(obj);
}

NAForm.prototype.addSelect = function(id, name, req, err) {
    var obj = {
        type : "select",
        name : name,
        id : id,
        required : (req) ? true : false,
        validated : false,
        errMsg : err
    };
    
    this.elements.push(obj);
}

NAForm.prototype.addCheckbox = function(id, name, req, err) {
    var obj = {
        type : "checkbox",
        name : name,
        id : id,
        required : (req) ? true : false,
        validated : false,
        errMsg : err
    };
    
    this.elements.push(obj);
}

NAForm.prototype.addEmail = function(id, name, req, val, n, err) {
    var obj = {
        type : "email",
        name : name,
        id : id,
        required : (req) ? true : false,
        validated : false,
        maxChaes : n,
        errMsg : err
    };
    
    this.elements.push(obj);
}


function NACalendar(id) {
    this.elm = $("#" + id);
    this.cnt = null;
    //init with current date
    var dt = new Date();
    this.year = dt.getFullYear();
    this.month = dt.getMonth() + 1;
    this.day = dt.getDate();

    var _this =  this;
    this.elm.click(function(e) {
        e.stopPropagation();            
        _this.open();
    }).attr("readonly", true);
    
    if (typeof NACalendar.opened == 'undefined' ) {
        // It has not... perform the initilization
        NACalendar.opened = [];
    }
}

NACalendar.prototype.open = function() {
    if (this.cnt == null) {
        //is anyone still opened
        for (var i=0; i<NACalendar.opened.length; i++) {
            NACalendar.opened[i].close();
        }
        
        NACalendar.opened = [this];
        
        this.cnt = $("<div />").addClass("nacalendar-container").css("position", "absolute").click(function(e) {
            e.stopPropagation();
        });
        
        //set position
        var pos = this.elm.position();
        this.cnt.css("top", pos.top + NAFormLocale.calender.offsetTop).css("left", pos.left + NAFormLocale.calender.offsetLeft);
        var _this = this;
        $("body").append(this.cnt);

        $("html").click(function() {
            _this.close();
        });
        
        this.render();
    }
    
}

NACalendar.prototype.setYear = function(year) {
    this.year = year;
    this.render();
}

NACalendar.prototype.nextYear = function() {
    if (this.year == 9999)
        this.year = 0;
    else
        this.year++;
    
    this.render();
}

NACalendar.prototype.prevYear = function() {
    if (this.year == 0)
        //really??
        this.year = 9999;
    else
        this.year--;
        
    this.render();
}

NACalendar.prototype.nextMonth = function() {
    if (this.month == 12) {
        this.month = 1;
        this.nextYear();
    }
    else {
        this.month++;
        this.render();
    }
}


NACalendar.prototype.prevMonth = function() {
    if (this.month == 1) {
        this.month = 12;
        this.prevYear();
    }
    else {
        this.month--;
        this.render();
    }
}


NACalendar.prototype.render = function() {
    this.cnt.empty();
    var totalDays = this.daysInMonth(this.year, this.month);
    var firstDay = this.dayOfWeek(this.year, this.month, 1);
    
    //year select
    var $sel = $("<select />").addClass("nacalendar-year-select");
    var dt = new Date();
    var curYear = dt.getFullYear();
    var start = curYear - 80;
    var end = curYear + 20;
    if (start < 0)
        //someone didn't set clock
        start = 0;
    
    if (end > 9999)
        end = 9999;
        
    //is curYear out of scope
    if (this.year > end || this.year < start)
        this.year = curYear;
    
    for (var i=start; i<=end; i++) {
        $sel.append(
            $("<option />").val(i).html(i)
        );
    }

    var _this = this;    
    $sel.val(this.year).bind("change", function() {
        _this.setYear($(this).val());
    });
    
    //year line
    this.cnt.append(
        $("<div />").addClass("nacalendar-url-block").click(function() {
            _this.prevYear();
        }).html("&lt;")
    ).append(
        $("<div />").addClass("nacalendar-year-block").append($sel)
    ).append(
        $("<div />").addClass("nacalendar-url-block").click(function() {
            _this.nextYear();
        }).html("&gt;")
    );
    
    //month line
    this.cnt.append(
        $("<div />").addClass("nacalendar-url-block").click(function() {
            _this.prevMonth();
        }).html("&lt;")
    ).append(
        $("<div />").addClass("nacalendar-month-block").html(NAFormLocale.calender.months[this.month])
    ).append(
        $("<div />").addClass("nacalendar-url-block").click(function() {
            _this.nextMonth();
        }).html("&gt;")
    );
        
        
    //padd
    for (var i=0; i<firstDay; i++) {
        this.cnt.append(
            $("<div />").addClass("nacalendar-block nacalendar-padd")
        );
    }
    
    //show days
    for (var i=1; i<=totalDays; i++) {
        this.cnt.append(
            this.createDayElement(i, this.month, this.year)
        );
    }
    
    //pad to complete row

    var drawn = ((totalDays + firstDay) % 7);
    if (drawn != 0) {
        var padd = 7 - drawn;
        for (var i=0; i<padd; i++) {
            this.cnt.append(
                $("<div />").addClass("nacalendar-block nacalendar-padd")
            );
        }
    }
    
}

NACalendar.prototype.createDayElement = function(day, month, year) {
    var _this = this;
    return $("<div />")
            .addClass("nacalendar-block nacalendar-day")
            .html(day).click(function() {
                _this.close();
                _this.elm.val(_this.buildDateString(day, month, year));
                _this.day = day;
            });
}

NACalendar.prototype.buildDateString = function(day, month, year) {
    var fmt = NAFormLocale.calender.dateFormat;
    var sep = NAFormLocale.calender.dateSeperator;
    var str = "";

    for (var i=0; i<fmt.length; i++) {
        var c = fmt.charAt(i);
        switch (c) {
            case 'd':
                str += day;
                break;
            
            case 'm':
                str += month;            
                break;
            
            case 'y':
                str += year;
                break;
        }
        
        if (i < fmt.length - 1)
            str += sep;
    }
    
    return str;
}


NACalendar.prototype.close = function() {
    if (this.cnt != null) {
        this.cnt.remove();
        this.cnt = null;
    }
}

NACalendar.prototype.isLeap = function(year) {
	var year4 = year/4;
	var year100 = year/100;
	var year400 = year/400;

	if (Math.round(year4) != year4)
		return false;		//cant be leap

	if (Math.round(year100) != year100)
		return true;

	if (Math.round(year400) == year400)
		return true;

	return false;
}

NACalendar.prototype.daysInMonth = function(year, month) {
	if (month == 2)	//february on leap year or not
		return (this.isLeap(year)) ? 29 : 28;
	if (month == 4 || month == 6 || month == 9 || month == 11)
		return 30;

	return 31;
}

NACalendar.prototype.dayOfWeek = function(year, month, day) {
    var a = Math.floor((14 - month) / 12);
    var y = year - a;
    var m = month + 12 * a - 2;
    var d = (day + y + Math.floor(y / 4) - Math.floor(y / 100) +
             Math.floor(y / 400) + Math.floor((31 * m) / 12))  % 7;
             
    return d;
}


