$.fn.form || (function($)
{
    function FirstToUpper(value) {
       return value.substr(0, 1).toUpperCase() + value.substr(1);
    }

    function makeSubmit()
    {
        var $this = this;
        var data = this.data('form');

        if (data.progress)
            return;

        this.form('setErrors');

        data.progress = true;
        data.showProgress();

        var form = this.serialize();

        jQuery.ajax($.extend({
            type: "POST",
            url:data.url,
            data: form,
            success: function(res)
            {
                data.hideProgress();
                data.progress = false;

                if (res==null)
                {
                    $this.form('setErrors', {global:data.networkErrorMessage});
                }
                else if (res.errors)
                {
                    $this.form('setErrors', res.errors);
                }
                else
                {
                    data.success(res);
                }
            },
            error: function()
            {
                data.progress = false;
                data.hideProgress();
                $this.form('setErrors', {global: data.networkErrorMessage});
            },
            dataType: 'json'
        }, data.ajax));
    }

    var methods = {
        init : function( options ) {return this.each(function(){
            var $this = $(this);
            var data = $this.data('form');

            if (!$this.is("form"))
                $.error("jQuery.form support only form tags");

            var id = $this.attr("id") || "";
            var prefix = id.replace(/Form$/, "");

            var settings = {
                'prefix'         : prefix,
                'showProgress': function(){
                    var bar = $("#"+prefix+"Progress");
                    if (bar.length>0)
                        bar.show();
                    else if (WaitDialog)
                        WaitDialog.show();
                },
                'hideProgress': function(){
                    var bar = $("#"+prefix+"Progress");
                    if (bar.length>0)
                        bar.hide();
                    else if (WaitDialog)
                        WaitDialog.hide();
                },
                'ajax':{},
                'url':"",
                "progress":false,
                "errorClass":"",
                "defaultValues":{},
                'success':function(res){},
                'networkErrorMessage':"Network error. Try again later.",
                'items':{}
            };

            $.each($('#'+id+'>[name]'), function(){
                var id1 = $(this).attr('id');
                if (id1) settings.items[id1] = $(this);
            });

            $.each(settings.items, function(k,v){
                settings.defaultValues[k] = v.val();
            });

            if ( data )
                $.extend( settings, data );

            if ( options )
                $.extend( settings, options );

            $this.data('form', settings);

            $this.bind('submit.form', {}, function(event){
                $this.form('submit');
                return false;
            });
        })},
        destroy : function( ) {return this.each(function(){
            var $this = $(this);
            //var data = this.data('form');

            $this.unbind('.form');
            $this.removeData('form');
            return $this;
        })},
        reset: function () {return this.each(function(){
            var $this = $(this);
            $this.form('setErrors');
            $this.form('setValues', $this.data('form').defaultValues);
        })},
        setErrors: function (errors) {return this.each(function(){
            var $this = $(this);
            var data = $this.data('form');

            var id = $this.attr("id");

            $('[id$=Error][id^='+data.prefix+']').html("");
            $('#'+id+'>[name]').removeClass(data.errorClass);
            if (!errors) return;
            if (errors.global)
            {
                if (!$.isArray(errors.global)) errors.global = [errors.global];

                var str = "";
                $.each(errors.global, function(k,v){
                    str= str+(str==""?"":"<br>")+v;
                });
                $('#'+data.prefix+"Error").html(str);
            }
            if (errors.item)
            {
                $.each(errors.item, function(k, v){
                    $('#'+id+'>[id='+k+']').addClass(data.errorClass);
                    $('#'+data.prefix+FirstToUpper(k)+"Error").html(v);
                });
            }
        })},
        setValues: function (val) {return this.each(function(){
            if(!val) return;
            
            var $this = $(this);
            var data = $this.data('form');

            $.each(val, function(k, v){
                if (data.items[k])
                    data.items[k].val(v);
            });
        })},
        getValues: function () {
            if(this.length==0) return {};
            if(this.length==1) $this = this; else $this=$(this[0]);

            var data = $this.data('form');
            var val={};

            $.each(data.items, function(k, v){
                val[k]=v.val();
            });
            return val;
        },
        values: function(val)
        {
            if (val==undefined)
                return this.form('getValues');
            else
                return this.form('setValues', val);
        },
        submit:function() {return this.each(function(){
            var $this = $(this);
            makeSubmit.apply( $this, arguments);
        })},
        wrapDialog:function (dialog, options) {return this.each(function(){
            var $this = $(this);
            var data = $this.data('form');

            data.dialogOptions = {
                autoOpen: false, 
                modal:true,
                width:"400px",
                buttons: {
                  submit:{
                    text:'Submit',
                    click: function() {
                      $this.form('submit');
                    }
                  },
                  cancel:{
                    text:"Cancel",
                    click: function() {
                      if ($this.form('progress'))
                        return;
                      $( this ).dialog( "close" );
                    }
                  }
                },
                beforeClose: function() {return !$this.form('progress');},
                open: function(){$this.form('reset');}
            };

            data.dialog ={
              $dialog: $('#'+data.prefix+"Dialog")
            };
            
            if (dialog)
                $.extend(data.dialog, dialog);

            if ( options )
                $.extend(true, data.dialogOptions, options );
                
            data.dialog.$dialog.dialog(data.dialogOptions);
        })},
        progress:function(value)
        {
            if (value!==undefined) {return this.each(function()
            {
                var data = $(this).data('form');
                data.progress = value;
                if (value)
                    data.showProgress();
                else
                    data.hideProgress();
            })}
            else
                return this.data('form').progress==true;
        }
    };

    $.fn.form = function(method)
    {
        var args = arguments;

        if ( methods[method] ) {
            return methods[ method ].apply(this, Array.prototype.slice.call( args, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
            return methods.init.apply(this, args );
        } else {
            $.error( 'Method ' +  method + ' does not exist on jQuery.form' );
        }

    };

})(jQuery);
