Source: main.js

(function () {

  var type = require('component-type');
  var protoInherits = require('inherits');

  /**
   * A collection of helpers for class building
   * @namespace allot
   */
  var allot = {};

  /**
   * Run a function under a certain context - this is a useful abstraction for performance and ease of maintenance
   * @memberof allot
   * @param {function} func function we are extending
   * @param {object} context The context we are applying the function to
   * @returns {function} Returns a proxy method which will call the corresponding methods with passed in arguments
   */
  allot.proxyMethod = function proxyMethod(func, context) {
    return function () {
      return func.apply(context, arguments);
    };
  };

  /**
   * Returns an array of strings of the method names of the object provided
   * @memberof allot
   * @param {object} objectName
   * @param {array} methodNames
   */
  allot.getAllMethodNames = function getAllMethodNames(objectName) {
    var methodNames = [];

    if ('object' !== type(objectName) && 'function' !== type(objectName)) {
      return null;
    }

    for (var propertyName in objectName) {
      if ('function' === type(objectName[propertyName])) {
        methodNames.push(propertyName);
      }
    }
    return methodNames;
  };

  /**
   * Run all the functions specified in the methodNames under the context
   * @memberof allot
   * @param {array|true} methodNames An array of methodNames to copy over or true to copy all
   * @param {object} functions An context object to take functions from
   * @param {object} context A context to run functions under
   */
  allot.proxyMethods = function proxyMethods(methodNames, functions, context) {
    //If true has been provided find all the relevant methods
    if (true === methodNames) {
      methodNames = this.getAllMethodNames(functions);
    }

    if ('array' !== type(methodNames)) {
      return false;
    }
    methodNames.forEach(function (methodName) {
      if (!(methodName in functions)) {
        return false;
      }
      if ('function' !== type(functions[methodName])) {
        return false;
      }
      //If there is a method already, skip this
      if (methodName in context) {
        return true;
      }
      context[methodName] = allot.proxyMethod(functions[methodName], context);
    });

    return true;
  };

  /**
   * Factory options - A set of options to configure the built function
   * @typedef {object} allot~factoryOptions
   * @property {array|true} [staticProxyPrototypes] an array of method names to map from SuperClass prototype to ChildClass
   * @property {array|true} [instanceProxyPrototypes] an array of method names to map from SuperClass prototype to ChildClass prototype
   * @property {array|true} [staticProxyMethods] an array of method names to map from SuperClass to ChildClass
   * @property {array|true} [instanceProxyMethods] an array of method names to map from SuperClass to ChildClass prototype
   * @property {boolean} [buildFromSuper=false] Sets the returned object with a prototype of the superClasses constructed object
   * @property {class} [constructor=AbstractClass] an object to which to abstract classes from
   */

  /**
   * Factory to build new objects based upon other ones
   * @memberof allot
   * @param {function} SuperClass A superclass for the new function to be constructed from
   * @param {allot~factoryOptions} options A set of options to configure the built function
   * @returns {object} ChildClass
   */
  allot.factory = function factory(SuperClass, options) {
    var AbstractClass;

    options = options || {};

    //Allows passed in named function declarations
    if ('function' === typeof options.constructor) {
      AbstractClass = options.constructor;
    } else {
      AbstractClass = function AbstractClass() {};
    }

    if ('buildFromSuper' in options) {
      AbstractClass.prototype = new SuperClass();
    }

    if ('staticProxyPrototypes' in options) {
      allot.proxyMethods(options.staticProxyPrototypes, SuperClass.prototype, AbstractClass);
    }
    if ('staticProxyMethods' in options) {
      allot.proxyMethods(options.staticProxyMethods, SuperClass, AbstractClass);
    }
    if ('instanceProxyMethods' in options) {
      allot.proxyMethods(options.instanceProxyMethods, SuperClass, AbstractClass.prototype);
    }
    if ('instanceProxyPrototypes' in options) {
      allot.proxyMethods(options.instanceProxyPrototypes, SuperClass.prototype, AbstractClass.prototype);
    }
        
    return AbstractClass;
  };

  /**
   * A way to give classical inheritance to another class.
   * Use sparingly as normal inherits npm / Utils.inherits is better usually.
   * @memberof allot
   * @param {function} ChildConstructor A superclass for the new function to be constructed from
   * @param {function} SuperConstructor A superclass for the new function to be constructed from
   * @returns {object} ChildClass
   */
  allot.inherits = function inherits(ChildConstructor, SuperConstructor) {
    ChildConstructor = allot.factory(SuperConstructor, {
      constructor: ChildConstructor,
      staticProxyMethods: true
    });

    protoInherits(ChildConstructor, SuperConstructor);
    return ChildConstructor;
  };

  module.exports = allot;

}());