Source: class.js

'use strict';

(function() {
  // Already defined, then we can skip.
  if (window.wcPlayNodes && window.wcPlayNodes.wcClass) {
    return;
  }

  if (!window.wcPlayNodes) {
    window.wcPlayNodes = {};
  }

  // Bind polyfill
  if (!Function.prototype.bind) {
    /* eslint-disable no-extend-native */
    Function.prototype.bind = function(oThis) {
      if (typeof this !== 'function') {
        // closest thing possible to the ECMAScript 5
        // internal IsCallable function
        throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
      }

      var aArgs = Array.prototype.slice.call(arguments, 1),
        fToBind = this,
        FNOP    = function() {},
        fBound  = function() {
          return fToBind.apply(this instanceof FNOP? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
        };

      if (this.prototype) {
        // Function.prototype doesn't have a prototype property
        FNOP.prototype = this.prototype;
      }
      fBound.prototype = new FNOP();

      return fBound;
    };
    /* eslint-enable no-extend-native */
  }

  var initializing = false;

  /**
   * JavaScript class inheritance system.
   * @class wcClass
   */
  var wcClass = function() {};

  /**
   * Extends the class object.
   * @function wcClass.extend
   * @param {string} className - The name of the class to define.
   * @param {...Object} _args - Any parameters to pass on to the class constructor.
   * @returns {Object} - The new inherited class object.
   */
  wcClass.extend = function(className, _args) {
    // Validate class name.
    if (!className.match(/^[a-z]+[\w]*$/i)) {
      throw new Error('Class name contains invalid characters!');
    }

    // Last argument is always the class definition.
    var props = arguments[arguments.length-1];

    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this(arguments);
    initializing = false;

    // Create a bound super class object that contains all of the
    // parent methods, but bound to the current object.
    var _boundSuper = {};
    for (var key in _super) {
      if (typeof _super[key] === 'function') {
        _boundSuper[key] = _super[key].bind(prototype);
      }
    }

    function bindSuper(owner, name) {
      var bound = null;
      if (_super && typeof _super[name] === 'function') {
        bound = _super[name].bind(owner);
      } else {
        bound = function() {};
      }
      bound.prototype = _boundSuper;
      return bound;
    }

    // Copy the properties over onto the new prototype
    for (var propName in props) {
      // Check if we're overwriting an existing function
      // prototype[name] = typeof props[name] === 'function' && typeof _super[name] === 'function'?
      prototype[propName] = typeof props[propName] === 'function'?
        (function(name, fn) {
          return function() {
            var tmp = this._super;

            // Add a new this._super() method that is the same method
            // but on the super-class
            this._super = bindSuper(this, name);

            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);
            this._super = tmp;

            return ret;
          };
        })(propName, props[propName]):
        props[propName];
    }

    function __init() {
      if (initializing) {
        if (this.classInit) {
          this.classInit.apply(this, arguments[0]);
        }
      } else if (this.init) {
        this.init.apply(this, arguments);
      }
    }

    // Is a functionality.
    prototype.isA = function(name) {
      return name === className;
    };

    // Instance of functionality.
    prototype.instanceOf = function(name) {
      return this.isA(name) || (_super.instanceOf && _super.instanceOf(name));
    };

    /* eslint-disable no-eval */
    // Converts __init to a new function that is named after className
    var Class = 'wcPlayNodes.' + className + ' = function ' + className + '() {' + __init.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1] + '};';
    eval(Class);
    /* eslint-enable no-eval */

    // Populate our constructed prototype object
    wcPlayNodes[className].prototype = prototype;

    /* eslint-disable no-caller */
    // And make this class extendable
    wcPlayNodes[className].extend = wcClass.extend;
    /* eslint-enable no-caller */
    return Class;
  };
  window.wcPlayNodes.wcClass = wcClass;

  /**
   * Class constructor.
   * @function wcClass#init
   * @params {..Object} Any parameters to pass on to the class constructor.
   */
  /**
   * Initializes a class type.
   * @function wcClass#classInit
   * @params {..Object} Any parameters to pass on to the class constructor.
   */
})();