Source: construct.js

'use strict';
var
Legio = require("./std");

/** @module legio/construct */

/**
 * @typedef config
 * @property {Function} init The constructor function
 * @property {Object} [proto] The prototype of the constructor
 * @property {constructor} [inherit] An inherited constructor
 * @property {Object} [own] An object of static properties
 * @property {Object[]} [mixin] An array of mixins
 */

/**
 * @callback superCall
 * @param {String} methodName
 * @param {Array} [args]
 */

/**
 * @typedef construct
 * @type {Function}
 * @property {Function} [Super] The inherited constructor
 * @property {Function} [superInit] A method which calls the inherited constructor with given arguments.
 * @property {superCall} [superCall] A method which calls an inherited method.
 */

/**
 * A function which generates a special constructor using the data argument.
 * @alias module:legio/construct
 * @param {config}
 * @returns {construct}
 *
 * @example
    var A = construct({
      init: function (a) {
        this.a = a;
      },

      proto: {
        toString: function () {
          return "a: " + this.a;
        }
      }
    });

    var Show = {
      show: function () {
        alert(this.a + ", " + this.b);
      }
    };

    var B = construct({
      inherit: A,

      init: function (a, b) {
        this.superInit(a);

        this.b = b;
      },

      proto: {
        toString: function () {
          return this.superCall("toString") + ", b: " + this.b;
        }
      },

      mixin: [Show],

      own: {
        create: function (a, b) {
          return new B(a, b);
        }
      }
    });
 */
function construct(data) {
  var
  Con = data.init,
  Super = data.inherit,
  own = data.own,
  mixins = data.mixin,
  proto = data.proto;

  if (Super) {
    inherits(Con, Super);
    Con.assign(Super);
  }
  // else if (Parent === null) {
    // Fn.prototype = Object.create(null);
    // Parent = true;
  // }

  if (mixins) {
    for (var i = 0, j = mixins.length; i < j; ++i) {
      Con.mixin(mixins[i]);
    }
  }

  if (proto) {
    if (Super || mixins) {
      Con.mixin(proto);
    }
    else {
      Con.prototype = proto;
    }
  }

  if (own) {
    Con.assign(own);
  }

  Con.prototype.constructor = Con;

  return Con;
}

// This function is used to call the super constructor (in inherited constructs)
function superInit() {
  var
  sup = this.Super,
  proto = sup.prototype;

  this.Super = proto.Super;
  sup.apply(this, arguments);
  this.Super = sup;
}

// This method calls a method of the super construct
function superCall(name, args) {
  var
  sup = this.Super,
  proto = sup.prototype;

  this.Super = proto.Super;
  var res = proto[name].apply(this, args);
  this.Super = sup;

  return res;
}

// This method is used for the inheritance using the Object.create for the prototype
function inherits(Con, Super) {
  var proto = Con.prototype = Object.create(Super.prototype);

  proto.Super = Super;
  proto.superInit = superInit;
  proto.superCall = superCall;
}

module.exports = construct;