baja/sys/inherit.js
/**
* @copyright 2015 Tridium, Inc. All Rights Reserved.
* @author Gareth Johnson
*/
/**
* Defines class inheritance functions {@link baja.subclass},
* {@link baja.callSuper}, and {@link baja.mixin}.
* @module baja/sys/inherit
*/
define([], function () {
"use strict";
/**
* Extends an Object by setting up the prototype chain.
*
* This method can have a Function or Object passed in as the SuperCtor argument.
* If SuperCtor Function is passed in, 'new' will be called on it to create an Object.
* The new Object will then be set on the target Function's prototype.
*
* @function baja.subclass
*
* @see baja.mixin
*
* @param {Function} Ctor the subclass constructor
* @param {Function|Object} SuperCtor the superclass constructor
* @param {Function} [mixIn] a function that can apply a MixIn to the subclass.
* @returns {Function} Ctor.
*/
function subclass(Ctor, SuperCtor) {
// So the 'super' class can be tracked, a '$super' property is
// created and attached to the Function instance
if (typeof SuperCtor === 'string') {
//TODO: require registry
SuperCtor = require('baja!').findCtor(SuperCtor);
}
if (typeof SuperCtor === 'function') {
Ctor.prototype = Object.create(SuperCtor.prototype);
Ctor.$super = SuperCtor;
} else {
Ctor.prototype = SuperCtor;
Ctor.$super = SuperCtor.constructor;
}
Ctor.prototype.constructor = Ctor;
// If there are more than two arguments then assume some Mix-Ins are being passed in.
if (arguments.length > 2) {
var i;
for (i = 2; i < arguments.length; ++i) {
Ctor = mixin(Ctor, arguments[i]);
}
}
return Ctor;
}
/**
* Call the instance's super method.
*
* @function baja.callSuper
*
* @param {String} [methodName] the name of the method to invoke on the Super JavaScript Constructor. If not specified
* then it's assumed the super JavaScript Constructor is being called.
* @param instance the instance to the Super JavaScript Constructor will be looked up from.
* @param {Array} [args] the arguments to invoke on the super method.
* @returns value returned from super method.
*/
function callSuper(methodName, Ctor, instance, args) {
if (typeof methodName === "string") {
return Ctor.$super.prototype[methodName].apply(instance, args || []);
}
args = instance;
instance = Ctor;
Ctor = methodName;
return Ctor.$super.apply(instance, args || []);
}
/**
* Apply a Mix-In to the Constructor/Object.
*
* The Constructor passed in is extended then the MixIn function is
* invoked so that it can add to the newly extended object's prototype
* chain.
*
* This method can be invoked via a call to `baja.subclass`.
*
* @function baja.mixin
*
* @param {Function} Ctor the Constructor that we're mixing into.
* @param {Function} mixin the Mix-In Function that when invoked will append
* new method's to the newly subclassed Constructor.
* When invoked, the new Constructor is passed in as the first
* argument.
* @returns {Function} the newly extended Constructor that has been extended with the Mix-In.
*/
function mixin(Ctor, mixin) {
if (Ctor &&
mixin &&
typeof Ctor === "function" &&
typeof mixin === "function") {
// If we're applying a MixIn then extend
// the prototype chain and then apply the MixIn
// to the prototype.
Ctor = subclass(function () {
callSuper(Ctor, this, arguments);
}, Ctor);
// Apply the Mix-In
mixin(Ctor);
} else {
throw new Error("Invalid MixIn arguments: " + Ctor + " -> " + mixin);
}
return Ctor;
}
return {
callSuper: callSuper,
mixin: mixin,
subclass: subclass
};
});