Source: abc-view-controller.js

define([
    'jquery',
    'underscore',
    'three'
], function($, _, THREE) {
  /**
   *
   * @class EmperorViewControllerABC
   *
   * Initializes an abstract tab. This has to be contained in a DOM object and
   * will use the full size of that container.  The title represents the title
   * of the jQuery tab.  The description will be used as help text to describe
   * the functionality of each subclass tab.
   *
   * @param {UIState} uiState the shared state object
   * @param {Node} container Container node to create the controller in.
   * @param {String} title Title of the tab.
   * @param {String} description Helper description.
   *
   * @return {EmperorViewControllerABC} Returns an instance of the
   * EmperorViewControllerABC.
   * @constructs EmperorViewControllerABC
   *
   */
  function EmperorViewControllerABC(uiState, container, title, description) {
    THREE.EventDispatcher.call(this);

    /**
     * @type {UIState}
     * The shared state
     */
    this.UIState = uiState;

    /**
     * @type {Node}
     * jQuery element for the parent container.
     */
    this.$container = $(container);
    /**
     * @type {String}
     * Human-readable title of the tab.
     */
    this.title = title;
    /**
     * @type {String}
     * Human-readable description of the tab.
     */
    this.description = description;

    /**
     * @type {Node}
     * jQuery element for the canvas, which contains the header and the body.
     */
    this.$canvas = null;
    /**
     * @type {Node}
     * jQuery element for the body, which contains the lowermost elements
     * displayed in tab. This goes below the header.
     */
    this.$body = null;
    /**
     * @type {Node}
     * jQuery element for the header which contains the uppermost elements
     * displayed in a tab.
     */
    this.$header = null;
    /**
     * @type {Boolean}
     * Indicates whether the tab is front most
     * @default false
     */
    this.active = false;
    /**
     * @type {String}
     * Unique hash identifier for the tab instance.
     * @default "EMPtab-xxxxxxx"
     */
    this.identifier = 'EMPtab-' + Math.round(1000000 * Math.random());
    /**
     * @type {Boolean}
     * Indicates if tab can be accessed.
     * @default true
     */
    this.enabled = true;

    if (this.$container.length < 1) {
      throw new Error('Emperor requires a valid container, ' +
          this.$container + ' does not exist in the DOM.');
    }

    // the canvas contains both the header and the body, note that for all
    // these divs the width should be 100% (whatever we have available), but
    // the height is much trickier, see the resize method for more information
    this.$canvas = $('<div name="emperor-view-controller-canvas"></div>');
    this.$canvas.width('100%');
    this.$container.append(this.$canvas);

    this.$canvas.width(this.$container.width());
    this.$canvas.height(this.$container.height());

    // the margin and width properties are set this way to center all the
    // contents of the divs themselves, see this SO answer:
    // http://stackoverflow.com/a/114549
    this.$header = $('<div name="emperor-view-controller-header"></div>');
    this.$header.css('margin', '0 auto');
    this.$header.css('width', '100%');

    this.$body = $('<div name="emperor-view-controller-body"></div>');
    this.$body.css('margin', '0 auto');
    this.$body.css('width', '100%');

    // inherit the size of the container minus the space being used for the
    // header
    this.$body.height(this.$canvas.height() - this.$header.height());
    this.$body.width(this.$canvas.width());

    this.$canvas.append(this.$header);
    this.$canvas.append(this.$body);

    return this;
  }
  EmperorViewControllerABC.prototype = Object.create(
      THREE.EventDispatcher.prototype);
  EmperorViewControllerABC.prototype.constructor = THREE.EventDispatcher;

  /**
   * Sets whether or not elements in the tab can be modified.
   *
   * @param {Boolean} trulse option to enable elements.
   */
  EmperorViewControllerABC.prototype.setEnabled = function(trulse) {
    if (typeof(trulse) === 'boolean') {
      this.enabled = trulse;
    }
    else {
      throw new Error('`trulse` can only be of boolean type');
    }
  };

  /**
   * Sets whether or not the tab is visible.
   *
   * @param {Boolean} trulse option to activate tab
   * (i.e. move tab to foreground).
   */
  EmperorViewControllerABC.prototype.setActive = function(trulse) {
    if (this.enabled === true) {
      if (typeof(trulse) === 'boolean') {
        this.active = trulse;
      }
      else {
        throw new Error('`trulse` can only be of boolean type');
      }
    }
  };

  /**
   * Resizes the container, note that the body will take whatever space is
   * available after considering the size of the header. The header shouldn't
   * have height variable objects, once added their height shouldn't really
   * change.
   *
   * @param {Float} width the container width.
   * @param {Float} height the container height.
   */
  EmperorViewControllerABC.prototype.resize = function(width, height) {
    // This padding is required in order to make space
    // for the horizontal menus
    var padding = 10;
    this.$canvas.height(height);
    this.$canvas.width(width - padding);

    this.$header.width(width - padding);

    // the body has to account for the size used by the header
    this.$body.width(width - padding);
    this.$body.height(height - this.$header.height());
  };

  /**
   *
   * Converts the current instance into a JSON string.
   *
   * @return {Object} ready to serialize representation of self.
   */
  EmperorViewControllerABC.prototype.toJSON = function() {
    throw Error('Not implemented');
  };

  /**
   * Decodes JSON string and modifies its own instance variables accordingly.
   *
   * @param {Object} parsed JSON string representation of an instance.
   */
  EmperorViewControllerABC.prototype.fromJSON = function(jsonString) {
    throw Error('Not implemented');
  };

  /**
   * Writes the current settings to the active decomposition view(s).
   * Will be called when the decomposition view is swapped out
   * for a different view type.
   */
  EmperorViewControllerABC.prototype.forceRefresh = function() {
    this.fromJSON(this.toJSON());
  };

  return {'EmperorViewControllerABC': EmperorViewControllerABC};
});