(function () { /** * @projectName declare * @github http://github.com/doug-martin/declare.js * @header * * Declare is a library designed to allow writing object oriented code the same way in both the browser and node.js. * * ##Installation * * `npm install declare.js` * * Or [download the source](https://raw.github.com/doug-martin/declare.js/master/declare.js) ([minified](https://raw.github.com/doug-martin/declare.js/master/declare-min.js)) * * ###Requirejs * * To use with requirejs place the `declare` source in the root scripts directory * * ``` * * define(["declare"], function(declare){ * return declare({ * instance : { * hello : function(){ * return "world"; * } * } * }); * }); * * ``` * * * ##Usage * * declare.js provides * * Class methods * * * `as(module | object, name)` : exports the object to module or the object with the name * * `mixin(mixin)` : mixes in an object but does not inherit directly from the object. **Note** this does not return a new class but changes the original class. * * `extend(proto)` : extend a class with the given properties. A shortcut to `declare(Super, {})`; * * Instance methods * * * `_super(arguments)`: calls the super of the current method, you can pass in either the argments object or an array with arguments you want passed to super * * `_getSuper()`: returns a this methods direct super. * * `_static` : use to reference class properties and methods. * * `get(prop)` : gets a property invoking the getter if it exists otherwise it just returns the named property on the object. * * `set(prop, val)` : sets a property invoking the setter if it exists otherwise it just sets the named property on the object. * * * ###Declaring a new Class * * Creating a new class with declare is easy! * * ``` * * var Mammal = declare({ * //define your instance methods and properties * instance : { * * //will be called whenever a new instance is created * constructor: function(options) { * options = options || {}; * this._super(arguments); * this._type = options.type || "mammal"; * }, * * speak : function() { * return "A mammal of type " + this._type + " sounds like"; * }, * * //Define your getters * getters : { * * //can be accessed by using the get method. (mammal.get("type")) * type : function() { * return this._type; * } * }, * * //Define your setters * setters : { * * //can be accessed by using the set method. (mammal.set("type", "mammalType")) * type : function(t) { * this._type = t; * } * } * }, * * //Define your static methods * static : { * * //Mammal.soundOff(); //"Im a mammal!!" * soundOff : function() { * return "Im a mammal!!"; * } * } * }); * * * ``` * * You can use Mammal just like you would any other class. * * ``` * Mammal.soundOff("Im a mammal!!"); * * var myMammal = new Mammal({type : "mymammal"}); * myMammal.speak(); // "A mammal of type mymammal sounds like" * myMammal.get("type"); //"mymammal" * myMammal.set("type", "mammal"); * myMammal.get("type"); //"mammal" * * * ``` * * ###Extending a class * * If you want to just extend a single class use the .extend method. * * ``` * * var Wolf = Mammal.extend({ * * //define your instance method * instance: { * * //You can override super constructors just be sure to call `_super` * constructor: function(options) { * options = options || {}; * this._super(arguments); //call our super constructor. * this._sound = "growl"; * this._color = options.color || "grey"; * }, * * //override Mammals `speak` method by appending our own data to it. * speak : function() { * return this._super(arguments) + " a " + this._sound; * }, * * //add new getters for sound and color * getters : { * * //new Wolf().get("type") * //notice color is read only as we did not define a setter * color : function() { * return this._color; * }, * * //new Wolf().get("sound") * sound : function() { * return this._sound; * } * }, * * setters : { * * //new Wolf().set("sound", "howl") * sound : function(s) { * this._sound = s; * } * } * * }, * * static : { * * //You can override super static methods also! And you can still use _super * soundOff : function() { * //You can even call super in your statics!!! * //should return "I'm a mammal!! that growls" * return this._super(arguments) + " that growls"; * } * } * }); * * Wolf.soundOff(); //Im a mammal!! that growls * * var myWolf = new Wolf(); * myWolf instanceof Mammal //true * myWolf instanceof Wolf //true * * ``` * * You can also extend a class by using the declare method and just pass in the super class. * * ``` * //Typical hierarchical inheritance * // Mammal->Wolf->Dog * var Dog = declare(Wolf, { * instance: { * constructor: function(options) { * options = options || {}; * this._super(arguments); * //override Wolfs initialization of sound to woof. * this._sound = "woof"; * * }, * * speak : function() { * //Should return "A mammal of type mammal sounds like a growl thats domesticated" * return this._super(arguments) + " thats domesticated"; * } * }, * * static : { * soundOff : function() { * //should return "I'm a mammal!! that growls but now barks" * return this._super(arguments) + " but now barks"; * } * } * }); * * Dog.soundOff(); //Im a mammal!! that growls but now barks * * var myDog = new Dog(); * myDog instanceof Mammal //true * myDog instanceof Wolf //true * myDog instanceof Dog //true * * * //Notice you still get the extend method. * * // Mammal->Wolf->Dog->Breed * var Breed = Dog.extend({ * instance: { * * //initialize outside of constructor * _pitch : "high", * * constructor: function(options) { * options = options || {}; * this._super(arguments); * this.breed = options.breed || "lab"; * }, * * speak : function() { * //Should return "A mammal of type mammal sounds like a * //growl thats domesticated with a high pitch!" * return this._super(arguments) + " with a " + this._pitch + " pitch!"; * }, * * getters : { * pitch : function() { * return this._pitch; * } * } * }, * * static : { * soundOff : function() { * //should return "I'M A MAMMAL!! THAT GROWLS BUT NOW BARKS!" * return this._super(arguments).toUpperCase() + "!"; * } * } * }); * * * Breed.soundOff()//"IM A MAMMAL!! THAT GROWLS BUT NOW BARKS!" * * var myBreed = new Breed({color : "gold", type : "lab"}), * myBreed instanceof Dog //true * myBreed instanceof Wolf //true * myBreed instanceof Mammal //true * myBreed.speak() //"A mammal of type lab sounds like a woof thats domesticated with a high pitch!" * myBreed.get("type") //"lab" * myBreed.get("color") //"gold" * myBreed.get("sound")" //"woof" * ``` * * ###Multiple Inheritance / Mixins * * declare also allows the use of multiple super classes. * This is useful if you have generic classes that provide functionality but shouldnt be used on their own. * * Lets declare a mixin that allows us to watch for property changes. * * ``` * //Notice that we set up the functions outside of declare because we can reuse them * * function _set(prop, val) { * //get the old value * var oldVal = this.get(prop); * //call super to actually set the property * var ret = this._super(arguments); * //call our handlers * this.__callHandlers(prop, oldVal, val); * return ret; * } * * function _callHandlers(prop, oldVal, newVal) { * //get our handlers for the property * var handlers = this.__watchers[prop], l; * //if the handlers exist and their length does not equal 0 then we call loop through them * if (handlers && (l = handlers.length) !== 0) { * for (var i = 0; i < l; i++) { * //call the handler * handlers[i].call(null, prop, oldVal, newVal); * } * } * } * * * //the watch function * function _watch(prop, handler) { * if ("function" !== typeof handler) { * //if its not a function then its an invalid handler * throw new TypeError("Invalid handler."); * } * if (!this.__watchers[prop]) { * //create the watchers if it doesnt exist * this.__watchers[prop] = [handler]; * } else { * //otherwise just add it to the handlers array * this.__watchers[prop].push(handler); * } * } * * function _unwatch(prop, handler) { * if ("function" !== typeof handler) { * throw new TypeError("Invalid handler."); * } * var handlers = this.__watchers[prop], index; * if (handlers && (index = handlers.indexOf(handler)) !== -1) { * //remove the handler if it is found * handlers.splice(index, 1); * } * } * * declare({ * instance:{ * constructor:function () { * this._super(arguments); * //set up our watchers * this.__watchers = {}; * }, * * //override the default set function so we can watch values * "set":_set, * //set up our callhandlers function * __callHandlers:_callHandlers, * //add the watch function * watch:_watch, * //add the unwatch function * unwatch:_unwatch * }, * * "static":{ * * init:function () { * this._super(arguments); * this.__watchers = {}; * }, * //override the default set function so we can watch values * "set":_set, * //set our callHandlers function * __callHandlers:_callHandlers, * //add the watch * watch:_watch, * //add the unwatch function * unwatch:_unwatch * } * }) * * ``` * * Now lets use the mixin * * ``` * var WatchDog = declare([Dog, WatchMixin]); * * var watchDog = new WatchDog(); * //create our handler * function watch(id, oldVal, newVal) { * console.log("watchdog's %s was %s, now %s", id, oldVal, newVal); * } * * //watch for property changes * watchDog.watch("type", watch); * watchDog.watch("color", watch); * watchDog.watch("sound", watch); * * //now set the properties each handler will be called * watchDog.set("type", "newDog"); * watchDog.set("color", "newColor"); * watchDog.set("sound", "newSound"); * * * //unwatch the property changes * watchDog.unwatch("type", watch); * watchDog.unwatch("color", watch); * watchDog.unwatch("sound", watch); * * //no handlers will be called this time * watchDog.set("type", "newDog"); * watchDog.set("color", "newColor"); * watchDog.set("sound", "newSound"); * * * ``` * * ###Accessing static methods and properties witin an instance. * * To access static properties on an instance use the `_static` property which is a reference to your constructor. * * For example if your in your constructor and you want to have configurable default values. * * ``` * consturctor : function constructor(opts){ * this.opts = opts || {}; * this._type = opts.type || this._static.DEFAULT_TYPE; * } * ``` * * * * ###Creating a new instance of within an instance. * * Often times you want to create a new instance of an object within an instance. If your subclassed however you cannot return a new instance of the parent class as it will not be the right sub class. `declare` provides a way around this by setting the `_static` property on each isntance of the class. * * Lets add a reproduce method `Mammal` * * ``` * reproduce : function(options){ * return new this._static(options); * } * ``` * * Now in each subclass you can call reproduce and get the proper type. * * ``` * var myDog = new Dog(); * var myDogsChild = myDog.reproduce(); * * myDogsChild instanceof Dog; //true * ``` * * ###Using the `as` * * `declare` also provides an `as` method which allows you to add your class to an object or if your using node.js you can pass in `module` and the class will be exported as the module. * * ``` * var animals = {}; * * Mammal.as(animals, "Dog"); * Wolf.as(animals, "Wolf"); * Dog.as(animals, "Dog"); * Breed.as(animals, "Breed"); * * var myDog = new animals.Dog(); * * ``` * * Or in node * * ``` * Mammal.as(exports, "Dog"); * Wolf.as(exports, "Wolf"); * Dog.as(exports, "Dog"); * Breed.as(exports, "Breed"); * * ``` * * To export a class as the `module` in node * * ``` * Mammal.as(module); * ``` * * */ function createDeclared() { var arraySlice = Array.prototype.slice, classCounter = 0, Base, forceNew = new Function(); var SUPER_REGEXP = /(super)/g; function argsToArray(args, slice) { slice = slice || 0; return arraySlice.call(args, slice); } function isArray(obj) { return Object.prototype.toString.call(obj) === "[object Array]"; } function isObject(obj) { var undef; return obj !== null && obj !== undef && typeof obj === "object"; } function isHash(obj) { var ret = isObject(obj); return ret && obj.constructor === Object; } var isArguments = function _isArguments(object) { return Object.prototype.toString.call(object) === '[object Arguments]'; }; if (!isArguments(arguments)) { isArguments = function _isArguments(obj) { return !!(obj && obj.hasOwnProperty("callee")); }; } function indexOf(arr, item) { if (arr && arr.length) { for (var i = 0, l = arr.length; i < l; i++) { if (arr[i] === item) { return i; } } } return -1; } function merge(target, source, exclude) { var name, s; for (name in source) { if (source.hasOwnProperty(name) && indexOf(exclude, name) === -1) { s = source[name]; if (!(name in target) || (target[name] !== s)) { target[name] = s; } } } return target; } function callSuper(args, a) { var meta = this.__meta, supers = meta.supers, l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos; if (l > pos) { args = !args ? [] : (!isArguments(args) && !isArray(args)) ? [args] : args; var name = superMeta.name, f = superMeta.f, m; do { m = supers[pos][name]; if ("function" === typeof m && (m = m._f || m) !== f) { superMeta.pos = 1 + pos; return m.apply(this, args); } } while (l > ++pos); } return null; } function getSuper() { var meta = this.__meta, supers = meta.supers, l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos; if (l > pos) { var name = superMeta.name, f = superMeta.f, m; do { m = supers[pos][name]; if ("function" === typeof m && (m = m._f || m) !== f) { superMeta.pos = 1 + pos; return m.bind(this); } } while (l > ++pos); } return null; } function getter(name) { var getters = this.__getters__; if (getters.hasOwnProperty(name)) { return getters[name].apply(this); } else { return this[name]; } } function setter(name, val) { var setters = this.__setters__; if (isHash(name)) { for (var i in name) { var prop = name[i]; if (setters.hasOwnProperty(i)) { setters[name].call(this, prop); } else { this[i] = prop; } } } else { if (setters.hasOwnProperty(name)) { return setters[name].apply(this, argsToArray(arguments, 1)); } else { return this[name] = val; } } } function defaultFunction() { var meta = this.__meta || {}, supers = meta.supers, l = supers.length, superMeta = meta.superMeta, pos = superMeta.pos; if (l > pos) { var name = superMeta.name, f = superMeta.f, m; do { m = supers[pos][name]; if ("function" === typeof m && (m = m._f || m) !== f) { superMeta.pos = 1 + pos; return m.apply(this, arguments); } } while (l > ++pos); } return null; } function functionWrapper(f, name) { if (f.toString().match(SUPER_REGEXP)) { var wrapper = function wrapper() { var ret, meta = this.__meta || {}; var orig = meta.superMeta; meta.superMeta = {f: f, pos: 0, name: name}; switch (arguments.length) { case 0: ret = f.call(this); break; case 1: ret = f.call(this, arguments[0]); break; case 2: ret = f.call(this, arguments[0], arguments[1]); break; case 3: ret = f.call(this, arguments[0], arguments[1], arguments[2]); break; default: ret = f.apply(this, arguments); } meta.superMeta = orig; return ret; }; wrapper._f = f; return wrapper; } else { f._f = f; return f; } } function defineMixinProps(child, proto) { var operations = proto.setters || {}, __setters = child.__setters__, __getters = child.__getters__; for (var i in operations) { if (!__setters.hasOwnProperty(i)) { //make sure that the setter isnt already there __setters[i] = operations[i]; } } operations = proto.getters || {}; for (i in operations) { if (!__getters.hasOwnProperty(i)) { //make sure that the setter isnt already there __getters[i] = operations[i]; } } for (var j in proto) { if (j !== "getters" && j !== "setters") { var p = proto[j]; if ("function" === typeof p) { if (!child.hasOwnProperty(j)) { child[j] = functionWrapper(defaultFunction, j); } } else { child[j] = p; } } } } function mixin() { var args = argsToArray(arguments), l = args.length; var child = this.prototype; var childMeta = child.__meta, thisMeta = this.__meta, bases = child.__meta.bases, staticBases = bases.slice(), staticSupers = thisMeta.supers || [], supers = childMeta.supers || []; for (var i = 0; i < l; i++) { var m = args[i], mProto = m.prototype; var protoMeta = mProto.__meta, meta = m.__meta; !protoMeta && (protoMeta = (mProto.__meta = {proto: mProto || {}})); !meta && (meta = (m.__meta = {proto: m.__proto__ || {}})); defineMixinProps(child, protoMeta.proto || {}); defineMixinProps(this, meta.proto || {}); //copy the bases for static, mixinSupers(m.prototype, supers, bases); mixinSupers(m, staticSupers, staticBases); } return this; } function mixinSupers(sup, arr, bases) { var meta = sup.__meta; !meta && (meta = (sup.__meta = {})); var unique = sup.__meta.unique; !unique && (meta.unique = "declare" + ++classCounter); //check it we already have this super mixed into our prototype chain //if true then we have already looped their supers! if (indexOf(bases, unique) === -1) { //add their id to our bases bases.push(unique); var supers = sup.__meta.supers || [], i = supers.length - 1 || 0; while (i >= 0) { mixinSupers(supers[i--], arr, bases); } arr.unshift(sup); } } function defineProps(child, proto) { var operations = proto.setters, __setters = child.__setters__, __getters = child.__getters__; if (operations) { for (var i in operations) { __setters[i] = operations[i]; } } operations = proto.getters || {}; if (operations) { for (i in operations) { __getters[i] = operations[i]; } } for (i in proto) { if (i != "getters" && i != "setters") { var f = proto[i]; if ("function" === typeof f) { var meta = f.__meta || {}; if (!meta.isConstructor) { child[i] = functionWrapper(f, i); } else { child[i] = f; } } else { child[i] = f; } } } } function _export(obj, name) { if (obj && name) { obj[name] = this; } else { obj.exports = obj = this; } return this; } function extend(proto) { return declare(this, proto); } function getNew(ctor) { // create object with correct prototype using a do-nothing // constructor forceNew.prototype = ctor.prototype; var t = new forceNew(); forceNew.prototype = null; // clean up return t; } function __declare(child, sup, proto) { var childProto = {}, supers = []; var unique = "declare" + ++classCounter, bases = [], staticBases = []; var instanceSupers = [], staticSupers = []; var meta = { supers: instanceSupers, unique: unique, bases: bases, superMeta: { f: null, pos: 0, name: null } }; var childMeta = { supers: staticSupers, unique: unique, bases: staticBases, isConstructor: true, superMeta: { f: null, pos: 0, name: null } }; if (isHash(sup) && !proto) { proto = sup; sup = Base; } if ("function" === typeof sup || isArray(sup)) { supers = isArray(sup) ? sup : [sup]; sup = supers.shift(); child.__meta = childMeta; childProto = getNew(sup); childProto.__meta = meta; childProto.__getters__ = merge({}, childProto.__getters__ || {}); childProto.__setters__ = merge({}, childProto.__setters__ || {}); child.__getters__ = merge({}, child.__getters__ || {}); child.__setters__ = merge({}, child.__setters__ || {}); mixinSupers(sup.prototype, instanceSupers, bases); mixinSupers(sup, staticSupers, staticBases); } else { child.__meta = childMeta; childProto.__meta = meta; childProto.__getters__ = childProto.__getters__ || {}; childProto.__setters__ = childProto.__setters__ || {}; child.__getters__ = child.__getters__ || {}; child.__setters__ = child.__setters__ || {}; } child.prototype = childProto; if (proto) { var instance = meta.proto = proto.instance || {}; var stat = childMeta.proto = proto.static || {}; stat.init = stat.init || defaultFunction; defineProps(childProto, instance); defineProps(child, stat); if (!instance.hasOwnProperty("constructor")) { childProto.constructor = instance.constructor = functionWrapper(defaultFunction, "constructor"); } else { childProto.constructor = functionWrapper(instance.constructor, "constructor"); } } else { meta.proto = {}; childMeta.proto = {}; child.init = functionWrapper(defaultFunction, "init"); childProto.constructor = functionWrapper(defaultFunction, "constructor"); } if (supers.length) { mixin.apply(child, supers); } if (sup) { //do this so we mixin our super methods directly but do not ov merge(child, merge(merge({}, sup), child)); } childProto._super = child._super = callSuper; childProto._getSuper = child._getSuper = getSuper; childProto._static = child; } function declare(sup, proto) { function declared() { switch (arguments.length) { case 0: this.constructor.call(this); break; case 1: this.constructor.call(this, arguments[0]); break; case 2: this.constructor.call(this, arguments[0], arguments[1]); break; case 3: this.constructor.call(this, arguments[0], arguments[1], arguments[2]); break; default: this.constructor.apply(this, arguments); } } __declare(declared, sup, proto); return declared.init() || declared; } function singleton(sup, proto) { var retInstance; function declaredSingleton() { if (!retInstance) { this.constructor.apply(this, arguments); retInstance = this; } return retInstance; } __declare(declaredSingleton, sup, proto); return declaredSingleton.init() || declaredSingleton; } Base = declare({ instance: { "get": getter, "set": setter }, "static": { "get": getter, "set": setter, mixin: mixin, extend: extend, as: _export } }); declare.singleton = singleton; return declare; } if ("undefined" !== typeof exports) { if ("undefined" !== typeof module && module.exports) { module.exports = createDeclared(); } } else if ("function" === typeof define && define.amd) { define(createDeclared); } else { this.declare = createDeclared(); } }());