
/*
 * MODULE - Class
 */
var g_bDontCallInitialize = false;
var Class =
{
    // Creators
    create: function (proto)
    {
        /* Accepting prototype definition of the class.  Idea presented by Greg
         * Wiley: http://wiki.script.aculo.us/scriptaculous/show/ExtendClass
         */
        var klass = function ()
        {
            if (this.initialize && arguments.callee.caller != Class.extend && !g_bDontCallInitialize )
            {	
            	 this.__class__ = arguments.callee.prototype;
                Object.extend(this, Class.Methods);
                this.initialize.apply(this, arguments);
            }
            g_bDontCallInitialize = false;
        };
        klass.prototype = proto || {};
        klass.extend = Class.extend;
        return klass;
    },
    singleton: function (proto)
    {
        var klass =
        {
            instance: function ()
            {
                if (!this.__instance__)
                {
                    var klass = Class.create(proto);
                    this.__instance__ = eval('new klass');
                    proto = null;
                }
                return this.__instance__;
            }
        };
        return klass;
    },

    // Care-takers
    append_features: function (object, module)
    {
        for (var prop in module)
            if (Class.kind_of(module[prop], Function))
                (function (method)
                {
                    object[method] = function ()
                    {
                        return module[method].apply(object, arguments);
                    };
                })(prop);
            else
                object[prop] = module[prop];
    },
    extend: function (subobj)   // implementation of inheritance
    {
        /* Create an instance of the superclass.  This is necessary to maintain
         * a prototype chain correctly.
         */
        g_bDontCallInitialize = true; //we have to prevent the constructor from calling initialize  
        var subproto = new this;
        // Update it with the subclass definitions
        Object.extend(subproto, subobj);
        subproto.__super__ = this.prototype;
        return Class.create(subproto);
    },
    get_method: function (caller, klass, args)  // obtain method name being called
    {
        //var c = args.callee.caller;   --- getting caller is depreceated!
        var c = caller;
        for (var method in klass)
        {
        	if (klass[method] == c)
              	return method;
        }
        return null;
    },
    call_super: function (superclass, self, method, args)
    {
        // NOTE: `self' should always refer to the object being instantiated
        if (superclass && superclass[method])
        {
            /* Make the next call of `this.SUPER()' in the superclass refer
             * farther up the class hierarchy */
            var __class__  = self.__class__;
            self.__class__ = superclass;
            self.__super__ = superclass.__super__;

            try
            {
                superclass[method].apply(self, args);
            }
            finally
            {
                // Restore values
                self.__class__ = __class__;
                self.__super__ = superclass;
            }
        }
    },
    kind_of: function (object, klass)
    {
//    		alert("klass"+klass);
        return eval('klass.prototype.isPrototypeOf(object)');
    }
};

/*
 * MODULE - Class instance methods
 * Mixed into class-instances, so that these methods will become available as
 * instance methods
 */
Class.Methods =
{
    extend: function ()
    {
        var i = arguments.length;
        while (--i >= 0)
            Object.extend(this, arguments[i]);
        return this;
    },
    include: function ()
    {
        var i = arguments.length;
        while (--i >= 0)
            Class.append_features(this, arguments[i]);
        return this;
    },
    kind_of: function (klass)
    {
        return Class.kind_of(this, klass);
    },
    /* getting the function caller is depreceated ... so we have to set it by hand */
    SUPERCALLER: function (caller)
    {
    		this.superCaller = caller;
    },
    
    SUPER: function (caller)
    {	
    	//TODO find a more speedy method to remove the caller from the arguments Array! arguments.slice(0,1) doesnt work!
    		args = new Array();
    		for( var i=1;i<arguments.length;i++)
    			args.push(arguments[i]);
    		
        var method = Class.get_method( caller, this.__class__, args);
        Class.call_super(this.__super__, this, method, args);
    }
};



/*
// 1. Inherit a class and call "super" 
var Superclass = Class.create(
{
    initialize: function ()
    {
        this.message = 'Hello';
        alert(this.message);
    },
    hello: function ()
    {
        alert("funhello");
    }
});
var Subclass = Superclass.extend(
{
    hello: function ()
    {
        this.message += ' from Subclass';
        this.SUPER();
    }
});
(new Subclass).hello(); // -> "Hello from Subclass" 

// 2. Use Mixin feature
var Module =
{
    super_useful_method: function ()
    {
        //
    }
};
var Klass = Class.create(
{
    initialize: function ()
    {
        this.include(Module);
    },
    operate: function ()
    {
        this.super_useful_method();   // call it just as its own method
    }
});
(new Klass).operate();

// 3. Creating singleton class
var Singleton = Class.singleton(
{
    initialize: function ()
    {
        // Just define methods ordinarily
    }
});

//"new Singleton" causes error
var s1 = Singleton.instance();
var s2 = Singleton.instance();
alert(s1 == s2);  // -> true
*/
