(function($){
  /* Based on Alex Arnell's inheritance implementation for Prototype. */
  $.Class = function() {
    var parent = null;
    var length = arguments.length || 0, properties = new Array(length);
    while (length--) properties[length] = arguments[length];

    if (typeof properties[0] == "function")
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    $.extend(klass, $.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      var subclass = function() { };
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0; i < properties.length; i++)
        klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;

    return klass;
  };
  
  $.Methods = {
    wrap: function(__method, wrapper) {
      return function() {
        var length = arguments.length || 0, args = new Array(length);
        while (length--) args[length] = arguments[length];

        return wrapper.apply(this, [$.Methods.bind(__method, this)].concat(args));
      }
    },

    argumentNames: function(__method) {
      var names = (__method + '').match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",");
      names = $.map(names, function(b){
        return $.trim(b+'');
      })

      return names.length == 1 && !names[0] ? [] : names;
    },

    bind: function() {
      var __method = arguments[0];
      if (arguments.length < 3 && (typeof arguments[1] == "undefined")) return this;
      var length = arguments.length || 0, args = new Array(length);
      while (length--) args[length] = arguments[length];
      var object = args.shift();
      return function() {
        var flength = arguments.length || 0, fargs = new Array(flength);
        while (flength--) fargs[flength] = arguments[flength];          
        return __method.apply(object, args.concat(fargs));
      }
    },
    
    addMethods: function(source) {
      var ancestor   = this.superclass && this.superclass.prototype;

      if(ancestor && ancestor.$static) {
        if (typeof source.$static == 'undefined') source.$static = {}
        $.extend(source.$static, ancestor.$static)
      }

      var properties = [];
      for (var p in source)
        properties.push(p);
      
      for (var i = 0, length = properties.length; i < length; i++) {
        var property = properties[i], value = source[property];
        if(property == "$static" && typeof value == 'object') {
          var staticProperties = [];
          for (var sp in value)
            staticProperties.push(sp);
          for (var j = 0, length = staticProperties.length; j < length; j++) {
            var staticProperty = staticProperties[j], staticValue = value[staticProperty];
            if (ancestor && ancestor.$static && (typeof staticValue == "function") &&
              $.Methods.argumentNames(staticValue)[0] == "$super") {
              var method = staticValue, staticValue = $.Methods.wrap((function(m) {
                return function() { return ancestor.$static[m].apply(this, arguments) };
              })(staticProperty), method);
            }

            this[staticProperty] = staticValue;
          }
        }

        if (ancestor && (typeof value == "function") &&
          $.Methods.argumentNames(value)[0] == "$super") {
                
          var method = value, value = $.Methods.wrap((function(m) {
            return function() { return ancestor[m].apply(this, arguments) };
          })(property), method);        
        }

        this.prototype[property] = value;
      }

      return this;
    }  
  };
})(jQuery);
