<commit_before>goog.provide('ol.geom.Geometry');
goog.provide('ol.geom.GeometryLayout');
goog.provide('ol.geom.GeometryType');

goog.require('goog.functions');
goog.require('ol.Object');
goog.require('ol.extent');
goog.require('ol.proj');

/**
 * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`,
 * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`,
 * `'GeometryCollection'`, `'Circle'`.
 * @enum {string}
 * @api stable
 */
ol.geom.GeometryType = {
  POINT: 'Point',
  LINE_STRING: 'LineString',
  LINEAR_RING: 'LinearRing',
  POLYGON: 'Polygon',
  MULTI_POINT: 'MultiPoint',
  MULTI_LINE_STRING: 'MultiLineString',
  MULTI_POLYGON: 'MultiPolygon',
  GEOMETRY_COLLECTION: 'GeometryCollection',
  CIRCLE: 'Circle'
};

/**
 * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z')
 * or measure ('M') coordinate is available. Supported values are `'XY'`,
 * `'XYZ'`, `'XYM'`, `'XYZM'`.
 * @enum {string}
 * @api stable
 */
ol.geom.GeometryLayout = {
  XY: 'XY',
  XYZ: 'XYZ',
  XYM: 'XYM',
  XYZM: 'XYZM'
};

/**
 * @classdesc
 * Abstract base class; normally only used for creating subclasses and not
 * instantiated in apps.
 * Base class for vector geometries.
 *
 * @constructor
 * @extends {ol.Object}
 * @fires change Triggered when the geometry changes.
 * @api stable
 */
ol.geom.Geometry = function() {

  goog.base(this);

  /**
   * @private
   * @type {ol.Extent}
   */
  this.extent_ = ol.extent.createEmpty();

  /**
   * @private
   * @type {number}
   */
  this.extentRevision_ = -1;

  /**
   * @protected
   * @type {Object.<string, ol.geom.Geometry>}
   */
  this.simplifiedGeometryCache = {};

  /**
   * @protected
   * @type {number}
   */
  this.simplifiedGeometryMaxMinSquaredTolerance = 0;

  /**
   * @protected
   * @type {number}
   */
  this.simplifiedGeometryRevision = 0;

};
goog.inherits(ol.geom.Geometry, ol.Object);

/**
 * Make a complete copy of the geometry.
 * @function
 * @return {!ol.geom.Geometry} Clone.
 */
ol.geom.Geometry.prototype.clone = goog.abstractMethod;

/**
 * @param {number} x X.
 * @param {number} y Y.
 * @param {ol.Coordinate} closestPoint Closest point.
 * @param {number} minSquaredDistance Minimum squared distance.
 * @return {number} Minimum squared distance.
 */
ol.geom.Geometry.prototype.closestPointXY = goog.abstractMethod;

/**
 * @param {ol.Coordinate} point Point.
 * @param {ol.Coordinate=} opt_closestPoint Closest point.
 * @return {ol.Coordinate} Closest point.
 * @api stable
 */
ol.geom.Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) {
  var closestPoint = goog.isDef(opt_closestPoint) ?
      opt_closestPoint : [NaN, NaN];
  this.closestPointXY(point[0], point[1], closestPoint, Infinity);
  return closestPoint;
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @return {boolean} Contains coordinate.
 */
ol.geom.Geometry.prototype.containsCoordinate = function(coordinate) {
  return this.containsXY(coordinate[0], coordinate[1]);
};

/**
 * @param {ol.Extent} extent Extent.
 * @protected
 * @return {ol.Extent} extent Extent.
 */
ol.geom.Geometry.prototype.computeExtent = goog.abstractMethod;

/**
 * @param {number} x X.
 * @param {number} y Y.
 * @return {boolean} Contains (x, y).
 */
ol.geom.Geometry.prototype.containsXY = goog.functions.FALSE;

/**
 * Get the extent of the geometry.
 * @param {ol.Extent=} opt_extent Extent.
 * @return {ol.Extent} extent Extent.
 * @api stable
 */
ol.geom.Geometry.prototype.getExtent = function(opt_extent) {
  if (this.extentRevision_ != this.getRevision()) {
    this.extent_ = this.computeExtent(this.extent_);
    this.extentRevision_ = this.getRevision();
  }
  return ol.extent.returnOrUpdate(this.extent_, opt_extent);
};

/**
 * Create a simplified version of this geometry using the Douglas Peucker
 * algorithm.
 * @see http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
 * @function
 * @param {number} squaredTolerance Squared tolerance.
 * @return {ol.geom.Geometry} Simplified geometry.
 */
ol.geom.Geometry.prototype.getSimplifiedGeometry = goog.abstractMethod;

/**
 * Get the type of this geometry.
 * @function
 * @return {ol.geom.GeometryType} Geometry type.
 */
ol.geom.Geometry.prototype.getType = goog.abstractMethod;

/**
 * Apply a transform function to each coordinate of the geometry.
 * The geometry is modified in place.
 * If you do not want the geometry modified in place, first clone() it and
 * then use this function on the clone.
 * @function
 * @param {ol.TransformFunction} transformFn Transform.
 */
ol.geom.Geometry.prototype.applyTransform = goog.abstractMethod;

/**
 * Test if the geometry and the passed extent intersect.
 * @param {ol.Extent} extent Extent.
 * @return {boolean} `true` if the geometry and the extent intersect.
 * @function
 */
ol.geom.Geometry.prototype.intersectsExtent = goog.abstractMethod;

/**
 * Translate the geometry.  This modifies the geometry coordinates in place.  If
 * instead you want a new geometry, first `clone()` this geometry.
 * @param {number} deltaX Delta X.
 * @param {number} deltaY Delta Y.
 * @function
 */
ol.geom.Geometry.prototype.translate = goog.abstractMethod;

/**
 * Transform each coordinate of the geometry from one coordinate reference
 * system to another. The geometry is modified in place.
 * For example, a line will be transformed to a line and a circle to a circle.
 * If you do not want the geometry modified in place, first clone() it and
 * then use this function on the clone.
 *
 * @param {ol.proj.ProjectionLike} source The current projection.  Can be a
 *     string identifier or a {@link ol.proj.Projection} object.
 * @param {ol.proj.ProjectionLike} destination The desired projection.  Can be a
 *     string identifier or a {@link ol.proj.Projection} object.
 * @return {ol.geom.Geometry} This geometry.  Note that original geometry is
 *     modified in place.
 */
ol.geom.Geometry.prototype.transform = function(source, destination) {
  this.applyTransform(ol.proj.getTransform(source, destination));
  return this;
};
<commit_msg>Add basic docs for geom.geometry<commit_after>goog.provide('ol.geom.Geometry');
goog.provide('ol.geom.GeometryLayout');
goog.provide('ol.geom.GeometryType');

goog.require('goog.functions');
goog.require('ol.Object');
goog.require('ol.extent');
goog.require('ol.proj');

/**
 * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`,
 * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`,
 * `'GeometryCollection'`, `'Circle'`.
 * @enum {string}
 * @api stable
 */
ol.geom.GeometryType = {
  POINT: 'Point',
  LINE_STRING: 'LineString',
  LINEAR_RING: 'LinearRing',
  POLYGON: 'Polygon',
  MULTI_POINT: 'MultiPoint',
  MULTI_LINE_STRING: 'MultiLineString',
  MULTI_POLYGON: 'MultiPolygon',
  GEOMETRY_COLLECTION: 'GeometryCollection',
  CIRCLE: 'Circle'
};

/**
 * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z')
 * or measure ('M') coordinate is available. Supported values are `'XY'`,
 * `'XYZ'`, `'XYM'`, `'XYZM'`.
 * @enum {string}
 * @api stable
 */
ol.geom.GeometryLayout = {
  XY: 'XY',
  XYZ: 'XYZ',
  XYM: 'XYM',
  XYZM: 'XYZM'
};

/**
 * @classdesc
 * Abstract base class; normally only used for creating subclasses and not
 * instantiated in apps.
 * Base class for vector geometries.
 *
 * @constructor
 * @extends {ol.Object}
 * @fires change Triggered when the geometry changes.
 * @api stable
 */
ol.geom.Geometry = function() {

  goog.base(this);

  /**
   * @private
   * @type {ol.Extent}
   */
  this.extent_ = ol.extent.createEmpty();

  /**
   * @private
   * @type {number}
   */
  this.extentRevision_ = -1;

  /**
   * @protected
   * @type {Object.<string, ol.geom.Geometry>}
   */
  this.simplifiedGeometryCache = {};

  /**
   * @protected
   * @type {number}
   */
  this.simplifiedGeometryMaxMinSquaredTolerance = 0;

  /**
   * @protected
   * @type {number}
   */
  this.simplifiedGeometryRevision = 0;

};
goog.inherits(ol.geom.Geometry, ol.Object);

/**
 * Make a complete copy of the geometry.
 * @function
 * @return {!ol.geom.Geometry} Clone.
 */
ol.geom.Geometry.prototype.clone = goog.abstractMethod;

/**
 * @param {number} x X.
 * @param {number} y Y.
 * @param {ol.Coordinate} closestPoint Closest point.
 * @param {number} minSquaredDistance Minimum squared distance.
 * @return {number} Minimum squared distance.
 */
ol.geom.Geometry.prototype.closestPointXY = goog.abstractMethod;

/**
 * Return the closest point of the geometry to the passed point as
 * {@link ol.Coordinate coordinate}.
 * @param {ol.Coordinate} point Point.
 * @param {ol.Coordinate=} opt_closestPoint Closest point.
 * @return {ol.Coordinate} Closest point.
 * @api stable
 */
ol.geom.Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) {
  var closestPoint = goog.isDef(opt_closestPoint) ?
      opt_closestPoint : [NaN, NaN];
  this.closestPointXY(point[0], point[1], closestPoint, Infinity);
  return closestPoint;
};

/**
 * @param {ol.Coordinate} coordinate Coordinate.
 * @return {boolean} Contains coordinate.
 */
ol.geom.Geometry.prototype.containsCoordinate = function(coordinate) {
  return this.containsXY(coordinate[0], coordinate[1]);
};

/**
 * @param {ol.Extent} extent Extent.
 * @protected
 * @return {ol.Extent} extent Extent.
 */
ol.geom.Geometry.prototype.computeExtent = goog.abstractMethod;

/**
 * @param {number} x X.
 * @param {number} y Y.
 * @return {boolean} Contains (x, y).
 */
ol.geom.Geometry.prototype.containsXY = goog.functions.FALSE;

/**
 * Get the extent of the geometry.
 * @param {ol.Extent=} opt_extent Extent.
 * @return {ol.Extent} extent Extent.
 * @api stable
 */
ol.geom.Geometry.prototype.getExtent = function(opt_extent) {
  if (this.extentRevision_ != this.getRevision()) {
    this.extent_ = this.computeExtent(this.extent_);
    this.extentRevision_ = this.getRevision();
  }
  return ol.extent.returnOrUpdate(this.extent_, opt_extent);
};

/**
 * Create a simplified version of this geometry using the Douglas Peucker
 * algorithm.
 * @see http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
 * @function
 * @param {number} squaredTolerance Squared tolerance.
 * @return {ol.geom.Geometry} Simplified geometry.
 */
ol.geom.Geometry.prototype.getSimplifiedGeometry = goog.abstractMethod;

/**
 * Get the type of this geometry.
 * @function
 * @return {ol.geom.GeometryType} Geometry type.
 */
ol.geom.Geometry.prototype.getType = goog.abstractMethod;

/**
 * Apply a transform function to each coordinate of the geometry.
 * The geometry is modified in place.
 * If you do not want the geometry modified in place, first clone() it and
 * then use this function on the clone.
 * @function
 * @param {ol.TransformFunction} transformFn Transform.
 */
ol.geom.Geometry.prototype.applyTransform = goog.abstractMethod;

/**
 * Test if the geometry and the passed extent intersect.
 * @param {ol.Extent} extent Extent.
 * @return {boolean} `true` if the geometry and the extent intersect.
 * @function
 */
ol.geom.Geometry.prototype.intersectsExtent = goog.abstractMethod;

/**
 * Translate the geometry.  This modifies the geometry coordinates in place.  If
 * instead you want a new geometry, first `clone()` this geometry.
 * @param {number} deltaX Delta X.
 * @param {number} deltaY Delta Y.
 * @function
 */
ol.geom.Geometry.prototype.translate = goog.abstractMethod;

/**
 * Transform each coordinate of the geometry from one coordinate reference
 * system to another. The geometry is modified in place.
 * For example, a line will be transformed to a line and a circle to a circle.
 * If you do not want the geometry modified in place, first clone() it and
 * then use this function on the clone.
 *
 * @param {ol.proj.ProjectionLike} source The current projection.  Can be a
 *     string identifier or a {@link ol.proj.Projection} object.
 * @param {ol.proj.ProjectionLike} destination The desired projection.  Can be a
 *     string identifier or a {@link ol.proj.Projection} object.
 * @return {ol.geom.Geometry} This geometry.  Note that original geometry is
 *     modified in place.
 */
ol.geom.Geometry.prototype.transform = function(source, destination) {
  this.applyTransform(ol.proj.getTransform(source, destination));
  return this;
};
<|endoftext|>


<commit_before>'use strict';

const { validate } = require('../validation');

// Validation for main options
const validateOptions = function ({ options }) {
  const schema = {
    type: 'object',
    required: ['conf'],
    properties: {

      conf: {
        type: ['string', 'object'],
      },

      onRequestError: {
        typeof: 'function',
        arity: 1,
      },

      logger: {
        typeof: 'function',
        returnType: 'function',
        arity: 1,
      },

      maxDataLength: {
        type: 'integer',
        minimum: 0,
      },

      defaultPageSize: {
        type: 'integer',
        minimum: 0,
      },

      maxPageSize: {
        type: 'integer',
        minimum: {
          $data: '1/defaultPageSize',
        },
      },

    },
    additionalProperties: false,
  };

  validate({ schema, data: options, reportInfo: { type: 'options' } });
};

module.exports = {
  validateOptions,
};
<commit_msg>Improve error message for options validation<commit_after>'use strict';

const { validate } = require('../validation');

// Validation for main options
const validateOptions = function ({ options }) {
  const schema = {
    type: 'object',
    required: ['conf'],
    properties: {

      conf: {
        type: ['string', 'object'],
      },

      onRequestError: {
        typeof: 'function',
        arity: 1,
      },

      logger: {
        typeof: 'function',
        returnType: 'function',
        arity: 1,
      },

      maxDataLength: {
        type: 'integer',
        minimum: 0,
      },

      defaultPageSize: {
        type: 'integer',
        minimum: 0,
      },

      maxPageSize: {
        type: 'integer',
        minimum: {
          $data: '1/defaultPageSize',
        },
      },

    },
    additionalProperties: false,
  };

  validate({ schema, data: options, reportInfo: { type: 'options', dataVar: 'options' } });
};

module.exports = {
  validateOptions,
};
<|endoftext|>


<commit_before>// Definition of Player object.
var Player = function() {
	this.id 		= null;
	this.position 	= null;
	this.team 		= null;
	this.alive		= null;
	this.clock		= null;
	this.turret_direction = 0;
	this.hull_direction = null;
	this.received_damage = false;
}

Player.prototype = {

}

// Definition of Model object
var Model = function(game) {
	this.players 	= {};
	this.clock		= 0;
	this.game		= game;
}

Model.prototype = {
	getPlayer: function(id) {
		var player = this.players[id];
		if (typeof(player) == 'undefined') {
			player = new Player();
			player.id = id;
			player.alive = true;
			this.players[id] = player;
		}
		return player;
	},
	update: function(frame) {
		// update clock
		if (typeof(frame.clock) == 'undefined') {
			return;
		}

		if (frame.clock != null) {
			this.clock = frame.clock;
		}

		// we are only interested in the packet if it has a clock / player / team values
		if (typeof(frame.position) != 'undefined'
				&& typeof(frame.player_id) != 'undefined') {
			var player = this.getPlayer(frame.player_id);
			player.position = frame.position;
			player.turret_direction += frame.hull_orientation[1] * 0.34 / 2;
			player.hull_direction = frame.hull_orientation[0];
			player.team 	= frame.team;
			player.clock	= this.clock;
		}

		if (typeof(frame.source) != 'undefined'
				&& typeof(frame.health) != 'undefined') {
			var player = this.getPlayer(frame.player_id);
			player.received_damage = true;
			player.clock = this.clock;
			var vehicles = this.game.vehicles;
			console.log(vehicles[frame.source].name + " (" + vehicles[frame.source].vehicleType + ") hit "
				+ vehicles[player.id].name + " (" + vehicles[player.id].vehicleType + ")" );
		}

		if (typeof(frame.target) != 'undefined'
				&& typeof(frame.destroyed_by) != 'undefined') {
			var player 	 = this.getPlayer(frame.target);
			player.alive = false;
			player.clock = this.clock;
		}
	}
}

var Viewer = function(target, serviceUrl) {
	this.target = target;
	this.serviceUrl = serviceUrl;
	this.model = null;

	// configure the target element as a replay viewer
	target.classList.add('replay-viewer');

	this.map = document.createElement('img');
	this.map.classList.add('map');
	this.map.width = this.map.height = 500
	this.target.appendChild(this.map);

	this.overlay = document.createElement('canvas');
	this.overlay.classList.add('overlay');
	this.overlay.width = this.overlay .height = 500;
	this.target.appendChild(this.overlay);

	this.link = document.createElement('a');
	this.link.classList.add('permalink');
	this.link.innerText = "PERMALINK";
	this.link.style.display = "none";
	this.target.appendChild(this.link);
}

Viewer.prototype = {
	replay: function(data) {
		var ctx = this.overlay.getContext("2d");
		this.model = new Model(data.summary);

		var update = function(model, packets, window_start, window_size, start_ix) {
			// model of the viewer change -> stop
			if (this.model != model) {
				return;
			}

			var window_end = window_start + window_size, ix;
			for (ix = start_ix; ix < packets.length; ix++) {
				var packet = packets[ix];

				if (typeof(packet.clock) == 'undefined') {
					continue;
				}

				// escape when outside of window size
				if (packet.clock > window_end) {
					break;
				}

				// update model with packet
				model.update(packet);
			}

			this.show(data, ctx);

			if (ix < packets.length) {
				setTimeout(update.bind(this, model, packets, window_end, window_size, ix), 100);
			}
		}

		update.call(this, this.model, data.packets, 50, 0.2, 0);
	},
	show: function(data, ctx) {
		// reset overlay
		ctx.clearRect(0, 0, 500, 500);

		ctx.fillStyle = "#FFFF00";
		ctx.textBaseline = "top";
		ctx.font = "bold 20px sans-serif";
		ctx.fillText(String(this.model.clock.toFixed(2)), 8, 5);

		for (var player_id in this.model.players) {
			var player = this.model.players[player_id];
			if (typeof(player.team) == undefined
					|| player.position == null
					|| [0,1].indexOf(player.team) < 0) {
				continue;
			}

			var coord = to_2d_coord(player.position, [-500, 500, -500, 500], 500, 500);

			var colors = [
				[0, 255, 0],
				[255, 0, 0]
			];

			var recorder_color = [0, 0, 255];

			ctx.lineWidth = 2;
			var color = player.id == data.recorder_id ?  recorder_color : colors[player.team];

			if (player.received_damage) {
				color = [255, 255, 255];
				player.received_damage = false;
			}

			var age   = player.alive ? ((this.model.clock - player.clock) / 20) : 0;
			age = age > 0.66 ? 0.66 : age;
			var style = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + (1 - age) +  ")";

			ctx.strokeStyle = ctx.fillStyle = style;
			ctx.beginPath();
			ctx.arc(coord.x, coord.y, 3, 0, 2*Math.PI);

			if (player.alive) {
				ctx.fill();
			} else {
				ctx.stroke();
			}

			if (player.id == data.recorder_id) {
				// draw turret direction
				ctx.beginPath();
				ctx.moveTo(coord.x,coord.y);
				var rotation = player.hull_direction  - Math.PI / 2 +  player.turret_direction ;
				ctx.lineTo(coord.x + 50*Math.cos(rotation),coord.y + 50*Math.sin(rotation));
				ctx.stroke();

				// draw turret direction
				ctx.strokeStyle = "#FFFF00";
				ctx.beginPath();
				ctx.moveTo(coord.x,coord.y);
				rotation = player.hull_direction  - Math.PI / 2;
				ctx.lineTo(coord.x + 50*Math.cos(rotation),coord.y + 50*Math.sin(rotation));
				ctx.stroke();
			}
		}
	},
	fetch: function(id) {
		this.serviceRequest({id: id});
	},
	process: function(file) {
		this.serviceRequest({file: file});
	},
	serviceRequest: function(values) {
		// send data request
		var replayRequest = new XMLHttpRequest();
		replayRequest.open("POST", this.serviceUrl, true);

		var map = this.map;
		var viewer = this;
		map.classList.add('loading');
		this.link.style.display = "none";
		replayRequest.onreadystatechange = (function(state) {
			if(replayRequest.readyState != XMLHttpRequest.DONE) {
				return;
			}

			if (replayRequest.status != 200) {
				alert('Server request failed!');
				return;
			}

			// proces data
			var response = JSON.parse(replayRequest.response);
			var data = response.data;
			var mapURL = 'maps/' + data["map"] + "_" + data["mode"] + ".png";
			map.setAttribute('src', mapURL);
			map.classList.remove('loading');
			this.link.style.display = "block";
			this.link.href = 'http://' + response.permalink;
			// play
			viewer.replay(data);
		}).bind(this);

		var formData = new FormData();
		for (key in values) {
			formData.append(key, values[key]);
		}

		replayRequest.send(formData);
	}
}

function to_2d_coord(position, map_boundaries, width, height) {
    var x = position[0], y = position[2], z = position[1];
    x = (x - map_boundaries[0]) * (width / (map_boundaries[1] - map_boundaries[0] + 1));
    y = (map_boundaries[3] - y) * (height / (map_boundaries[3] - map_boundaries[2] + 1));
    return { x: x, y: y };
}

function onRangeInputChange(e) {
	if (e.target.updating) {
		e.target.updating = true;
	}
}<commit_msg>add map boundaries to correctly interpret coordinates<commit_after>// Definition of Player object.
var Player = function() {
	this.id 		= null;
	this.position 	= null;
	this.team 		= null;
	this.alive		= null;
	this.clock		= null;
	this.turret_direction = 0;
	this.hull_direction = null;
	this.received_damage = false;
}

Player.prototype = {

}

// Definition of Model object
var Model = function(game, map_boundaries) {
	this.players 		= {};
	this.clock			= 0;
	this.game			= game;
	this.map_boundaries = map_boundaries;
}

Model.prototype = {
	getPlayer: function(id) {
		var player = this.players[id];
		if (typeof(player) == 'undefined') {
			player = new Player();
			player.id = id;
			player.alive = true;
			this.players[id] = player;
		}
		return player;
	},
	update: function(frame) {
		// update clock
		if (typeof(frame.clock) == 'undefined') {
			return;
		}

		if (frame.clock != null) {
			this.clock = frame.clock;
		}

		// we are only interested in the packet if it has a clock / player / team values
		if (typeof(frame.position) != 'undefined'
				&& typeof(frame.player_id) != 'undefined') {
			var player = this.getPlayer(frame.player_id);
			player.position = frame.position;
			player.turret_direction += frame.hull_orientation[1] * 0.34 / 2;
			player.hull_direction = frame.hull_orientation[0];
			player.team 	= frame.team;
			player.clock	= this.clock;
		}

		if (typeof(frame.source) != 'undefined'
				&& typeof(frame.health) != 'undefined') {
			var player = this.getPlayer(frame.player_id);
			player.received_damage = true;
			player.clock = this.clock;
			var vehicles = this.game.vehicles;
			console.log(vehicles[frame.source].name + " (" + vehicles[frame.source].vehicleType + ") hit "
				+ vehicles[player.id].name + " (" + vehicles[player.id].vehicleType + ")" );
		}

		if (typeof(frame.target) != 'undefined'
				&& typeof(frame.destroyed_by) != 'undefined') {
			var player 	 = this.getPlayer(frame.target);
			player.alive = false;
			player.clock = this.clock;
		}
	}
}

var Viewer = function(target, serviceUrl) {
	this.target = target;
	this.serviceUrl = serviceUrl;
	this.model = null;

	// configure the target element as a replay viewer
	target.classList.add('replay-viewer');

	this.map = document.createElement('img');
	this.map.classList.add('map');
	this.map.width = this.map.height = 500
	this.target.appendChild(this.map);

	this.overlay = document.createElement('canvas');
	this.overlay.classList.add('overlay');
	this.overlay.width = this.overlay .height = 500;
	this.target.appendChild(this.overlay);

	this.link = document.createElement('a');
	this.link.classList.add('permalink');
	this.link.innerText = "PERMALINK";
	this.link.style.display = "none";
	this.target.appendChild(this.link);
}

Viewer.prototype = {
	replay: function(data) {
		var ctx = this.overlay.getContext("2d");
		this.model = new Model(data.summary, data.map_boundaries);

		var update = function(model, packets, window_start, window_size, start_ix) {
			// model of the viewer change -> stop
			if (this.model != model) {
				return;
			}

			var window_end = window_start + window_size, ix;
			for (ix = start_ix; ix < packets.length; ix++) {
				var packet = packets[ix];

				if (typeof(packet.clock) == 'undefined') {
					continue;
				}

				// escape when outside of window size
				if (packet.clock > window_end) {
					break;
				}

				// update model with packet
				model.update(packet);
			}

			this.show(data, ctx);

			if (ix < packets.length) {
				setTimeout(update.bind(this, model, packets, window_end, window_size, ix), 100);
			}
		}

		update.call(this, this.model, data.packets, 50, 0.2, 0);
	},
	show: function(data, ctx) {
		// reset overlay
		ctx.clearRect(0, 0, 500, 500);

		ctx.fillStyle = "#FFFF00";
		ctx.textBaseline = "top";
		ctx.font = "bold 20px sans-serif";
		ctx.fillText(String(this.model.clock.toFixed(2)), 8, 5);

		for (var player_id in this.model.players) {
			var player = this.model.players[player_id];
			if (typeof(player.team) == undefined
					|| player.position == null
					|| [0,1].indexOf(player.team) < 0) {
				continue;
			}

			var coord = to_2d_coord(player.position, this.model.map_boundaries, 500, 500);

			var colors = [
				[0, 255, 0],
				[255, 0, 0]
			];

			var recorder_color = [0, 0, 255];

			ctx.lineWidth = 2;
			var color = player.id == data.recorder_id ?  recorder_color : colors[player.team];

			if (player.received_damage) {
				color = [255, 255, 255];
				player.received_damage = false;
			}

			var age   = player.alive ? ((this.model.clock - player.clock) / 20) : 0;
			age = age > 0.66 ? 0.66 : age;
			var style = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + (1 - age) +  ")";

			ctx.strokeStyle = ctx.fillStyle = style;
			ctx.beginPath();
			ctx.arc(coord.x, coord.y, 3, 0, 2*Math.PI);

			if (player.alive) {
				ctx.fill();
			} else {
				ctx.stroke();
			}

			if (player.id == data.recorder_id) {
				// draw turret direction
				ctx.beginPath();
				ctx.moveTo(coord.x,coord.y);
				var rotation = player.hull_direction  - Math.PI / 2 +  player.turret_direction ;
				ctx.lineTo(coord.x + 50*Math.cos(rotation),coord.y + 50*Math.sin(rotation));
				ctx.stroke();

				// draw turret direction
				ctx.strokeStyle = "#FFFF00";
				ctx.beginPath();
				ctx.moveTo(coord.x,coord.y);
				rotation = player.hull_direction  - Math.PI / 2;
				ctx.lineTo(coord.x + 50*Math.cos(rotation),coord.y + 50*Math.sin(rotation));
				ctx.stroke();
			}
		}
	},
	fetch: function(id) {
		this.serviceRequest({id: id});
	},
	process: function(file) {
		this.serviceRequest({file: file});
	},
	serviceRequest: function(values) {
		// send data request
		var replayRequest = new XMLHttpRequest();
		replayRequest.open("POST", this.serviceUrl, true);

		var map = this.map;
		var viewer = this;
		map.classList.add('loading');
		this.link.style.display = "none";
		replayRequest.onreadystatechange = (function(state) {
			if(replayRequest.readyState != XMLHttpRequest.DONE) {
				return;
			}

			if (replayRequest.status != 200) {
				alert('Server request failed!');
				return;
			}

			// proces data
			var response = JSON.parse(replayRequest.response);
			var data = response.data;
			var mapURL = 'maps/' + data["map"] + "_" + data["mode"] + ".png";
			map.setAttribute('src', mapURL);
			map.classList.remove('loading');
			this.link.style.display = "block";
			this.link.href = 'http://' + response.permalink;
			// play
			viewer.replay(data);
		}).bind(this);

		var formData = new FormData();
		for (key in values) {
			formData.append(key, values[key]);
		}

		replayRequest.send(formData);
	}
}

function to_2d_coord(position, map_boundaries, width, height) {
    var x = position[0], y = position[2], z = position[1];
    x = (x - map_boundaries[0]) * (width / (map_boundaries[1] - map_boundaries[0] + 1));
    y = (map_boundaries[3] - y) * (height / (map_boundaries[3] - map_boundaries[2] + 1));
    return { x: x, y: y };
}

function onRangeInputChange(e) {
	if (e.target.updating) {
		e.target.updating = true;
	}
}<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import _ from 'lodash';
import settings from '../config/settings';
import ImmutableStore from '../lib/immutable-store';
import log from '../lib/log';

let userData = null;

// Check if code is running in Electron renderer process
const isRenderer = (window && window.process && window.process.type === 'renderer');

if (isRenderer) {
    const electron = window.require('electron');
    const app = electron.remote.app;
    userData = {
        path: path.join(app.getPath('userData'), 'cnc.json')
    };
}

const defaultState = {
    workspace: {
        container: {
            default: {
                widgets: ['visualizer']
            },
            primary: {
                show: true,
                widgets: [
                    'connection', 'grbl', 'console', 'webcam'
                ]
            },
            secondary: {
                show: true,
                widgets: [
                    'axes', 'gcode', 'probe', 'spindle'
                ]
            }
        }
    },
    widgets: {
        axes: {
            jog: {
                selectedDistance: '1',
                customDistance: 10
            },
            shuttle: {
                feedrateMin: 500,
                feedrateMax: 2000,
                hertz: 10,
                overshoot: 1
            }
        },
        connection: {
            port: '',
            baudrate: 115200,
            autoReconnect: true
        },
        console: {
        },
        gcode: {
        },
        grbl: {
        },
        probe: {
            probeCommand: 'G38.2',
            probeDepth: 10,
            probeFeedrate: 20,
            tlo: 10,
            retractionDistance: 4
        },
        spindle: {
        },
        visualizer: {
            animation: true
        },
        webcam: {
            disabled: false,
            url: ''
        }
    }
};

const cnc = {};

try {
    let value;

    if (userData) {
        value = fs.readFileSync(userData.path, 'utf8');
    } else {
        value = localStorage.getItem('cnc');
    }

    let json = JSON.parse(value) || {};
    cnc.version = json.version;
    cnc.state = json.state;
} catch (err) {
    // Ignore errors
}

cnc.version = cnc.version || settings.version;
cnc.state = _.merge({}, defaultState, cnc.state || {});

log.debug('cnc:', cnc);

{ // Remove duplicate widgets
    let defaultList = _.get(defaultState, 'workspace.container.default.widgets');
    let primaryList = _.get(cnc.state, 'workspace.container.primary.widgets');
    let secondaryList = _.get(cnc.state, 'workspace.container.secondary.widgets');

    primaryList = _(primaryList) // Keep the order of primaryList
        .uniq()
        .difference(defaultList) // exclude defaultList
        .value();

    secondaryList = _(secondaryList) // Keep the order of secondaryList
        .uniq()
        .difference(primaryList) // exclude primaryList
        .difference(defaultList) // exclude defaultList
        .value();

    _.set(cnc.state, 'workspace.container.default.widgets', defaultList);
    _.set(cnc.state, 'workspace.container.primary.widgets', primaryList);
    _.set(cnc.state, 'workspace.container.secondary.widgets', secondaryList);
}

const store = new ImmutableStore(cnc.state);

store.on('change', (state) => {
    cnc.version = settings.version;
    cnc.state = state;

    const value = JSON.stringify(cnc, null, 4);

    if (userData) {
        fs.writeFileSync(userData.path, value);
    }

    localStorage.setItem('cnc', value);
});

export default store;
<commit_msg>Use window.require to require fs module in Electron<commit_after>import path from 'path';
import _ from 'lodash';
import settings from '../config/settings';
import ImmutableStore from '../lib/immutable-store';
import log from '../lib/log';

let userData = null;

// Check if code is running in Electron renderer process
const isRenderer = (window && window.process && window.process.type === 'renderer');

if (isRenderer) {
    const electron = window.require('electron');
    const app = electron.remote.app;
    userData = {
        path: path.join(app.getPath('userData'), 'cnc.json')
    };
}

const defaultState = {
    workspace: {
        container: {
            default: {
                widgets: ['visualizer']
            },
            primary: {
                show: true,
                widgets: [
                    'connection', 'grbl', 'console', 'webcam'
                ]
            },
            secondary: {
                show: true,
                widgets: [
                    'axes', 'gcode', 'probe', 'spindle'
                ]
            }
        }
    },
    widgets: {
        axes: {
            jog: {
                selectedDistance: '1',
                customDistance: 10
            },
            shuttle: {
                feedrateMin: 500,
                feedrateMax: 2000,
                hertz: 10,
                overshoot: 1
            }
        },
        connection: {
            port: '',
            baudrate: 115200,
            autoReconnect: true
        },
        console: {
        },
        gcode: {
        },
        grbl: {
        },
        probe: {
            probeCommand: 'G38.2',
            probeDepth: 10,
            probeFeedrate: 20,
            tlo: 10,
            retractionDistance: 4
        },
        spindle: {
        },
        visualizer: {
            animation: true
        },
        webcam: {
            disabled: false,
            url: ''
        }
    }
};

const cnc = {};

try {
    let value;

    if (userData) {
        const fs = window.require('fs'); // Use window.require to require fs module in Electron
        value = fs.readFileSync(userData.path, 'utf8');
    } else {
        value = localStorage.getItem('cnc');
    }

    let json = JSON.parse(value) || {};
    cnc.version = json.version;
    cnc.state = json.state;
} catch (err) {
    // Ignore errors
}

cnc.version = cnc.version || settings.version;
cnc.state = _.merge({}, defaultState, cnc.state || {});

log.debug('cnc:', cnc);

{ // Remove duplicate widgets
    let defaultList = _.get(defaultState, 'workspace.container.default.widgets');
    let primaryList = _.get(cnc.state, 'workspace.container.primary.widgets');
    let secondaryList = _.get(cnc.state, 'workspace.container.secondary.widgets');

    primaryList = _(primaryList) // Keep the order of primaryList
        .uniq()
        .difference(defaultList) // exclude defaultList
        .value();

    secondaryList = _(secondaryList) // Keep the order of secondaryList
        .uniq()
        .difference(primaryList) // exclude primaryList
        .difference(defaultList) // exclude defaultList
        .value();

    _.set(cnc.state, 'workspace.container.default.widgets', defaultList);
    _.set(cnc.state, 'workspace.container.primary.widgets', primaryList);
    _.set(cnc.state, 'workspace.container.secondary.widgets', secondaryList);
}

const store = new ImmutableStore(cnc.state);

store.on('change', (state) => {
    cnc.version = settings.version;
    cnc.state = state;

    const value = JSON.stringify(cnc, null, 4);

    if (userData) {
        const fs = window.require('fs'); // Use window.require to require fs module in Electron
        fs.writeFileSync(userData.path, value);
    }

    localStorage.setItem('cnc', value);
});

export default store;
<|endoftext|>


<commit_before>/* @flow strict-local */
import type { ThemeName } from '../../types';
import { BRAND_COLOR } from '../../styles';
import cssEmojis from './cssEmojis';
import cssNight from './cssNight';

const cssBase = `
html {
  -webkit-user-select: none; /* Safari 3.1+ */
  -moz-user-select: none; /* Firefox 2+ */
  -ms-user-select: none; /* IE 10+ */
  user-select: none; /* Standard syntax */
  -khtml-user-select: none;
  -webkit-touch-callout: none;
}
body {
  font-family: sans-serif;
  line-height: 1.4;
  margin: 0;
  width: 100%;
  max-width: 100%;
}
a {
  color: #08c;
}
p {
  margin: 0;
}
code {
  font-size: .857rem;
  white-space: pre-wrap;
  padding: 0 0.25rem;
}
pre {
  padding: 0.5rem;
  margin: 0.5rem 0;
  font-size: 0.75rem;
  white-space: pre;
  overflow-x: auto;
  word-wrap: normal;
}
code, pre {
  border-radius: 3px;
  border: 1px solid rgba(127, 127, 127, 0.25);
  background-color: rgba(127, 127, 127, 0.125);
  font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
table {
  border-collapse: collapse;
  width: 100%;
}
table, th, td {
  border: 1px solid rgba(127, 127, 127, 0.25);
}
thead {
  background: rgba(127, 127, 127, 0.1);
}
th, td {
  align: center;
  padding: 0.25rem 0.5rem;
}
hr {
  margin: 1rem 0;
  border: 0;
  border-top: 1px solid rgba(127, 127, 127, 0.5);
}
.alert-word {
  background-color: hsla(102, 85%, 57%, .5);
}
.highlight {
  background-color: hsl(51, 94%, 74%);
}
.subheader {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  margin-bottom: 0.25rem;
}
.timerow {
  text-align: center;
  color: #999;
  display: flex;
  align-items: center;
  padding: 0.5rem 0;
}
.timerow-left,
.timerow-right {
  flex: 1;
  height: 1px;
  margin: 0.5rem;
}
.timerow-left {
  background: -webkit-linear-gradient(left, transparent 10%, #999 100%);
}
.timerow-right {
  background: -webkit-linear-gradient(left, #999 0%, transparent 90%);
}
.timestamp {
  color: #999;
  font-size: 0.9rem;
  white-space: nowrap;
}
.message,
.loading {
  display: flex;
  word-wrap: break-word;
  padding: 1rem;
}
.message-brief {
  padding: 0 1rem 1rem 4rem;
}
.message p + p {
  margin-top: 1rem;
}
.avatar,
.loading-avatar {
  min-width: 32px;
  width: 32px;
  height: 32px;
  margin-right: 1rem;
}
.avatar img {
  width: 100%;
  border-radius: 3px;
}
.content {
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}
.username {
  font-weight: bold;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}
.user-group-mention,
.user-mention {
  white-space: nowrap;
  background-color: rgba(0, 0, 0, 0.1);
  border-radius: 3px;
  padding: 0 .125rem;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}
.header-wrapper {
  position: -webkit-sticky;
  position: sticky;
  top: -1px;
  z-index: 100;
  display: flex;
  justify-content: space-between;
}
.avatar,
.header-wrapper {
  cursor: pointer;
}
.stream-header {
  padding: 0;
  display: flex;
  flex-direction: row;
}
.stream-text,
.topic-header,
.private-header {
  line-height: 2;
  white-space: nowrap;
}
.private-header {
  padding: 0 0.5rem;
}
.topic-header {
  background: #ccc;
  min-width: 30%;
}
.stream-text {
  padding: 0 0.5rem;
}
.topic-text {
  flex: 1;
  padding: 0 0.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
.topic-date {
  opacity: 0.5;
  padding: 0 0.5rem;
  pointer-events: none;
}
[data-mentioned="true"], [data-wildcard_mentioned="true"] {
  background: rgba(255, 0, 0, 0.05);
}
.message:not([data-read="true"]) {
  box-shadow: inset 4px 0 ${BRAND_COLOR};
}
.message[data-read="true"] {
  box-shadow: inset 4px 0 transparent;
  transition-property: box-shadow;
  transition-duration: 0.3s;
  transition-delay: 1s;
  transition-timing-function: ease-out;
}
.private-header {
  background: #444;
  color: white;
}
.loading-avatar {
  border-radius: 3px;
  background: rgba(127, 127, 127, 0.9);
}
.loading-content {
  width: 100%;
}
.loading-subheader {
  display: flex;
  justify-content: space-between;
}
.loading-content .block {
  background: linear-gradient(
    to right,
    rgba(127, 127, 127, 0.5) 0%,
    rgba(127, 127, 127, 0.5) 40%,
    rgba(127, 127, 127, 0.25) 51%,
    rgba(127, 127, 127, 0.5) 60%,
    rgba(127, 127, 127, 0.5) 100%
  );
  background-size: 200% 200%;
  animation: gradient-scroll 1s linear infinite;

  border-radius: 10px;
  height: 8px;
  margin-bottom: 10px;
}
@keyframes gradient-scroll {
  0% { background-position: 100% 50% }
  100% { background-position: 0 50% }
}
.loading-subheader .name {
  width: 10rem;
  background-color: rgba(127, 127, 127, 0.9);
}
.loading-subheader .timestamp {
  width: 5rem;
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.loading-spinner, .outbox-spinner {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  margin: 1rem auto;
  font-size: 10px;
  border: 3px solid rgba(82, 194, 175, 0.25);
  border-left: 3px solid rgba(82, 194, 175, 0.75);
  animation: spin 1s linear infinite;
}
.outbox-spinner {
  margin: -16px 0 0 0;
  border-width: 2px;
  width: 12px;
  height: 12px;
  float: right;
}
.message_inline_image {
  text-align: center;
}
.message_inline_image img,
.message_inline_ref img,
.twitter-image img {
  width: 100%;
  height: 30vh;
  object-fit: contain;
}
blockquote {
  padding-left: 0.5rem;
  margin: 0.5rem 0 0.5rem 0;
  border-left: 3px solid rgba(127, 127, 127, 0.5);
}
.message ul {
  padding-left: 1.25rem;
  margin: 0.25rem 0 0 0;
}
.message ul + p {
  margin-top: 1rem;
}
.codehilite .gi { color: #00a000; }
.codehilite .gd { color: #a00000; }
.codehilite .k { color: #008000; font-weight: bold; }
.codehilite .kd { color: #008000; font-weight: bold; }
.codehilite .nf { color: #00f; }
.codehilite .s2 { color: #ba2121; }
.codehilite .cp { color: #bc7a00; }
.codehilite .kt { color: #b00040; }
.codehilite .nc { color: #00f; font-weight: bold; }
.codehilite .nb { color: #008000; }
.codehilite .s1 { color: #ba2121; }
.twitter-tweet {
  border: 2px solid rgba(29, 161, 242, 0.5);
  background: rgba(29, 161, 242, 0.1);
  border-radius: 6px;
  padding: 0.5rem 1rem;
  margin: 0.5rem 0;
}
.twitter-avatar {
  border-radius: 3px;
  margin: 0.5rem 0.5rem 0.25rem 0;
  float: left;
  width: 2.2rem;
  height: 2.2rem;
}
.twitter-image {
  text-align: center;
  margin: 0.5rem auto;
}
.message-tags {
  text-align: right;
  margin: 0.25rem 0;
  font-size: 0.7rem;
}
.message-tag {
  padding: 0.25rem 0.25rem;
  margin-left: 4;
  border-radius: 3px;
  color: rgba(127, 127, 127, 0.75);
  background: rgba(0, 0, 0, 0.1);
}
.reaction-list {
  margin: 0.5rem 0;
}
.reaction {
  color: rgba(127, 127, 127, 1);
  display: inline-block;
  padding: 5px 6.5px;
  border-radius: 3px;
  border: 1px solid rgba(127, 127, 127, 0.75);
  line-height: 1rem;
  height: 1rem;
  margin: .25rem 0.5rem .25rem 0;
}
.reaction img {
  pointer-events: none;
  cursor: default;
  max-height: 1rem;
  max-width: 1rem;
  vertical-align: top;
}
.self-voted {
  color: ${BRAND_COLOR};
  border: 1px solid ${BRAND_COLOR};
  background: rgba(36, 202, 194, 0.1);
}
.hidden {
  display: none;
}
.emoji {
  display: inline-block;
  height: 1.25rem;
  width: 1.25rem;
  white-space: nowrap;
  color: transparent;
  vertical-align: text-top;
}
.emoji:before {
  color: white;
}
.widget {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(127, 127, 127, 0.1);
  border: 1px dashed rgba(127, 127, 127, 0.5);
  border-radius: 0.5rem;
}
#typing {
  display: flex;
  padding-left: 1rem;
}
#typing .content {
  padding-top: 0.5rem;
  padding-bottom: 1rem;
}
#typing span {
  display: inline-block;
  background-color: #B6B5BA;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 100%;
  margin-right: 5px;
  animation: bob 2s infinite;
}
#typing span:nth-child(2) {
  animation-delay: 0.15s;
}
#typing span:nth-child(3) {
  animation-delay: 0.3s;
  margin-right: 0;
}
@keyframes bob {
  10% {
    transform: translateY(-10px);
    background-color: #9E9DA2;
  }
  50% {
    transform: translateY(0);
    background-color: #B6B5BA;
  }
}
#message-loading {
  position: fixed;
  width: 100%;
  opacity: 0.25;
}
#js-error-detailed {
  position: fixed;
  width: 100%;
  background: red;
  color: white;
  font-size: 0.7rem;
}
#js-error-plain, #js-error-plain-dummy {
  z-index: 1000;
  width: 100%;
  background: red;
  color: white;
  padding: 4px;
  text-align: center;
}
#js-error-plain {
  position: fixed;
}
#js-error-plain-dummy {
  visibility: hidden;
}
#scroll-bottom {
  position: fixed;
  z-index: 200;
  right: 5px;
  bottom: 15px;
}
#scroll-bottom a {
  display: block;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(82, 194, 175, 0.5);
}
#scroll-bottom .text {
  clip: rect(0 0 0 0);
  overflow: hidden;
  position: absolute;
  height: 1px;
  width: 1px;
}
#scroll-bottom svg {
  width: 32px;
  height: 32px;
  fill: rgba(255, 255, 255, 0.75);
}
`;

export default (theme: ThemeName) => `
<style>
${cssBase}
${theme === 'night' ? cssNight : ''}
${cssEmojis}
</style>
<style id="style-hide-js-error-plain">
#js-error-plain, #js-error-plain-dummy {
  display: none;
}
</style>
<style id="generated-styles"></style>
`;
<commit_msg>msglist: Make padding and margin independent of font size.<commit_after>/* @flow strict-local */
import type { ThemeName } from '../../types';
import { BRAND_COLOR } from '../../styles';
import cssEmojis from './cssEmojis';
import cssNight from './cssNight';

const cssBase = `
html {
  -webkit-user-select: none; /* Safari 3.1+ */
  -moz-user-select: none; /* Firefox 2+ */
  -ms-user-select: none; /* IE 10+ */
  user-select: none; /* Standard syntax */
  -khtml-user-select: none;
  -webkit-touch-callout: none;
}
body {
  font-family: sans-serif;
  line-height: 1.4;
  margin: 0;
  width: 100%;
  max-width: 100%;
}
a {
  color: #08c;
}
p {
  margin: 0;
}
code {
  font-size: .857rem;
  white-space: pre-wrap;
  padding: 0 4px;
}
pre {
  padding: 8px;
  margin: 8px 0;
  font-size: 0.75rem;
  white-space: pre;
  overflow-x: auto;
  word-wrap: normal;
}
code, pre {
  border-radius: 3px;
  border: 1px solid rgba(127, 127, 127, 0.25);
  background-color: rgba(127, 127, 127, 0.125);
  font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
table {
  border-collapse: collapse;
  width: 100%;
}
table, th, td {
  border: 1px solid rgba(127, 127, 127, 0.25);
}
thead {
  background: rgba(127, 127, 127, 0.1);
}
th, td {
  align: center;
  padding: 4px 8px;
}
hr {
  margin: 16px 0;
  border: 0;
  border-top: 1px solid rgba(127, 127, 127, 0.5);
}
.alert-word {
  background-color: hsla(102, 85%, 57%, .5);
}
.highlight {
  background-color: hsl(51, 94%, 74%);
}
.subheader {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  margin-bottom: 4px;
}
.timerow {
  text-align: center;
  color: #999;
  display: flex;
  align-items: center;
  padding: 8px 0;
}
.timerow-left,
.timerow-right {
  flex: 1;
  height: 1px;
  margin: 8px;
}
.timerow-left {
  background: -webkit-linear-gradient(left, transparent 10%, #999 100%);
}
.timerow-right {
  background: -webkit-linear-gradient(left, #999 0%, transparent 90%);
}
.timestamp {
  color: #999;
  font-size: 0.9rem;
  white-space: nowrap;
}
.message,
.loading {
  display: flex;
  word-wrap: break-word;
  padding: 16px;
}
.message-brief {
  padding: 0 16px 16px 64px;
}
.message p + p {
  margin-top: 16px;
}
.avatar,
.loading-avatar {
  min-width: 32px;
  width: 32px;
  height: 32px;
  margin-right: 16px;
}
.avatar img {
  width: 100%;
  border-radius: 3px;
}
.content {
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}
.username {
  font-weight: bold;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}
.user-group-mention,
.user-mention {
  white-space: nowrap;
  background-color: rgba(0, 0, 0, 0.1);
  border-radius: 3px;
  padding: 0 2px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}
.header-wrapper {
  position: -webkit-sticky;
  position: sticky;
  top: -1px;
  z-index: 100;
  display: flex;
  justify-content: space-between;
}
.avatar,
.header-wrapper {
  cursor: pointer;
}
.stream-header {
  padding: 0;
  display: flex;
  flex-direction: row;
}
.stream-text,
.topic-header,
.private-header {
  line-height: 2;
  white-space: nowrap;
}
.private-header {
  padding: 0 8px;
}
.topic-header {
  background: #ccc;
  min-width: 30%;
}
.stream-text {
  padding: 0 0.5rem;
}
.topic-text {
  flex: 1;
  padding: 0 0.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}
.topic-date {
  opacity: 0.5;
  padding: 0 0.5rem;
  pointer-events: none;
}
[data-mentioned="true"], [data-wildcard_mentioned="true"] {
  background: rgba(255, 0, 0, 0.05);
}
.message:not([data-read="true"]) {
  box-shadow: inset 4px 0 ${BRAND_COLOR};
}
.message[data-read="true"] {
  box-shadow: inset 4px 0 transparent;
  transition-property: box-shadow;
  transition-duration: 0.3s;
  transition-delay: 1s;
  transition-timing-function: ease-out;
}
.private-header {
  background: #444;
  color: white;
}
.loading-avatar {
  border-radius: 3px;
  background: rgba(127, 127, 127, 0.9);
}
.loading-content {
  width: 100%;
}
.loading-subheader {
  display: flex;
  justify-content: space-between;
}
.loading-content .block {
  background: linear-gradient(
    to right,
    rgba(127, 127, 127, 0.5) 0%,
    rgba(127, 127, 127, 0.5) 40%,
    rgba(127, 127, 127, 0.25) 51%,
    rgba(127, 127, 127, 0.5) 60%,
    rgba(127, 127, 127, 0.5) 100%
  );
  background-size: 200% 200%;
  animation: gradient-scroll 1s linear infinite;

  border-radius: 10px;
  height: 8px;
  margin-bottom: 10px;
}
@keyframes gradient-scroll {
  0% { background-position: 100% 50% }
  100% { background-position: 0 50% }
}
.loading-subheader .name {
  width: 10rem;
  background-color: rgba(127, 127, 127, 0.9);
}
.loading-subheader .timestamp {
  width: 5rem;
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.loading-spinner, .outbox-spinner {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  margin: 16px auto;
  font-size: 10px;
  border: 3px solid rgba(82, 194, 175, 0.25);
  border-left: 3px solid rgba(82, 194, 175, 0.75);
  animation: spin 1s linear infinite;
}
.outbox-spinner {
  margin: -16px 0 0 0;
  border-width: 2px;
  width: 12px;
  height: 12px;
  float: right;
}
.message_inline_image {
  text-align: center;
}
.message_inline_image img,
.message_inline_ref img,
.twitter-image img {
  width: 100%;
  height: 30vh;
  object-fit: contain;
}
blockquote {
  padding-left: 8px;
  margin: 8px 0 8px 0;
  border-left: 3px solid rgba(127, 127, 127, 0.5);
}
.message ul {
  padding-left: 20px;
  margin: 4px 0 0 0;
}
.message ul + p {
  margin-top: 16px;
}
.codehilite .gi { color: #00a000; }
.codehilite .gd { color: #a00000; }
.codehilite .k { color: #008000; font-weight: bold; }
.codehilite .kd { color: #008000; font-weight: bold; }
.codehilite .nf { color: #00f; }
.codehilite .s2 { color: #ba2121; }
.codehilite .cp { color: #bc7a00; }
.codehilite .kt { color: #b00040; }
.codehilite .nc { color: #00f; font-weight: bold; }
.codehilite .nb { color: #008000; }
.codehilite .s1 { color: #ba2121; }
.twitter-tweet {
  border: 2px solid rgba(29, 161, 242, 0.5);
  background: rgba(29, 161, 242, 0.1);
  border-radius: 6px;
  padding: 8px 16px;
  margin: 8px 0;
}
.twitter-avatar {
  border-radius: 3px;
  margin: 8px 8px 4px 0;
  float: left;
  width: 2.2rem;
  height: 2.2rem;
}
.twitter-image {
  text-align: center;
  margin: 8px auto;
}
.message-tags {
  text-align: right;
  margin: 4px 0;
  font-size: 0.7rem;
}
.message-tag {
  padding: 4px 4px;
  margin-left: 4px;
  border-radius: 3px;
  color: rgba(127, 127, 127, 0.75);
  background: rgba(0, 0, 0, 0.1);
}
.reaction-list {
  margin: 8px 0;
}
.reaction {
  color: rgba(127, 127, 127, 1);
  display: inline-block;
  padding: 5px 6.5px;
  border-radius: 3px;
  border: 1px solid rgba(127, 127, 127, 0.75);
  line-height: 1rem;
  height: 1rem;
  margin: 4px 8px 4px 0;
}
.reaction img {
  pointer-events: none;
  cursor: default;
  max-height: 1rem;
  max-width: 1rem;
  vertical-align: top;
}
.self-voted {
  color: ${BRAND_COLOR};
  border: 1px solid ${BRAND_COLOR};
  background: rgba(36, 202, 194, 0.1);
}
.hidden {
  display: none;
}
.emoji {
  display: inline-block;
  height: 1.25rem;
  width: 1.25rem;
  white-space: nowrap;
  color: transparent;
  vertical-align: text-top;
}
.emoji:before {
  color: white;
}
.widget {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(127, 127, 127, 0.1);
  border: 1px dashed rgba(127, 127, 127, 0.5);
  border-radius: 0.5rem;
}
#typing {
  display: flex;
  padding-left: 16px;
}
#typing .content {
  padding-top: 8px;
  padding-bottom: 16px;
}
#typing span {
  display: inline-block;
  background-color: #B6B5BA;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 100%;
  margin-right: 5px;
  animation: bob 2s infinite;
}
#typing span:nth-child(2) {
  animation-delay: 0.15s;
}
#typing span:nth-child(3) {
  animation-delay: 0.3s;
  margin-right: 0;
}
@keyframes bob {
  10% {
    transform: translateY(-10px);
    background-color: #9E9DA2;
  }
  50% {
    transform: translateY(0);
    background-color: #B6B5BA;
  }
}
#message-loading {
  position: fixed;
  width: 100%;
  opacity: 0.25;
}
#js-error-detailed {
  position: fixed;
  width: 100%;
  background: red;
  color: white;
  font-size: 0.7rem;
}
#js-error-plain, #js-error-plain-dummy {
  z-index: 1000;
  width: 100%;
  background: red;
  color: white;
  padding: 4px;
  text-align: center;
}
#js-error-plain {
  position: fixed;
}
#js-error-plain-dummy {
  visibility: hidden;
}
#scroll-bottom {
  position: fixed;
  z-index: 200;
  right: 5px;
  bottom: 15px;
}
#scroll-bottom a {
  display: block;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(82, 194, 175, 0.5);
}
#scroll-bottom .text {
  clip: rect(0 0 0 0);
  overflow: hidden;
  position: absolute;
  height: 1px;
  width: 1px;
}
#scroll-bottom svg {
  width: 32px;
  height: 32px;
  fill: rgba(255, 255, 255, 0.75);
}
`;

export default (theme: ThemeName) => `
<style>
${cssBase}
${theme === 'night' ? cssNight : ''}
${cssEmojis}
</style>
<style id="style-hide-js-error-plain">
#js-error-plain, #js-error-plain-dummy {
  display: none;
}
</style>
<style id="generated-styles"></style>
`;
<|endoftext|>


<commit_before>(function(context, eval){

	// --------------------------------------------------------
	// helper functions that do stuff
	// --------------------------------------------------------

	// lets you declare recursive anonamyous recursive functions
	function recursive(fn){
	    var bound = function(){
	        var inputs = Array.prototype.concat.call([bound], Array.prototype.splice.call(arguments, 0))
	        return fn.apply(null, inputs)
	    }
	    return bound
	}

	// unused right now but checks if an object is empty
	function isEmpty(obj) {
		for(var prop in obj) {
			if(obj.hasOwnProperty(prop))
				return false
		}
		return JSON.stringify(obj) === JSON.stringify({})
	}

	// make ajax calls (used in stateless.import)
	function ajaxGet(url, successCallback, failCallback){
		var request = new XMLHttpRequest()

		successCallback = successCallback || function (data){/*console.log(data)*/}
		failCallback = failCallback || function (data){/*console.log(data)*/}

		request.onload = function(){
			successCallback(request)
		}
		request.onerror = function(){
			failCallback(request)
		}

		request.open('GET', url, true)
		request.send()
	}

	// exclusive or never a bad thing to have but unused right now
	function xor(a,b) {
		return ( a || b ) && !( a && b )
	}

	// make sure that only 1 of 2 or more variables are defined
	var onlyOneIsDefined = function(){
		var truthness = false;
		for (var i = 0; i < arguments.length; i++){
			if (arguments[i] !== null  && typeof arguments[i] != "undefined" && truthness) { // found a second truthful item in array
				return false
			}
			else if (arguments[i] !== null  && typeof arguments[i] != "undefined" && !truthness){ // found the first truthful item
				truthness = true;
			}
		}
		return truthness // you can only get here if there was one truthful item in the array
	}

	// --------------------------------------------------------
	// The templater that does the heavy lifting kinda
	// --------------------------------------------------------
	var _scope = function(ele){
		// detect if new or just calling it
		var self = {}

		if (!ele){
			throw new Error("No element selected")
		}

		// set public methods into the prototype of this object.
		var public_method = {}
		Object.setPrototypeOf(self, public_method)

		// define public propertyies that can be gotten with .varname
		Object.defineProperty(public_method, "parent", {
			enumerable: false,
			configurable: false,
			get: function(){
				return ele.parentNode && ele.parentNode.scope
			}
		})

		Object.defineProperty(public_method, "children", {
			enumerable: false,
			configurable: false,
			get: function(){
				var unique = [];
				Array.prototype.forEach.call(ele.querySelectorAll("*"), function(item){
					var scope = item.scope
					// todo: refactor this and make a better way of keeping track of child scopes.
					if (scope && scope != self && scope.parent == self && unique.indexOf(scope) === -1){
						unique.push(scope)
					}
				})
				return unique;
			}
		})

		Object.defineProperty(public_method, "root", {
			enumerable: false,
			configurable: false,
			get: recursive(function(getParent, scope){
				scope = scope || self
				return (scope.parent)? getParent(scope.parent) : scope
			})
		})

		// set the "scope" property of every HTML element associated to self to provide a reference to self
		// most depended upon property in the framework
		var recursiveDefineScope = function(ele, recur){
			Object.defineProperty(ele, "scope", {
				enumerable: false,
				configurable: false,
				get: function(){
					return self
				}
			})

			if (recur !== false){
				Array.prototype.forEach.call(ele.querySelectorAll("*"), function(item){
					recursiveDefineScope(item, false)
				})
			}
		}
		recursiveDefineScope(ele)

		// begin dom manip functions

		var addEventInterface = function(el, again){
			var listeners = {}

			el.on = function(type, callback){
				var typeList = listeners[type] || (listeners[type] = []),
					repeat = false

				typeList.forEach(function(fn){
					if (fn == callback || fn.toString() == callback.toString()){
						repeat = true
					}
				})

				if (!repeat){
					el.addEventListener(type, callback)
					listeners[type].push(callback)
				}
				else {
					console.warn("listener is already registered")
				}

				return callback
			}

			el.off = function(type, callback){
				var typeList = listeners[type],
					found = false

				typeList && typeList.forEach(function(fn){
					if (fn == callback || fn.toString() == callback.toString()){
						found = true
						el.removeEventListener(type, fn)
					}
				})

				if (!found){
					console.warn("Listener is not currently registered")
				}

				return callback
			}

			el.once = function(type, callback){
				var handler = function(ev){
					callback(ev)
					el.off(type, callback)
				}
				el.on(type, handler)

				return handler
			}

			if (again !== false){
				Array.prototype.forEach.call(el.querySelectorAll("*"), function(node){
					addEventInterface(node, false)
				})
			}
		}
		addEventInterface(ele)

		// public methods (kept in the prototype) for others to access
		public_method.on = function(type, callback){
			ele.on(type, callback)
			return self
		}

		public_method.off = function(type, callback){
			ele.off(type, callback)
			return self
		}

		public_method.once = function(type, callback){
			ele.once(type, callback)
			return self
		}

		var addClassInterface = function(ele, again){
			ele.hasClass = function(q){
				if (q) {
					return (ele.className.match(new RegExp("(^"+q+"$|\\s"+q+"\\s|^"+q+"\\s|\\s"+q+"$)")))? true : false
				}
			}

			ele.addClass = function(c, multiple){
				if (!ele.hasClass(c) || multiple === true){
					ele.className += " " + c
				}
			}

			ele.removeClass = function(c, multiple){
				if (ele.hasClass(c)){
					if (multiple === true){
						ele.className = ele.className
							.replace(new RegExp(c, "g"), "")
							.replace(/(^\s+|\s+$)/g, "")
							.replace(/\s+/g, " ")
					}
					else {
						ele.className = ele.className
							.replace(c, "")
							.replace(/(^\s+|\s+$)/g, "")
							.replace(/\s+/g, " ")
					}
				}
			}

			if (again !== false){
				Array.prototype.forEach.call(ele.querySelectorAll("*"), function(node){
					addClassInterface(node, false)
				})
			}
		}
		addClassInterface(ele)

		public_method.hasClass = function (className){
			return ele.hasClass(className)
		}

		public_method.addClass = function (className, multiple){
			ele.addClass(className, multiple)
			return self
		}

		public_method.removeClass = function (className, multiple){
			ele.removeClass(className, multiple)
			return self
		}

		public_method.attr = function(attribute, value){
			if (typeof value == "undefined") {
				return ele.getAttribute(attribute)
			}
			else {
				ele.setAttribute(attribute, value)
				return self
			}
		}

		public_method.css = function(){

		}

		public_method.data = function(){

		}

		public_method.text = function(){

		}

		// functions for dom manip end

		public_method.include = function(el, where){
			var insertAt = ele.querySelector(where) || ele
			insertAt.appendChild(el)
			recursiveDefineScope(el, false)
			addEventInterface(ele, false)
			addClassInterface(ele, false)
			return self
		}

		// functions for module manip begin

		public_method.define = function(prop, config){
			var deffs = {
				enumerable: (typeof config.enumerable == "undefined")?  true : config.enumerable,
				configurable: true
			}

			if (!onlyOneIsDefined((config.get || config.set), config.static, config.asVar)) {
				throw new Error("You can only have one of (getter[+setter]), static, or asVar");
			}
			else if (config.get || config.set){
				if (config.set && config.get){
					deffs.set = config.set
				}
				else if (config.set && !config.get){
					throw new Error("You need a getter with your setter")
				}

				deffs.get = config.get
			}
			else if (config.static){
				deffs.value = config.static
			}
			else if (config.asVar !== null || typeof config.asVar != "undefined"){
				deffs.value = config.asVar
				deffs.writable = true
			}
			else {
				throw new Error("Your congifs must include either a get, a static, or an asVar property")
			}

			Object.defineProperty(self, prop, deffs)
			return self
		}
		public_method.property = public_method.define

		public_method.element = function(selector){
			return (selector)? ele.querySelector(selector) : ele
		}

		public_method.elements = function(selector){
			if (selector){
				return Array.prototype.splice.call(ele.querySelectorAll(selector), 1)
			}
			else{
				var everything = Array.prototype.splice.call(ele.querySelectorAll("*"), 1)
				everything.push(ele)
				return everything
			}
		}

		public_method.appendChild = function(childScope, where){
			childScope.unlink()
			Object.setPrototypeOf(Object.getPrototypeOf(childScope), self)
			var insertAt = ele.querySelector(where) || ele
			insertAt.appendChild(childScope.element())
			return self
		}

		public_method.unlink = function(){
			if (ele.parentNode) {
				Object.setPrototypeOf(Object.getPrototypeOf(self), Object.prototype)
				ele.parentNode.removeChild(ele)
			}
			return self
		}

		public_method.render = function(){
			self.unlink()
			document.body.appendChild(ele)
			return self
		}

		return self
	}

	// --------------------------------------------------------
	// Stateless object exposed to the main execution scope for managing and playing with the templates
	// --------------------------------------------------------
	context.stateless = {}
	var statelessOpps = {}
	Object.setPrototypeOf(context.stateless, statelessOpps)

	// private values to be manipulated internally
	var length = 0

	var pushEle = function (ele){
		var index = length
		var id = ele.id || index
		if (ele.id){
			var className = ele.className
			ele.className = ele.id
			if (className){
				ele.className += " " + className
			}
			ele.removeAttribute("id")
		}
		length ++

		Object.defineProperty(context.stateless, id, {
			enumerable: false,
			configurable: false,
			writable: false,
			value: ele
		})

		if (!context.stateless[index]){
			Object.defineProperty(context.stateless, index, {
				enumerable: false,
				configurable: false,
				writable: false,
				value: ele
			})
		}

		return id
	}
	// public unchangeable variable
	Object.defineProperty(statelessOpps, "length", {
		enumerable: false,
		configurable: false,
		get: function(){
			return length
		}
	})

	Object.defineProperty(statelessOpps, "consume", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(ele){ // public static function
			if (ele instanceof HTMLElement){
				ele.parentElement && ele.parentElement.removeChild(ele)
				pushEle(ele)
			}
			else if (typeof ele === "string"){
				var converter = document.createElement("div")
				converter.innerHTML = ele
				stateless.consume(converter.children)
			}
			else if (ele.length && ele[0] ){ //
				for (var i = 0; i < ele.length; i++){
					stateless.consume(ele[i])
				}
			}
			else {
				throw new Error("Invalid inputs")
			}
			return context.stateless
		}
	})

	Object.defineProperty(statelessOpps, "import", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(url){
			ajaxGet(url, function(xmlhttp){
				stateless.consume(xmlhttp.responseText)
			}, function(xmlhttp){
				throw new Error("The URL failed to load")
			})
			return context.stateless
		}
	})

	Object.defineProperty(statelessOpps, "register", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: statelessOpps.consume
	})

	Object.defineProperty(statelessOpps, "each", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(callback){
			for (var i = 0; i < length; i++){
				callback(context.stateless[i], i)
			}
			return context.stateless
		}
	})

	Object.defineProperty(statelessOpps, "instantiate", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(identifyer){ // public static function
			if (stateless[identifyer]) {
				var instance = _scope(stateless[identifyer].cloneNode(true))
				return instance
			}
			else {
				throw new Error( identifyer + " cannot be found in the template librare")
			}
		}
	})
})(
	//var context =
		this,
	//var safeEval =
		function(code){
			var safeContext = this;
			eval.call(safeContext, code)
		}.bind(this)
)
<commit_msg>finally caved in and ported in method-overload<commit_after>(function(context, eval){

	// --------------------------------------------------------
	// helper functions that do stuff
	// --------------------------------------------------------

	// lets you declare recursive anonamyous recursive functions
	function recursive(fn){
	    var bound = function(){
	        var inputs = Array.prototype.concat.call([bound], Array.prototype.splice.call(arguments, 0))
	        return fn.apply(null, inputs)
	    }
	    return bound
	}

	// unused right now but checks if an object is empty
	function isEmpty(obj) {
		for(var prop in obj) {
			if(obj.hasOwnProperty(prop))
				return false
		}
		return JSON.stringify(obj) === JSON.stringify({})
	}

	// make ajax calls (used in stateless.import)
	function ajaxGet(url, successCallback, failCallback){
		var request = new XMLHttpRequest()

		successCallback = successCallback || function (data){/*console.log(data)*/}
		failCallback = failCallback || function (data){/*console.log(data)*/}

		request.onload = function(){
			successCallback(request)
		}
		request.onerror = function(){
			failCallback(request)
		}

		request.open('GET', url, true)
		request.send()
	}

	// exclusive or never a bad thing to have but unused right now
	function xor(a,b) {
		return ( a || b ) && !( a && b )
	}

	// make sure that only 1 of 2 or more variables are defined
	var onlyOneIsDefined = function(){
		var truthness = false;
		for (var i = 0; i < arguments.length; i++){
			if (arguments[i] !== null  && typeof arguments[i] != "undefined" && truthness) { // found a second truthful item in array
				return false
			}
			else if (arguments[i] !== null  && typeof arguments[i] != "undefined" && !truthness){ // found the first truthful item
				truthness = true;
			}
		}
		return truthness // you can only get here if there was one truthful item in the array
	}

	// --------------------------------------------------------
	// The templater that does the heavy lifting kinda
	// --------------------------------------------------------
	var _scope = function(ele){
		// detect if new or just calling it
		var self = {}

		if (!ele){
			throw new Error("No element selected")
		}

		// set public methods into the prototype of this object.
		var public_method = {}
		Object.setPrototypeOf(self, public_method)

		// define public propertyies that can be gotten with .varname
		Object.defineProperty(public_method, "parent", {
			enumerable: false,
			configurable: false,
			get: function(){
				return ele.parentNode && ele.parentNode.scope
			}
		})

		Object.defineProperty(public_method, "children", {
			enumerable: false,
			configurable: false,
			get: function(){
				var unique = [];
				Array.prototype.forEach.call(ele.querySelectorAll("*"), function(item){
					var scope = item.scope
					// todo: refactor this and make a better way of keeping track of child scopes.
					if (scope && scope != self && scope.parent == self && unique.indexOf(scope) === -1){
						unique.push(scope)
					}
				})
				return unique;
			}
		})

		Object.defineProperty(public_method, "root", {
			enumerable: false,
			configurable: false,
			get: recursive(function(getParent, scope){
				scope = scope || self
				return (scope.parent)? getParent(scope.parent) : scope
			})
		})

		// set the "scope" property of every HTML element associated to self to provide a reference to self
		// most depended upon property in the framework
		var recursiveDefineScope = function(ele, recur){
			Object.defineProperty(ele, "scope", {
				enumerable: false,
				configurable: false,
				get: function(){
					return self
				}
			})

			if (recur !== false){
				Array.prototype.forEach.call(ele.querySelectorAll("*"), function(item){
					recursiveDefineScope(item, false)
				})
			}
		}
		recursiveDefineScope(ele)

		// begin dom manip functions

		var addEventInterface = function(el, again){
			var listeners = {}

			el.on = function(type, callback){
				var typeList = listeners[type] || (listeners[type] = []),
					repeat = false

				typeList.forEach(function(fn){
					if (fn == callback || fn.toString() == callback.toString()){
						repeat = true
					}
				})

				if (!repeat){
					el.addEventListener(type, callback)
					listeners[type].push(callback)
				}
				else {
					console.warn("listener is already registered")
				}

				return callback
			}

			el.off = function(type, callback){
				var typeList = listeners[type],
					found = false

				typeList && typeList.forEach(function(fn){
					if (fn == callback || fn.toString() == callback.toString()){
						found = true
						el.removeEventListener(type, fn)
					}
				})

				if (!found){
					console.warn("Listener is not currently registered")
				}

				return callback
			}

			el.once = function(type, callback){
				var handler = function(ev){
					callback(ev)
					el.off(type, callback)
				}
				el.on(type, handler)

				return handler
			}

			if (again !== false){
				Array.prototype.forEach.call(el.querySelectorAll("*"), function(node){
					addEventInterface(node, false)
				})
			}
		}
		addEventInterface(ele)

		// public methods (kept in the prototype) for others to access
		public_method.on = overload()
			.args("string", "string", "function").use(function(where, type, callback){
				self.elements(where).forEach(function(subEle){
					subEle.on(type, callback)
				})
				return self
			})
			.args("string", "function").use(function(type, callback){
				ele.on(type, callback)
				return self
			})

		/*function(type, callback){
			ele.on(type, callback)
			return self
		}*/

		public_method.off = function(type, callback){
			ele.off(type, callback)
			return self
		}

		public_method.once = function(type, callback){
			ele.once(type, callback)
			return self
		}

		var addClassInterface = function(ele, again){
			ele.hasClass = function(q){
				if (q) {
					return (ele.className.match(new RegExp("(^"+q+"$|\\s"+q+"\\s|^"+q+"\\s|\\s"+q+"$)")))? true : false
				}
			}

			ele.addClass = function(c, multiple){
				if (!ele.hasClass(c) || multiple === true){
					ele.className += " " + c
				}
			}

			ele.removeClass = function(c, multiple){
				if (ele.hasClass(c)){
					if (multiple === true){
						ele.className = ele.className
							.replace(new RegExp(c, "g"), "")
							.replace(/(^\s+|\s+$)/g, "")
							.replace(/\s+/g, " ")
					}
					else {
						ele.className = ele.className
							.replace(c, "")
							.replace(/(^\s+|\s+$)/g, "")
							.replace(/\s+/g, " ")
					}
				}
			}

			if (again !== false){
				Array.prototype.forEach.call(ele.querySelectorAll("*"), function(node){
					addClassInterface(node, false)
				})
			}
		}
		addClassInterface(ele)

		public_method.hasClass = function (className){
			return ele.hasClass(className)
		}

		public_method.addClass = function (className, multiple){
			ele.addClass(className, multiple)
			return self
		}

		public_method.removeClass = function (className, multiple){
			ele.removeClass(className, multiple)
			return self
		}

		public_method.attr = function(attribute, value){
			if (typeof value == "undefined") {
				return ele.getAttribute(attribute)
			}
			else {
				ele.setAttribute(attribute, value)
				return self
			}
		}

		public_method.css = function(){

		}

		public_method.data = function(){

		}

		public_method.text = function(){

		}

		// functions for dom manip end

		public_method.include = function(el, where){
			var insertAt = ele.querySelector(where) || ele
			insertAt.appendChild(el)
			recursiveDefineScope(el, false)
			addEventInterface(ele, false)
			addClassInterface(ele, false)
			return self
		}

		// functions for module manip begin

		public_method.define = function(prop, config){
			var deffs = {
				enumerable: (typeof config.enumerable == "undefined")?  true : config.enumerable,
				configurable: true
			}

			if (!onlyOneIsDefined((config.get || config.set), config.static, config.asVar)) {
				throw new Error("You can only have one of (getter[+setter]), static, or asVar");
			}
			else if (config.get || config.set){
				if (config.set && config.get){
					deffs.set = config.set
				}
				else if (config.set && !config.get){
					throw new Error("You need a getter with your setter")
				}

				deffs.get = config.get
			}
			else if (config.static){
				deffs.value = config.static
			}
			else if (config.asVar !== null || typeof config.asVar != "undefined"){
				deffs.value = config.asVar
				deffs.writable = true
			}
			else {
				throw new Error("Your congifs must include either a get, a static, or an asVar property")
			}

			Object.defineProperty(self, prop, deffs)
			return self
		}
		public_method.property = public_method.define

		public_method.element = function(selector){
			return (selector)? ele.querySelector(selector) : ele
		}

		public_method.elements = function(selector){
			if (selector){
				return Array.prototype.splice.call(ele.querySelectorAll(selector), 1)
			}
			else{
				var everything = Array.prototype.splice.call(ele.querySelectorAll("*"), 1)
				everything.push(ele)
				return everything
			}
		}

		public_method.appendChild = function(childScope, where){
			childScope.unlink()
			Object.setPrototypeOf(Object.getPrototypeOf(childScope), self)
			var insertAt = ele.querySelector(where) || ele
			insertAt.appendChild(childScope.element())
			return self
		}

		public_method.unlink = function(){
			if (ele.parentNode) {
				Object.setPrototypeOf(Object.getPrototypeOf(self), Object.prototype)
				ele.parentNode.removeChild(ele)
			}
			return self
		}

		public_method.render = function(){
			self.unlink()
			document.body.appendChild(ele)
			return self
		}

		return self
	}

	// --------------------------------------------------------
	// Stateless object exposed to the main execution scope for managing and playing with the templates
	// --------------------------------------------------------
	context.stateless = {}
	var statelessOpps = {}
	Object.setPrototypeOf(context.stateless, statelessOpps)

	// private values to be manipulated internally
	var length = 0

	var pushEle = function (ele){
		var index = length
		var id = ele.id || index
		if (ele.id){
			var className = ele.className
			ele.className = ele.id
			if (className){
				ele.className += " " + className
			}
			ele.removeAttribute("id")
		}
		length ++

		Object.defineProperty(context.stateless, id, {
			enumerable: false,
			configurable: false,
			writable: false,
			value: ele
		})

		if (!context.stateless[index]){
			Object.defineProperty(context.stateless, index, {
				enumerable: false,
				configurable: false,
				writable: false,
				value: ele
			})
		}

		return id
	}
	// public unchangeable variable
	Object.defineProperty(statelessOpps, "length", {
		enumerable: false,
		configurable: false,
		get: function(){
			return length
		}
	})

	Object.defineProperty(statelessOpps, "consume", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(ele){ // public static function
			if (ele instanceof HTMLElement){
				ele.parentElement && ele.parentElement.removeChild(ele)
				pushEle(ele)
			}
			else if (typeof ele === "string"){
				var converter = document.createElement("div")
				converter.innerHTML = ele
				stateless.consume(converter.children)
			}
			else if (ele.length && ele[0] ){ //
				for (var i = 0; i < ele.length; i++){
					stateless.consume(ele[i])
				}
			}
			else {
				throw new Error("Invalid inputs")
			}
			return context.stateless
		}
	})

	Object.defineProperty(statelessOpps, "import", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(url){
			ajaxGet(url, function(xmlhttp){
				stateless.consume(xmlhttp.responseText)
			}, function(xmlhttp){
				throw new Error("The URL failed to load")
			})
			return context.stateless
		}
	})

	Object.defineProperty(statelessOpps, "register", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: statelessOpps.consume
	})

	Object.defineProperty(statelessOpps, "each", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(callback){
			for (var i = 0; i < length; i++){
				callback(context.stateless[i], i)
			}
			return context.stateless
		}
	})

	Object.defineProperty(statelessOpps, "instantiate", {
		enumerable: false,
		configurable: false,
		writable: false,
		value: function(identifyer){ // public static function
			if (stateless[identifyer]) {
				var instance = _scope(stateless[identifyer].cloneNode(true))
				return instance
			}
			else {
				throw new Error( identifyer + " cannot be found in the template librare")
			}
		}
	})
})(
	//var context =
		this,
	//var safeEval =
		function(code){
			var safeContext = this;
			eval.call(safeContext, code)
		}.bind(this),
	// method-overload v0.0.1
	(function(a){var b=function(a,c){var d=!0;for(var e in c)"object"==typeof a[e]&&"object"==typeof c[e]?d=b(a[e],c[e])&&d:typeof a[e]!==c[e]&&(d=!1);return d};a.overload=a.overload||function(){var a=this,c=arguments;1===arguments.length&&(c=arguments[0]);var d=[],e=function(){};e.use=e.args=function(){return e};var f=function(){for(var a in d){var c=d[a];if(b(arguments,c.a))return c.b.apply(this,arguments)}},g=function(){var g=arguments;return{use:function(h){return d.push({a:g,b:h}),c.length&&b(c,g)?(h.apply(a,c),e):f}}};return f.args=g,f},a.overload.define=a.overload.define||function(b){if("function"!=typeof b){if("object"!=typeof b)return;for(var c={},d=Object.keys(b),e=0;e<d.length;e++){var f=d[e];c[f]=typeof b[f]}if(b.__proto__)for(var g=a.overload.define(b.__proto__),h=Object.keys(g),e=0;e<h.length;e++){var i=h[e];c[i]=c[i]||g[i]}return c}try{return a.overload.define(new b)}catch(a){return void console.log("Class does not have a null constructor")}},"undefined"!=typeof module&&module.exports&&(module.exports=a.overload)})(this);
)
<|endoftext|>


<commit_before>(function(context){

    // --------------------------------------------------------
    // helper functions that do stuff
    // --------------------------------------------------------
    function isEmpty(obj) {
        for(var prop in obj) {
            if(obj.hasOwnProperty(prop))
                return false;
        }
        return JSON.stringify(obj) === JSON.stringify({});
    }

    var ajaxGet = function(url, successCallback, failCallback){
        var request = new XMLHttpRequest();

        successCallback = successCallback || function (data){/*console.log(data)*/};
        failCallback = failCallback || function (data){/*console.log(data)*/};

        request.onload = function(){
            successCallback(request)
        };
        request.onerror = function(){
            failCallback(request)
        }

        request.open('GET', url, true);
        request.send();
    }

    // --------------------------------------------------------
    // templater that does the heavy lifting kinda
    // --------------------------------------------------------
    var templateInstance = context.templateInstance = function(ele){
        // detect if new or just calling it
        var self = (context == this)? {} : this;

        if (!ele){
            throw "No element selected";
        }
        // initialize some private values
        self.ele = ele;

        var insertAt = ele.querySelector("ins") || ele;
        var children = [];
        var parent = [];

        // set public methods into the prototype of this object.
        var public_method = {};
        Object.setPrototypeOf(self, public_method);

        public_method.on = function(){

        }

        public_method.off = function(){

        }

        public_method.once = function(){

        }

        public_method.appendChild = function(){

        }

        public_method.unlink = function(){

        }

        public_method.render = function(){

        }

        public_method.text = function(){

        }

        public_method.hasClass = function(){

        }

        public_method.addClass = function(){

        }

        public_method.removeClass = function(){

        }

        public_method.attr = function(){

        }

        public_method.css = function(){

        }

        public_method.data = function(){

        }

        return self;
    }

    // --------------------------------------------------------
    // Stateless object exposed to the main execution scope for managing and playing with the templates
    // --------------------------------------------------------
    context.stateless = {};
    var statelessOpps = {};
    Object.setPrototypeOf(context.stateless, statelessOpps);

    var length = 0;
    // public unchangeable variable
    Object.defineProperty(statelessOpps, "length", {
        enumerable: false,
        configurable: false,
        get: function(){
            return length;
        }
    });

    var pushEle = function (ele){
        var index = length;
        var id = ele.id || index;
        if (ele.id){
            var className = ele.className;
            ele.className = ele.id;
            if (className){
                ele.className += " " + className;
            }
            ele.removeAttribute("id");
        }
        length ++;

        Object.defineProperty(context.stateless, id, {
            enumerable: false,
            configurable: false,
            writable: false,
            value: ele
        });

        if (!context.stateless[index]){
            Object.defineProperty(context.stateless, index, {
                enumerable: false,
                configurable: false,
                writable: false,
                value: ele
            });
        }

        return id;
    }

    Object.defineProperty(statelessOpps, "consume", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(ele){ // public static function
            if (ele instanceof HTMLElement){
                ele.parentElement && ele.parentElement.removeChild(ele);
                return pushEle(ele);
            }
            else if (typeof ele === "string"){
                var converter = document.createElement("div");
                converter.innerHTML = ele;
                stateless.consume(converter.children);
            }
            else if (ele.length && ele[0] ){ //
                for (var i = 0; i < ele.length; i++){
                    stateless.consume(ele[i]);
                }
            }
            else {
                throw new Error("Invalid inputs");
            }
        }
    });

    Object.defineProperty(statelessOpps, "import", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(url){
            ajaxGet(url, function(xmlhttp){
                stateless.consume(xmlhttp.responseText);
            }, function(xmlhttp){
                throw new Error("The URL failed to load");
            })
        }
    });

    Object.defineProperty(statelessOpps, "register", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: statelessOpps.consume
    });

    Object.defineProperty(statelessOpps, "forEach", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(callback){
            for (var i = 0; i < length; i++){
                callback(context.stateless[i], i, context.stateless);
            }
        }
    })

    Object.defineProperty(statelessOpps, "instantiate", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(identifyer){ // public static function

        }
    });

})(this)
<commit_msg>better segmenting of code for readability<commit_after>(function(context){

    // --------------------------------------------------------
    // helper functions that do stuff
    // --------------------------------------------------------
    function isEmpty(obj) {
        for(var prop in obj) {
            if(obj.hasOwnProperty(prop))
                return false;
        }
        return JSON.stringify(obj) === JSON.stringify({});
    }

    var ajaxGet = function(url, successCallback, failCallback){
        var request = new XMLHttpRequest();

        successCallback = successCallback || function (data){/*console.log(data)*/};
        failCallback = failCallback || function (data){/*console.log(data)*/};

        request.onload = function(){
            successCallback(request)
        };
        request.onerror = function(){
            failCallback(request)
        }

        request.open('GET', url, true);
        request.send();
    }

    // --------------------------------------------------------
    // templater that does the heavy lifting kinda
    // --------------------------------------------------------
    var templateInstance = context.templateInstance = function(ele){
        // detect if new or just calling it
        var self = (context == this)? {} : this;

        if (!ele){
            throw "No element selected";
        }
        // initialize some private values
        self.ele = ele;

        var insertAt = ele.querySelector("ins") || ele;
        var children = [];
        var parent = [];

        // set public methods into the prototype of this object.
        var public_method = {};
        Object.setPrototypeOf(self, public_method);

        public_method.on = function(){

        }

        public_method.off = function(){

        }

        public_method.once = function(){

        }

        public_method.appendChild = function(){

        }

        public_method.unlink = function(){

        }

        public_method.render = function(){

        }

        public_method.text = function(){

        }

        public_method.hasClass = function(){

        }

        public_method.addClass = function(){

        }

        public_method.removeClass = function(){

        }

        public_method.attr = function(){

        }

        public_method.css = function(){

        }

        public_method.data = function(){

        }

        return self;
    }

    // --------------------------------------------------------
    // Stateless object exposed to the main execution scope for managing and playing with the templates
    // --------------------------------------------------------
    context.stateless = {};
    var statelessOpps = {};
    Object.setPrototypeOf(context.stateless, statelessOpps);

    // private values to be manipulated internally
    var length = 0;

    var pushEle = function (ele){
        var index = length;
        var id = ele.id || index;
        if (ele.id){
            var className = ele.className;
            ele.className = ele.id;
            if (className){
                ele.className += " " + className;
            }
            ele.removeAttribute("id");
        }
        length ++;

        Object.defineProperty(context.stateless, id, {
            enumerable: false,
            configurable: false,
            writable: false,
            value: ele
        });

        if (!context.stateless[index]){
            Object.defineProperty(context.stateless, index, {
                enumerable: false,
                configurable: false,
                writable: false,
                value: ele
            });
        }

        return id;
    }
    // public unchangeable variable
    Object.defineProperty(statelessOpps, "length", {
        enumerable: false,
        configurable: false,
        get: function(){
            return length;
        }
    });

    Object.defineProperty(statelessOpps, "consume", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(ele){ // public static function
            if (ele instanceof HTMLElement){
                ele.parentElement && ele.parentElement.removeChild(ele);
                return pushEle(ele);
            }
            else if (typeof ele === "string"){
                var converter = document.createElement("div");
                converter.innerHTML = ele;
                stateless.consume(converter.children);
            }
            else if (ele.length && ele[0] ){ //
                for (var i = 0; i < ele.length; i++){
                    stateless.consume(ele[i]);
                }
            }
            else {
                throw new Error("Invalid inputs");
            }
        }
    });

    Object.defineProperty(statelessOpps, "import", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(url){
            ajaxGet(url, function(xmlhttp){
                stateless.consume(xmlhttp.responseText);
            }, function(xmlhttp){
                throw new Error("The URL failed to load");
            })
        }
    });

    Object.defineProperty(statelessOpps, "register", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: statelessOpps.consume
    });

    Object.defineProperty(statelessOpps, "forEach", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(callback){
            for (var i = 0; i < length; i++){
                callback(context.stateless[i], i, context.stateless);
            }
        }
    })

    Object.defineProperty(statelessOpps, "instantiate", {
        enumerable: false,
        configurable: false,
        writable: false,
        value: function(identifyer){ // public static function

        }
    });

})(this)
<|endoftext|>


<commit_before>var canvas;
var ctx;
var image;
var points, triangles;

function init() {
	canvas = document.getElementById("tesselate");
	ctx = canvas.getContext("2d");

	ctx.fillStyle = "#FFFFFF";
	image = new Image();
	image.src = "stary_night.jpg";
	//improveImage(1,1,triangles);
	image.onload = function() {
		points = genPoints(4000, image)
		triangles = tesselate(points);
		tesselateImage(image, triangles);
		//improveImage(image, triangles, 5);
		//tesselateImage(image, triangles);
	};
}

function tesselate(points) {
	points.sort(function (p1,p2) {
		return (p1.x > p2.x) || (p1.x === p2.x && p1.y > p2.y);
	});

	triangles = delaunay(points);

	for (var i = 0; i < triangles.length; ++i) {
		ctx.beginPath();
		ctx.moveTo(triangles[i][0].x, triangles[i][0].y);
		ctx.lineTo(triangles[i][1].x, triangles[i][1].y);
		ctx.lineTo(triangles[i][2].x, triangles[i][2].y);
		ctx.lineTo(triangles[i][0].x, triangles[i][0].y);
		ctx.strokeStyle = "#FFFFFF";
		ctx.stroke();
	}
	return triangles;
}

function tesselateImage(image, triangles) {
	var imgCanvas = document.createElement('canvas');
	imgCanvas.width = image.width;
	imgCanvas.height = image.height;
	var imageSize = {w: image.width, h: image.height};
	var imageCtx = imgCanvas.getContext('2d');
	imageCtx.drawImage(image, 0, 0, image.width, image.height);
	imageData = imageCtx.getImageData(0, 0, image.width, image.height).data;

	for (var i = 0; i < triangles.length; ++i) {
		var c = avgColor(imageData, imageSize, triangles[i]);
		var triangle = bitBigger(triangles[i]);
		ctx.beginPath();
		ctx.moveTo(triangle[0].x, triangle[0].y);
		ctx.lineTo(triangle[1].x, triangle[1].y);
		ctx.lineTo(triangle[2].x, triangle[2].y);
		ctx.lineTo(triangle[0].x, triangle[0].y);
		//ctx.strokeStyle = "rgb(0,0,0)";
		//ctx.stroke();
		ctx.fillStyle = "rgb("+c.r+","+c.g+","+c.b+")";
		ctx.fill();
	}

	for (var i = 0; i < triangles.length; ++i) {
		var c = avgColor(imageData, imageSize, triangles[i]);
		var triangle = triangles[i];
		ctx.beginPath();
		ctx.moveTo(triangle[0].x, triangle[0].y);
		ctx.lineTo(triangle[1].x, triangle[1].y);
		ctx.lineTo(triangle[2].x, triangle[2].y);
		ctx.lineTo(triangle[0].x, triangle[0].y);
		//ctx.strokeStyle = "rgb(0,0,0)";
		//ctx.stroke();
		ctx.fillStyle = "rgb("+c.r+","+c.g+","+c.b+")";
		ctx.fill();
	}
}

function avgColor(imageData, imageSize, triangle) {

	box = new BoundingBox(triangle);
	var c = 0;
	var r = 0, g = 0, b = 0;
	for (var x = box.leftX; x < box.rightX; ++x)
		for (var y = box.leftY; y < box.rightY; ++y) {
			p = new Point(x,y);
			if (inTriangle(p, triangle)) {
				++c;
				r += imageData[4*(imageSize.w*y+x)];
				g += imageData[4*(imageSize.w*y+x)+1];
				b += imageData[4*(imageSize.w*y+x)+2];
			}
		}
	return {
		r: Math.floor(r/c),
		g: Math.floor(g/c),
		b: Math.floor(b/c),
	}
}<commit_msg>added fast approximate method for image colouring<commit_after>var canvas;
var ctx;
var image;
var points, triangles;

function init() {
	canvas = document.getElementById("tesselate");
	ctx = canvas.getContext("2d");

	ctx.fillStyle = "#FFFFFF";
	image = new Image();
	image.src = "stary_night.jpg";
	//improveImage(1,1,triangles);
	image.onload = function() {
		points = genPoints(4000, image)
		triangles = tesselate(points);
		setTimeout(function() {tesselateImage(image, triangles);});
	};
}

function tesselate(points) {
	points.sort(function (p1,p2) {
		return (p1.x > p2.x) || (p1.x === p2.x && p1.y > p2.y);
	});

	triangles = delaunay(points);

	for (var i = 0; i < triangles.length; ++i) {
		ctx.beginPath();
		ctx.moveTo(triangles[i][0].x, triangles[i][0].y);
		ctx.lineTo(triangles[i][1].x, triangles[i][1].y);
		ctx.lineTo(triangles[i][2].x, triangles[i][2].y);
		ctx.lineTo(triangles[i][0].x, triangles[i][0].y);
		ctx.strokeStyle = "#FFFFFF";
		ctx.stroke();
	}
	return triangles;
}

function tesselateImage(image, triangles) {
	var imgCanvas = document.createElement('canvas');
	imgCanvas.width = image.width;
	imgCanvas.height = image.height;
	var imageSize = {w: image.width, h: image.height};
	var imageCtx = imgCanvas.getContext('2d');
	imageCtx.drawImage(image, 0, 0, image.width, image.height);
	imageData = imageCtx.getImageData(0, 0, image.width, image.height).data;

	for (var i = 0; i < triangles.length; ++i) {
		var c = avgColor(imageData, imageSize, triangles[i]);
		var triangle = bitBigger(triangles[i]);
		ctx.beginPath();
		ctx.moveTo(triangle[0].x, triangle[0].y);
		ctx.lineTo(triangle[1].x, triangle[1].y);
		ctx.lineTo(triangle[2].x, triangle[2].y);
		ctx.lineTo(triangle[0].x, triangle[0].y);
		//ctx.strokeStyle = "rgb(0,0,0)";
		//ctx.stroke();
		ctx.fillStyle = "rgb("+c.r+","+c.g+","+c.b+")";
		ctx.fill();
	}

	for (var i = 0; i < triangles.length; ++i) {
		var c = avgColor(imageData, imageSize, triangles[i]);
		var triangle = triangles[i];
		ctx.beginPath();
		ctx.moveTo(triangle[0].x, triangle[0].y);
		ctx.lineTo(triangle[1].x, triangle[1].y);
		ctx.lineTo(triangle[2].x, triangle[2].y);
		ctx.lineTo(triangle[0].x, triangle[0].y);
		//ctx.strokeStyle = "rgb(0,0,0)";
		//ctx.stroke();
		ctx.fillStyle = "rgb("+c.r+","+c.g+","+c.b+")";
		ctx.fill();
	}
}

function fullAvgColor(imageData, imageSize, triangle) {

	box = new BoundingBox(triangle);
	var c = 0;
	var r = 0, g = 0, b = 0;
	for (var x = box.leftX; x < box.rightX; ++x)
		for (var y = box.leftY; y < box.rightY; ++y) {
			p = new Point(x,y);
			if (inTriangle(p, triangle)) {
				++c;
				r += imageData[4*(imageSize.w*y+x)];
				g += imageData[4*(imageSize.w*y+x)+1];
				b += imageData[4*(imageSize.w*y+x)+2];
			}
		}
	return {
		r: Math.floor(r/c),
		g: Math.floor(g/c),
		b: Math.floor(b/c),
	}
}

function monteCarloAvgColor(imageData, imageSize, triangle, samples) {
	if (samples === undefined) {
		samples = 200;
	}
	var edge1 = pDiff(triangle[1],triangle[0]);
	var edge2 = pDiff(triangle[2],triangle[0]);
	var r = 0, g = 0, b = 0;

	for (var i = 0; i < samples; ++i) {
		var r1 = Math.random();
		var r2 = Math.random();
		if (r1 + r2 > 1) {
			r1 = 1 - r1;
			r2 = 1 - r2;
		}
		var samplePoint = pSum(
				triangle[0],
				pSum(
					scale(edge1, r1),
					scale(edge2, r2)
				)
			);
		x = ~~(samplePoint.x);
		y = ~~(samplePoint.y);

		r += imageData[4*(imageSize.w*y+x)];
		g += imageData[4*(imageSize.w*y+x)+1];
		b += imageData[4*(imageSize.w*y+x)+2];

	}

	return {
		r: Math.floor(r/samples),
		g: Math.floor(g/samples),
		b: Math.floor(b/samples),
	}
}
//// Alternatives:
//	fullAvgColor - slow method, O(w*h), where w*h
//		is size of triangle's bounding box
//
//	monteCarloAvgColor - fast approximation, O(1)
var avgColor = monteCarloAvgColor;<|endoftext|>