/* Self v1.0.0 https://github.com/munro/self * https://github.com/munro/self/blob/master/LICENSE */ /*jslint browser: true, nomen: true, forin: true */ var Self = (function () { 'use strict'; var makeClass, objectCreate; // Base class, and convenience function for extending the Base function Self() { return Self.extend.apply(Self, arguments); } Self.VERSION = '1.0.0'; // Create a new object based on the old one // http://javascript.crockford.com/prototypal.html if (typeof Object.create === 'function') { objectCreate = Object.create; } else { objectCreate = function (o) { function F() {} F.prototype = o; return new F(); }; } // Wrap an existing method so that it unshifts self onto the arguments function wrapMethodWithSelf(fn) { return function () { // Push `this` in front of arguments before calling var args = Array.prototype.slice.call(arguments, 0); args.unshift(this); return fn.apply(this, args); }; } // Return a function to extend a class function makeExtendMethod(Class) { return function (def) { return makeClass(Class, def); }; } // Copies another object's prototype into the Class, skipping any properties // that already exists, or are from `Object.prototype`. function makeMixinMethod(Class) { return function (Mixin) { var key; for (key in Mixin) { if (Mixin.hasOwnProperty(key) && key !== 'prototype' && key !== '__super__' && key !== 'extend' && key !== 'mixin' && key !== 'staticProps') { Class[key] = Mixin[key]; } } for (key in Mixin.prototype) { if (typeof Class.prototype[key] === 'undefined' && !Object.hasOwnProperty(key)) { Class.prototype[key] = Mixin.prototype[key]; } } return Class; }; } function makeStaticPropsMethod(Class) { return function (def) { var key; def = def || {}; for (key in def) { if (def.hasOwnProperty(key)) { Class[key] = def[key]; } } return Class; }; } // Create a prototype object from a Parent class, and a class definition. // In the created prototype, all class methods will be wrapped to // automatically insert the `self` variable when called. makeClass = function (Parent, def) { var key; function Class() { var obj; // Call the Mixin constructor if (this && this.__class__) { Class.prototype.constructor.apply(this, arguments); return this; } // Create new object if the `new` keyword was not used. Check // against `global` for Node.js, and `window` for browser side // JavaScript. if (this instanceof Class) { obj = this; } else { obj = objectCreate(Class.prototype); } obj.__class__ = Class; obj.__super__ = Parent.prototype; // Call the constructor if (typeof obj.constructor === 'function') { obj.constructor.apply(obj, arguments); } // Return the constructed object if `new` keyword was not used. return obj; } // Inherit static properties for (key in Parent) { if (Parent.hasOwnProperty(key) && Parent[key] !== Self[key]) { Class[key] = Parent[key]; } } // Use differential inheritance Class.prototype = objectCreate(Parent.prototype); // Helper property & methods Class.__super__ = Parent.prototype; Class.extend = makeExtendMethod(Class); Class.mixin = makeMixinMethod(Class); Class.staticProps = makeStaticPropsMethod(Class); // Copy class definition into prototype for (key in def) { if (!Object.hasOwnProperty(key)) { if (typeof def[key] === 'function') { Class.prototype[key] = wrapMethodWithSelf(def[key]); } else { Class.prototype[key] = def[key]; } } } return Class; }; // Manually setup the base class Self.__super__ = Object.prototype; // For the sake of convenience, allow the Self.extend to instead directly // inherit from a prototype, instead of the Self class itself. Self.extend = function (arg1, arg2) { // `arg1` is the class definition if (typeof arg2 === 'undefined') { return makeClass(Self, arg1); } // Extend prototype, `arg1` is the prototype, `arg2` is the class definition return makeClass(Self.create(arg1), arg2); }; // I don't recommend mixing into the base class! But for the sake of // consistency... :) Self.mixin = makeMixinMethod(Self); // Create a new class that directly inherits from a prototype. Self.create = function (Proto) { var key; function Class() { var obj; // Create a new object if the `new` keyword was not used. We can // detected this by checking to see if the context of this function // is an instance of itself (the constructor). if (this instanceof Class || (this && this.__class__)) { obj = this; } else { obj = objectCreate(Class.prototype); } // Call the Prototypal constructor Proto.apply(obj, arguments); // Return the constructed object if `new` keyword was not used. return obj; } // Inherit static properties for (key in Proto) { if (Proto.hasOwnProperty(key)) { Class[key] = Proto[key]; } } Class.__super__ = Object.prototype; Class.extend = makeExtendMethod(Class); Class.mixin = makeMixinMethod(Class); Class.staticProps = makeStaticPropsMethod(Class); Class.prototype = objectCreate(Proto.prototype); return Class; }; return Self; }()); // Export as a module for Node.js if (typeof module !== 'undefined') { module.exports = Self; }