<commit_before>function exit(){

  var b = document.getElementById("a");
  b.style.visibility = "visible";
  var b = document.getElementById("s");
  b.style.visibility = "visible";
  var o = document.getElementById("buttons");
  o.style.display = "none";

  var o = document.getElementsByClassName("bmax");
  o[0].setAttribute("class", "bmin");
  var o = document.getElementsByClassName("smax");
  o[0].setAttribute("class", "smin");
  var o = document.getElementById("right");
  o.style.overflow ="scroll";

  var o = document.getElementsByClassName("max");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].scrollTop = 0;
    o[i].setAttribute("class", "min");
  };

  var o = document.getElementsByClassName("encabezadoMax");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].setAttribute("class", "encabezado");
  };

  var o = document.getElementsByClassName("texto");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].style.display = "none";
  };

}

function max(n){
  var o = document.getElementsByClassName("bmin");
  o[0].setAttribute("class", "bmax");
  var o = document.getElementsByClassName("smin");
  o[0].setAttribute("class", "smax");
  var o = document.getElementById("right");
  o.style.overflow ="hidden";

  var o = document.getElementsByClassName("texto");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].style.display = "block";
  };

  var o = document.getElementsByClassName("encabezado");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].setAttribute("class", "encabezadoMax");
  };

  var o = document.getElementsByClassName("min");

  for (var i = o.length - 1; i >= n; i--) {
    o[i].setAttribute("class", "max after");
  };

  for (var i = n - 1; i >= 0; i--) {
    o[i].setAttribute("class", "max before");
  };

  var o = document.getElementsByClassName("max");
  o[n].setAttribute("class", "max present ani");

  if(o.length == n+1){
    var b = document.getElementById("s");
    b.style.visibility = "hidden";
  } else o[n+1].setAttribute("class", "max future");

  if(n == 0){
    var b = document.getElementById("a");
    b.style.visibility = "hidden";
  } else o[n-1].setAttribute("class", "max past");

  var o = document.getElementById("buttons");
  o.style.display = "block";
}

function before(){
  var o = document.getElementsByClassName("future");
  if (o.length == 0) {
    var b = document.getElementById("s");
    b.style.visibility = "visible";
  } else o[0].setAttribute("class", "max after");

  var o = document.getElementsByClassName("present");
  o[0].scrollTop = 0;
  o[0].setAttribute("class", "max future");

  var o = document.getElementsByClassName("past");
  o[0].setAttribute("class", "max present");
  var o = document.getElementsByClassName("before");
  if (o.length == 0) {
    var b = document.getElementById("a");
    b.style.visibility = "hidden";
  } else {
  o[o.length - 1].setAttribute("class", "max past");
  }

}

function after(){

  var o = document.getElementsByClassName("past");
  if (o.length == 0) {
    var b = document.getElementById("a");
    b.style.visibility = "visible";
  } else o[0].setAttribute("class", "max before");

  var o = document.getElementsByClassName("present");
  o[0].scrollTop = 0;
  o[0].setAttribute("class", "max past");

  var o = document.getElementsByClassName("future");
  if (o.length == 0) {
    var o = document.getElementsByClassName("after");
    o[0].setAttribute("class", "max present");
    if(o.length > 0) {
      o[0].setAttribute("class", "max future");
    } else {
      var b = document.getElementById("s");
      b.style.visibility = "hidden";
    }
  } else {
    o[0].setAttribute("class", "max present");
    var o = document.getElementsByClassName("after");
    if(o.length == 0) {
      var b = document.getElementById("s");
      b.style.visibility = "hidden";
    } else {
      o[0].setAttribute("class", "max future");
    }
  }

}

function night(){
  var o = document.getElementsByClassName("present");
  o[0].setAttribute("class", "max present night");
  var n = document.getElementById("n");
  n.style.display = "none";
  var d = document.getElementById("d");
  d.style.display = "block";

}

function day(){
  var o = document.getElementsByClassName("present");
  o[0].setAttribute("class", "max present day");
  var n = document.getElementById("d");
  n.style.display = "none";
  var d = document.getElementById("n");
  d.style.display = "block";
}

function reg(){
  var n = document.getElementById("l");
  n.style.display = "none";
  var d = document.getElementById("r");
  d.style.display = "block";

}

function log(){
  var n = document.getElementById("r");
  n.style.display = "none";
  var d = document.getElementById("l");
  d.style.display = "block";
}<commit_msg>bug d/n<commit_after>function exit(){

  var n = document.getElementById("d");
  n.style.display = "none";
  var d = document.getElementById("n");
  d.style.display = "block";

  var b = document.getElementById("a");
  b.style.visibility = "visible";
  var b = document.getElementById("s");
  b.style.visibility = "visible";
  var o = document.getElementById("buttons");
  o.style.display = "none";

  var o = document.getElementsByClassName("bmax");
  o[0].setAttribute("class", "bmin");
  var o = document.getElementsByClassName("smax");
  o[0].setAttribute("class", "smin");
  var o = document.getElementById("right");
  o.style.overflow ="scroll";

  var o = document.getElementsByClassName("max");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].scrollTop = 0;
    o[i].setAttribute("class", "min");
  };

  var o = document.getElementsByClassName("encabezadoMax");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].setAttribute("class", "encabezado");
  };

  var o = document.getElementsByClassName("texto");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].style.display = "none";
  };

}

function max(n){
  var o = document.getElementsByClassName("bmin");
  o[0].setAttribute("class", "bmax");
  var o = document.getElementsByClassName("smin");
  o[0].setAttribute("class", "smax");
  var o = document.getElementById("right");
  o.style.overflow ="hidden";

  var o = document.getElementsByClassName("texto");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].style.display = "block";
  };

  var o = document.getElementsByClassName("encabezado");

  for (var i = o.length - 1; i >= 0; i--) {
    o[i].setAttribute("class", "encabezadoMax");
  };

  var o = document.getElementsByClassName("min");

  for (var i = o.length - 1; i >= n; i--) {
    o[i].setAttribute("class", "max after");
  };

  for (var i = n - 1; i >= 0; i--) {
    o[i].setAttribute("class", "max before");
  };

  var o = document.getElementsByClassName("max");
  o[n].setAttribute("class", "max present ani");

  if(o.length == n+1){
    var b = document.getElementById("s");
    b.style.visibility = "hidden";
  } else o[n+1].setAttribute("class", "max future");

  if(n == 0){
    var b = document.getElementById("a");
    b.style.visibility = "hidden";
  } else o[n-1].setAttribute("class", "max past");

  var o = document.getElementById("buttons");
  o.style.display = "block";
}

function before(){
  var o = document.getElementsByClassName("future");
  if (o.length == 0) {
    var b = document.getElementById("s");
    b.style.visibility = "visible";
  } else o[0].setAttribute("class", "max after");

  var o = document.getElementsByClassName("present");
  o[0].scrollTop = 0;
  o[0].setAttribute("class", "max future");

  var o = document.getElementsByClassName("past");
  o[0].setAttribute("class", "max present");
  var o = document.getElementsByClassName("before");
  if (o.length == 0) {
    var b = document.getElementById("a");
    b.style.visibility = "hidden";
  } else {
  o[o.length - 1].setAttribute("class", "max past");
  }

}

function after(){

  var o = document.getElementsByClassName("past");
  if (o.length == 0) {
    var b = document.getElementById("a");
    b.style.visibility = "visible";
  } else o[0].setAttribute("class", "max before");

  var o = document.getElementsByClassName("present");
  o[0].scrollTop = 0;
  o[0].setAttribute("class", "max past");

  var o = document.getElementsByClassName("future");
  if (o.length == 0) {
    var o = document.getElementsByClassName("after");
    o[0].setAttribute("class", "max present");
    if(o.length > 0) {
      o[0].setAttribute("class", "max future");
    } else {
      var b = document.getElementById("s");
      b.style.visibility = "hidden";
    }
  } else {
    o[0].setAttribute("class", "max present");
    var o = document.getElementsByClassName("after");
    if(o.length == 0) {
      var b = document.getElementById("s");
      b.style.visibility = "hidden";
    } else {
      o[0].setAttribute("class", "max future");
    }
  }

}

function night(){
  var o = document.getElementsByClassName("present");
  o[0].setAttribute("class", "max present night");
  var n = document.getElementById("n");
  n.style.display = "none";
  var d = document.getElementById("d");
  d.style.display = "block";

}

function day(){
  var o = document.getElementsByClassName("present");
  o[0].setAttribute("class", "max present day");
  var n = document.getElementById("d");
  n.style.display = "none";
  var d = document.getElementById("n");
  d.style.display = "block";
}

function reg(){
  var n = document.getElementById("l");
  n.style.display = "none";
  var d = document.getElementById("r");
  d.style.display = "block";

}

function log(){
  var n = document.getElementById("r");
  n.style.display = "none";
  var d = document.getElementById("l");
  d.style.display = "block";
}<|endoftext|>


<commit_before>// Behavior
// --------
//
// Represents a behavior of some element or group of elements.
// The objetive is define a set of rules and events who
// can be associated to an element and reutilized later on
//
// When a behavior is defined, a hash of events must be defined too,
// and on initialization a DOM element must be provided
//
// Also you can define an `init` function, who is always called when the
// behavior is initialized
//
// **Example**
// ```javascript
// Carousel = Essential.Behavior.extend({
//   events: {
//     "click .next": "goToNextSlide"
//   },
//
//  init: function() {
//    // Called on behavior initialization
//  },
//
//   goToNextSlide: function(e) {
//     //...
//   }
// });
//
// var carousel = Carousel.new(domElement);
// ```

Essential.Behavior = Proto.extend({
  constructor: function (domElement) {
    this.el = domElement;
    this.delegateEvents();

    if (typeof this.init === "function") {
      this.init();
    }
  },

  delegateEvents: function () {
    if (typeof this.events === "undefined") {
      return;
    }

    var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    for (var key in this.events) {
      var method = this.events[key];

      var match = key.match(delegateEventSplitter);
      var eventName = match[1],
        selector = match[2],
        nodeList = selector ? this.el.querySelectorAll(selector) : [this.el];

      if (typeof this[method] === "undefined") {
        continue;
      }

      Essential.Core.bind(eventName, nodeList, this[method]);
    }
  }
});
<commit_msg>Fixes a grammar typo in the description of Essential.Behavior<commit_after>// Behavior
// --------
//
// Represents a behavior of some element or group of elements.
// The objetive is define a set of rules and events which
// can be associated to an element and reutilized later on
//
// When a behavior is defined, a hash of events must be defined too,
// and on initialization a DOM element must be provided
//
// Also you can define an `init` function, which is always called when the
// behavior is initialized
//
// **Example**
// ```javascript
// Carousel = Essential.Behavior.extend({
//   events: {
//     "click .next": "goToNextSlide"
//   },
//
//  init: function() {
//    // Called on behavior initialization
//  },
//
//   goToNextSlide: function(e) {
//     //...
//   }
// });
//
// var carousel = Carousel.new(domElement);
// ```

Essential.Behavior = Proto.extend({
  constructor: function (domElement) {
    this.el = domElement;
    this.delegateEvents();

    if (typeof this.init === "function") {
      this.init();
    }
  },

  delegateEvents: function () {
    if (typeof this.events === "undefined") {
      return;
    }

    var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    for (var key in this.events) {
      var method = this.events[key];

      var match = key.match(delegateEventSplitter);
      var eventName = match[1],
        selector = match[2],
        nodeList = selector ? this.el.querySelectorAll(selector) : [this.el];

      if (typeof this[method] === "undefined") {
        continue;
      }

      Essential.Core.bind(eventName, nodeList, this[method]);
    }
  }
});
<|endoftext|>


<commit_before>import charm from 'charm';

/**
 * Map of colors that can be used to change the color of cursor
 */
export const COLORS = {
  RED: 'red',
  YELLOW: 'yellow',
  GREEN: 'green',
  BLUE: 'blue',
  CYAN: 'cyan',
  MAGENTA: 'magenta',
  BLACK: 'black',
  WHITE: 'white'
};

/**
 * Map of regions that can be passed to erase method to determine what need to erase
 */
export const ERASE_REGIONS = {
  FROM_CURSOR_TO_END: 'end',
  FROM_CURSOR_TO_START: 'start',
  FROM_CURSOR_TO_DOWN: 'down',
  FROM_CURSOR_TO_UP: 'up',
  CURRENT_LINE: 'line',
  ENTIRE_SCREEN: 'screen'
};

export class Cursor {
  /**
   * Creates new Cursor instance
   * @constructor
   * @param {Stream|Boolean} [stdout = process.stdout]
   * @param {Stream|Boolean} [stdin = false]
   */
  constructor({stdout = process.stdout, stdin = false}) {
    // TODO: implement chain of transform streams for cursor
    this._cursor = charm();

    if (stdout) this._cursor.pipe(stdout);
    if (stdin) stdin.pipe(this._cursor);

    this.off('^C').on('^C', this._onExit);
  }

  /**
   * Sets a listener to the specified event
   * @param {String} event Event name
   * @param {Function} handler Handler for the specified event
   * @returns {Cursor}
   */
  on(event, handler) {
    this._cursor.on(event, handler);
    return this;
  }

  /**
   * Removes all listeners or specified listener from the event
   * @param {String} event Event name
   * @param {Function} [handler] Handler that you want to delete
   * @returns {Cursor}
   */
  off(event, handler) {
    handler ? this._cursor.off(event, handler) : this._cursor.removeAllListeners(event);
    return this;
  }

  /**
   * Write to the stream
   * @param {String} message Message to write to the stream
   * @returns {Cursor}
   */
  write(message) {
    this._cursor.write(message);
    return this;
  }

  /**
   * Set the cursor position to the absolute coordinates
   * @param {Number} x Coordinate X
   * @param {Number} y Coordinate Y
   * @returns {Cursor}
   */
  setPosition(x, y) {
    this._cursor.position(x, y);
    return this;
  }

  /**
   * Get the absolute cursor position
   * @returns {Promise}
   */
  getPosition() {
    return new Promise((resolve, reject) => this._cursor.position((x, y) => resolve({x, y})));
  }

  /**
   * Move the cursor by the relative coordinates
   * @param {Number} x Coordinate X
   * @param {Number} y Coordinate Y
   * @returns {Cursor}
   */
  move(x, y) {
    this._cursor.move(x, y);
    return this;
  }

  /**
   * Move the cursor up
   * @param {Number} [y] Rows count
   * @returns {Cursor}
   */
  up(y = 1) {
    this._cursor.up(y);
    return this;
  }

  /**
   * Move the cursor down
   * @param {Number} [y] Rows count
   * @returns {Cursor}
   */
  down(y = 1) {
    this._cursor.down(y);
    return this;
  }

  /**
   * Move the cursor left
   * @param {Number} [x] Columns count
   * @returns {Cursor}
   */
  left(x = 1) {
    this._cursor.left(x);
    return this;
  }

  /**
   * Move the cursor right
   * @param {Number} [x] Columns count
   * @returns {Cursor}
   */
  right(x = 1) {
    this._cursor.right(x);
    return this;
  }

  /**
   * Erase a defined region
   * @param {String} region Constant from ERASE_REGIONS
   * @returns {Cursor}
   */
  erase(region) {
    this._cursor.erase(region);
    return this;
  }

  /**
   * Set the foreground color
   * @param {String|Number} color Constant from COLORS or number from 0 to 255
   * @returns {Cursor}
   */
  foreground(color) {
    this._cursor.foreground(color);
    return this;
  }

  /**
   * Set the background color
   * @param {String|Number} color Constant from COLORS or number from 0 to 255
   * @returns {Cursor}
   */
  background(color) {
    this._cursor.background(color);
    return this;
  }

  /**
   * Fill the specified region with symbol
   * @param {String} [symbol] Symbol that will be used for filling the region
   * @param {String} [background] Background color from COLORS
   * @param {String} [foreground] Foreground color from COLORS
   * @param {Number} x1 Start coordinate X
   * @param {Number} y1 Start coordinate Y
   * @param {Number} x2 End coordinate X
   * @param {Number} y2 End coordinate Y
   * @returns {Cursor}
   */
  fill({x1, y1, x2, y2, symbol = ' ', background, foreground}) {
    let filler = new Array(x2 - x1).fill(symbol).join('');

    if (background) this.background(background);
    if (foreground) this.foreground(foreground);

    this.setPosition(x1, y1);

    while (y1 <= y2) {
      this.write(filler);
      this.setPosition(x1, ++y1);
    }

    return this;
  }

  /**
   * Set the cursor invisible
   * @returns {Cursor}
   */
  hide() {
    this._cursor.cursor(false);
    return this;
  }

  /**
   * Set the cursor visible
   * @returns {Cursor}
   */
  show() {
    this._cursor.cursor(true);
    return this;
  }

  /**
   * Resets the entire screen
   * @returns {Cursor}
   */
  reset() {
    this._cursor.reset();
    return this;
  }

  /**
   * Destroy the cursor
   * @returns {Cursor}
   */
  destroy() {
    this._cursor.destroy();
    return this;
  }

  /**
   * Triggers when user types ^C combination for exit
   * @private
   * @returns {Cursor}
   */
  _onExit() {
    this.setPosition(Cursor.getTerminalWidth(), Cursor.getTerminalHeight());
    process.exit(0);
  }

  /**
   * Get width of terminal
   * @returns {Number}
   */
  static getTerminalWidth() {
    return process.stdout.columns;
  }

  /**
   * Get height of terminal
   * @returns {Number}
   */
  static getTerminalHeight() {
    return process.stdout.rows;
  }
}
<commit_msg>fix(cursor): Fixes issue with reseting the screen after exit from presentation<commit_after>import charm from 'charm';

/**
 * Map of colors that can be used to change the color of cursor
 */
export const COLORS = {
  RED: 'red',
  YELLOW: 'yellow',
  GREEN: 'green',
  BLUE: 'blue',
  CYAN: 'cyan',
  MAGENTA: 'magenta',
  BLACK: 'black',
  WHITE: 'white'
};

/**
 * Map of regions that can be passed to erase method to determine what need to erase
 */
export const ERASE_REGIONS = {
  FROM_CURSOR_TO_END: 'end',
  FROM_CURSOR_TO_START: 'start',
  FROM_CURSOR_TO_DOWN: 'down',
  FROM_CURSOR_TO_UP: 'up',
  CURRENT_LINE: 'line',
  ENTIRE_SCREEN: 'screen'
};

export class Cursor {
  /**
   * Creates new Cursor instance
   * @constructor
   * @param {Stream|Boolean} [stdout = process.stdout]
   * @param {Stream|Boolean} [stdin = false]
   */
  constructor({stdout = process.stdout, stdin = false}) {
    // TODO: implement chain of transform streams for cursor
    this._cursor = charm();

    if (stdout) this._cursor.pipe(stdout);
    if (stdin) stdin.pipe(this._cursor);

    this.off('^C').on('^C', this._onExit);
  }

  /**
   * Sets a listener to the specified event
   * @param {String} event Event name
   * @param {Function} handler Handler for the specified event
   * @returns {Cursor}
   */
  on(event, handler) {
    this._cursor.on(event, handler);
    return this;
  }

  /**
   * Removes all listeners or specified listener from the event
   * @param {String} event Event name
   * @param {Function} [handler] Handler that you want to delete
   * @returns {Cursor}
   */
  off(event, handler) {
    handler ? this._cursor.off(event, handler) : this._cursor.removeAllListeners(event);
    return this;
  }

  /**
   * Write to the stream
   * @param {String} message Message to write to the stream
   * @returns {Cursor}
   */
  write(message) {
    this._cursor.write(message);
    return this;
  }

  /**
   * Set the cursor position to the absolute coordinates
   * @param {Number} x Coordinate X
   * @param {Number} y Coordinate Y
   * @returns {Cursor}
   */
  setPosition(x, y) {
    this._cursor.position(x, y);
    return this;
  }

  /**
   * Get the absolute cursor position
   * @returns {Promise}
   */
  getPosition() {
    return new Promise((resolve, reject) => this._cursor.position((x, y) => resolve({x, y})));
  }

  /**
   * Move the cursor by the relative coordinates
   * @param {Number} x Coordinate X
   * @param {Number} y Coordinate Y
   * @returns {Cursor}
   */
  move(x, y) {
    this._cursor.move(x, y);
    return this;
  }

  /**
   * Move the cursor up
   * @param {Number} [y] Rows count
   * @returns {Cursor}
   */
  up(y = 1) {
    this._cursor.up(y);
    return this;
  }

  /**
   * Move the cursor down
   * @param {Number} [y] Rows count
   * @returns {Cursor}
   */
  down(y = 1) {
    this._cursor.down(y);
    return this;
  }

  /**
   * Move the cursor left
   * @param {Number} [x] Columns count
   * @returns {Cursor}
   */
  left(x = 1) {
    this._cursor.left(x);
    return this;
  }

  /**
   * Move the cursor right
   * @param {Number} [x] Columns count
   * @returns {Cursor}
   */
  right(x = 1) {
    this._cursor.right(x);
    return this;
  }

  /**
   * Erase a defined region
   * @param {String} region Constant from ERASE_REGIONS
   * @returns {Cursor}
   */
  erase(region) {
    this._cursor.erase(region);
    return this;
  }

  /**
   * Set the foreground color
   * @param {String|Number} color Constant from COLORS or number from 0 to 255
   * @returns {Cursor}
   */
  foreground(color) {
    this._cursor.foreground(color);
    return this;
  }

  /**
   * Set the background color
   * @param {String|Number} color Constant from COLORS or number from 0 to 255
   * @returns {Cursor}
   */
  background(color) {
    this._cursor.background(color);
    return this;
  }

  /**
   * Fill the specified region with symbol
   * @param {String} [symbol] Symbol that will be used for filling the region
   * @param {String} [background] Background color from COLORS
   * @param {String} [foreground] Foreground color from COLORS
   * @param {Number} x1 Start coordinate X
   * @param {Number} y1 Start coordinate Y
   * @param {Number} x2 End coordinate X
   * @param {Number} y2 End coordinate Y
   * @returns {Cursor}
   */
  fill({x1, y1, x2, y2, symbol = ' ', background, foreground}) {
    let filler = new Array(x2 - x1).fill(symbol).join('');

    if (background) this.background(background);
    if (foreground) this.foreground(foreground);

    this.setPosition(x1, y1);

    while (y1 <= y2) {
      this.write(filler);
      this.setPosition(x1, ++y1);
    }

    return this;
  }

  /**
   * Set the cursor invisible
   * @returns {Cursor}
   */
  hide() {
    this._cursor.cursor(false);
    return this;
  }

  /**
   * Set the cursor visible
   * @returns {Cursor}
   */
  show() {
    this._cursor.cursor(true);
    return this;
  }

  /**
   * Resets the entire screen
   * @returns {Cursor}
   */
  reset() {
    this._cursor.reset();
    return this;
  }

  /**
   * Destroy the cursor
   * @returns {Cursor}
   */
  destroy() {
    this._cursor.destroy();
    return this;
  }

  /**
   * Triggers when user types ^C combination for exit
   * @private
   * @returns {Cursor}
   */
  _onExit() {
    this.reset();
    process.exit(0);
  }

  /**
   * Get width of terminal
   * @returns {Number}
   */
  static getTerminalWidth() {
    return process.stdout.columns;
  }

  /**
   * Get height of terminal
   * @returns {Number}
   */
  static getTerminalHeight() {
    return process.stdout.rows;
  }
}
<|endoftext|>


<commit_before>
const RG = require('./rg');
const debug = require('debug')('bitn:WorldFromJSON');

/* This class converts a serialized world back to World.Top object. It supports
 * unloaded AreaTiles, and does not create them as objects. */
export default class WorldFromJSON {

    constructor(id2level) {
        this.id2level = id2level;
        this._conf = new RG.Factory.ConfStack();
        this._verif = new RG.Verify.Conf('WorldFromJSON');
        this.worldElemByID = {}; // Stores world elements by ID
        this.createAllZones = true;
        this._IND = 0; // Used for indenting debug messages
    }

    createWorld(placeJSON) {
        let world = null;
        if (placeJSON.conf) {
            this.dbg('Creating a restored world now');
            world = this.createRestoredWorld(placeJSON);
        }
        else {
            RG.err('WorldFromJSON', 'Should not be called at all.', 'ERROR');
            this.dbg('Creating world using Factory.World fully');
            const fact = new RG.Factory.World();
            fact.setId2Level(this.id2level);
            world = fact.createWorld(placeJSON);
        }
        return world;
    }

    /* Given a serialized world in JSON, returns the created World.Top object.
     * */
    createRestoredWorld(worldJSON) {
        if (!worldJSON.conf) {
            RG.err('WorldFromJSON', 'createRestoredWorld',
                'No worldJSON.conf. Does not look like restored world.');
        }
        const world = this.createWorldFromJSON(worldJSON);

        // Need to restore configurations here
        world.setConf(worldJSON.conf);

        const areas = world.getAreas();
        if (areas.length > 0) {
            const keys = `${Object.keys(worldJSON.conf)}`;
            if (!worldJSON.conf.hasOwnProperty('area')) {
                RG.err('WorldFromJSON', 'createRestoredWorld',
                    `No prop 'area' in ${worldJSON.conf}. Props ${keys}`);
            }
        }

        areas.forEach((area, i) => {
            area.setConf(worldJSON.conf.area[i]);
        });
        return world;
    }

    pushScope(json) {
        this._conf.pushScope(json);
        this.fact.pushScope(json);
        ++this.IND;
    }

    popScope(json) {
        this._conf.popScope(json);
        this.fact.popScope(json);
        --this.IND;
    }

    getHierName() {return this._conf.getScope().join('.');}

    createWorldFromJSON(worldJSON) {
        const fact = new RG.Factory.World();
        fact.setId2Level(this.id2level);
        this.fact = fact;

        this.verify('createWorld', worldJSON, ['name', 'nAreas']);
        if (worldJSON.hasOwnProperty('createAllZones')) {
            this.createAllZones = worldJSON.createAllZones;
            this.dbg('createAllZones set to ' + this.createAllZones);
            fact.createAllZones = this.createAllZones;
        }
        this.pushScope(worldJSON);
        const world = new RG.World.Top(worldJSON.name);
        world.setConf(worldJSON);
        for (let i = 0; i < worldJSON.nAreas; i++) {
            const areaJSON = worldJSON.area[i];
            this.printKeys('areaJSON keys', areaJSON);
            const area = this.createArea(areaJSON);

            // >>>>>>>>>>>>>>>>>>>>>> Factory.World START
            if (areaJSON.zonesCreated) { // Only during restore game
                this.fact.restoreCreatedZones(world, area, areaJSON);
            }
            // >>>>>>>>>>>>>>>>>>>>>> Factory.World END

            world.addArea(area);
            this.addWorldID(areaJSON, area);
        }
        this.popScope(worldJSON);
        this.addWorldID(worldJSON, world);
        return world;
    }

    /* Creates an area which can be added to a world. */
    createArea(areaJSON) {
        this.verify('createArea', areaJSON,
            ['name', 'maxX', 'maxY']);
        this.pushScope(areaJSON);

        const areaLevels = this.getAreaLevels(areaJSON);

        const {name, maxX, maxY, cols, rows} = areaJSON;
        const area = new RG.World.Area(name, maxX, maxY, cols, rows,
            areaLevels);
        area.setConf(areaJSON);
        area.setHierName(this.getHierName());

        // When player enters a given area tile, create zones for that tile
        if (this.createAllZones) {
        // >>>>>>>>>>>>>>>>>> Factory.World START
            this.fact._createAllZones(area, areaJSON);
        // >>>>>>>>>>>>>>>>>> Factory.World END
            area.markAllZonesCreated();
        }
        else {
            this.dbg('Skipping the zone creating due to createZones=false');
        }
        this.popScope(areaJSON);
        return area;
    }

    /* Used when creating area from existing levels. Uses id2level lookup table
     * to construct 2-d array of levels.*/
    getAreaLevels(areaJSON) {
        this.verify('getAreaLevels', areaJSON, ['tilesLoaded']);
        ++this._IND;
        const levels = [];
        if (areaJSON.tiles) {
            areaJSON.tiles.forEach((tileCol, x) => {
                const levelCol = [];
                tileCol.forEach((tile, y) => {
                    if (areaJSON.tilesLoaded[x][y]) {
                        this.dbg(`Tile ${x},${y} is loaded`);
                        const level = this.id2level[tile.level];
                        if (level) {
                            levelCol.push(level);
                        }
                        else {
                            RG.err('WorldFromJSON', 'getAreaLevels',
                                `No level ID ${tile.level} in id2level`);
                        }
                    }
                    else {
                        this.dbg(`Will NOT load Tile ${x},${y}`);
                    }
                });
                levels.push(levelCol);
            });
        }
        else {
            RG.err('WorldFromJSON', 'getAreaLevels',
                'areaJSON.tiles cannot be null/undefined');
        }
        --this._IND;
        return levels;
    }

    /* Adds a world ID to given world element. */
    addWorldID(conf, worldElem) {
        if (!RG.isNullOrUndef([conf.id])) {
            worldElem.setID(conf.id);
        }
        this.worldElemByID[worldElem.getID()] = worldElem;
    }

    dbg(msg) {
        if (debug.enabled) {
            const ind = ' '.repeat(this._IND);
            console.log(ind + msg);
        }
    }

    /* Verifies that given config is OK. */
    verify(funcName, conf, list) {
        this._verif.verifyConf(funcName, conf, list);
    }

    printKeys(msg, obj) {
        console.log(msg);
        console.log(Object.keys(obj));
    }

}
<commit_msg>Added setJSONForUnloadedTiles().<commit_after>
const RG = require('./rg');
const debug = require('debug')('bitn:WorldFromJSON');

/* This class converts a serialized world back to World.Top object. It supports
 * unloaded AreaTiles, and does not create them as objects. */
export default class WorldFromJSON {

    constructor(id2level) {
        this.id2level = id2level;
        this._conf = new RG.Factory.ConfStack();
        this._verif = new RG.Verify.Conf('WorldFromJSON');
        this.worldElemByID = {}; // Stores world elements by ID
        this.createAllZones = true;
        this._IND = 0; // Used for indenting debug messages
    }

    createWorld(placeJSON) {
        let world = null;
        if (placeJSON.conf) {
            this.dbg('Creating a restored world now');
            world = this.createRestoredWorld(placeJSON);
        }
        else {
            RG.err('WorldFromJSON', 'Should not be called at all.', 'ERROR');
            this.dbg('Creating world using Factory.World fully');
            const fact = new RG.Factory.World();
            fact.setId2Level(this.id2level);
            world = fact.createWorld(placeJSON);
        }
        return world;
    }

    /* Given a serialized world in JSON, returns the created World.Top object.
     * */
    createRestoredWorld(worldJSON) {
        if (!worldJSON.conf) {
            RG.err('WorldFromJSON', 'createRestoredWorld',
                'No worldJSON.conf. Does not look like restored world.');
        }
        const world = this.createWorldFromJSON(worldJSON);

        // Need to restore configurations here
        world.setConf(worldJSON.conf);

        const areas = world.getAreas();
        if (areas.length > 0) {
            const keys = `${Object.keys(worldJSON.conf)}`;
            if (!worldJSON.conf.hasOwnProperty('area')) {
                RG.err('WorldFromJSON', 'createRestoredWorld',
                    `No prop 'area' in ${worldJSON.conf}. Props ${keys}`);
            }
        }

        areas.forEach((area, i) => {
            area.setConf(worldJSON.conf.area[i]);
        });
        return world;
    }

    pushScope(json) {
        this._conf.pushScope(json);
        this.fact.pushScope(json);
        ++this.IND;
    }

    popScope(json) {
        this._conf.popScope(json);
        this.fact.popScope(json);
        --this.IND;
    }

    getHierName() {return this._conf.getScope().join('.');}

    createWorldFromJSON(worldJSON) {
        const fact = new RG.Factory.World();
        fact.setId2Level(this.id2level);
        this.fact = fact;

        this.verify('createWorld', worldJSON, ['name', 'nAreas']);
        if (worldJSON.hasOwnProperty('createAllZones')) {
            this.createAllZones = worldJSON.createAllZones;
            this.dbg('createAllZones set to ' + this.createAllZones);
            fact.createAllZones = this.createAllZones;
        }
        this.pushScope(worldJSON);
        const world = new RG.World.Top(worldJSON.name);
        world.setConf(worldJSON);
        for (let i = 0; i < worldJSON.nAreas; i++) {
            const areaJSON = worldJSON.area[i];
            this.printKeys('areaJSON keys', areaJSON);
            const area = this.createArea(areaJSON);

            // >>>>>>>>>>>>>>>>>>>>>> Factory.World START
            if (areaJSON.zonesCreated) { // Only during restore game
                this.fact.restoreCreatedZones(world, area, areaJSON);
            }
            // >>>>>>>>>>>>>>>>>>>>>> Factory.World END

            world.addArea(area);
            this.addWorldID(areaJSON, area);
        }
        this.popScope(worldJSON);
        this.addWorldID(worldJSON, world);
        return world;
    }

    /* Creates an area which can be added to a world. */
    createArea(areaJSON) {
        this.verify('createArea', areaJSON,
            ['name', 'maxX', 'maxY']);
        this.pushScope(areaJSON);

        const areaLevels = this.getAreaLevels(areaJSON);

        const {name, maxX, maxY, cols, rows} = areaJSON;
        const area = new RG.World.Area(name, maxX, maxY, cols, rows,
            areaLevels);
        area.setConf(areaJSON);
        area.setHierName(this.getHierName());

        this.setTileJSONForUnloadedTiles(area, areaJSON);

        // When player enters a given area tile, create zones for that tile
        if (this.createAllZones) {
        // >>>>>>>>>>>>>>>>>> Factory.World START
            this.fact._createAllZones(area, areaJSON);
        // >>>>>>>>>>>>>>>>>> Factory.World END
            area.markAllZonesCreated();
        }
        else {
            this.dbg('Skipping the zone creating due to createZones=false');
        }
        this.popScope(areaJSON);
        return area;
    }

    /* Used when creating area from existing levels. Uses id2level lookup table
     * to construct 2-d array of levels.*/
    getAreaLevels(areaJSON) {
        this.verify('getAreaLevels', areaJSON, ['tilesLoaded']);
        ++this._IND;
        const levels = [];
        if (areaJSON.tiles) {
            areaJSON.tiles.forEach((tileCol, x) => {
                const levelCol = [];
                tileCol.forEach((tile, y) => {
                    if (areaJSON.tilesLoaded[x][y]) {
                        this.dbg(`Tile ${x},${y} is loaded`);
                        const level = this.id2level[tile.level];
                        if (level) {
                            levelCol.push(level);
                        }
                        else {
                            RG.err('WorldFromJSON', 'getAreaLevels',
                                `No level ID ${tile.level} in id2level`);
                        }
                    }
                    else {
                        this.dbg(`Will NOT load Tile ${x},${y}`);
                        levelCol.push(RG.LEVEL_NOT_LOADED);
                    }
                });
                levels.push(levelCol);
            });
        }
        else {
            RG.err('WorldFromJSON', 'getAreaLevels',
                'areaJSON.tiles cannot be null/undefined');
        }
        --this._IND;
        return levels;
    }

    setTileJSONForUnloadedTiles(area, areaJSON) {
        const tiles = area.getTiles();
        tiles.forEach((tileCol, x) => {
            tileCol.forEach((tile, y) => {
                if (tiles[x][y] === RG.TILE_NOT_LOADED) {
                    tiles[x][y] = areaJSON.tiles[x][y];
                }
            });
        });
    }

    /* Adds a world ID to given world element. */
    addWorldID(conf, worldElem) {
        if (!RG.isNullOrUndef([conf.id])) {
            worldElem.setID(conf.id);
        }
        this.worldElemByID[worldElem.getID()] = worldElem;
    }

    dbg(msg) {
        if (debug.enabled) {
            const ind = ' '.repeat(this._IND);
            console.log(ind + msg);
        }
    }

    /* Verifies that given config is OK. */
    verify(funcName, conf, list) {
        this._verif.verifyConf(funcName, conf, list);
    }

    printKeys(msg, obj) {
        console.log(msg);
        console.log(Object.keys(obj));
    }

}
<|endoftext|>


<commit_before>Template.colophon.onCreated(trackPageView);

var defaultBoard = [
  [1,3,0,2,1,0,0,0],
  [0,3,1,1,2,2,1,0],
  [3,1,3,4,3,1,1,2],
  [0,3,4,3,3,3,2,1],
  [0,6,5,3,1,4,5,1],
  [5,0,6,5,4,1,4,0],
  [0,6,5,6,5,1,3,1],
  [0,0,0,1,0,3,6,0],
];

var board = [
  [1,3,0,2,1,0,0,0],
  [0,3,1,1,2,2,1,0],
  [3,1,3,4,3,1,1,2],
  [0,3,4,3,3,3,2,1],
  [0,6,5,3,1,4,5,1],
  [5,0,6,5,4,1,4,0],
  [0,6,5,6,5,1,3,1],
  [0,0,0,1,0,3,6,0],
];

var colors = [
  'rgb(250,250,250)',
  'rgb(254,202,8)',
  'rgb(248,162,40)',
  'rgb(18,148,223)',
  'rgb(16,129,199)',
  'rgb(234,47,29)',
  'rgb(229,19,25)',
];

Session.set('moves count', 0);
Session.set('games count', 0);
Session.set('current count', 0);
Session.set('best score', 20);
Session.set('win condition', false);
Session.set('winning', '');
var checkedSquares = [];

Template.colophon.helpers({
  ver() {
    return VERSION;
  },
  movesCount() {
    return Session.get('moves count');
  },
  gamesCount() {
    return Session.get('games count');
  },
  bestScore() {
    return Math.min(Session.get('best score'), Session.get('current count'));
  },
  wonClass() {
    if (Session.get('win condition')) {
      return 'won';
    }
  },

});

function draw(){

  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  for (var j = 0; j < board.length; j++) {
    for (var i = 0; i < board.length; i++) {

      ctx.fillStyle = colors[board[j][i]];

      ctx.fillRect (i*35, j*35, 35, 35);
    }
  }
}

function drawDefault(){

  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  for (var j = 0; j < defaultBoard.length; j++) {
    for (var i = 0; i < defaultBoard.length; i++) {

      ctx.fillStyle = colors[defaultBoard[j][i]];

      ctx.fillRect (i*35, j*35, 35, 35);
    }
  }
}

Template.colophon.onRendered(draw);

Template.colophon.events({
  'click button': pickColor,
  'click .won': resetGame,
});

function resetGame(evt){
  var winning = Session.get('win condition');
  if (winning) {
    drawDefault();
    Session.set('winning', '');
    Session.set('win condition', false);
    Session.set('current count', Session.get('moves count'));
    Session.set('moves count', 0);
    var gamesCnt = Session.get('games count');
    Session.set('games count', gamesCnt + 1);
    board = JSON.parse(JSON.stringify(defaultBoard));
  }
}

function pickColor(evt) {

  var count = Session.get('moves count');
  Session.set('moves count', count + 1);
  var color = evt.currentTarget.getAttribute('data-color');
  checkElement(0, 0, checkColor(0, 0), parseInt(color, 10));
  checkedSquares = [];
  draw();
  checkWinningConditions();
}

function checkColor(x, y) {
  sourceColor = board[y][x];
  return parseInt(sourceColor, 10);
}

function checkElement(x, y, sourceColor, pickColor) {
  if (checkedSquares.indexOf(x + '-' + y) > -1) {
    return;
  }

  checkedSquares.push(x + '-' + y);

  if ((x === 0 && y === 0) || checkColor(x, y) === sourceColor) {
    updateElement(x, y, pickColor);

    if (x > 0) {
      checkElement(x-1, y, sourceColor, pickColor);
    }

    if (y > 0) {
      checkElement(x, y-1, sourceColor, pickColor);
    }

    if (x < board.length-1) {
      checkElement(x+1, y, sourceColor, pickColor);
    }

    if (y < board[0].length-1) {
      checkElement(x, y+1, sourceColor, pickColor);
    }
  }
}

function checkWinningConditions(){
  var win = Session.get('win condition');
  var winningColor = board[0][0];
  for (var j = 0; j < board.length; j++) {
    for (var i = 0; i < board.length; i++) {
      if (parseInt([board[j][i]], 10) !== winningColor) {
        win = Session.set('win condition', false);
      } else {
        Session.set('win condition', true);
      }
    }
  }
}

function updateElement(x, y, color) {
  board[y][x] = color;
}

<commit_msg>prevent link clicking in eula<commit_after>Template.colophon.onCreated(trackPageView);

var defaultBoard = [
  [1,3,0,2,1,0,0,0],
  [0,3,1,1,2,2,1,0],
  [3,1,3,4,3,1,1,2],
  [0,3,4,3,3,3,2,1],
  [0,6,5,3,1,4,5,1],
  [5,0,6,5,4,1,4,0],
  [0,6,5,6,5,1,3,1],
  [0,0,0,1,0,3,6,0],
];

var board = [
  [1,3,0,2,1,0,0,0],
  [0,3,1,1,2,2,1,0],
  [3,1,3,4,3,1,1,2],
  [0,3,4,3,3,3,2,1],
  [0,6,5,3,1,4,5,1],
  [5,0,6,5,4,1,4,0],
  [0,6,5,6,5,1,3,1],
  [0,0,0,1,0,3,6,0],
];

var colors = [
  'rgb(250,250,250)',
  'rgb(254,202,8)',
  'rgb(248,162,40)',
  'rgb(18,148,223)',
  'rgb(16,129,199)',
  'rgb(234,47,29)',
  'rgb(229,19,25)',
];

Session.set('moves count', 0);
Session.set('games count', 0);
Session.set('current count', 0);
Session.set('best score', 20);
Session.set('win condition', false);
Session.set('winning', '');
var checkedSquares = [];

Template.colophon.helpers({
  ver() {
    return VERSION;
  },
  movesCount() {
    return Session.get('moves count');
  },
  gamesCount() {
    return Session.get('games count');
  },
  bestScore() {
    return Math.min(Session.get('best score'), Session.get('current count'));
  },
  wonClass() {
    if (Session.get('win condition')) {
      return 'won';
    }
  },

});

function draw(){

  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  for (var j = 0; j < board.length; j++) {
    for (var i = 0; i < board.length; i++) {

      ctx.fillStyle = colors[board[j][i]];

      ctx.fillRect (i*35, j*35, 35, 35);
    }
  }
}

function drawDefault(){

  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');

  for (var j = 0; j < defaultBoard.length; j++) {
    for (var i = 0; i < defaultBoard.length; i++) {

      ctx.fillStyle = colors[defaultBoard[j][i]];

      ctx.fillRect (i*35, j*35, 35, 35);
    }
  }
}

Template.colophon.onRendered(draw);

Template.colophon.events({
  'click button': pickColor,
  'click .won': resetGame,
});

function resetGame(evt){
  var winning = Session.get('win condition');
  if (winning) {
    drawDefault();
    Session.set('winning', '');
    Session.set('win condition', false);
    Session.set('current count', Session.get('moves count'));
    Session.set('moves count', 0);
    var gamesCnt = Session.get('games count');
    Session.set('games count', gamesCnt + 1);
    board = JSON.parse(JSON.stringify(defaultBoard));
  }
}

function pickColor(evt) {

  var count = Session.get('moves count');
  Session.set('moves count', count + 1);
  var color = evt.currentTarget.getAttribute('data-color');
  checkElement(0, 0, checkColor(0, 0), parseInt(color, 10));
  checkedSquares = [];
  draw();
  checkWinningConditions();
}

function checkColor(x, y) {
  sourceColor = board[y][x];
  return parseInt(sourceColor, 10);
}

function checkElement(x, y, sourceColor, pickColor) {
  if (checkedSquares.indexOf(x + '-' + y) > -1) {
    return;
  }

  checkedSquares.push(x + '-' + y);

  if ((x === 0 && y === 0) || checkColor(x, y) === sourceColor) {
    updateElement(x, y, pickColor);

    if (x > 0) {
      checkElement(x-1, y, sourceColor, pickColor);
    }

    if (y > 0) {
      checkElement(x, y-1, sourceColor, pickColor);
    }

    if (x < board.length-1) {
      checkElement(x+1, y, sourceColor, pickColor);
    }

    if (y < board[0].length-1) {
      checkElement(x, y+1, sourceColor, pickColor);
    }
  }
}

function checkWinningConditions(){
  var win = Session.get('win condition');
  var winningColor = board[0][0];
  for (var j = 0; j < board.length; j++) {
    for (var i = 0; i < board.length; i++) {
      if (parseInt([board[j][i]], 10) !== winningColor) {
        win = Session.set('win condition', false);
      } else {
        Session.set('win condition', true);
      }
    }
  }
}

function updateElement(x, y, color) {
  board[y][x] = color;
}

Template.eula.events({
  ['click section a'](evt) {
    evt.preventDefault();
    evt.stopImmediatePropagation();
  }
});
<|endoftext|>


<commit_before>var MAP_DEFAULT_CENTER = [ 37.78, -122.419 ];
var MAP_DEFAULT_ZOOM = 13;
var NO_OP = function(){};

var Backbone = require('backbone');
var $ = Backbone.$ = require('jquery');
var _ = require('underscore');
var async = require('async');
require('mapbox.js'); // attaches "automatically" to window.L

module.exports = Backbone.View.extend({
	initialize: function(){
		_.bindAll( this, 'geocodeQuery' );
		this.render();
		this.listenTo( this.collection, 'reset', this.drawMarkers );
	},
	render: function(){
		this.map = new google.maps.Map( this.el, {
			center: new google.maps.LatLng( MAP_DEFAULT_CENTER[0], MAP_DEFAULT_CENTER[1] ),
			zoom: MAP_DEFAULT_ZOOM
		});
		this.geocoder = new google.maps.Geocoder();
		this.geocoder_bounds = new google.maps.LatLngBounds(
			new google.maps.LatLng( MAP_DEFAULT_CENTER[0] - 10, MAP_DEFAULT_CENTER[1] - 10 ),
			new google.maps.LatLng( MAP_DEFAULT_CENTER[0] + 10, MAP_DEFAULT_CENTER[1] + 10 )
		);
	},
	// custom batch geocoding method
	// build in geocoder doesn't work with batch requests for some reason
	// issue filed: https://github.com/mapbox/mapbox.js/issues/708
	geocodeQuery: function( query, callback ){
		query = _.isArray( query ) ? query : [ query ];
		callback = callback || NO_OP;
		async.map( query, function( location, cb ){
			this.geocoder.geocode({
				address: location,
				bounds: this.geocoder_bounds
			}, function( results, status ){
				if( status !== 'OK' ) return cb( null, null );
				var lat = results[0].geometry.location.d;
				var lon = results[0].geometry.location.e;
				return cb( null, [ lat, lon ] );
			});
		}.bind( this ), callback.bind( this ) );
	},
	drawMarkers: function(){
		var map = this.map;
		var location_names = this.collection.pluck('locations');
		this.geocodeQuery( location_names, function( err, data ){
			this.collection.each( function( location, i ){
				var geocoding_results = data[i];
				if( !geocoding_results ) return;
				var marker = new google.maps.Marker({
					position: new google.maps.LatLng( geocoding_results[0], geocoding_results[1] ),
					title: location.get('title'),
					map: map
				});
			});
		});
	}
});<commit_msg>improve initial map center<commit_after>var MAP_DEFAULT_CENTER = [ 37.79, -122.419 ];
var MAP_DEFAULT_ZOOM = 12;
var NO_OP = function(){};

var Backbone = require('backbone');
var $ = Backbone.$ = require('jquery');
var _ = require('underscore');
var async = require('async');
require('mapbox.js'); // attaches "automatically" to window.L

module.exports = Backbone.View.extend({
	initialize: function(){
		_.bindAll( this, 'geocodeQuery' );
		this.render();
		this.listenTo( this.collection, 'reset', this.drawMarkers );
	},
	render: function(){
		this.map = new google.maps.Map( this.el, {
			center: new google.maps.LatLng( MAP_DEFAULT_CENTER[0], MAP_DEFAULT_CENTER[1] ),
			zoom: MAP_DEFAULT_ZOOM
		});
		this.geocoder = new google.maps.Geocoder();
		this.geocoder_bounds = new google.maps.LatLngBounds(
			new google.maps.LatLng( MAP_DEFAULT_CENTER[0] - 10, MAP_DEFAULT_CENTER[1] - 10 ),
			new google.maps.LatLng( MAP_DEFAULT_CENTER[0] + 10, MAP_DEFAULT_CENTER[1] + 10 )
		);
	},
	// custom batch geocoding method
	// build in geocoder doesn't work with batch requests for some reason
	// issue filed: https://github.com/mapbox/mapbox.js/issues/708
	geocodeQuery: function( query, callback ){
		query = _.isArray( query ) ? query : [ query ];
		callback = callback || NO_OP;
		async.map( query, function( location, cb ){
			this.geocoder.geocode({
				address: location,
				bounds: this.geocoder_bounds
			}, function( results, status ){
				if( status !== 'OK' ) return cb( null, null );
				var lat = results[0].geometry.location.d;
				var lon = results[0].geometry.location.e;
				return cb( null, [ lat, lon ] );
			});
		}.bind( this ), callback.bind( this ) );
	},
	drawMarkers: function(){
		var map = this.map;
		var location_names = this.collection.pluck('locations');
		this.geocodeQuery( location_names, function( err, data ){
			this.collection.each( function( location, i ){
				var geocoding_results = data[i];
				if( !geocoding_results ) return;
				var marker = new google.maps.Marker({
					position: new google.maps.LatLng( geocoding_results[0], geocoding_results[1] ),
					title: location.get('title'),
					map: map
				});
			});
		});
	}
});<|endoftext|>


<commit_before>// Constants
var EVENT_TYPES = ['onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout'];
var EVENT_TYPES_LENGTH = EVENT_TYPES.length;
var IS_BROWSER = typeof document != 'undefined';
var TEXT_ALIGNMENTS = ['left', 'center', 'right'];

// Constructor
function Entity(defaultAttrHashName, name, canviz, rootGraph, parentGraph, immediateGraph) {
  if (!(this instanceof Entity)) return new Entity(defaultAttrHashName, name, canviz, rootGraph, parentGraph, immediateGraph);
  this.defaultAttrHashName = defaultAttrHashName;
  this.name = name;
  this.canviz = canviz;
  this.rootGraph = rootGraph;
  this.parentGraph = parentGraph;
  this.immediateGraph = immediateGraph;
  this.attrs = {};
  this.drawAttrs = {};
}

// Prototype
Entity.prototype = {
  constructor: Entity,
  initBB: function () {
    var matches = this.getAttr('pos').match(/([0-9.]+),([0-9.]+)/);
    var x = Number(matches[1]);
    var y = Number(matches[2]);
    this.bbRect = Rect(x, y, x, y);
  },
  getAttr: function (attrName, escString) {
    if (typeof escString == 'undefined') escString = false;
    var self = this;
    var attrValue = this.attrs[attrName];
    if (typeof attrValue == 'undefined') {
      var graph = this.parentGraph;
      while (typeof graph != 'undefined') {
        attrValue = graph[this.defaultAttrHashName][attrName];
        if (typeof attrValue == 'undefined') {
          graph = graph.parentGraph;
        } else {
          break;
        }
      }
    }
    if (attrValue && escString) {
      attrValue = attrValue.replace(this.ESC_STRING_MATCH_RE, function (match, p1) {
        switch (p1) {
          case 'N': // fall through
          case 'E': return self.name;
          case 'T': return self.tailNode;
          case 'H': return self.headNode;
          case 'G': return self.immediateGraph.name;
          case 'L': return self.getAttr('label', true);
        }
        return match;
      });
    }
    return attrValue;
  },
  draw: function (ctx, ctxScale, redrawCanvasOnly) {
    var i, tokens, fillColor, strokeColor;
    if (!redrawCanvasOnly) {
      this.initBB();
      if (IS_BROWSER) {
        var bbDiv = document.createElement('div');
        this.canviz.elements.appendChild(bbDiv);
      }
    }
    ctx.lineWidth = 1;
    var keys = objectKeys(this.drawAttrs),
      keysLength = keys.length;
    for (var k = 0; k < keysLength; ++k) {
      var command = this.drawAttrs[keys[k]],
        tokenizer = Tokenizer(command),
        token = tokenizer.takeChars();
//      debug(command);
      if (token) {
        var dashStyle = 'solid';
        ctx.save();
        while (token) {
//          debug('processing token ' + token);
          switch (token) {
            case 'E': // filled ellipse
            case 'e': // unfilled ellipse
              var filled = ('E' == token);
              var cx = tokenizer.takeNumber();
              var cy = tokenizer.takeNumber();
              var rx = tokenizer.takeNumber();
              var ry = tokenizer.takeNumber();
              var path = Ellipse(cx, cy, rx, ry);
              break;
            case 'P': // filled polygon
            case 'p': // unfilled polygon
            case 'L': // polyline
              var filled = ('P' == token);
              var closed = ('L' != token);
              var numPoints = tokenizer.takeNumber();
              tokens = tokenizer.takeNumber(2 * numPoints); // points
              var path = Path();
              for (i = 2; i < 2 * numPoints; i += 2) {
                path.addBezier([
                  Point(tokens[i - 2], tokens[i - 1]),
                  Point(tokens[i], tokens[i + 1])
                ]);
              }
              if (closed) {
                path.addBezier([
                  Point(tokens[2 * numPoints - 2], tokens[2 * numPoints - 1]),
                  Point(tokens[0], tokens[1])
                ]);
              }
              break;
            case 'B': // unfilled b-spline
            case 'b': // filled b-spline
              var filled = ('b' == token);
              var numPoints = tokenizer.takeNumber();
              tokens = tokenizer.takeNumber(2 * numPoints); // points
              var path = Path();
              for (i = 2; i < 2 * numPoints; i += 6) {
                path.addBezier([
                  Point(tokens[i - 2], tokens[i - 1]),
                  Point(tokens[i], tokens[i + 1]),
                  Point(tokens[i + 2], tokens[i + 3]),
                  Point(tokens[i + 4], tokens[i + 5])
                ]);
              }
              break;
            case 'I': // image
              var l = tokenizer.takeNumber();
              var b = tokenizer.takeNumber();
              var w = tokenizer.takeNumber();
              var h = tokenizer.takeNumber();
              var src = tokenizer.takeString();
              if (!this.canviz.images[src]) {
                this.canviz.images[src] = CanvizImage(this.canviz, src);
              }

              ctx.save();
              ctx.translate(l, b);
              // Uninvert the coordinate system so the image isn't drawn upside down.
              if (this.canviz.invertY) ctx.scale(1, -1);
              this.canviz.images[src].draw(ctx, 0, -h, w, h);
              ctx.restore();
              break;
            case 'T': // text
              var left = tokenizer.takeNumber(),
                bottom = tokenizer.takeNumber(),
                textAlignIndex = 1 + tokenizer.takeNumber(),
                textAlign = TEXT_ALIGNMENTS[textAlignIndex],
                textWidth = tokenizer.takeNumber(),
                str = tokenizer.takeString();

              if (!/^\s*$/.test(str)) {
                switch (this.canviz.textMode) {
                  case 'canvas':
                    ctx.save();
                    ctx.translate(left - textAlignIndex * textWidth / 2, bottom);
                    // Uninvert the coordinate system so the text isn't drawn upside down.
                    if (this.canviz.invertY) ctx.scale(1, -1);
                    ctx.font = fontSize + 'px ' + fontFamily;
                    // xdot uses pen color for text, but canvas uses fill color
                    ctx.fillStyle = ctx.strokeStyle;
                    //var metrics = ctx.measureText(str);
                    //console.log(str, metrics);
                    ctx.fillText(str, 0, 0); // TODO: the Y-coordinate is even less correct than when using the DOM
                    ctx.restore();
                    break;
                  case 'dom':
                    if (!redrawCanvasOnly) {
                      str = escapeHtml(str).replace(/ /g, ' ');
                      left = this.canviz.marginX + this.canviz.paddingX + left - textAlignIndex * textWidth;
                      var top = this.canviz.marginY + this.canviz.paddingY + (this.canviz.invertY ? this.canviz.height - bottom : bottom) - this.canviz.bbScale * fontSize;
                      var text;
                      var href = this.getAttr('URL', true) || this.getAttr('href', true);
                      if (href) {
                        var target = this.getAttr('target', true) || '_self';
                        var tooltip = this.getAttr('tooltip', true) || this.getAttr('label', true);
//                        debug(this.name + ', href ' + href + ', target ' + target + ', tooltip ' + tooltip);
                        text = document.createElement('a');
                        text.setAttribute('href', href);
                        text.setAttribute('target', target);
                        text.setAttribute('title', tooltip);
                        for (var e = 0; e < EVENT_TYPES_LENGTH; ++e) {
                          var attrName = EVENT_TYPES[e];
                          var attrValue = this.getAttr(attrName, true);
                          if (attrValue) {
                            text.setAttribute(attrName, attrValue);
                          }
                        }
                        text.style.textDecoration = 'none';
                      } else {
                        text = document.createElement('span');
                      }
                      text.innerHTML = str;
                      text.style.fontSize = (fontSize * ctxScale * this.canviz.bbScale) + 'px';
                      text.style.fontFamily = fontFamily;
                      text.style.color = strokeColor.textColor;
                      text.style.position = 'absolute';
                      text.style.textAlign = textAlign;
                      text.style.left = ctxScale * left + 'px';
                      text.style.top = ctxScale * top + 'px';
                      text.style.width = (ctxScale * 2 * textWidth) + 'px';
                      if (strokeColor.opacity < 1) setOpacity(text, strokeColor.opacity);
                      this.canviz.elements.appendChild(text);
                    }
                    break;
                }
              }
              break;
            case 'C': // set fill color
            case 'c': // set pen color
              var fill = ('C' == token);
              var color = this.parseColor(tokenizer.takeString());
              if (fill) {
                fillColor = color;
                ctx.fillStyle = color.canvasColor;
              } else {
                strokeColor = color;
                ctx.strokeStyle = color.canvasColor;
              }
              break;
            case 'F': // set font
              var fontSize = tokenizer.takeNumber();
              var fontFamily = tokenizer.takeString();
              if (fontFamily == 'Times-Roman') fontFamily = 'Times';
//              debug('set font ' + fontSize + 'pt ' + fontFamily);
              break;
            case 'S': // set style
              var style = tokenizer.takeString();
              switch (style) {
                case 'dashed':
                case 'dotted':
                  dashStyle = style;
                  break;
                case 'bold':
                  ctx.lineWidth = 2;
                  break;
                default:
                  var matches = style.match(/^setlinewidth\((.*)\)$/);
                  if (matches) {
                    ctx.lineWidth = Number(matches[1]);
                  }
              }
              break;
            default:
              debug('unknown token ' + token);
              return;
          }
          if (path) {
            this.canviz.drawPath(ctx, path, filled, dashStyle);
            if (!redrawCanvasOnly) this.bbRect.expandToInclude(path.getBB());
            path = undefined;
          }
          token = tokenizer.takeChars();
        }
        if (IS_BROWSER && !redrawCanvasOnly) {
          bbDiv.style.position = 'absolute';
          bbDiv.style.left = Math.round(ctxScale * (this.canviz.marginX + this.canviz.paddingX + this.bbRect.l)) + 'px';
          bbDiv.style.top = Math.round(ctxScale * (this.canviz.marginY + this.canviz.paddingY + this.bbRect.t)) + 'px';
          bbDiv.style.width = Math.round(ctxScale * this.bbRect.getWidth()) + 'px';
          bbDiv.style.height = Math.round(ctxScale * this.bbRect.getHeight()) + 'px';
        }
        ctx.restore();
      }
    }
  },
  parseColor: function (color) {
    var parsedColor = {opacity: 1};
    // rgb/rgba
    if (/^#(?:[0-9a-f]{2}\s*){3,4}$/i.test(color)) {
      return parseHexColor(color);
    }
    // hsv
    var matches = color.match(/^(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)$/);
    if (matches) {
      parsedColor.canvasColor = parsedColor.textColor = hsvToRgbColor(matches[1], matches[2], matches[3]);
      return parsedColor;
    }
    // gradient
    matches = color.match(/^(\[[^\]]+\]|\(([^)]+)\))$/);
    if (matches) {
      var ctx = this.canviz.ctx;
      var match = matches[1];
      var radial = matches[2];
      var tokenizer = Tokenizer(match.substr(1, match.length - 2));
      var x0 = tokenizer.takeNumber();
      var y0 = tokenizer.takeNumber();
      var r0, x1, y1, r1, colorStops, gradient;
      if (radial) r0 = tokenizer.takeNumber();
      x1 = tokenizer.takeNumber();
      y1 = tokenizer.takeNumber();
      if (1) { // http://www.graphviz.org/mantisbt/view.php?id=2336
        if (this.canviz.invertY) {
          y0 *= -1;
          y1 *= -1;
        } else {
          y0 -= this.canviz.height;
          y1 -= this.canviz.height;
          r1 = y1;
          y1 = y0;
          y0 = r1;
        }
      }
      if (radial) {
        r1 = tokenizer.takeNumber();
        gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
      } else {
        gradient = ctx.createLinearGradient(x0, y0, x1, y1);
      }
      colorStops = tokenizer.takeNumber();
      while (colorStops--) {
        gradient.addColorStop(tokenizer.takeNumber(), tokenizer.takeString());
      }
      parsedColor.canvasColor = gradient;
      return parsedColor;
    }
    // named color
    var colorScheme = this.getAttr('colorscheme') || 'X11';
    var colorName = color;
    matches = color.match(/^\/(.*)\/(.*)$/);
    if (matches) {
      if (matches[1]) {
        colorScheme = matches[1];
      }
      colorName = matches[2];
    } else {
      matches = color.match(/^\/(.*)$/);
      if (matches) {
        colorScheme = 'X11';
        colorName = matches[1];
      }
    }
    colorName = colorName.toLowerCase();
    var colorSchemeName = colorScheme.toLowerCase();
    var colorSchemeData = Canviz.colors[colorSchemeName];
    if (colorSchemeData) {
      var colorData = colorSchemeData[colorName];
      if (colorData) {
        return parseHexColor('#' + colorData);
      }
    }
    colorData = Canviz.colors.fallback[colorName];
    if (colorData) {
      return parseHexColor('#' + colorData);
    }
    if (!colorSchemeData) {
      debug('unknown color scheme ' + colorScheme);
    }
    // unknown
    debug('unknown color ' + color + '; color scheme is ' + colorScheme);
    parsedColor.canvasColor = parsedColor.textColor = '#000000';
    return parsedColor;
  }
};

// Exports
module.exports = Entity;

// Dependencies
var Canviz = require('./Canviz.js');
var CanvizImage = require('./Image.js');
var debug = require('./debug.js');
var Ellipse = require('./path/Ellipse.js');
var escapeHtml = require('escape-html');
var hsvToRgbColor = require('./hsvToRgbColor.js');
var objectKeys = require('./path/objectKeys.js');
var parseHexColor = require('./parseHexColor.js');
var Path = require('./path/Path.js');
var Point = require('./path/Point.js');
var Rect = require('./path/Rect.js');
var setOpacity = require('./setOpacity.js');
var Tokenizer = require('./Tokenizer.js');
<commit_msg>rewrite some lines to save space<commit_after>// Constants
var EVENT_TYPES = ['onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout'];
var EVENT_TYPES_LENGTH = EVENT_TYPES.length;
var IS_BROWSER = typeof document != 'undefined';
var TEXT_ALIGNMENTS = ['left', 'center', 'right'];

// Constructor
function Entity(defaultAttrHashName, name, canviz, rootGraph, parentGraph, immediateGraph) {
  if (!(this instanceof Entity)) return new Entity(defaultAttrHashName, name, canviz, rootGraph, parentGraph, immediateGraph);
  this.defaultAttrHashName = defaultAttrHashName;
  this.name = name;
  this.canviz = canviz;
  this.rootGraph = rootGraph;
  this.parentGraph = parentGraph;
  this.immediateGraph = immediateGraph;
  this.attrs = {};
  this.drawAttrs = {};
}

// Prototype
Entity.prototype = {
  constructor: Entity,
  initBB: function () {
    var xy = this.getAttr('pos').split(',');
    var x = Number(xy[0]);
    var y = Number(xy[1]);
    this.bbRect = Rect(x, y, x, y);
  },
  getAttr: function (attrName, escString) {
    if (typeof escString == 'undefined') escString = false;
    var self = this;
    var attrValue = this.attrs[attrName];
    if (typeof attrValue == 'undefined') {
      var graph = this.parentGraph;
      while (typeof graph != 'undefined') {
        attrValue = graph[this.defaultAttrHashName][attrName];
        if (typeof attrValue != 'undefined') break;
        graph = graph.parentGraph;
      }
    }
    if (attrValue && escString) {
      attrValue = attrValue.replace(this.ESC_STRING_MATCH_RE, function (match, p1) {
        switch (p1) {
          case 'N': // fall through
          case 'E': return self.name;
          case 'T': return self.tailNode;
          case 'H': return self.headNode;
          case 'G': return self.immediateGraph.name;
          case 'L': return self.getAttr('label', true);
        }
        return match;
      });
    }
    return attrValue;
  },
  draw: function (ctx, ctxScale, redrawCanvasOnly) {
    var i, tokens, fillColor, strokeColor;
    if (!redrawCanvasOnly) {
      this.initBB();
      if (IS_BROWSER) {
        var bbDiv = document.createElement('div');
        this.canviz.elements.appendChild(bbDiv);
      }
    }
    ctx.lineWidth = 1;
    var keys = objectKeys(this.drawAttrs),
      keysLength = keys.length;
    for (var k = 0; k < keysLength; ++k) {
      var command = this.drawAttrs[keys[k]],
        tokenizer = Tokenizer(command),
        token = tokenizer.takeChars();
//      debug(command);
      if (token) {
        var dashStyle = 'solid';
        ctx.save();
        while (token) {
//          debug('processing token ' + token);
          switch (token) {
            case 'E': // filled ellipse
            case 'e': // unfilled ellipse
              var filled = ('E' == token);
              var cx = tokenizer.takeNumber();
              var cy = tokenizer.takeNumber();
              var rx = tokenizer.takeNumber();
              var ry = tokenizer.takeNumber();
              var path = Ellipse(cx, cy, rx, ry);
              break;
            case 'P': // filled polygon
            case 'p': // unfilled polygon
            case 'L': // polyline
              var filled = ('P' == token);
              var closed = ('L' != token);
              var numPoints = tokenizer.takeNumber();
              tokens = tokenizer.takeNumber(2 * numPoints); // points
              var path = Path();
              for (i = 2; i < 2 * numPoints; i += 2) {
                path.addBezier([
                  Point(tokens[i - 2], tokens[i - 1]),
                  Point(tokens[i], tokens[i + 1])
                ]);
              }
              if (closed) {
                path.addBezier([
                  Point(tokens[2 * numPoints - 2], tokens[2 * numPoints - 1]),
                  Point(tokens[0], tokens[1])
                ]);
              }
              break;
            case 'B': // unfilled b-spline
            case 'b': // filled b-spline
              var filled = ('b' == token);
              var numPoints = tokenizer.takeNumber();
              tokens = tokenizer.takeNumber(2 * numPoints); // points
              var path = Path();
              for (i = 2; i < 2 * numPoints; i += 6) {
                path.addBezier([
                  Point(tokens[i - 2], tokens[i - 1]),
                  Point(tokens[i], tokens[i + 1]),
                  Point(tokens[i + 2], tokens[i + 3]),
                  Point(tokens[i + 4], tokens[i + 5])
                ]);
              }
              break;
            case 'I': // image
              var l = tokenizer.takeNumber();
              var b = tokenizer.takeNumber();
              var w = tokenizer.takeNumber();
              var h = tokenizer.takeNumber();
              var src = tokenizer.takeString();
              if (!this.canviz.images[src]) {
                this.canviz.images[src] = CanvizImage(this.canviz, src);
              }

              ctx.save();
              ctx.translate(l, b);
              // Uninvert the coordinate system so the image isn't drawn upside down.
              if (this.canviz.invertY) ctx.scale(1, -1);
              this.canviz.images[src].draw(ctx, 0, -h, w, h);
              ctx.restore();
              break;
            case 'T': // text
              var left = tokenizer.takeNumber(),
                bottom = tokenizer.takeNumber(),
                textAlignIndex = 1 + tokenizer.takeNumber(),
                textAlign = TEXT_ALIGNMENTS[textAlignIndex],
                textWidth = tokenizer.takeNumber(),
                str = tokenizer.takeString();

              if (!/^\s*$/.test(str)) {
                switch (this.canviz.textMode) {
                  case 'canvas':
                    ctx.save();
                    ctx.translate(left - textAlignIndex * textWidth / 2, bottom);
                    // Uninvert the coordinate system so the text isn't drawn upside down.
                    if (this.canviz.invertY) ctx.scale(1, -1);
                    ctx.font = fontSize + 'px ' + fontFamily;
                    // xdot uses pen color for text, but canvas uses fill color
                    ctx.fillStyle = ctx.strokeStyle;
                    //var metrics = ctx.measureText(str);
                    //console.log(str, metrics);
                    ctx.fillText(str, 0, 0); // TODO: the Y-coordinate is even less correct than when using the DOM
                    ctx.restore();
                    break;
                  case 'dom':
                    if (!redrawCanvasOnly) {
                      str = escapeHtml(str).replace(/ /g, ' ');
                      left = this.canviz.marginX + this.canviz.paddingX + left - textAlignIndex * textWidth;
                      var top = this.canviz.marginY + this.canviz.paddingY + (this.canviz.invertY ? this.canviz.height - bottom : bottom) - this.canviz.bbScale * fontSize;
                      var text;
                      var href = this.getAttr('URL', true) || this.getAttr('href', true);
                      if (href) {
                        var target = this.getAttr('target', true) || '_self';
                        var tooltip = this.getAttr('tooltip', true) || this.getAttr('label', true);
//                        debug(this.name + ', href ' + href + ', target ' + target + ', tooltip ' + tooltip);
                        text = document.createElement('a');
                        text.setAttribute('href', href);
                        text.setAttribute('target', target);
                        text.setAttribute('title', tooltip);
                        for (var e = 0; e < EVENT_TYPES_LENGTH; ++e) {
                          var attrName = EVENT_TYPES[e];
                          var attrValue = this.getAttr(attrName, true);
                          if (attrValue) {
                            text.setAttribute(attrName, attrValue);
                          }
                        }
                        text.style.textDecoration = 'none';
                      } else {
                        text = document.createElement('span');
                      }
                      text.innerHTML = str;
                      text.style.fontSize = (fontSize * ctxScale * this.canviz.bbScale) + 'px';
                      text.style.fontFamily = fontFamily;
                      text.style.color = strokeColor.textColor;
                      text.style.position = 'absolute';
                      text.style.textAlign = textAlign;
                      text.style.left = ctxScale * left + 'px';
                      text.style.top = ctxScale * top + 'px';
                      text.style.width = (ctxScale * 2 * textWidth) + 'px';
                      if (strokeColor.opacity < 1) setOpacity(text, strokeColor.opacity);
                      this.canviz.elements.appendChild(text);
                    }
                    break;
                }
              }
              break;
            case 'C': // set fill color
            case 'c': // set pen color
              var fill = ('C' == token);
              var color = this.parseColor(tokenizer.takeString());
              if (fill) {
                fillColor = color;
                ctx.fillStyle = color.canvasColor;
              } else {
                strokeColor = color;
                ctx.strokeStyle = color.canvasColor;
              }
              break;
            case 'F': // set font
              var fontSize = tokenizer.takeNumber();
              var fontFamily = tokenizer.takeString();
              if (fontFamily == 'Times-Roman') fontFamily = 'Times';
//              debug('set font ' + fontSize + 'pt ' + fontFamily);
              break;
            case 'S': // set style
              var style = tokenizer.takeString();
              switch (style) {
                case 'dashed':
                case 'dotted':
                  dashStyle = style;
                  break;
                case 'bold':
                  ctx.lineWidth = 2;
                  break;
                default:
                  var matches = style.match(/^setlinewidth\((.*)\)$/);
                  if (matches) {
                    ctx.lineWidth = Number(matches[1]);
                  }
              }
              break;
            default:
              debug('unknown token ' + token);
              return;
          }
          if (path) {
            this.canviz.drawPath(ctx, path, filled, dashStyle);
            if (!redrawCanvasOnly) this.bbRect.expandToInclude(path.getBB());
            path = undefined;
          }
          token = tokenizer.takeChars();
        }
        if (IS_BROWSER && !redrawCanvasOnly) {
          bbDiv.style.position = 'absolute';
          bbDiv.style.left = Math.round(ctxScale * (this.canviz.marginX + this.canviz.paddingX + this.bbRect.l)) + 'px';
          bbDiv.style.top = Math.round(ctxScale * (this.canviz.marginY + this.canviz.paddingY + this.bbRect.t)) + 'px';
          bbDiv.style.width = Math.round(ctxScale * this.bbRect.getWidth()) + 'px';
          bbDiv.style.height = Math.round(ctxScale * this.bbRect.getHeight()) + 'px';
        }
        ctx.restore();
      }
    }
  },
  parseColor: function (color) {
    var parsedColor = {opacity: 1};
    // rgb/rgba
    if (/^#(?:[0-9a-f]{2}\s*){3,4}$/i.test(color)) {
      return parseHexColor(color);
    }
    // hsv
    var matches = color.match(/^(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)[\s,]+(\d+(?:\.\d+)?)$/);
    if (matches) {
      parsedColor.canvasColor = parsedColor.textColor = hsvToRgbColor(matches[1], matches[2], matches[3]);
      return parsedColor;
    }
    // gradient
    matches = color.match(/^(\[[^\]]+\]|\(([^)]+)\))$/);
    if (matches) {
      var ctx = this.canviz.ctx;
      var match = matches[1];
      var radial = matches[2];
      var tokenizer = Tokenizer(match.substr(1, match.length - 2));
      var x0 = tokenizer.takeNumber();
      var y0 = tokenizer.takeNumber();
      var r0, x1, y1, r1, colorStops, gradient;
      if (radial) r0 = tokenizer.takeNumber();
      x1 = tokenizer.takeNumber();
      y1 = tokenizer.takeNumber();
      if (1) { // http://www.graphviz.org/mantisbt/view.php?id=2336
        if (this.canviz.invertY) {
          y0 *= -1;
          y1 *= -1;
        } else {
          y0 -= this.canviz.height;
          y1 -= this.canviz.height;
          r1 = y1;
          y1 = y0;
          y0 = r1;
        }
      }
      if (radial) {
        r1 = tokenizer.takeNumber();
        gradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
      } else {
        gradient = ctx.createLinearGradient(x0, y0, x1, y1);
      }
      colorStops = tokenizer.takeNumber();
      while (colorStops--) {
        gradient.addColorStop(tokenizer.takeNumber(), tokenizer.takeString());
      }
      parsedColor.canvasColor = gradient;
      return parsedColor;
    }
    // named color
    var colorScheme = this.getAttr('colorscheme') || 'X11';
    var colorName = color;
    matches = color.match(/^\/(.*)\/(.*)$/);
    if (matches) {
      if (matches[1]) {
        colorScheme = matches[1];
      }
      colorName = matches[2];
    } else {
      matches = color.match(/^\/(.*)$/);
      if (matches) {
        colorScheme = 'X11';
        colorName = matches[1];
      }
    }
    colorName = colorName.toLowerCase();
    var colorSchemeName = colorScheme.toLowerCase();
    var colorSchemeData = Canviz.colors[colorSchemeName];
    if (colorSchemeData) {
      var colorData = colorSchemeData[colorName];
      if (colorData) {
        return parseHexColor('#' + colorData);
      }
    }
    colorData = Canviz.colors.fallback[colorName];
    if (colorData) {
      return parseHexColor('#' + colorData);
    }
    if (!colorSchemeData) {
      debug('unknown color scheme ' + colorScheme);
    }
    // unknown
    debug('unknown color ' + color + '; color scheme is ' + colorScheme);
    parsedColor.canvasColor = parsedColor.textColor = '#000000';
    return parsedColor;
  }
};

// Exports
module.exports = Entity;

// Dependencies
var Canviz = require('./Canviz.js');
var CanvizImage = require('./Image.js');
var debug = require('./debug.js');
var Ellipse = require('./path/Ellipse.js');
var escapeHtml = require('escape-html');
var hsvToRgbColor = require('./hsvToRgbColor.js');
var objectKeys = require('./path/objectKeys.js');
var parseHexColor = require('./parseHexColor.js');
var Path = require('./path/Path.js');
var Point = require('./path/Point.js');
var Rect = require('./path/Rect.js');
var setOpacity = require('./setOpacity.js');
var Tokenizer = require('./Tokenizer.js');
<|endoftext|>