/**
 *$Id: form.js 125 2009-03-05 10:57:55Z aanpilogov $
 *
 */
PuskFramework.form = new function()
{
    var pf = PuskFramework;

    // form.elements не возвращает input.type == 'image'
    var tags = {
        'input':[],
        'button':[],
        'textarea':[],
        'select':[]
    };

    var eachFormElement = function(theform, callback)
    {                
        for (var tag in tags)
        {
            tags[tag] = theform.getElementsByTagName(tag);
            for (var i = 0, l = tags[tag].length; i < l; i++)
            {
                callback(tag, i);
            }
        }
    };

    /**
     * Возвращает набор значений элементов формы
     * @param {Object} theform форма
     * @return {Object}
     */
    this.getValues = function(theform)
    {        
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.getValues(): ' + 'typeof(theform) argument is ' + pf.$type(theform) + ', "element" expected');
        var els = theform.elements;
        if ((pf.$type(els) != "collection") && (pf.$type(els) != "element")) throw pf.$exGen('pf.form.getValues(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" or "element" expected');
     
        var ename, value, values = {}; 
        for (var i = 0, l = els.length; i < l; i++)
        {
            if (!(ename = els[i].name)) continue;
            value = pf.form.getElementValue(els[i]);
            if (value === null) continue;

            if (!values[ename])
            {
                values[ename] = value;
                continue;
            }

            if (typeof(values[ename]) == 'string')
            {
                values[ename] = [values[ename]];
            }
            
            if (typeof(value) == 'string')
            {
                values[ename].push(value);
            } 
            else 
            {
                values[ename] = values[ename].concat(value);
            }
        }
        return values;
    };

    /**
     * Сериализует форму в GET строку
     *
     * @param {Object} theform форма
     * @return {String}
     */
    this.serialize = function(theform)
    {                
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.serialize(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        var els = theform.elements;
        if ((pf.$type(els) != "collection") && (pf.$type(els) != "element")) throw pf.$exGen('pf.form.serialize(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" expected');
        
        var ename, value, values = [];
        for (var i = 0, l = els.length; i < l; i++)
        {
            if (!(ename = els[i].name)) continue;
            value = pf.form.getElementValue(els[i]);
            if (value !== null)
            {
                if (typeof(value) == 'string')
                {
                    values.push(ename + '=' + encodeURIComponent(value));
                } else {
                    for (var j = 0, l2 = value.length; j < l2; j++) {
                        values.push(ename + '=' + encodeURIComponent(value[j]));
                    }
                }
            }
        }
        return values.join('&');
    };

    /**
     * Разблокирует все элементы формы
     *
     * @param {Object} theform форма
     */
    this.enable = function(theform)
    {
        
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.enable(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        if ((pf.$type(theform.elements) != "collection") && (pf.$type(theform.elements) != "element")) throw pf.$exGen('pf.form.enable(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" or "element" expected');
        eachFormElement(theform, function(tag, j){tags[tag][j].removeAttribute('disabled')});
        return true;
    };

    /**
     * Блокирует элементы формы
     *
     * @param {Object} theform форма
     */
    this.disable = function(theform)
    {        
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.disable(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        if ((pf.$type(theform.elements) != "collection") && (pf.$type(theform.elements) != "element")) throw pf.$exGen('pf.form.disable(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" or "element" expected');
        eachFormElement(theform, function(tag, j){tags[tag][j].setAttribute('disabled', 'true')});
        return true;
    };

    /**
     * Валидация формы согласно служебным атрибутам в элементах
     * validate = "mail|url|date|integer|float"
     * required = "Y|required"
     * mask = regexp
     * minVal, maxVal
     * проверяет совпадение значений ELEMENT_NAME и ELEMENT_NAME_copy
     * не проверяет select-multiple
     *
     * выставляет ошибочным элементам css-класс errorInput
     *
     * @param {Object} theform форма
     * @return {Bool}
     */
    this.validate = function(theform)
    {
        if (pf.$type(theform) == "string") theform = pf.$(theform);
        if (pf.$type(theform) != "element") throw pf.$exGen('pf.form.validate(): ' + 'typeof(theform) is ' + pf.$type(theform) + ', "element" expected');
        if ((pf.$type(theform.elements) != "collection") && (pf.$type(theform.elements) != "element")) throw pf.$exGen('pf.form.validate(): ' + 'typeof(theform.elements) is ' + pf.$type(theform.elements) + ', "collection" expected');
        
        var values = {};
        for (var i = 0, l = theform.elements.length; i < l; i++)
        {
            var elem = theform.elements[i];
            if (elem.disabled || (elem.type && elem.type.toLowerCase() == 'select-multiple')) { continue; }
            var value = pf.form.getElementValue(elem);
            values[elem.name] = value;
            
            /* Проверка обязательных для заполнения полей
            ======================================== */
            if ((elem.getAttribute('required')=="Y" || elem.getAttribute('required')=="required") && (value.trim() == ''))
            {
                return pf.form._markError(elem);
            }

            /* Проверка формата поля
            ======================================== */
            var validate = elem.getAttribute('validate');
            if (validate && (value != '' && value != elem.getAttribute('default')))
            {
                if (!check(value, validate))
                {
                    return pf.form._markError(elem);
                }
            }

            /* Проверка на минимальное и максимальное значение
            ======================================== */
            var minVal = elem.getAttribute('minVal');
            var maxVal = elem.getAttribute('maxVal');
            var maxminVal = minVal & maxVal;
            if (minVal)
                minVal = minVal > parseInt(value); // теперь переменная в качестве флага
            if (maxVal)
                maxVal = parseInt(value) > maxVal; // теперь переменная в качестве флага
            if (((minVal || maxVal) && !maxminVal) || ((minVal || maxVal) && maxminVal))
            {
                return pf.form._markError(elem);
            }

            /* Проверка на соответствие маске
            ======================================== */
            var mask = elem.getAttribute('mask');
            if (mask && value != '')
            {
                try {
                    var reg = new RegExp('^'+mask+'$');
                    mask = reg.test(value);
                    if (!mask)
                    {
                        return pf.form._markError(elem);
                    }
                } catch(e){}
            }
            
            pf.form._unmarkError(elem);
        }
        for (var prop in values)
        {
            if (values[prop+'_copy'] && (values[prop+'_copy'] != values[prop]))
            {
                return pf.form._markError(values[prop+'_copy']);
            }
        }
        return true;

        function check(val, type)
        {
            var regs = {
                'mail': /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
                /*  dd-mm-yyyy    d-m-yy
                    dd mm yyyy    d m yy
                    dd/mm/yyyy    d/m/yy
                    dd.mm.yyyy    d.m.yy */
                'date': /\b(0?[1-9]|[12][0-9]|3[01])[- /.](0?[1-9]|1[012])[- /.](19|20)?[0-9]{2}\b/,
                'integer':/^-?[0-9]+$/,
                'float':  /^-?[0-9]+(\.[0-9]+|)$/,
                'url':/^(?:https?:\/\/|ftp:\/\/|)[-A-Z0-9.]+\.[-A-Z0-9]{2,6}(?:\/[-A-Z0-9+&@#\/%=~_|!:,.;]*|)(\?[-A-Z0-9+&@#/%=~_|!:,.;]*|)$/i
            }
            if (!regs[type]) return true;
            return regs[type].test(val);
        }
    };

    this._markError = function(elem)
    {
        pf.elem.addClass(elem, 'errorInput');
        try {elem.focus();} catch(e){};
        return false;
    };
    this._unmarkError = function(elem)
    {
        if (pf.elem.hasClass(elem, 'errorInput'))
            pf.elem.delClass(elem, 'errorInput');
        return true;
    };
    
    /**
     * Возвращает значение элемента
     * @param {Object} element элемент формы
     * @return {String|Array}
     */
    this.getElementValue = function(element)
    {
        var value = null;
        if (!element.disabled && element.type) switch (element.type.toLowerCase())
        {
            case 'checkbox':
            case 'radio':
                if (!element.checked) break;
            case 'hidden':
            case 'password':
            case 'search':
            case 'text':
            case 'textarea':
                value = element.value;
                break;
            case 'select-one':
                if (element.selectedIndex >= 0)
                {
                    value = element.options[element.selectedIndex].value;
                }
                break;
            case 'select-multiple':
                value = [];
                for (var i = 0, l = element.options.length; i < l; i++) 
                {
                    if (element.options[i].selected) value.push(element.options[i].value);
                }
                break;
        }
        return value;
    };
};

