Source: world.js

/*global exports, document */
/**
 * Creates a new World.
 *
 * @param {Object} [opt_options=] A map of initial properties.
 * @param {Object} [opt_options.gravity = new Vector(0, 0.01)] Gravity vector.
 * @param {number} [opt_options.c = 0.1] Coefficient of friction.
 * @param {number} [opt_options.resolution = 4] The world resolution. Items with scale = 1 will
 *   render with this value as their spread value.
 * @param {number} [opt_options.width = viewportSize.width / this.resolution] The world's width.
 * @param {number} [opt_options.height = viewportSize.height / this.resolution] The world's height.
 * @param {number} [opt_options.borderRadius = 0] The world's borderRadius expressed as a percentage.
 *   Some browsers (Safari) will pass this value to the world's box-shadows.
 * @param {number} [opt_options.opacity = 1] The world's opacity.
 * @param {number} [opt_options.zIndex = 1] The world's zIndex.
 * @param {number} [opt_options.colorMode = 'rgba'] The world's colorMode. Valid values are 'rgba' and 'hsla'.
 *   If Modernizr detects that the browser does not support alpha channels, the world's opacity property
 *   will be used for opacity.
 * @param {Array|string} [opt_options.backgroundColor = 'transparent'] The world's color. If colorMode = 'rgba',
 *   pass in an array formatted as [red, green, blue, alpha]. If colorMode = 'hsla', this property will
 *   be ignored.
 * @param {number} [opt_options.hue = 0] The world's hue. Used if colorMode = 'hsla'.
 * @param {number} [opt_options.saturation = 1] The world's saturation. Used if colorMode = 'hsla'.
 * @param {number} [opt_options.lightness = 0.5] The world's lightness. Used if colorMode = 'hsla'.
 * @param {string} [opt_options.addMenuText = ''] If the world's menu is activated, this text is appended.
 * @param {boolean} [opt_options.noMenu = undefined] If true, the world does not display a menu.
 *
 * @constructor
 */
function World(opt_el, opt_options) {

  var el, options = opt_options || {},
      viewportSize = exports.Utils.getWindowSize();

  this.gravity = options.gravity || new exports.Vector(0, 0.01);
  this.c = options.c || 0.1;
  this.resolution = options.resolution || 4;
  this.width = options.width / this.resolution || viewportSize.width / this.resolution;
  this.height = options.height / this.resolution || viewportSize.height / this.resolution;
  this.borderRadius = options.borderRadius || 0;
  this.location = options.location || new Vector(((viewportSize.width - (this.width * this.resolution)) / 2),
      ((viewportSize.height - (this.height * this.resolution)) / 2));
  this.opacity = typeof options.opacity === 'undefined' ? 1 : options.opacity;
  this.zIndex = typeof options.zIndex === 'undefined' ? 1 : options.zIndex;
  this.colorMode = options.colorMode || 'rgba';
  this.backgroundColor = options.backgroundColor || 'transparent';
  this.hue = options.hue || 0;
  this.saturation = typeof options.saturation === 'undefined' ? 1 : options.saturation;
  this.lightness = typeof options.lightness === 'undefined' ? 0.5 : options.lightness;
  this.addMenuText = typeof options.addMenuText === 'undefined' ? '' : options.addMenuText;

  // if no element is passed, use document.body
  if (!opt_el) {
    el = document.body;
  } else {
    el = opt_el;
  }

  this.el = el;
  this.name = 'World';
  this.el.className = this.name.toLowerCase();
  this.id = this.name + exports.System.getNewId();
  this.pauseStep = false;

  // create container if not document.body
  if (el !== document.body) {
    var container = document.createElement('div'),
        style = container.style;

    container.id = 'container_' + this.name.toLowerCase();
    container.className = 'worldContainer';
    style.left = this.location.x + 'px';
    style.top = this.location.y + 'px';
    style.width = this.width * this.resolution + 'px';
    style.height = this.height * this.resolution + 'px';
    style.zIndex = this.zIndex;
    style.backgroundColor = this.colorMode === 'rgba' ?
        'rgba(' + this.backgroundColor[0] + ', ' + this.backgroundColor[1] + ', ' + this.backgroundColor[2] + ', ' + this.opacity + ')' :
        'hsla(' + this.hue + ', ' + (this.saturation * 100) + '%, ' + (this.lightness * 100) + '%, ' + this.opacity + ')';
    container.appendChild(this.el);

    if (!options.noMenu) {
      this.menu = document.createElement('div');
      this.menu.id = 'inputMenu';
      this.menu.className = 'inputMenu';
      var menuText = "'p' = pause | 'r' = reset | 's' = stats | 'h' = hide " + this.addMenuText;
      this.menu.textContent = menuText;
      container.appendChild(this.menu);
      this.menuHidden = false;
    }

    document.body.appendChild(container);

  }

  /**
   * Object pool used to recycle objects.
   * @private
   */
  this._pool = [];

  /**
   * Worlds do not have worlds. However, assigning an
   * object literal makes for less conditions in the
   * update loop.
   */
  this.world = {};
}

World.prototype.toggleMenu = function() {
  this.menuHidden = !this.menuHidden;
  if (this.menuHidden) {
    this.menu.style.visibility = 'hidden';
    return;
  }
  this.menu.style.visibility = 'visible';
};