(function($) {
  var liveForm = {
    form : null,
//    field : null,
    listener : null,
    conditions : {
      'value' : function(element, params) {
        var matched = false;
        if (params.value.constructor == Array)
        {
          var val = element.val();
          for (var i = 0; i < params.value.length; i++)
          {
            matched = val == params.value[i];
//            alert(val + ' = ' + params.value[i]);
            if (matched)
            {
              break;
            }
          }
        }
        else
        {
          matched = element.val() == params.value;
        }

        return matched;
      },

      'checked' : function(element, params) {
        return element.filter('*[value=' + params.value + ']:checked').size() > 0;
      }
    },
    actions : {
      'show' : function(form, element, params, matched, initial) {
        element = params.selector ? form.find(params.selector) : element;

        if (matched) {
          element.show();
//          initial ? element.show() : element.slideDown();
        } else {
          element.hide();
//          initial ? element.hide() : element.slideUp();
        }
      },

      'hide' : function(form, element, params, matched, initial) {
        element = params.selector ? form.find(params.selector) : element;

        if (!matched) {
          element.show();
//          initial ? element.show() : element.slideDown();
        } else {
          element.hide();
//          initial ? element.hide() : element.slideUp();
        }
      },

      'enable' : function(form, element, params, matched, initial) {
        element = params.selector ? form.find(params.selector) : element;

        element.prop('disabled', !matched);
      },

      'disable' : function(form, element, params, matched, initial) {
        element = params.selector ? form.find(params.selector) : element;

        if (!matched) {
          element.attr('disabled', '');
        } else {
          element.attr('disabled', 'disabled');
        }
      }
    }
  };

  //this will allow selectors like 'input[value=foo]' to work with all jQuery versions
  $('input,textarea').live('keyup change', function(e) {
    var ignoreCodes = [33,34,36,35,45,38,40,37,39]; //arrows and others
    if (e.keyCode && $.inArray(e.keyCode, ignoreCodes) < 0) { //ignore this keyUps to let this keys work as expected
      jQuery(this).attr('value',this.value);
    }
  });

  $.fn.liveform = function(options) {
    var self = this;
    var defaults = {
      // 'listeners'
    };
    var settings = $.extend(false, defaults, options);


    var forms = [];
    $(this).each(function() {
      var form = new liveForm.form(this, settings);
      forms.push(form);
    });

    return forms;
  }

  with (liveForm.form = function(element, settings) {
    this.element  = element;
    this.settings = settings;
    this.listeners = [];

    this.initListeners();
  }) {
    prototype = {
      element : false,
      settings : false,
      listeners : false,

      initListeners : function() {
        var listenersConfigSet = this.settings.listeners || {},
            listenersConfig;
        for (var selector in listenersConfigSet) {
          if (Array == listenersConfigSet[selector].constructor) {
            for (var i = 0; i < listenersConfigSet[selector].length; i++) {
              this.listeners.push(this.createListener(selector, listenersConfigSet[selector][i]))
            }
          } else {
            listenersConfig = listenersConfigSet;
            this.listeners.push(this.createListener(selector, listenersConfig[selector]));
          }
        }
      },

      createListener : function(selector, options) {
        return new liveForm.listener($(this.element), selector, options);
      }
    };
  };

  with (liveForm.listener = function(form, selector, settings) {
    this.init(form, selector, settings);

    this.bindListeners();

    // forcing initial check
    this._onTimeout.call(this.$element, this, true);
  }) {
    prototype = {
      form : false,
      $element : false,
      selector : false,
      condition : false,
      action : false,
      timeout : false,
//      settings : false,

      init : function(form, selector, settings) {
        this.selector  = selector;
        this.$form     = form;
        this.$element  = $(selector);
        this.condition = settings.condition;
        this.action    = settings.action;
        this.settings  = settings;
      },

      bindListeners : function() {
        var self = this;
        this.$element.bind('keyup', function() {
          var el = this;

          clearTimeout(this.timeout);
          this.timeout = setTimeout(function() {
            self._onTimeout.call(el, self);
          }, 300);
        });

        this.$element.bind('change', function() {
          self._onTimeout.call(this, self);
        });
      },

      _onTimeout : function(listenerInst, initial) {
        initial = initial || false;

        if (liveForm.conditions[listenerInst.condition.name] != undefined
          && liveForm.actions[listenerInst.action.name] != undefined) {

          liveForm.actions[listenerInst.action.name].call(listenerInst,
            listenerInst.$form,
            listenerInst.$element,
            listenerInst.action.params,

            liveForm.conditions[listenerInst.condition.name].call(listenerInst,
              listenerInst.$element,
              listenerInst.condition.params
            ),

            initial
          );
        }
      }
    };
  };
})(jQuery);
