/**
 * Niz objekata i funkcija za evaluaciju unosa u obrasce.
 * NAPOMENA: Ova skripta zasad radi samo u Internet Exploreru s ugradenim
 * MSXML3. U buducim verzijama trebalo bi proširiti funkcionalnost i na
 * Mozillu, i to najbolje zasebnom skriptom koja bi objedinjavala
 * funkcionalnosti dvaju browsera u jedan interface.
 *
 * @author Berislav Lopac berislav.lopac@lopsica.com
 **/

/* generalne konstante */
var VAL_XML_URL         = '../javascript/validator/validator.xml';
var VAL_NS              = 'val';
var VAL_NAMESPACE       = 'xmlns:' + VAL_NS + '="http://www.dimedia.hr/formValidator"';
var DATE_DEFAULT_FORMAT = 'dd.MM.yyyy';

/*
 * Objekt (u suštini, asocijativni array) s porukama.
 * Ovo je privremeno rješenje; u konačnoj instanci poruke bi trebalo definirati
 * kroz neki vanjski dokument (npr. XML) zbog internacionalizacije.
 */
var MESSAGES                        = new Object();
    MESSAGES.REQUIRED               = new Object();
    MESSAGES.REQUIRED.YES           = validationMessages.required.yes;
    MESSAGES.REQUIRED.MAYBE         = validationMessages.required.maybe;
    MESSAGES.VALIDATION             = new Object();

    MESSAGES.VALIDATION.a           = validationMessages.alphanum;
    MESSAGES.VALIDATION.an          = validationMessages.alphanum;
    MESSAGES.VALIDATION.alpha       = validationMessages.alphanum;
    MESSAGES.VALIDATION.alphanum    = validationMessages.alphanum;

    MESSAGES.VALIDATION.n           = validationMessages.numeric;
    MESSAGES.VALIDATION.num         = validationMessages.numeric;
    MESSAGES.VALIDATION.number      = validationMessages.numeric;
    MESSAGES.VALIDATION.numeric     = validationMessages.numeric;

    MESSAGES.VALIDATION.jmbg        = validationMessages.jmbg;

    MESSAGES.VALIDATION.d           = validationMessages.date;
    MESSAGES.VALIDATION.date        = validationMessages.date;

    MESSAGES.VALIDATION.t           = validationMessages.time;
    MESSAGES.VALIDATION.time        = validationMessages.time;

/**
  * Funkcija za inicijalizaciju stranice. Postavlja se na onload event
  * dokumenta i poziva sve funkcije potrebne kod otvaranja stranice.
  */
function init()
{
    InitValidator();
}

/**
  * Funkcija za inicijalizaciju validatora.
  * Ova funkcija prolazi kroz odgovarajuci XML dokument i postavlja
  * odgovarajuce validacije.
  */
function InitValidator()
{
    var allForms = document.forms;
    if(allForms.length) {
        var val = LoadXmlDocument(VAL_XML_URL);
        if(val) {
            for(var i = 0; i < allForms.length; i++) {
                var frm = allForms[i];
                var elms = val.selectNodes('//' + VAL_NS + ':form[@name="' +  frm.name + '"]/' + VAL_NS + ':input');
                for(var j = 0; j < elms.length; j++) {
                    var el = elms[j];
                    var fld = frm[el.getAttribute('name')];
                    if(el && fld.type == 'text') {
                        InitRequired(fld, el);
                        InitValidate(fld, el);
                        InitFill(fld, el);
                        InitSkip(fld, el);
                    }
                }
                frm.onsubmit = TestFormRequired;
            }
        }
    }
}

/**
  * Ova funkcija inicijalizira funkcionalnost validacije.
  *
  * @argument fld Referenca na <input> polje kojem se dodjeljuje funkcionalnost.
  * @argument el Referenca na element definicije validatora koji se odnosi na zadani element.
  */
function InitValidate(fld, el)
{
    fld.validate = el.getAttribute('validate');
    fld.datatype = el.getAttribute('datatype');
    if(fld.validate)
    {
        switch (fld.validate)
        {
            case 'change' :
                fld.onchange = ValidateField;
                break;
            case 'submit' :
                //fld.form.onsubmit = ValidateForm;
                //break;
            default :
        }
    }
    else
    {
        fld.valid = true;
    }
}

/**
  * Ova funkcija se poziva onSubmit i prolazi sva polja, te ih testira.
  */
function TestFormRequired()
{
    var a = 0;
    var inputs = this.getElementsByTagName('input');
    for(i=0;i<inputs.length;i++)
    {
        if(inputs[i].required)
        {
            if(!inputs[i].testRequired()) return false;
        }
    }
    return true;
}

/**
  * Ovo je kljucna funkcija za funkcionalnost validacije,
  * i ona provjerava odgovara li vrijednost polja njegovom datatypeu.
  */
function ValidateField()
{
    this.valid = true;
    switch (this.datatype)
    {
        // brojevi
        case 'n' :
        case 'num' :
        case 'number' :
        case 'numeric' :
            TestNumber(this);
            break;

        // datum
        case 'd' :
        case 'date' :
            TestDate(this);
            break;

        // vrijeme
        case 't' :
        case 'time' :
            alert(this.datatype);
            break;

        // jmbg
        case 'jmbg' :
            TestJmbg(this);
            break;

        // e-mail adresa
        case 'e' :
        case 'e-mail' :
        case 'email' :
            alert(this.datatype);
            break;

        // alfanumericki sadržaj
        case 'a' :
        case 'an' :
        case 'alpha' :
        case 'alphanum' :
        default :
            TestAlpha(this);
            break;
    }
}

/**
  * Ova se funkcija poziva kad validacija javi pogrešku.
  * Ona se brine o izbacivanju odgovarajuce poruke, te selektiranju vrijednosti polja.
  *
  * @argument field Referenca na polje cija vrijednost nije prošla validaciju.
  */
function AlertInvalid(field)
{
    var alertMessage = field.name + ': ' + MESSAGES.VALIDATION[field.datatype];
    alert(alertMessage);
    field.blur();
    field.focus();
    field.select();
    field.valid = false;
}

/**
  * Ova funkcija provjerava da li se u nekom nizu nalaze samo znamenke i decimalna tocka.
  * Napomena: U ovoj verziji se ipak može potkrasti neispravan unos,
  * ako se npr. stavi više tocaka ili zareza. To bi trebalo ispraviti.
  *
  * @argument field Referenca na polje cija vrijednost se evaluira.
  */
function TestNumber(field)
{
    var numberRegExp = /[^\d\.]/;
    field.value = field.value.replace(/,/, "."); // mijenja eventualni decimalni zarez u tocku
    if(field.value.search(numberRegExp)+1)
    {
        AlertInvalid(field);
    }
}

/**
  * Ova funkcija provjerava da li se u nekom nizu nalaze samo znamenke i decimalna tocka.
  *
  * @argument field Referenca na polje cija vrijednost se evaluira.
  */
function TestDate(field)
{
    var dateArray = field.value.split('.');
    var date = new Date(dateArray[2], dateArray[1]-1, dateArray[0]);
    if(isNaN(date))
    {
        AlertInvalid(field);
    }
}

/**
  * Ova funkcija provjerava ispravnost vremena.
  *
  * @argument field Referenca na polje cija vrijednost se evaluira.
  */
function TestAlpha(field)
{
    var alphaRegEx = /[^\wČčĆćÐdŠšŽž]/;
    if(field.value.search(alphaRegEx)+1)
    {
        AlertInvalid(field);
    }
}

/**
  * Ova funkcija provjerava ispravnost JMBG-a prema kontrolnoj znamenki.
  *
  * @argument field Referenca na polje cija vrijednost se evaluira.
  */
function TestJmbg(field)
{
    var jmbg = field.value;
    var control = new Array(7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2);
    var checkSum = 0;
    for(var i = 0; i < control.length; i++)
    {
        checkSum += control[i] * jmbg.charAt(i);
    }
    var controlDigit = 11 - (checkSum % 11);
    if(controlDigit == 11)
    {
        controlDigit = 0;
    }
    if(!(controlDigit == jmbg.charAt(jmbg.length-1)))
    {
        AlertInvalid(field);
    }
}

/**
  * Ova funkcija inicijalizira funkcionalnost obaveznog unosa.
  *
  * @argument fld Referenca na <input> polje kojem se dodjeljuje funkcionalnost.
  * @argument el Referenca na element definicije validatora koji se odnosi na zadani element.
  */
function InitRequired(fld, el)
{
    if(el.getAttribute('required'))
    {
        fld.required = el.getAttribute('required');
        fld.testRequired = TestRequired;
        //fld.onblur = TestRequired;
    }
}

/**
  * Ova funkcija provjerava da li je u polje upisana ikakva vrijednost, te
  * ako nije izbacuje upozorenje.
  */
function TestRequired()
{
    if(!this.value)
    {
        var reqAlertMessage = this.title ? this.title : this.name;
        switch(this.required)
        {
            case 'maybe' :
                reqAlertMessage += ': ' + MESSAGES.REQUIRED.MAYBE;
                if(!confirm(reqAlertMessage))
                {
                    this.focus();
                    this.select();
                    return false;
                }
                break;
            case 'yes' :
                reqAlertMessage += ': ' + MESSAGES.REQUIRED.YES;
                alert(reqAlertMessage);
                this.focus();
                this.select();
                return false;
        }
    }
    return true;
}

/**
  * Ova funkcija izvršava sve testove na zadanom polju.
  *
  * @argument field Referenca na polje koje se testira.
  */
function TestAll(field)
{
    TestRequired(field);
    TestDate(field);
    TestJmbg(field);
    TestAlpha(field);
    TestNumber(field);
}

/**
  * Ova funkcija inicijalizira Skip funkcionalnost.
  *
  * @argument fld Referenca na <input> polje kojem se dodjeljuje funkcionalnost.
  * @argument el Referenca na element definicije validatora koji se odnosi na zadani element.
  */
function InitSkip(fld, el)
{
    if(el.getAttribute('skip'))
    {
        fld.next = NextTabIndex;
        fld.onkeyup = Skip;
    }
}

/**
  * Ova funkcija definira Skip funkcionalnost.
  */
function Skip()
{
    if(this.value.length >= this.maxLength || this.value.length >= this.size)
    {
        this.next();
    }
}

/**
  * Ova funkcija definira metodu next() kojom polje šalje focus na iduce
  * polje u tabIndex redoslijedu.
  * Napomena: Zasad se podrazumijeva da polje ima definiran tabIndex, kao
  * i da iduce polje ima tabIndex veci za 1. U buducim verzijama trebalo bi
  * definirati da ako polje nema tabIndex ova metoda šalje focus na najmanji
  * tabIndex u formi, te da ako iduci tabIndex nije veci za 1 focus ide na
  * prvi veci tabIndex.
  */
function NextTabIndex()
{
    var nextIndex = this.tabIndex + 1;
    var formElements = this.form.elements;
    for(var i = 0; i < formElements.length; i++)
    {
        var el = formElements[i];
        if(el.tabIndex == nextIndex)
        {
            this.blur();
            if(this.valid)
            {
                el.focus();
                el.select();
            }
            return;
        }
    }
}

/**
  * Ova funkcija inicijalizira Fill funkcionalnost.
  *
  * @argument fld Referenca na <input> polje kojem se dodjeljuje funkcionalnost.
  * @argument el Referenca na element definicije validatora koji se odnosi na zadani element.
  */
function InitFill(fld, el)
{
    if(el.getAttribute('fill'))
    {
        fld.fill = el.getAttribute('fill');
        fld.onfocus = Fill;
    }
}

/**
  * Funkcija koja popunjava prazna polja odgovarajucim vrijednostima.
  */
function Fill()
{
    if(!this.value)
    {
        if(this.fill == 'now')
        {
            var now = new Date();
            switch (this.datatype)
            {
                // datum
                case 'd' :
                case 'date' :
                    var nowDate = now.getDate() < 10 ? '0' + now.getDate() : now.getDate();
                    var nowMonth = now.getMonth() < 9 ? '0' + (now.getMonth() + 1) : now.getMonth() + 1;
                    var nowString = nowDate + '.' + nowMonth + '.' + now.getFullYear();
                    this.value = nowString;
                    break;

                // vrijeme
                case 't' :
                case 'time' :
                    var nowHours = now.getHours() < 10 ? '0' + now.getHours() : now.getHours();
                    var nowMinutes = now.getMinutes() < 10 ? '0' + now.getMinutes() : now.getMinutes();
                    var nowString = now.getHours() + ':' + now.getMinutes();
                    this.value = nowString;
                    break;

                default : ;
            }
        }
        else if(this.fill.search(/\./)+1)
        {
            var origin = this.fill.split('.');
            this.value = document[origin[0]][origin[1]].value;
        }
    }
}

/**
  * Pomocna funkcija koja ucitava XML dokument s definicijom validacije.
  * Ova funkcija definira i XPath kao default jezik za pretraživanje.
  *
  * @argument url Adresa XML dokumenta.
  */
function LoadXmlDocument(url)
{
    var doc = new ActiveXObject('Microsoft.XmlDom');
    doc.async = false;
    doc.resolveExternals = true;
    doc.load(url);
    doc.setProperty('SelectionLanguage', 'XPath');
    doc.setProperty('SelectionNamespaces', VAL_NAMESPACE);
    if (doc.parseError.errorCode != 0)
    {
        alert("XML error:\n" + doc.parseError.reason);
        return null;
    }
    else
    {
        return doc;
    }
}

/**
  * Pomocna funkcija koja se poziva kad je potrebno formu submitati
  * pomocu submit() metode (npr. kad se koristi link umjesto gumba).
  * Forma se submita otprilike ovako: onclick="submitForm(document.myForm)";
  *
  * @argument form Forma (cijeli objekt) koja se treba submitati.
  */
function submitForm(form) {
    if(form.fireEvent('onsubmit')) form.submit();
}


/**
  * Pomocna funkcija koja prolazi sve atribute nekog objekta i vraca
  * tekst s ispisom naziva i vrijednosti svakog od njih.
  *
  * @argument object Objekt ciji se atributi zele vidjeti.
  * @argument objectName Naziv objekta (ili klase kojoj pripada).
  */
function GetObjectProperties(object, objectName)
{
   var result = '';
   for(var i in object)
   {
      result += objectName + '.' + i + ' = ' + object[i] + '\n';
   }
   return result;
}

window.onload = init;