<commit_before>(function() {
	/**
	 * Main script for the game AD. Uses canvas to display the game board.
	 */

	// Creation of the 4 arrows to move the rows and columns.
	var d = document, createElement = 'createElement',
		arrow = d[createElement]('canvas'),
		controlSize = 100,
		controlThird = controlSize / 3,
		controlTwoThird = controlThird * 2,
		controlHalf = controlSize / 2,
		controls = [
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlTwoThird, controlThird],
				[controlHalf, controlTwoThird]
			],
			[
				d[createElement]('img'),
				[controlTwoThird, controlThird],
				[controlTwoThird, controlTwoThird],
				[controlThird, controlHalf]
			],
			[
				d[createElement]('img'),
				[controlThird, controlTwoThird],
				[controlTwoThird, controlTwoThird],
				[controlHalf, controlThird]
			],
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlThird, controlTwoThird],
				[controlTwoThird, controlHalf]
			]
		],
		ctx,
		c,
		und = undefined,
		// attributes strings
		cellSize = 'cellSize',
		gridWidth = 'gridWidth',
		grid = 'grid',

		// Methods
		ad,
		_init,
		_createInformationsTable,
		_randomInt,
		_generateColors,
		_generateGrid,
		_displayGrid,
		_setEvents,
		_detectBlobs;

	if (!arrow.getContext) {
		throw 'This browser does not support the use of canvas';
	}

	arrow.width = arrow.height = controlSize;
	ctx = arrow.getContext('2d');

	ctx.fillStyle = '#888';
	ctx.strokeRect(0, 0, controlSize, controlSize);
	for (c = 0; c < 4; c++) {
		ctx.clearRect(1, 1, controlSize - 2, controlSize - 2);
		ctx.beginPath();
		ctx.moveTo(controls[c][1][0], controls[c][1][1]);
		ctx.lineTo(controls[c][2][0], controls[c][2][1]);
		ctx.lineTo(controls[c][3][0], controls[c][3][1]);
		ctx.fill();
		controls[c][0].src = arrow.toDataURL("image/png").replace("image/png", "image/octet-stream");
	}

	/**
	 * Game construct
	 */
	ad = function(parent, options) {
		var tmp, size, canvas;
		if (parent.tagName != 'DIV') {
			tmp = B.create('div');
			parent.parentNode.replaceChild(tmp, parent);
			parent = tmp;
		}

		this.parent = parent;
		options = options || {};
		size = options.size||300;
		canvas = B.create('canvas', {width: size, height: size}, this.parent);

		this.ctx = canvas.getContext('2d');
		this._controlButtons = [],
		_init.apply(this, [options.nbCellsSide||4, options.nbColors||4]);
	};

	_createInformationsTable = function() {
		this.infos = {blobs: ['Blobs', 0], moves: ['Moves', 0], colors: ['Colors', this.colors.length]};

		Object.keys(this.infos).forEach(function(key) {
			this.infos[key][1] = B.create('span', {text: this.infos[key][1]}, B.create('div', {text: this.infos[key][0] + ': '}, this.parent));
		}.bind(this));
	};

	/**
	 * Method to init the game
	 * Private method
	 */
	_init = function(gridWidth, nbColors) {
		_generateColors.apply(this, [nbColors]);
		_generateGrid.apply(this, [gridWidth, nbColors]);
		_displayGrid.apply(this, [true]);
		this._nbBlobs = _detectBlobs.apply(this);
		_createInformationsTable.apply(this);
		_setEvents.apply(this);
	};

	/**
	 * Method to generate a random integer
	 *
	 * Private method
	 */
	_randomInt = function(maxExcluded) {
		return 0 | Math.random() * maxExcluded;
	};

	/**
	 * Generate an array of colors. A color is an array of 3 integer(0, 255).
	 * The first color is randomly generated, the last one is the opposite of
	 * the first one. The others are calculated with a lerp computation.
	 */
	_generateColors = function(nb) {
		var colors = [],
			c,
			last = nb - 1,
			percentColor,
			_generateRandomColor,
			_lerp;

		_generateRandomColor = function() {
			return [
				_randomInt(256),
				_randomInt(256),
				_randomInt(256)
			]
		};

		_lerp = function(val1, val2, ratio) {
			return 0 | (Math.min(val1, val2) + Math.abs(val1 - val2) * ratio);
		};

		colors[0] = _generateRandomColor();
		colors[last] = [];

		for (c = 0; c < 3; c++) {
			colors[last].push((colors[0][c] + 128) % 256);
		}

		for (c = 1; c < last; c++) {
			percentColor = c / last;
			colors[c] = [
				_lerp(colors[0][0], colors[last][0], percentColor),
				_lerp(colors[0][2], colors[last][1], percentColor),
				_lerp(colors[0][1], colors[last][2], percentColor)
			];
		}

		this.colors = colors;
		this.nbColors = colors.length;
	};

	/**
	 * Generate the grid from a width (the grid is a square) and a number of
	 * colors.
	 * The returned grid is a 2 dimensions array containing, for each cell, an
	 * integer corresponding to the cell's color index (0, nbColors - 1)
	 */
	_generateGrid = function(width, nbColors) {
		var x, y;

		this[grid] = [];
		this[gridWidth] = width;

		for (x = 0; x < width; x++) {
			this[grid][x] = [];
			for (y = 0; y < width; y++) {
				this[grid][x][y] = _randomInt(nbColors);
			}
		}
	};

	/**
	 * Display the grid in the canvas
	 */
	_displayGrid = function(init) {
		var canvasWidth = this.ctx.canvas.clientWidth,
			x, y,
			_displayCell,
			_displayControl;

		this[cellSize] = canvasWidth / (this[gridWidth] + 2);

		_displayCell = function(x, y) {
			var color = this.colors[this[grid][x][y]];
			this.ctx.fillStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
			this.ctx.fillRect((x + 1) * this[cellSize], (y + 1) * this[cellSize], this[cellSize], this[cellSize]);
		};

		_displayControl = function(contX, contY, ctrls, c, tmp) {
			for (c = 0; c < ctrls.length; c++) {
				this._controlButtons.push([contX, contY]);
				this.ctx.drawImage(controls[ctrls[c]][0], contX, contY, this[cellSize], this[cellSize]);
				tmp = contX;
				contX = contY;
				contY = tmp;
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			if (init) {
				// control buttons
				_displayControl.apply(this, [(x + 1) * this[cellSize], 0, [0, 3]]);
				_displayControl.apply(this, [canvasWidth - this[cellSize], (x + 1) * this[cellSize], [1, 2]]);
			}

			for (y = 0; y < this[gridWidth]; y++) {
				_displayCell.apply(this, [x, y]);
			}
		}
	};

	/**
	 * Method to set the click event on the game, to move the controls.
	 */
	_setEvents = function() {
		/**
		 * Method to check if the player clicked on a control.
		 */
		var _onControl = function(x, y) {
			var b, nbButtons = this._controlButtons.length;

			for (b = 0; b < nbButtons; b++) {
				if (
					this._controlButtons[b][0] <= x
					&& x <= this._controlButtons[b][0] + this[cellSize]
					&& this._controlButtons[b][1] <= y
					&& y <= this._controlButtons[b][1] + this[cellSize]
				) {
					break;
				}
			}

			return b == nbButtons ? null : b;
		};

		B.addEvent(this.parent, 'click', function(e){
			var button = _onControl.apply(this, [e.layerX, e.layerY]),
				_shiftDown, _shiftRight, _shiftLeft, _shiftUp,
				row;
			if (button == null) return;

			/**
			 * Methods to move a row or column in the 4 directions
			 */
			_shiftDown = function(col) {
				var last = this[grid][col][this[grid][col].length - 1];
				this[grid][col].pop();
				this[grid][col].unshift(last);
			};
			_shiftRight = function(row) {
				var old1, old2 = -1, r, last = this[grid].length - 1;
				for (r in this[grid]) {
					old1 = this[grid][r][row];
					this[grid][r][row] = ~old2 ? old2 : this[grid][(r + last) % this[grid].length][row];
					old2 = old1;
				}
			};
			_shiftUp = function(col) {
				var first = this[grid][col][0];
				this[grid][col].shift();
				this[grid][col].push(first);
			};
			_shiftLeft = function(row) {
				var r, first = this[grid][0][row];
				for (r = 0; r < this[gridWidth]; r++) {
					this[grid][r][row] = this[grid][(parseInt(r) + 1) % this[grid].length][row];
				}
				this[grid][this[grid].length - 1][row] = first;
			};

			row = 0|button/4;
			switch (button % 4) {
				case 0:
					_shiftDown.apply(this, [row]);
					break;
				case 1:
					_shiftRight.apply(this, [row]);
					break;
				case 2:
					_shiftLeft.apply(this, [row]);
					break;
				case 3:
					_shiftUp.apply(this, [row]);
					break;
			};

			this._nbBlobs = _detectBlobs.apply(this);
			_displayGrid.apply(this, [false]);

			// Winning condition
			if (this._nbBlobs == this.nbColors) {
			}
		}.bind(this));
	};

	/**
	 * Method to detect the blobs (like in picture processing) in the board.
	 * Returns the number of blobs of the board.
	 */
	_detectBlobs = function() {
		var blobs, nbBlobs = 0,
			x, y,
			nbCells = this[gridWidth] * this[gridWidth],
			_setBlobsAndPropagate;

		blobs = new Array(nbCells);

		_setBlobsAndPropagate = function(x, y, id) {
			var current = this[grid][x][y],
				currentIndex = y * this[gridWidth] + x,
				north = currentIndex - this[gridWidth],
				south = currentIndex + this[gridWidth],
				east = currentIndex + 1,
				west = currentIndex - 1;
			blobs[currentIndex] = id;

			if (north > 0 && blobs[north] == und && this[grid][x][y-1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y-1, id]);
			}
			if (east%this[gridWidth] > currentIndex%this[gridWidth] && blobs[east] == und && this[grid][x+1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x+1, y, id]);
			}
			if (south < nbCells && blobs[south] == und && this[grid][x][y+1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y+1, id]);
			}
			if (Math.abs(west%this[gridWidth]) < currentIndex%this[gridWidth] && blobs[west] == und && this[grid][x-1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x-1, y, id]);
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			for (y = 0; y < this[gridWidth]; y++) {
				if (blobs[y * this[gridWidth] + x] != und) {
					continue;
				}

				_setBlobsAndPropagate.apply(this, [x, y, nbBlobs]);
				nbBlobs++;
			}
		}

		return nbBlobs;
	};

	window.Ad = ad;
})();
<commit_msg>methods to fetch and set informations<commit_after>(function() {
	/**
	 * Main script for the game AD. Uses canvas to display the game board.
	 */

	// Creation of the 4 arrows to move the rows and columns.
	var d = document, createElement = 'createElement',
		arrow = d[createElement]('canvas'),
		controlSize = 100,
		controlThird = controlSize / 3,
		controlTwoThird = controlThird * 2,
		controlHalf = controlSize / 2,
		controls = [
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlTwoThird, controlThird],
				[controlHalf, controlTwoThird]
			],
			[
				d[createElement]('img'),
				[controlTwoThird, controlThird],
				[controlTwoThird, controlTwoThird],
				[controlThird, controlHalf]
			],
			[
				d[createElement]('img'),
				[controlThird, controlTwoThird],
				[controlTwoThird, controlTwoThird],
				[controlHalf, controlThird]
			],
			[
				d[createElement]('img'),
				[controlThird, controlThird],
				[controlThird, controlTwoThird],
				[controlTwoThird, controlHalf]
			]
		],
		ctx,
		c,
		und = undefined,
		// attributes strings
		cellSize = 'cellSize',
		gridWidth = 'gridWidth',
		grid = 'grid',

		// Methods
		ad,
		_init,
		_createInformationsTable,
		_getInformation,
		_updateInformation,
		_randomInt,
		_generateColors,
		_generateGrid,
		_displayGrid,
		_setEvents,
		_detectBlobs;

	if (!arrow.getContext) {
		throw 'This browser does not support the use of canvas';
	}

	arrow.width = arrow.height = controlSize;
	ctx = arrow.getContext('2d');

	ctx.fillStyle = '#888';
	ctx.strokeRect(0, 0, controlSize, controlSize);
	for (c = 0; c < 4; c++) {
		ctx.clearRect(1, 1, controlSize - 2, controlSize - 2);
		ctx.beginPath();
		ctx.moveTo(controls[c][1][0], controls[c][1][1]);
		ctx.lineTo(controls[c][2][0], controls[c][2][1]);
		ctx.lineTo(controls[c][3][0], controls[c][3][1]);
		ctx.fill();
		controls[c][0].src = arrow.toDataURL("image/png").replace("image/png", "image/octet-stream");
	}

	/**
	 * Game construct
	 */
	ad = function(parent, options) {
		var tmp, size, canvas;
		if (parent.tagName != 'DIV') {
			tmp = B.create('div');
			parent.parentNode.replaceChild(tmp, parent);
			parent = tmp;
		}

		this.parent = parent;
		options = options || {};
		size = options.size||300;
		canvas = B.create('canvas', {width: size, height: size}, this.parent);

		this.ctx = canvas.getContext('2d');
		this._controlButtons = [],
		_init.apply(this, [options.nbCellsSide||4, options.nbColors||4]);
	};

	_createInformationsTable = function() {
		this.infos = {blobs: ['Blobs', 0], moves: ['Moves', 0], colors: ['Colors', this.colors.length]};

		Object.keys(this.infos).forEach(function(key) {
			this.infos[key][1] = B.create('span', {text: this.infos[key][1]}, B.create('div', {text: this.infos[key][0] + ': '}, this.parent));
		}.bind(this));
	};

	_getInformation = function(info) {
		return this.infos[info] ? parseInt(this.infos[info][1].innerHTML) : false;
	};

	_updateInformation = function(info, value) {
		if (!this.infos[info]) return false;

		this.infos[info][1].innerHTML = value;
	};

	/**
	 * Method to init the game
	 * Private method
	 */
	_init = function(gridWidth, nbColors) {
		_generateColors.apply(this, [nbColors]);
		_generateGrid.apply(this, [gridWidth, nbColors]);
		_displayGrid.apply(this, [true]);
		this._nbBlobs = _detectBlobs.apply(this);
		_createInformationsTable.apply(this);
		_setEvents.apply(this);
	};

	/**
	 * Method to generate a random integer
	 *
	 * Private method
	 */
	_randomInt = function(maxExcluded) {
		return 0 | Math.random() * maxExcluded;
	};

	/**
	 * Generate an array of colors. A color is an array of 3 integer(0, 255).
	 * The first color is randomly generated, the last one is the opposite of
	 * the first one. The others are calculated with a lerp computation.
	 */
	_generateColors = function(nb) {
		var colors = [],
			c,
			last = nb - 1,
			percentColor,
			_generateRandomColor,
			_lerp;

		_generateRandomColor = function() {
			return [
				_randomInt(256),
				_randomInt(256),
				_randomInt(256)
			]
		};

		_lerp = function(val1, val2, ratio) {
			return 0 | (Math.min(val1, val2) + Math.abs(val1 - val2) * ratio);
		};

		colors[0] = _generateRandomColor();
		colors[last] = [];

		for (c = 0; c < 3; c++) {
			colors[last].push((colors[0][c] + 128) % 256);
		}

		for (c = 1; c < last; c++) {
			percentColor = c / last;
			colors[c] = [
				_lerp(colors[0][0], colors[last][0], percentColor),
				_lerp(colors[0][2], colors[last][1], percentColor),
				_lerp(colors[0][1], colors[last][2], percentColor)
			];
		}

		this.colors = colors;
		this.nbColors = colors.length;
	};

	/**
	 * Generate the grid from a width (the grid is a square) and a number of
	 * colors.
	 * The returned grid is a 2 dimensions array containing, for each cell, an
	 * integer corresponding to the cell's color index (0, nbColors - 1)
	 */
	_generateGrid = function(width, nbColors) {
		var x, y;

		this[grid] = [];
		this[gridWidth] = width;

		for (x = 0; x < width; x++) {
			this[grid][x] = [];
			for (y = 0; y < width; y++) {
				this[grid][x][y] = _randomInt(nbColors);
			}
		}
	};

	/**
	 * Display the grid in the canvas
	 */
	_displayGrid = function(init) {
		var canvasWidth = this.ctx.canvas.clientWidth,
			x, y,
			_displayCell,
			_displayControl;

		this[cellSize] = canvasWidth / (this[gridWidth] + 2);

		_displayCell = function(x, y) {
			var color = this.colors[this[grid][x][y]];
			this.ctx.fillStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
			this.ctx.fillRect((x + 1) * this[cellSize], (y + 1) * this[cellSize], this[cellSize], this[cellSize]);
		};

		_displayControl = function(contX, contY, ctrls, c, tmp) {
			for (c = 0; c < ctrls.length; c++) {
				this._controlButtons.push([contX, contY]);
				this.ctx.drawImage(controls[ctrls[c]][0], contX, contY, this[cellSize], this[cellSize]);
				tmp = contX;
				contX = contY;
				contY = tmp;
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			if (init) {
				// control buttons
				_displayControl.apply(this, [(x + 1) * this[cellSize], 0, [0, 3]]);
				_displayControl.apply(this, [canvasWidth - this[cellSize], (x + 1) * this[cellSize], [1, 2]]);
			}

			for (y = 0; y < this[gridWidth]; y++) {
				_displayCell.apply(this, [x, y]);
			}
		}
	};

	/**
	 * Method to set the click event on the game, to move the controls.
	 */
	_setEvents = function() {
		/**
		 * Method to check if the player clicked on a control.
		 */
		var _onControl = function(x, y) {
			var b, nbButtons = this._controlButtons.length;

			for (b = 0; b < nbButtons; b++) {
				if (
					this._controlButtons[b][0] <= x
					&& x <= this._controlButtons[b][0] + this[cellSize]
					&& this._controlButtons[b][1] <= y
					&& y <= this._controlButtons[b][1] + this[cellSize]
				) {
					break;
				}
			}

			return b == nbButtons ? null : b;
		};

		B.addEvent(this.parent, 'click', function(e){
			var button = _onControl.apply(this, [e.layerX, e.layerY]),
				_shiftDown, _shiftRight, _shiftLeft, _shiftUp,
				row;
			if (button == null) return;

			/**
			 * Methods to move a row or column in the 4 directions
			 */
			_shiftDown = function(col) {
				var last = this[grid][col][this[grid][col].length - 1];
				this[grid][col].pop();
				this[grid][col].unshift(last);
			};
			_shiftRight = function(row) {
				var old1, old2 = -1, r, last = this[grid].length - 1;
				for (r in this[grid]) {
					old1 = this[grid][r][row];
					this[grid][r][row] = ~old2 ? old2 : this[grid][(r + last) % this[grid].length][row];
					old2 = old1;
				}
			};
			_shiftUp = function(col) {
				var first = this[grid][col][0];
				this[grid][col].shift();
				this[grid][col].push(first);
			};
			_shiftLeft = function(row) {
				var r, first = this[grid][0][row];
				for (r = 0; r < this[gridWidth]; r++) {
					this[grid][r][row] = this[grid][(parseInt(r) + 1) % this[grid].length][row];
				}
				this[grid][this[grid].length - 1][row] = first;
			};

			row = 0|button/4;
			switch (button % 4) {
				case 0:
					_shiftDown.apply(this, [row]);
					break;
				case 1:
					_shiftRight.apply(this, [row]);
					break;
				case 2:
					_shiftLeft.apply(this, [row]);
					break;
				case 3:
					_shiftUp.apply(this, [row]);
					break;
			};

			this._nbBlobs = _detectBlobs.apply(this);
			_displayGrid.apply(this, [false]);

			// Winning condition
			if (this._nbBlobs == this.nbColors) {
			}
		}.bind(this));
	};

	/**
	 * Method to detect the blobs (like in picture processing) in the board.
	 * Returns the number of blobs of the board.
	 */
	_detectBlobs = function() {
		var blobs, nbBlobs = 0,
			x, y,
			nbCells = this[gridWidth] * this[gridWidth],
			_setBlobsAndPropagate;

		blobs = new Array(nbCells);

		_setBlobsAndPropagate = function(x, y, id) {
			var current = this[grid][x][y],
				currentIndex = y * this[gridWidth] + x,
				north = currentIndex - this[gridWidth],
				south = currentIndex + this[gridWidth],
				east = currentIndex + 1,
				west = currentIndex - 1;
			blobs[currentIndex] = id;

			if (north > 0 && blobs[north] == und && this[grid][x][y-1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y-1, id]);
			}
			if (east%this[gridWidth] > currentIndex%this[gridWidth] && blobs[east] == und && this[grid][x+1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x+1, y, id]);
			}
			if (south < nbCells && blobs[south] == und && this[grid][x][y+1] == current) {
				_setBlobsAndPropagate.apply(this, [x, y+1, id]);
			}
			if (Math.abs(west%this[gridWidth]) < currentIndex%this[gridWidth] && blobs[west] == und && this[grid][x-1][y] == current) {
				_setBlobsAndPropagate.apply(this, [x-1, y, id]);
			}
		};

		for (x = 0; x < this[gridWidth]; x++) {
			for (y = 0; y < this[gridWidth]; y++) {
				if (blobs[y * this[gridWidth] + x] != und) {
					continue;
				}

				_setBlobsAndPropagate.apply(this, [x, y, nbBlobs]);
				nbBlobs++;
			}
		}

		return nbBlobs;
	};

	window.Ad = ad;
})();
<|endoftext|>


<commit_before>const AdmZip = require('adm-zip')
const cheerio = require('cheerio')
const natural = require('natural')
const path = require('path')
const request = require('request-promise')

const SUBSCENE_URL = 'https://subscene.com'

class SearchResult {
  constructor ({title, url, coefficient}) {
    this.title = title
    this.url = url
    this.coefficient = coefficient
  }
}

/**
 * Get subtitles for the given file.
 *
 * @param {string} file - path to a file
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
async function getSubtitles (file) {
  const filename = path.parse(file).name

  // TODO: revamp this + add unit tests
  return getSubtitlesList(filename)
    .then(parseSearchResults)
    .then(getBestSearchResult(filename))
    .then(getSubtitlesPage)
    .then(parseDownloadLink)
    .then(downloadZip)
    .then(extractSrt)
}

/**
 * Search for the given file on SubScene.
 *
 * @param {string} filename
 * @returns {Promise<string>} the HTML string, containing the search results
 */
async function getSubtitlesList (filename) {
  return request({
    method: 'GET',
    uri: SUBSCENE_URL + '/subtitles/release',
    qs: {
      q: filename, // search query
      l: '',       // language (english)
      r: true      // released or whatever
    }
  })
}

/**
 * Parse an HTML document that contains a list of SubScene search results.
 *
 * @param {string} html
 * @returns {Array<SearchResult>} the list of search results
 */
function parseSearchResults (html) {
  const $ = cheerio.load(html)
  return $('a')
    .filter(function (index, element) {
      const spans = $(element).find('span')
      return spans.length === 2 && spans.eq(0).text().trim() === 'English'
    })
    .map(function (index, element) {
      const title = $(element).find('span').eq(1).text().trim()
      const url = $(element).attr('href')
      return new SearchResult({
        title: title,
        url: url
      })
    })
    .get()
}

function getBestSearchResult (filename) {
  /**
   * Get the search result whose title is as close to filename as possible.
   *
   * @param {Array<SearchResult>} searchResults
   * @returns {SearchResult} the "best" search result
   */
  return function (searchResults) {
    const weightedSearchResults = searchResults
      .map(function (searchResult) {
        return new SearchResult({
          title: searchResult.title,
          url: searchResult.url,
          coefficient: natural.DiceCoefficient(filename, searchResult.title)
        })
      })
      .sort((a, b) => b.coefficient - a.coefficient)
    return weightedSearchResults[0]
  }
}

/**
 * Get the details page of the given SearchResult.
 *
 * @param {SearchResult} searchResult
 * @returns {Promise<string>} the HTML page for the given SearchResult
 */
async function getSubtitlesPage (searchResult) {
  return request({
    method: 'GET',
    uri: SUBSCENE_URL + searchResult.url
  })
}

/**
 * Extract the download link from the HTML page.
 *
 * @param {string} html
 * @returns {string} the path to download the subtitles
 */
function parseDownloadLink (html) {
  const $ = cheerio.load(html)
  return $('#downloadButton').eq(0).attr('href')
}

/**
 * Download the subtitles based on the given link.
 *
 * @param {string} href
 * @returns {Promise<Buffer>} the ZIP content
 */
async function downloadZip (href) {
  return request({
    method: 'GET',
    uri: SUBSCENE_URL + href,
    encoding: null
  })
}

/**
 * Extract the first .srt file found in the given ZIP buffer.
 *
 * @param {Buffer} buffer
 * @returns {string} the content of the first .srt file found in the given ZIP
 */
function extractSrt (buffer) {
  const zip = new AdmZip(buffer)
  const srtZipEntry = zip
    .getEntries()
    .find(zipEntry =>
      zipEntry.entryName.endsWith('.srt')
    )
  return zip.readAsText(srtZipEntry)
}

module.exports = {
  name: 'Subscene',
  getSubtitles
}
<commit_msg>feat(subscene): expose `getSubtitlesByFilename`<commit_after>const AdmZip = require('adm-zip')
const cheerio = require('cheerio')
const natural = require('natural')
const path = require('path')
const request = require('request-promise')

const SUBSCENE_URL = 'https://subscene.com'

class SearchResult {
  constructor ({title, url, coefficient}) {
    this.title = title
    this.url = url
    this.coefficient = coefficient
  }
}

/**
 * Get subtitles for the given file.
 *
 * @param {string} file - path to a file
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
async function getSubtitles (file) {
  const filename = path.parse(file).name
  return getSubtitlesByFilename(filename)
}

/**
 * Get subtitles for the given filename.
 *
 * @param {string} filename
 * @returns {Promise<string>} the subtitles, formatted as .srt
 */
async function getSubtitlesByFilename (filename) {
  // TODO: revamp this + add unit tests
  return getSubtitlesList(filename)
    .then(parseSearchResults)
    .then(getBestSearchResult(filename))
    .then(getSubtitlesPage)
    .then(parseDownloadLink)
    .then(downloadZip)
    .then(extractSrt)
}

/**
 * Search for the given file on SubScene.
 *
 * @param {string} filename
 * @returns {Promise<string>} the HTML string, containing the search results
 */
async function getSubtitlesList (filename) {
  return request({
    method: 'GET',
    uri: SUBSCENE_URL + '/subtitles/release',
    qs: {
      q: filename, // search query
      l: '',       // language (english)
      r: true      // released or whatever
    }
  })
}

/**
 * Parse an HTML document that contains a list of SubScene search results.
 *
 * @param {string} html
 * @returns {Array<SearchResult>} the list of search results
 */
function parseSearchResults (html) {
  const $ = cheerio.load(html)
  return $('a')
    .filter(function (index, element) {
      const spans = $(element).find('span')
      return spans.length === 2 && spans.eq(0).text().trim() === 'English'
    })
    .map(function (index, element) {
      const title = $(element).find('span').eq(1).text().trim()
      const url = $(element).attr('href')
      return new SearchResult({
        title: title,
        url: url
      })
    })
    .get()
}

function getBestSearchResult (filename) {
  /**
   * Get the search result whose title is as close to filename as possible.
   *
   * @param {Array<SearchResult>} searchResults
   * @returns {SearchResult} the "best" search result
   */
  return function (searchResults) {
    const weightedSearchResults = searchResults
      .map(function (searchResult) {
        return new SearchResult({
          title: searchResult.title,
          url: searchResult.url,
          coefficient: natural.DiceCoefficient(filename, searchResult.title)
        })
      })
      .sort((a, b) => b.coefficient - a.coefficient)
    return weightedSearchResults[0]
  }
}

/**
 * Get the details page of the given SearchResult.
 *
 * @param {SearchResult} searchResult
 * @returns {Promise<string>} the HTML page for the given SearchResult
 */
async function getSubtitlesPage (searchResult) {
  return request({
    method: 'GET',
    uri: SUBSCENE_URL + searchResult.url
  })
}

/**
 * Extract the download link from the HTML page.
 *
 * @param {string} html
 * @returns {string} the path to download the subtitles
 */
function parseDownloadLink (html) {
  const $ = cheerio.load(html)
  return $('#downloadButton').eq(0).attr('href')
}

/**
 * Download the subtitles based on the given link.
 *
 * @param {string} href
 * @returns {Promise<Buffer>} the ZIP content
 */
async function downloadZip (href) {
  return request({
    method: 'GET',
    uri: SUBSCENE_URL + href,
    encoding: null
  })
}

/**
 * Extract the first .srt file found in the given ZIP buffer.
 *
 * @param {Buffer} buffer
 * @returns {string} the content of the first .srt file found in the given ZIP
 */
function extractSrt (buffer) {
  const zip = new AdmZip(buffer)
  const srtZipEntry = zip
    .getEntries()
    .find(zipEntry =>
      zipEntry.entryName.endsWith('.srt')
    )
  return zip.readAsText(srtZipEntry)
}

module.exports = {
  name: 'Subscene',
  getSubtitles,
  getSubtitlesByFilename
}
<|endoftext|>


<commit_before>// @flow

import test from 'ava';
import * as actions from 'actions/timetables';
import * as themeActions from 'actions/theme';
import reducer from 'reducers/app';

import type { Lesson, ModuleCode, Semester } from 'types/modules';
import type { FSA } from 'types/redux';
import type { AppState } from 'types/reducers';
import lessons from '../mocks/lessons-array.json';

const semester: Semester = 1;
const lesson: Lesson = lessons[0];
const moduleCode: ModuleCode = 'CS1010S';
const appInitialState: AppState = { activeLesson: null, activeModule: null };
const appHasActiveLessonState: AppState = { ...appInitialState, activeLesson: lesson };
const appHasActiveModuleState: AppState = { ...appInitialState, activeModule: moduleCode };

test('app should return initial state', (t) => {
  const nextState: AppState = reducer(undefined, {});

  t.deepEqual(nextState, appInitialState);
});

// Test for active lesson.

test('app should instantiate active lesson', (t) => {
  const action: FSA = actions.modifyLesson(lesson);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, appHasActiveLessonState);
});

test('app should set active lesson', (t) => {
  const anotherLesson: Lesson = lessons[1];
  const action: FSA = actions.modifyLesson(anotherLesson);
  const nextState: AppState = reducer(appHasActiveLessonState, action);

  t.deepEqual(nextState, { ...appInitialState, activeLesson: anotherLesson });
});

test('app should accept lesson change and unset active lesson', (t) => {
  const action: FSA = actions.changeLesson(semester, lesson);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, appInitialState);
});

test('app should cancel and unset active lesson', (t) => {
  const nextState: AppState = reducer(appHasActiveLessonState, actions.cancelModifyLesson());

  t.deepEqual(nextState, appInitialState);
});

// Test for active module.

test('app should instantiate active module', (t) => {
  const action: FSA = themeActions.modifyModuleColor(moduleCode);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, appHasActiveModuleState);
});

test('app should set active module', (t) => {
  const anotherModuleCode: ModuleCode = 'CS3216';
  const action: FSA = themeActions.modifyModuleColor(anotherModuleCode);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, { ...appInitialState, activeModule: anotherModuleCode });
});

test('app should accept module color change and unset active module', (t) => {
  const action: FSA = themeActions.selectModuleColor(moduleCode, 1);
  const nextState: AppState = reducer(appHasActiveModuleState, action);

  t.deepEqual(nextState, appInitialState);
});

test('app should cancel and unset active module', (t) => {
  const nextState: AppState = reducer(appHasActiveModuleState,
                                      themeActions.cancelModifyModuleColor());

  t.deepEqual(nextState, appInitialState);
});
<commit_msg>Fix reducer test flow types<commit_after>// @flow

import test from 'ava';
import * as actions from 'actions/timetables';
import * as themeActions from 'actions/theme';
import reducer from 'reducers/app';

import type { Lesson, ModuleCode, Semester } from 'types/modules';
import type { FSA } from 'types/redux';
import type { AppState } from 'types/reducers';
import lessons from '../mocks/lessons-array.json';

const semester: Semester = 1;
const lesson: Lesson = lessons[0];
const moduleCode: ModuleCode = 'CS1010S';
const appInitialState: AppState = { activeLesson: null, activeModule: null };
const appHasActiveLessonState: AppState = { ...appInitialState, activeLesson: lesson };
const appHasActiveModuleState: AppState = { ...appInitialState, activeModule: moduleCode };

test('app should return initial state', (t) => {
  const nextState: AppState = reducer(undefined, {type: 'INIT'});

  t.deepEqual(nextState, appInitialState);
});

// Test for active lesson.

test('app should instantiate active lesson', (t) => {
  const action: FSA = actions.modifyLesson(lesson);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, appHasActiveLessonState);
});

test('app should set active lesson', (t) => {
  const anotherLesson: Lesson = lessons[1];
  const action: FSA = actions.modifyLesson(anotherLesson);
  const nextState: AppState = reducer(appHasActiveLessonState, action);

  t.deepEqual(nextState, { ...appInitialState, activeLesson: anotherLesson });
});

test('app should accept lesson change and unset active lesson', (t) => {
  const action: FSA = actions.changeLesson(semester, lesson);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, appInitialState);
});

test('app should cancel and unset active lesson', (t) => {
  const nextState: AppState = reducer(appHasActiveLessonState, actions.cancelModifyLesson());

  t.deepEqual(nextState, appInitialState);
});

// Test for active module.

test('app should instantiate active module', (t) => {
  const action: FSA = themeActions.modifyModuleColor(moduleCode);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, appHasActiveModuleState);
});

test('app should set active module', (t) => {
  const anotherModuleCode: ModuleCode = 'CS3216';
  const action: FSA = themeActions.modifyModuleColor(anotherModuleCode);
  const nextState: AppState = reducer(appInitialState, action);

  t.deepEqual(nextState, { ...appInitialState, activeModule: anotherModuleCode });
});

test('app should accept module color change and unset active module', (t) => {
  const action: FSA = themeActions.selectModuleColor(moduleCode, 1);
  const nextState: AppState = reducer(appHasActiveModuleState, action);

  t.deepEqual(nextState, appInitialState);
});

test('app should cancel and unset active module', (t) => {
  const nextState: AppState = reducer(appHasActiveModuleState,
                                      themeActions.cancelModifyModuleColor());

  t.deepEqual(nextState, appInitialState);
});
<|endoftext|>


<commit_before>class Rtf {
    constructor(name, data) {
        this.name = name;
        this.data = data;
    }
}

class Viewer {
    constructor(rtf, baseUrl){
        this.rtf = rtf;
        this.baseUrl = baseUrl;
        document.getElementById("download").addEventListener("click", event => this._downloadRtfFile(event));
    }

    _stringToBinaryArray(string) {
        var buffer = new ArrayBuffer(string.length);
        var bufferView = new Uint8Array(buffer);
        for (var i=0; i<string.length; i++) {
            bufferView[i] = string.charCodeAt(i);
        }
        return buffer;
    }

    renderDocument(){
        try {
            RTFJS.loggingEnabled(false);
            WMFJS.loggingEnabled(false);
            EMFJS.loggingEnabled(false);

            var baseUrl = this.baseUrl;

            var settings = {
                onImport: function(relURL, cb) {
                    const file = baseUrl + relURL;
                    const ext  = relURL.replace(/^.*\.([^\.]+)$/, '$1').toLowerCase();
                    let keyword;
                    switch(ext) {
                        case 'emf':
                            keyword = 'emfblip';
                            break;
                        case 'wmf':
                            keyword = 'wmetafile';
                            break;
                        default:
                            return cb({error});
                    }

                    var request = new XMLHttpRequest();
                    request.open("GET", file, true);
                    request.responseType = "arraybuffer";

                    request.onload = function (event) {
                        var blob = request.response;
                        if (blob) {
                            let height = 300;
                            cb({keyword, blob, height});
                        } else {
                            let error = new Error(request.statusText);
                            cb({error});
                        }
                    };

                    request.send(null);
                }
            }

            var doc = new RTFJS.Document(this._stringToBinaryArray(this.rtf.data), settings);

            //Set title if meta data available
            var meta = doc.metadata();
            if(meta.title && meta.title.trim() !== ""){
                document.title = meta.title;
            }else{
                document.title = this.rtf.name;
            }

            //Display document
            doc.render().then(renderedElements => {
                var mainElement = $("#main").empty();
                renderedElements.forEach(function(renderedElement){
                    mainElement.append(DOMPurify.sanitize(renderedElement[0], {SAFE_FOR_JQUERY: true}));
                });
            });
        }catch (error) {
            if (error instanceof RTFJS.Error || error instanceof WMFJS.Error || error instanceof EMFJS.Error) {
                $("#main").text("Error: " + error.message);
                console.error(error);
            } else {
                throw error;
            }
        }
    }

    _downloadRtfFile(event){
        var blob = new Blob([this.rtf.data], {type: "text/rtf"});
        var url = URL.createObjectURL(blob);
        var a = event.target;
        a.href = url;
        a.download = this.rtf.name;
    }
}

function loadDataXhr(){
    var rtfUrl = decodeURIComponent(location.search.replace("?file=",""));
    var xhr = new XMLHttpRequest();
    xhr.open("GET", rtfUrl, true);
    xhr.onreadystatechange = function() {
        if(xhr.readyState < 4) {
            $("#main").text("Loading ...");
        }
        if (xhr.readyState == 4) {
            //XHR requests for local files (file://) don't have a status
            if ((xhr.status == 0 || (xhr.status >= 200 && xhr.status < 300))
                && xhr.responseText && xhr.responseText != "") {
                //Save data for later use
                const rtf = new Rtf(rtfUrl.substring(rtfUrl.lastIndexOf("/") + 1), xhr.responseText)

                //Render document
                const viewer = new Viewer(rtf, rtfUrl.replace(/^(.*\/)[^\/]*$/, '$1'));
                viewer.renderDocument();
            } else{
                $("#main").text("Error: File not Found");
            }
        }
    };
    xhr.send();
}

function loadDataFileReader(file){
    let reader = new FileReader();
    reader.onload = function(event) {
        const rtf = new Rtf(file.name, event.target.result);

        //Render document
        const viewer = new Viewer(rtf, "");
        viewer.renderDocument();
    };
    reader.readAsText(file);
}

document.addEventListener('DOMContentLoaded', function(){
    if(location.search.startsWith("?file=")) {
        document.getElementById("main").classList.remove("hidden");
        document.getElementById("toolbar").classList.remove("hidden");
        loadDataXhr();
    } else {
        document.getElementById("file-upload").classList.remove("hidden");

        document.getElementById("upload-field").addEventListener("change", function () {
            document.getElementById("file-upload").classList.add("hidden");
            document.getElementById("main").classList.remove("hidden");
            document.getElementById("toolbar").classList.remove("hidden");
            loadDataFileReader(this.files[0])
        }, false)
    }
}, true);<commit_msg>Improve downloaded file name<commit_after>class Rtf {
    constructor(name, data) {
        this.name = name;
        this.data = data;
    }
}

class Viewer {
    constructor(rtf, baseUrl){
        this.rtf = rtf;
        this.baseUrl = baseUrl;
        document.getElementById("download").addEventListener("click", event => this._downloadRtfFile(event));
    }

    _stringToBinaryArray(string) {
        var buffer = new ArrayBuffer(string.length);
        var bufferView = new Uint8Array(buffer);
        for (var i=0; i<string.length; i++) {
            bufferView[i] = string.charCodeAt(i);
        }
        return buffer;
    }

    renderDocument(){
        try {
            RTFJS.loggingEnabled(false);
            WMFJS.loggingEnabled(false);
            EMFJS.loggingEnabled(false);

            var baseUrl = this.baseUrl;

            var settings = {
                onImport: function(relURL, cb) {
                    const file = baseUrl + relURL;
                    const ext  = relURL.replace(/^.*\.([^\.]+)$/, '$1').toLowerCase();
                    let keyword;
                    switch(ext) {
                        case 'emf':
                            keyword = 'emfblip';
                            break;
                        case 'wmf':
                            keyword = 'wmetafile';
                            break;
                        default:
                            return cb({error});
                    }

                    var request = new XMLHttpRequest();
                    request.open("GET", file, true);
                    request.responseType = "arraybuffer";

                    request.onload = function (event) {
                        var blob = request.response;
                        if (blob) {
                            let height = 300;
                            cb({keyword, blob, height});
                        } else {
                            let error = new Error(request.statusText);
                            cb({error});
                        }
                    };

                    request.send(null);
                }
            }

            var doc = new RTFJS.Document(this._stringToBinaryArray(this.rtf.data), settings);

            //Set title if meta data available
            var meta = doc.metadata();
            if(meta.title && meta.title.trim() !== ""){
                document.title = meta.title;
            }else{
                document.title = this.rtf.name;
            }

            //Display document
            doc.render().then(renderedElements => {
                var mainElement = $("#main").empty();
                renderedElements.forEach(function(renderedElement){
                    mainElement.append(DOMPurify.sanitize(renderedElement[0], {SAFE_FOR_JQUERY: true}));
                });
            });
        }catch (error) {
            if (error instanceof RTFJS.Error || error instanceof WMFJS.Error || error instanceof EMFJS.Error) {
                $("#main").text("Error: " + error.message);
                console.error(error);
            } else {
                throw error;
            }
        }
    }

    _downloadRtfFile(event){
        var blob = new Blob([this.rtf.data], {type: "text/rtf"});
        var url = URL.createObjectURL(blob);
        var a = event.target;
        a.href = url;
        a.download = this.rtf.name;
    }
}

function loadDataXhr(){
    var rtfUrl = decodeURIComponent(location.search.replace("?file=",""));
    var xhr = new XMLHttpRequest();
    xhr.open("GET", rtfUrl, true);
    xhr.onreadystatechange = function() {
        if(xhr.readyState < 4) {
            $("#main").text("Loading ...");
        }
        if (xhr.readyState == 4) {
            //XHR requests for local files (file://) don't have a status
            if ((xhr.status == 0 || (xhr.status >= 200 && xhr.status < 300))
                && xhr.responseText && xhr.responseText != "") {
                // Remove everything before the file name from the url
                let rtfTitle = rtfUrl.substring(rtfUrl.lastIndexOf("/") + 1);
                if(rtfTitle.includes("?")){
                    // Remove trailing url parameters
                    rtfTitle = rtfTitle.substring(0, rtfTitle.indexOf("?"));
                }
                // Make sure the file name has a .rtf extension
                if(!rtfTitle.toLowerCase().endsWith(".rtf")){
                    rtfTitle = rtfTitle + ".rtf";
                }

                //Save data for later use
                const rtf = new Rtf(rtfTitle, xhr.responseText);

                //Render document
                const viewer = new Viewer(rtf, rtfUrl.replace(/^(.*\/)[^\/]*$/, '$1'));
                viewer.renderDocument();
            } else{
                $("#main").text("Error: File not Found");
            }
        }
    };
    xhr.send();
}

function loadDataFileReader(file){
    let reader = new FileReader();
    reader.onload = function(event) {
        const rtf = new Rtf(file.name, event.target.result);

        //Render document
        const viewer = new Viewer(rtf, "");
        viewer.renderDocument();
    };
    reader.readAsText(file);
}

document.addEventListener('DOMContentLoaded', function(){
    if(location.search.startsWith("?file=")) {
        document.getElementById("main").classList.remove("hidden");
        document.getElementById("toolbar").classList.remove("hidden");
        loadDataXhr();
    } else {
        document.getElementById("file-upload").classList.remove("hidden");

        document.getElementById("upload-field").addEventListener("change", function () {
            document.getElementById("file-upload").classList.add("hidden");
            document.getElementById("main").classList.remove("hidden");
            document.getElementById("toolbar").classList.remove("hidden");
            loadDataFileReader(this.files[0])
        }, false)
    }
}, true);
<|endoftext|>


<commit_before>module.exports = function(config){
    config.set({
    basePath : '../',

    files : [
      'vendor/assets/javascripts/jquery-1.9.1.js',
      'vendor/assets/javascripts/jquery-ui-1.10.3.js',
      'vendor/assets/javascripts/angular/angular.js',
      'vendor/assets/javascripts/angular/angular-*.js',
      'vendor/assets/javascripts/underscore.js',
      'test/lib/angular/angular-mocks.js',
      'app/assets/javascripts/app.js',
      'app/assets/javascripts/directives.js',
      'app/assets/javascripts/controllers/*.js',
      'app/assets/javascripts/models/*.js',
      'test/unit/**/*.js',
      'test/lib/jasmine-jquery.js'
    ],

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['Chrome'],

    plugins : [
            'karma-junit-reporter',
            'karma-chrome-launcher',
            'karma-firefox-launcher',
            'karma-jasmine'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

    });
};
<commit_msg>Fix jquery UI reference in karma config<commit_after>module.exports = function(config){
    config.set({
    basePath : '../',

    files : [
      'vendor/assets/javascripts/jquery-1.9.1.js',
      'vendor/assets/javascripts/jquery-ui-1.9.2.custom.js',
      'vendor/assets/javascripts/angular/angular.js',
      'vendor/assets/javascripts/angular/angular-*.js',
      'vendor/assets/javascripts/underscore.js',
      'test/lib/angular/angular-mocks.js',
      'app/assets/javascripts/app.js',
      'app/assets/javascripts/directives.js',
      'app/assets/javascripts/controllers/*.js',
      'app/assets/javascripts/models/*.js',
      'test/unit/**/*.js',
      'test/lib/jasmine-jquery.js'
    ],

    autoWatch : true,

    frameworks: ['jasmine'],

    browsers : ['Chrome'],

    plugins : [
            'karma-junit-reporter',
            'karma-chrome-launcher',
            'karma-firefox-launcher',
            'karma-jasmine'
            ],

    junitReporter : {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

    });
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Portal from './Portal';
import { Stage, Layer, Rect, Text } from 'react-konva';

class App extends Component {
  render() {
    return (
      <Stage width={window.innerWidth} height={window.innerHeight}>
        <Layer>
          <Portal isOpened={true}>
            <input
              style={{ position: 'absolute', top: 0, left: 0, width: '200px' }}
              placeholder="DOM input from Konva nodes"
            />
          </Portal>
          <Rect
            x={20}
            y={20}
            width={50}
            height={50}
            fill="red"
            shadowBlur={5}
          />
        </Layer>
      </Stage>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<commit_msg>fix code<commit_after>import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Portal from './Portal';
import { Stage, Layer, Rect, Text } from 'react-konva';

class App extends Component {
  render() {
    return (
      <Stage width={window.innerWidth} height={window.innerHeight}>
        <Layer>
          <Portal>
            <input
              style={{
                position: 'absolute',
                top: 10,
                left: 10,
                width: '200px'
              }}
              placeholder="DOM input from Konva nodes"
            />
          </Portal>
          <Rect
            x={20}
            y={20}
            width={50}
            height={50}
            fill="red"
            shadowBlur={5}
          />
        </Layer>
      </Stage>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<|endoftext|>


<commit_before>import { ReducerRegistry } from '../base/redux';

import {
    REMOVE_TRANSCRIPT_MESSAGE, TOGGLE_REQUESTING_SUBTITLES,
    UPDATE_TRANSCRIPT_MESSAGE
} from './actionTypes';

/**
 * Default State for 'features/transcription' feature
 */
const defaultState = {
    _transcriptMessages: new Map(),
    _requestingSubtitles: false
};

/**
 * Listen for actions for the transcription feature to be used by the actions
 * to update the rendered transcription subtitles.
 */
ReducerRegistry.register('features/subtitles', (
        state = defaultState, action) => {
    switch (action.type) {
    case REMOVE_TRANSCRIPT_MESSAGE:
        return _removeTranscriptMessage(state, action);
    case UPDATE_TRANSCRIPT_MESSAGE:
        return _updateTranscriptMessage(state, action);

    case TOGGLE_REQUESTING_SUBTITLES:
        return {
            ...state,
            _requestingSubtitles: !state._requestingSubtitles
        };
    }

    return state;
});

/**
 * Reduces a specific Redux action REMOVE_TRANSCRIPT_MESSAGE of the feature
 * transcription.
 *
 * @param {Object} state - The Redux state of the feature transcription.
 * @param {Action} action -The Redux action REMOVE_TRANSCRIPT_MESSAGE to reduce.
 * @returns {Object} The new state of the feature transcription after the
 * reduction of the specified action.
 */
function _removeTranscriptMessage(state, { transcriptMessageID }) {
    const newTranscriptMessages = new Map(state.transcriptMessages);

    // Deletes the key from Map once a final message arrives.
    newTranscriptMessages.delete(transcriptMessageID);

    return {
        ...state,
        _transcriptMessages: newTranscriptMessages
    };
}

/**
 * Reduces a specific Redux action UPDATE_TRANSCRIPT_MESSAGE of the feature
 * transcription.
 *
 * @param {Object} state - The Redux state of the feature transcription.
 * @param {Action} action -The Redux action UPDATE_TRANSCRIPT_MESSAGE to reduce.
 * @returns {Object} The new state of the feature transcription after the
 * reduction of the specified action.
 */
function _updateTranscriptMessage(state,
        { transcriptMessageID, newTranscriptMessage }) {
    const newTranscriptMessages = new Map(state.transcriptMessages);

    // Updates the new message for the given key in the Map.
    newTranscriptMessages.set(transcriptMessageID, newTranscriptMessage);

    return {
        ...state,
        _transcriptMessages: newTranscriptMessages
    };
}
<commit_msg>fix(subtitles): fix typo that was blowing away subtitles on update<commit_after>import { ReducerRegistry } from '../base/redux';

import {
    REMOVE_TRANSCRIPT_MESSAGE, TOGGLE_REQUESTING_SUBTITLES,
    UPDATE_TRANSCRIPT_MESSAGE
} from './actionTypes';

/**
 * Default State for 'features/transcription' feature
 */
const defaultState = {
    _transcriptMessages: new Map(),
    _requestingSubtitles: false
};

/**
 * Listen for actions for the transcription feature to be used by the actions
 * to update the rendered transcription subtitles.
 */
ReducerRegistry.register('features/subtitles', (
        state = defaultState, action) => {
    switch (action.type) {
    case REMOVE_TRANSCRIPT_MESSAGE:
        return _removeTranscriptMessage(state, action);
    case UPDATE_TRANSCRIPT_MESSAGE:
        return _updateTranscriptMessage(state, action);

    case TOGGLE_REQUESTING_SUBTITLES:
        return {
            ...state,
            _requestingSubtitles: !state._requestingSubtitles
        };
    }

    return state;
});

/**
 * Reduces a specific Redux action REMOVE_TRANSCRIPT_MESSAGE of the feature
 * transcription.
 *
 * @param {Object} state - The Redux state of the feature transcription.
 * @param {Action} action -The Redux action REMOVE_TRANSCRIPT_MESSAGE to reduce.
 * @returns {Object} The new state of the feature transcription after the
 * reduction of the specified action.
 */
function _removeTranscriptMessage(state, { transcriptMessageID }) {
    const newTranscriptMessages = new Map(state._transcriptMessages);

    // Deletes the key from Map once a final message arrives.
    newTranscriptMessages.delete(transcriptMessageID);

    return {
        ...state,
        _transcriptMessages: newTranscriptMessages
    };
}

/**
 * Reduces a specific Redux action UPDATE_TRANSCRIPT_MESSAGE of the feature
 * transcription.
 *
 * @param {Object} state - The Redux state of the feature transcription.
 * @param {Action} action -The Redux action UPDATE_TRANSCRIPT_MESSAGE to reduce.
 * @returns {Object} The new state of the feature transcription after the
 * reduction of the specified action.
 */
function _updateTranscriptMessage(state,
        { transcriptMessageID, newTranscriptMessage }) {
    const newTranscriptMessages = new Map(state._transcriptMessages);

    // Updates the new message for the given key in the Map.
    newTranscriptMessages.set(transcriptMessageID, newTranscriptMessage);

    return {
        ...state,
        _transcriptMessages: newTranscriptMessages
    };
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Global
///////////////////////////////////////////////////////////////////////////////
document.addEventListener("dragstart", function(evt) {
    if (evt.target.tagName === "IMG") {
        evt.preventDefault();
    }
}, false);

ui.bind("click", document.querySelectorAll("[data-page]"), function(evt) {
    ui.page(this.getAttribute("data-page"));
});

ui.handleshortcuts = function(evt) {
    if (!game.paused) {
        switch (evt.keyCode) {
            case 49:
                {
                    if (game.selection) {
                        ui.action.upgrade("damage");
                    } else {
                        ui.action.build("Laser");
                    }
                    break;
                }
            case 50:
                {
                    if (game.selection) {
                        ui.action.upgrade("rate");
                    } else {
                        ui.action.build("Missile");
                    }
                    break;
                }
            case 51:
                {
                    if (game.selection) {
                        ui.action.upgrade("range");
                    } else {
                        ui.action.build("Tazer");
                    }
                    break;
                }
            case 52:
                {
                    if (game.selection) {
                        ui.action.move();
                    } else {
                        ui.action.build("Mortar");
                    }
                    break;
                }
            case 56:
                {
                    if (evt.shiftKey && game.selection) {
                        ui.action.sell();
                    }
                    break;
                }
            case 187:
                {
                    $("control-fast").click();
                    break;
                }
            case 27:
                {
                    if (game.selection) {
                        ui.action.deselect();
                    } else {
                        $("control-pause").click();
                    }
                    break;
                }
            case 13:
                {
                    game._wave = game.ticks - 1200;
                    break;
                }
        }
    } else {
        if (evt.keyCode === 27) {
            $("control-pause").click();
        }
    }
};

ui.handleunload = function(e) {
    return "A game is currently running, are you sure you want to close it?";
};

///////////////////////////////////////////////////////////////////////////////
// Actions
///////////////////////////////////////////////////////////////////////////////
ui.action.scores = function() {
    var list = JSON.parse(localStorage.scores || "{}");

    for (var map in list) {
        var out = "";

        list[map].forEach(function(r) {
            out += '
' +
                '' + new Date(r.date).toDateString() + ' ' +
                r.score + ' ☠' + r.kills + ' $' + r.spent +
                '
';
        });

        $("pages-scores-local-" + map.toLowerCase()).innerHTML = out;
    }
};

ui.action.build = function(type) {
    var model = new Model();
    var tdata = model.turrets[type];
    var turret = {
        x: -1000,
        y: -1000,
        levels: {
            range: 0,
            rate: 0,
            damage: 0,
            full: false
        },
        kills: 0,
        lastshot: 0,
        img: document.querySelector("#control-turrets [data-name=" + type + "] img"),
        id: game.turrets.length
    };

    for (var k in tdata) {
        turret[k] = tdata[k];
    }

    game.selection = game.cash - tdata.cost >= 0 ? {
        status: "placing",
        turret: turret,
        placeable: false
    } : false;
};

ui.action.upgrade = function(stat) {
    var model = new Model();
    var turret = game.selection.turret;
    var levels = turret.levels;
    var level = levels[stat];
    var cost = model.turrets.upgrades[level];

    if (game.selection.status === "selected" && cost && game.cash - cost >= 0) {
        levels[stat] ++;
        turret[stat] = turret.upgrades[level][stat];
        levels.full = levels.damage === 10 && levels.rate === 10 && levels.range === 10;
        turret.cost += cost;
        game.cash -= cost;
        game.spent += cost;
        ui.action.refresh();
    }
};

ui.action.move = function() {
    if (game.selection.status === "selected" && game.cash - 90 >= 0) {
        var turret = game.selection.turret;

        game.selection = {
            status: "moving",
            turret: turret,
            placeable: true
        };

        turret._x = turret.x;
        turret._y = turret.y;

        var tx = (turret._x + 2.5) / 5,
            ty = (turret._y + 2.5) / 5;
        for (var i = 5; i--;) {
            for (var ii = 5; ii--;) {
                game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = false;
            }
        }

        delete game.turrets[turret.id];
    }
};

ui.action.sell = function() {
    var turret = game.selection.turret,
        value = Math.round(turret.cost * 0.7);
    game.cash += value;
    game.spent -= value;

    var tx = (turret.x + 2.5) / 5,
        ty = (turret.y + 2.5) / 5;
    for (var i = 5; i--;) {
        for (var ii = 5; ii--;) {
            game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = false;
        }
    }

    ui.panel("turrets");
    game.selection = false;
    delete game.turrets[turret.id];
    ui.action.refresh();
};

ui.action.refresh = function() {
    ui.cash.textContent = game.cash;

    if (game.selection) {
        var model = new Model();
        var turret = game.selection.turret;
        var levels = turret.levels;
        var costs = model.turrets.upgrades;

        ["Damage", "Rate", "Range"].forEach(function(proper) {
            var id = proper.toLowerCase();
            var level = levels[id];
            var cost = costs[level] || "";
            $("control-manage-" + id).innerHTML = proper + " (" + level + ")
" + (cost && "$" + cost);
        });

        $("control-manage-sell").innerHTML = "Sell
$" + Math.round(turret.cost * 0.7);

        $("control-manage-stats").innerHTML = turret.kills + " kills
" + (((turret.kills / game.kills) || 0) * 100).toFixed(2) + "% of ∑";
    }
};

ui.action.deselect = function() {
    if (game.selection.status === "moving") {
        var turret = game.selection.turret;
        game.turrets[turret.id] = turret;

        turret.x = turret._x;
        turret.y = turret._y;

        var tx = (turret.x + 2.5) / 5,
            ty = (turret.y + 2.5) / 5;
        for (var i = 5; i--;) {
            for (var ii = 5; ii--;) {
                game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = turret;
            }
        }
    }

    game.selection = false;

    ui.panel("turrets");
};

///////////////////////////////////////////////////////////////////////////////
// Canvas
///////////////////////////////////////////////////////////////////////////////
var canvas = $("pages-canvas").getContext("2d");
/*var canvasBackground = $("background-canvas").getContext("2d");
 */
/*var stage = new Kinetic.Stage({
    container: "pages-canvas",
    width: 800,
    height: 500
});

var layer = new Kinetic.Layer();*/

var creepImage = document.getElementById("creepImageId");
var creepImg = new Image();
creepImg.src = 'images/creeps/creep.png';
var lightningImg = new Image();
lightningImg.src = 'images/effects/lightning.png';

var backgroundImage1 = document.getElementById("backgroundImageId1");
var backgroundImage2 = document.getElementById("backgroundImageId2");
var backgroundImage3 = document.getElementById("backgroundImageId3");

//to make blasts
var boom = [];

//for the map path
var floorPatternMap1 = new Image();
floorPatternMap1.src = 'images/labirint/map1-snow-floor.jpg';

var floorPatternMap2 = new Image();
floorPatternMap2.src = 'images/labirint/map2-space-floor.jpg';

var floorPatternMap3 = new Image();
floorPatternMap3.src = 'images/labirint/map3-desert-floor.jpg';

$("pages-canvas").addEventListener("mousemove", function(evt) {
    var selection = game.selection;
    var turret = selection.turret;

    if (selection && selection.status !== "selected") {
        var tx = Math.ceil((evt.pageX - this.offsetLeft) / 5);
        var ty = Math.ceil((evt.pageY - this.offsetTop) / 5);

        turret.x = (tx * 5) - 2.5;
        turret.y = (ty * 5) - 2.5;
        selection.placeable = tx >= 3 && tx <= 158 && ty >= 3 && ty <= 98;

        for (var i = 8; i--;) {
            for (var ii = 8; ii--;) {
                if (game.tiles[(tx + i - 2) + "," + (ty + ii - 2)]) {
                    selection.placeable = false;
                    return;
                }
            }
        }
    }
}, false);

$("pages-canvas").addEventListener("click", function(evt) {
    var selection = game.selection;
    var turret = selection.turret;
    var tile = game.tiles[Math.ceil((evt.pageX - this.offsetLeft) / 5) + "," + Math.ceil((evt.pageY - this.offsetTop) / 5)];

    if (selection.status === "moving") {
        if (selection.placeable && game.cash - 90 >= 0) {
            game.cash -= 90;
            game.turrets[turret.id] = turret;

            var tx = (turret.x + 2.5) / 5,
                ty = (turret.y + 2.5) / 5;
            for (var i = 5; i--;) {
                for (var ii = 5; ii--;) {
                    game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = turret;
                }
            }

            ui.panel("turrets");
            game.selection = false;
            ui.action.refresh();
        }
    } else if (selection.status === "placing") {
        if (selection.placeable) {
            game.cash -= turret.cost;
            game.spent += turret.cost;
            game.turrets.push(turret);

            var tx = (turret.x + 2.5) / 5,
                ty = (turret.y + 2.5) / 5;
            for (var i = 5; i--;) {
                for (var ii = 5; ii--;) {
                    game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = turret;
                }
            }

            game.selection = false;
            ui.action.refresh();
        }
    } else if (typeof tile === "object") {
        game.selection = {
            status: "selected",
            turret: tile
        };

        ui.action.refresh();
        ui.panel("manage");
    } else {
        ui.action.deselect();
    }
}, false);

///////////////////////////////////////////////////////////////////////////////
// Control panel
///////////////////////////////////////////////////////////////////////////////
$("control").addEventListener("click", function(evt) {
    if (evt.target.id === "control") {
        ui.action.deselect();
    }
}, false);

ui.bind("click", $("control-turrets").children, function(evt) {
    if (!game.paused) {
        ui.action.build(this.getAttribute("data-name"));
    }
});

ui.bind("click", $("control-manage").getElementsByTagName("a"), function(evt) {
    var action = evt.target.id.split("-")[2];

    if (!game.paused) {
        (ui.action[action] || ui.action.upgrade)(action);
    }
});

$("control-timer").addEventListener("click", function(evt) {
    if (!game.paused) {
        game._wave = game.ticks - 1200;
    }
}, false);

$("control-fast").addEventListener("click", function(evt) {
    if (!game.paused) {
        this.style.backgroundColor = (game.fast = !game.fast) ? "#97D164" : "#85ADE6";
        game.pause();
        game.start();
    }
}, false);

$("control-pause").addEventListener("click", function(evt) {
    this.textContent = game.paused ? (game.start(), "Pause") : (game.pause(), "Start");
}, false);

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
ui.bind("click", $("pages-start-maps").children, function(evt) {
    var model = new Model();
    var name = this.textContent;
    game.map = model.maps[name];
    game.map.name = name;

    game.map.map(function(p) {
        return {
            x: p.x,
            y: p.y
        };
    }).forEach(function(cur, i, a) {
        var next = a[i + 1] || cur,
            dx = next.x - cur.x,
            dy = next.y - cur.y;

        if (Math.abs(dx) > Math.abs(dy)) {
            cur.x += dx < 0 ? 21 : -16;
            var m = dy / dx,
                b = cur.y - m * cur.x;
            dx = dx < 0 ? -1 : 1;

            while (cur.x !== next.x) {
                cur.x += dx;

                for (var i = -3; i <= 4; i++) {
                    game.tiles[Math.round(cur.x / 5) + "," + ((Math.round(m * cur.x + b) / 5) + i)] = true;
                }
            }
        } else if (dy !== 0) {
            cur.y += dy < 0 ? 21 : -16;
            var m = dx / dy,
                b = cur.x - m * cur.y;
            dy = dy < 0 ? -1 : 1;

            while (cur.y !== next.y) {
                cur.y += dy;

                for (var i = -3; i <= 4; i++) {
                    game.tiles[((Math.round(m * cur.y + b) / 5) + i) + "," + Math.round(cur.y / 5)] = true;
                }
            }
        }
    });

    document.addEventListener("keydown", ui.handleshortcuts, false);
    window.addEventListener("beforeunload", ui.handleunload, false);

    game.start();
    ui.panel("turrets");
    ui.page("canvas");

});

ui.handletweets = function(data) {
    var maps = {
        loopy: $("pages-scores-twitter-loopy"),
        backtrack: $("pages-scores-twitter-backtrack"),
        dash: $("pages-scores-twitter-dash")
    };

    data.results.forEach(function(tweet) {
        var m = tweet.text.match(/I scored (\d+) \((\d+) kills, \$(\d+) spent\) on (Loopy|Backtrack|Dash) in #canvastd/i);

        if (m) {
            var map = maps[m[4].toLowerCase()];

            if (m[1] == m[2] * m[3] && map.children.length < 31) {
                var url = "https://twitter.com/" + tweet.from_user + "/status/" + tweet.id_str,
                    title = "@" + tweet.from_user + " on " + tweet.created_at,
                    a = '@' + tweet.from_user + ' ';

                map.innerHTML += '
' + a + m[1] + ' ☠' + m[2] + ' $' + m[3] + '
';
            }
        }
    });
};

ui.action.scores();
<commit_msg>images assigned to vars with getElementById from the DOM<commit_after>///////////////////////////////////////////////////////////////////////////////
// Global
///////////////////////////////////////////////////////////////////////////////
document.addEventListener("dragstart", function(evt) {
    if (evt.target.tagName === "IMG") {
        evt.preventDefault();
    }
}, false);

ui.bind("click", document.querySelectorAll("[data-page]"), function(evt) {
    ui.page(this.getAttribute("data-page"));
});

ui.handleshortcuts = function(evt) {
    if (!game.paused) {
        switch (evt.keyCode) {
            case 49:
                {
                    if (game.selection) {
                        ui.action.upgrade("damage");
                    } else {
                        ui.action.build("Laser");
                    }
                    break;
                }
            case 50:
                {
                    if (game.selection) {
                        ui.action.upgrade("rate");
                    } else {
                        ui.action.build("Missile");
                    }
                    break;
                }
            case 51:
                {
                    if (game.selection) {
                        ui.action.upgrade("range");
                    } else {
                        ui.action.build("Tazer");
                    }
                    break;
                }
            case 52:
                {
                    if (game.selection) {
                        ui.action.move();
                    } else {
                        ui.action.build("Mortar");
                    }
                    break;
                }
            case 56:
                {
                    if (evt.shiftKey && game.selection) {
                        ui.action.sell();
                    }
                    break;
                }
            case 187:
                {
                    $("control-fast").click();
                    break;
                }
            case 27:
                {
                    if (game.selection) {
                        ui.action.deselect();
                    } else {
                        $("control-pause").click();
                    }
                    break;
                }
            case 13:
                {
                    game._wave = game.ticks - 1200;
                    break;
                }
        }
    } else {
        if (evt.keyCode === 27) {
            $("control-pause").click();
        }
    }
};

ui.handleunload = function(e) {
    return "A game is currently running, are you sure you want to close it?";
};

///////////////////////////////////////////////////////////////////////////////
// Actions
///////////////////////////////////////////////////////////////////////////////
ui.action.scores = function() {
    var list = JSON.parse(localStorage.scores || "{}");

    for (var map in list) {
        var out = "";

        list[map].forEach(function(r) {
            out += '
' +
                '' + new Date(r.date).toDateString() + ' ' +
                r.score + ' ☠' + r.kills + ' $' + r.spent +
                '
';
        });

        $("pages-scores-local-" + map.toLowerCase()).innerHTML = out;
    }
};

ui.action.build = function(type) {
    var model = new Model();
    var tdata = model.turrets[type];
    var turret = {
        x: -1000,
        y: -1000,
        levels: {
            range: 0,
            rate: 0,
            damage: 0,
            full: false
        },
        kills: 0,
        lastshot: 0,
        img: document.querySelector("#control-turrets [data-name=" + type + "] img"),
        id: game.turrets.length
    };

    for (var k in tdata) {
        turret[k] = tdata[k];
    }

    game.selection = game.cash - tdata.cost >= 0 ? {
        status: "placing",
        turret: turret,
        placeable: false
    } : false;
};

ui.action.upgrade = function(stat) {
    var model = new Model();
    var turret = game.selection.turret;
    var levels = turret.levels;
    var level = levels[stat];
    var cost = model.turrets.upgrades[level];

    if (game.selection.status === "selected" && cost && game.cash - cost >= 0) {
        levels[stat] ++;
        turret[stat] = turret.upgrades[level][stat];
        levels.full = levels.damage === 10 && levels.rate === 10 && levels.range === 10;
        turret.cost += cost;
        game.cash -= cost;
        game.spent += cost;
        ui.action.refresh();
    }
};

ui.action.move = function() {
    if (game.selection.status === "selected" && game.cash - 90 >= 0) {
        var turret = game.selection.turret;

        game.selection = {
            status: "moving",
            turret: turret,
            placeable: true
        };

        turret._x = turret.x;
        turret._y = turret.y;

        var tx = (turret._x + 2.5) / 5,
            ty = (turret._y + 2.5) / 5;
        for (var i = 5; i--;) {
            for (var ii = 5; ii--;) {
                game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = false;
            }
        }

        delete game.turrets[turret.id];
    }
};

ui.action.sell = function() {
    var turret = game.selection.turret,
        value = Math.round(turret.cost * 0.7);
    game.cash += value;
    game.spent -= value;

    var tx = (turret.x + 2.5) / 5,
        ty = (turret.y + 2.5) / 5;
    for (var i = 5; i--;) {
        for (var ii = 5; ii--;) {
            game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = false;
        }
    }

    ui.panel("turrets");
    game.selection = false;
    delete game.turrets[turret.id];
    ui.action.refresh();
};

ui.action.refresh = function() {
    ui.cash.textContent = game.cash;

    if (game.selection) {
        var model = new Model();
        var turret = game.selection.turret;
        var levels = turret.levels;
        var costs = model.turrets.upgrades;

        ["Damage", "Rate", "Range"].forEach(function(proper) {
            var id = proper.toLowerCase();
            var level = levels[id];
            var cost = costs[level] || "";
            $("control-manage-" + id).innerHTML = proper + " (" + level + ")
" + (cost && "$" + cost);
        });

        $("control-manage-sell").innerHTML = "Sell
$" + Math.round(turret.cost * 0.7);

        $("control-manage-stats").innerHTML = turret.kills + " kills
" + (((turret.kills / game.kills) || 0) * 100).toFixed(2) + "% of ∑";
    }
};

ui.action.deselect = function() {
    if (game.selection.status === "moving") {
        var turret = game.selection.turret;
        game.turrets[turret.id] = turret;

        turret.x = turret._x;
        turret.y = turret._y;

        var tx = (turret.x + 2.5) / 5,
            ty = (turret.y + 2.5) / 5;
        for (var i = 5; i--;) {
            for (var ii = 5; ii--;) {
                game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = turret;
            }
        }
    }

    game.selection = false;

    ui.panel("turrets");
};

///////////////////////////////////////////////////////////////////////////////
// Canvas
///////////////////////////////////////////////////////////////////////////////
var canvas = $("pages-canvas").getContext("2d");
/*var canvasBackground = $("background-canvas").getContext("2d");
 */
/*var stage = new Kinetic.Stage({
    container: "pages-canvas",
    width: 800,
    height: 500
});

var layer = new Kinetic.Layer();*/

var creepImage = document.getElementById("creepImageId");
var creepImg = document.getElementById("creep1b");
var creep1b = document.getElementById("creep1b");
var creep2b = document.getElementById("creep2b");
var creep3b = document.getElementById("creep3b");
var creep1g = document.getElementById("creep1g");
var creep1g = document.getElementById("creep1g");
var creep2g = document.getElementById("creep2g");
var creep3g = document.getElementById("creep3g");
var creep1r = document.getElementById("creep1r");
var creep2r = document.getElementById("creep2r");
var creep3r = document.getElementById("creep3r");
var creep1y = document.getElementById("creep1y");
var creep2y = document.getElementById("creep2y");
var creep3y = document.getElementById("creep3y");
var lightningImg = document.getElementById("lightning");
var expl1 = document.getElementById("expl1");
var expl2 = document.getElementById("expl2");

var backgroundImage1 = document.getElementById("backgroundImageId1");
var backgroundImage2 = document.getElementById("backgroundImageId2");
var backgroundImage3 = document.getElementById("backgroundImageId3");

//to make blasts
var boom = [];

//for the map path
var floorPatternMap1 = new Image();
floorPatternMap1.src = 'images/labirint/map1-snow-floor.jpg';

var floorPatternMap2 = new Image();
floorPatternMap2.src = 'images/labirint/map2-space-floor.jpg';

var floorPatternMap3 = new Image();
floorPatternMap3.src = 'images/labirint/map3-desert-floor.jpg';

$("pages-canvas").addEventListener("mousemove", function(evt) {
    var selection = game.selection;
    var turret = selection.turret;

    if (selection && selection.status !== "selected") {
        var tx = Math.ceil((evt.pageX - this.offsetLeft) / 5);
        var ty = Math.ceil((evt.pageY - this.offsetTop) / 5);

        turret.x = (tx * 5) - 2.5;
        turret.y = (ty * 5) - 2.5;
        selection.placeable = tx >= 3 && tx <= 158 && ty >= 3 && ty <= 98;

        for (var i = 8; i--;) {
            for (var ii = 8; ii--;) {
                if (game.tiles[(tx + i - 2) + "," + (ty + ii - 2)]) {
                    selection.placeable = false;
                    return;
                }
            }
        }
    }
}, false);

$("pages-canvas").addEventListener("click", function(evt) {
    var selection = game.selection;
    var turret = selection.turret;
    var tile = game.tiles[Math.ceil((evt.pageX - this.offsetLeft) / 5) + "," + Math.ceil((evt.pageY - this.offsetTop) / 5)];

    if (selection.status === "moving") {
        if (selection.placeable && game.cash - 90 >= 0) {
            game.cash -= 90;
            game.turrets[turret.id] = turret;

            var tx = (turret.x + 2.5) / 5,
                ty = (turret.y + 2.5) / 5;
            for (var i = 5; i--;) {
                for (var ii = 5; ii--;) {
                    game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = turret;
                }
            }

            ui.panel("turrets");
            game.selection = false;
            ui.action.refresh();
        }
    } else if (selection.status === "placing") {
        if (selection.placeable) {
            game.cash -= turret.cost;
            game.spent += turret.cost;
            game.turrets.push(turret);

            var tx = (turret.x + 2.5) / 5,
                ty = (turret.y + 2.5) / 5;
            for (var i = 5; i--;) {
                for (var ii = 5; ii--;) {
                    game.tiles[(tx + i - 2) + "," + (ty + ii - 2)] = turret;
                }
            }

            game.selection = false;
            ui.action.refresh();
        }
    } else if (typeof tile === "object") {
        game.selection = {
            status: "selected",
            turret: tile
        };

        ui.action.refresh();
        ui.panel("manage");
    } else {
        ui.action.deselect();
    }
}, false);

///////////////////////////////////////////////////////////////////////////////
// Control panel
///////////////////////////////////////////////////////////////////////////////
$("control").addEventListener("click", function(evt) {
    if (evt.target.id === "control") {
        ui.action.deselect();
    }
}, false);

ui.bind("click", $("control-turrets").children, function(evt) {
    if (!game.paused) {
        ui.action.build(this.getAttribute("data-name"));
    }
});

ui.bind("click", $("control-manage").getElementsByTagName("a"), function(evt) {
    var action = evt.target.id.split("-")[2];

    if (!game.paused) {
        (ui.action[action] || ui.action.upgrade)(action);
    }
});

$("control-timer").addEventListener("click", function(evt) {
    if (!game.paused) {
        game._wave = game.ticks - 1200;
    }
}, false);

$("control-fast").addEventListener("click", function(evt) {
    if (!game.paused) {
        this.style.backgroundColor = (game.fast = !game.fast) ? "#97D164" : "#85ADE6";
        game.pause();
        game.start();
    }
}, false);

$("control-pause").addEventListener("click", function(evt) {
    this.textContent = game.paused ? (game.start(), "Pause") : (game.pause(), "Start");
}, false);

///////////////////////////////////////////////////////////////////////////////
// Init
///////////////////////////////////////////////////////////////////////////////
ui.bind("click", $("pages-start-maps").children, function(evt) {
    var model = new Model();
    var name = this.textContent;
    game.map = model.maps[name];
    game.map.name = name;

    game.map.map(function(p) {
        return {
            x: p.x,
            y: p.y
        };
    }).forEach(function(cur, i, a) {
        var next = a[i + 1] || cur,
            dx = next.x - cur.x,
            dy = next.y - cur.y;

        if (Math.abs(dx) > Math.abs(dy)) {
            cur.x += dx < 0 ? 21 : -16;
            var m = dy / dx,
                b = cur.y - m * cur.x;
            dx = dx < 0 ? -1 : 1;

            while (cur.x !== next.x) {
                cur.x += dx;

                for (var i = -3; i <= 4; i++) {
                    game.tiles[Math.round(cur.x / 5) + "," + ((Math.round(m * cur.x + b) / 5) + i)] = true;
                }
            }
        } else if (dy !== 0) {
            cur.y += dy < 0 ? 21 : -16;
            var m = dx / dy,
                b = cur.x - m * cur.y;
            dy = dy < 0 ? -1 : 1;

            while (cur.y !== next.y) {
                cur.y += dy;

                for (var i = -3; i <= 4; i++) {
                    game.tiles[((Math.round(m * cur.y + b) / 5) + i) + "," + Math.round(cur.y / 5)] = true;
                }
            }
        }
    });

    document.addEventListener("keydown", ui.handleshortcuts, false);
    window.addEventListener("beforeunload", ui.handleunload, false);

    game.start();
    ui.panel("turrets");
    ui.page("canvas");

});

ui.handletweets = function(data) {
    var maps = {
        loopy: $("pages-scores-twitter-loopy"),
        backtrack: $("pages-scores-twitter-backtrack"),
        dash: $("pages-scores-twitter-dash")
    };

    data.results.forEach(function(tweet) {
        var m = tweet.text.match(/I scored (\d+) \((\d+) kills, \$(\d+) spent\) on (Loopy|Backtrack|Dash) in #canvastd/i);

        if (m) {
            var map = maps[m[4].toLowerCase()];

            if (m[1] == m[2] * m[3] && map.children.length < 31) {
                var url = "https://twitter.com/" + tweet.from_user + "/status/" + tweet.id_str,
                    title = "@" + tweet.from_user + " on " + tweet.created_at,
                    a = '@' + tweet.from_user + ' ';

                map.innerHTML += '
' + a + m[1] + ' ☠' + m[2] + ' $' + m[3] + '
';
            }
        }
    });
};

ui.action.scores();
<|endoftext|>


<commit_before>$(document).ready(function() {
  var screenInfo = $('.expression-container p');
  var answerScreen = $('.answer-container p');
  answerScreen.text('Answers go here!');
  screenInfo.text('');
  buttonEvents(screenInfo, answerScreen);
  keyboardEvents(screenInfo, answerScreen);
});

function buttonEvents(sourceElement, targetElement) {
  $('.button-container button').on('click', function(e) {
    var elementText = sourceElement.text();
    switch (e.target.id) {
      case 'equals':
        targetElement.text(evaluateExpression(elementText));
        sourceElement.text('');
        break;
      case 'delete':
        sourceElement.text(elementText.substring(0, elementText.length - 1));
        break;
      case 'plus':
        sourceElement.text(elementText + '+');
        break;
      case 'decimal':
        sourceElement.text(elementText + '.');
        break;
      case 'leftParen':
        sourceElement.text(elementText + '(');
        break;
      case 'rightParen':
        sourceElement.text(elementText + ')');
        break;
      case 'times':
        sourceElement.text(elementText + '*');
        break;
      case 'divide':
        sourceElement.text(elementText + '/');
        break;
      case 'exp':
        sourceElement.text(elementText + '^');
        break;
      default:
        sourceElement.text(elementText + e.target.id);
        break;
    }
  });
}

/**
 * Takes valid keyboard input and puts onto screen
*/
function keyboardEvents(source, target) {
  $(document).keypress(function(key) {
    var keyPress = String.fromCharCode(key.which);
    var idString = '#';
    if (key.which === 13) {
      idString += 'equals';
    } else {
      switch(keyPress) {
        case '+':
          idString += 'plus';
          break;
        case undefined:
          idString += 'equals';
          break;
        case '.':
          idString += 'decimal';
          break;
        case '=':
          idString += 'equals';
          break;
        case '(':
          idString += 'leftParen';
          break;
        case ')':
          idString += 'rightParen';
          break;
        case '*':
          idString += 'times';
          break;
        case '/':
          idString += 'divide';
          break;
        case '^':
          idString += 'exp';
          break;
        default:
          idString += keyPress;
          break;
      }
    }
    $(idString).trigger('click');
  });
}

<commit_msg>Updated keyboard events<commit_after>$(document).ready(function() {
  var screenInfo = $('.expression-container p');
  var answerScreen = $('.answer-container p');
  answerScreen.text('Answers go here!');
  screenInfo.text('');
  buttonEvents(screenInfo, answerScreen);
  keyboardEvents(screenInfo, answerScreen);
});

function buttonEvents(sourceElement, targetElement) {
  $('.button-container button').on('click', function(e) {
    var elementText = sourceElement.text();
    switch (e.target.id) {
      case 'equals':
        targetElement.text(evaluateExpression(elementText));
        sourceElement.text('');
        break;
      case 'delete':
        sourceElement.text(elementText.substring(0, elementText.length - 1));
        break;
      case 'plus':
        sourceElement.text(elementText + '+');
        break;
      case 'decimal':
        sourceElement.text(elementText + '.');
        break;
      case 'leftParen':
        sourceElement.text(elementText + '(');
        break;
      case 'rightParen':
        sourceElement.text(elementText + ')');
        break;
      case 'times':
        sourceElement.text(elementText + '*');
        break;
      case 'divide':
        sourceElement.text(elementText + '/');
        break;
      case 'exp':
        sourceElement.text(elementText + '^');
        break;
      default:
        sourceElement.text(elementText + e.target.id);
        break;
    }
  });
}

/**
 * Takes valid keyboard input and puts onto screen
*/
function keyboardEvents(source, target) {
  $(document).keypress(function(key) {
    console.log(key.which);
    var keyPress = String.fromCharCode(key.which);
    if (key.which === 13 || key.which === 8 || key.which === 46) {
      pressEnterOrDelete(key.which);
    } else {
      pressDigitOrOperator(keyPress);
    }
  });
}

function pressEnterOrDelete(value) {
  var idString = '#';
  switch(value) {
    case 13:
      idString += 'equals';
      break;
    case 8:
      idString += 'delete';
      break;
    case 46:
      idString += 'delete';
      break;
    default:
      break;
  }
  $(idString).trigger('click');
}
function pressDigitOrOperator(keyPress) {
  var idString = '#';
  switch(keyPress) {
    case '+':
      idString += 'plus';
      break;
    case '.':
      idString += 'decimal';
      break;
    case '=':
      idString += 'equals';
      break;
    case '(':
      idString += 'leftParen';
      break;
    case ')':
      idString += 'rightParen';
      break;
    case '*':
      idString += 'times';
      break;
    case '/':
      idString += 'divide';
      break;
    case '^':
      idString += 'exp';
      break;
    default:
      idString += keyPress;
      break;
  }
  $(idString).trigger('click');
}<|endoftext|>


<commit_before>describe("About Expects", function() {

  //We shall contemplate truth by testing reality, via spec expectations.
  it("should expect true", function() {
    expect(false).toBeTruthy(); //This should be true
  });

  //To understand reality, we must compare our expectations against reality.
  it("should expect equality", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

	  expect(actualValue === expectedValue).toBeTruthy();
  });

  //Some ways of asserting equality are better than others.
  it("should assert equality a better way", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

  // toEqual() compares using common sense equality.
	  expect(actualValue).toEqual(expectedValue);
  });

  //Sometimes you need to be really exact about what you "type".
  it("should assert equality with ===", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = (1 + 1).toString();

  // toBe() will always use === to compare.
	  expect(actualValue).toBe(expectedValue);
  });

  //Sometimes we will ask you to fill in the values.
  it("should have filled in values", function () {
	  expect(1 + 1).toEqual(FILL_ME_IN);
  });
});
<commit_msg>Finished AboutExpects<commit_after>describe("About Expects", function() {

  //We shall contemplate truth by testing reality, via spec expectations.
  it("should expect true", function() {
    expect(true).toBeTruthy(); //This should be true
  });

  //To understand reality, we must compare our expectations against reality.
  it("should expect equality", function () {
	  var expectedValue = 2;
	  var actualValue = 1 + 1;

	  expect(actualValue === expectedValue).toBeTruthy();
  });

  //Some ways of asserting equality are better than others.
  it("should assert equality a better way", function () {
	  var expectedValue = 2;
	  var actualValue = 1 + 1;

  // toEqual() compares using common sense equality.
	  expect(actualValue).toEqual(expectedValue);
  });

  //Sometimes you need to be really exact about what you "type".
  it("should assert equality with ===", function () {
	  var expectedValue = "2";
	  var actualValue = (1 + 1).toString();

  // toBe() will always use === to compare.
	  expect(actualValue).toBe(expectedValue);
  });

  //Sometimes we will ask you to fill in the values.
  it("should have filled in values", function () {
	  expect(1 + 1).toEqual(2);
  });
});
<|endoftext|>


<commit_before>describe("About Expects", function() {

  //We shall contemplate truth by testing reality, via spec expectations.
  it("should expect true", function() {
    expect(false).toBeTruthy(); //This should be true
  });

  //To understand reality, we must compare our expectations against reality.
  it("should expect equality", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

	  expect(actualValue === expectedValue).toBeTruthy();
  });

  //Some ways of asserting equality are better than others.
  it("should assert equality a better way", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

  // toEqual() compares using common sense equality.
	  expect(actualValue).toEqual(expectedValue);
  });

  //Sometimes you need to be really exact about what you "type".
  it("should assert equality with ===", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = (1 + 1).toString();

  // toBe() will always use === to compare.
	  expect(actualValue).toBe(expectedValue);
  });

  //Sometimes we will ask you to fill in the values.
  it("should have filled in values", function () {
	  expect(1 + 1).toEqual(FILL_ME_IN);
  });
});
<commit_msg>first koan, first commit<commit_after>describe("About Expects", function() {

  //We shall contemplate truth by testing reality, via spec expectations.
  it("should expect true", function() {
    expect(true).toBeTruthy(); //This should be true
  });

  //To understand reality, we must compare our expectations against reality.
  it("should expect equality", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

	  expect(actualValue === expectedValue).toBeTruthy();
  });

  //Some ways of asserting equality are better than others.
  it("should assert equality a better way", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

  // toEqual() compares using common sense equality.
	  expect(actualValue).toEqual(expectedValue);
  });

  //Sometimes you need to be really exact about what you "type".
  it("should assert equality with ===", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = (1 + 1).toString();

  // toBe() will always use === to compare.
	  expect(actualValue).toBe(expectedValue);
  });

  //Sometimes we will ask you to fill in the values.
  it("should have filled in values", function () {
	  expect(1 + 1).toEqual(FILL_ME_IN);
  });
});
<|endoftext|>


<commit_before>describe("About Expects", function() {

  //We shall contemplate truth by testing reality, via spec expectations.
  it("should expect true", function() {
    expect(true).toBeTruthy(); //This should be true
  });

  //To understand reality, we must compare our expectations against reality.
  it("should expect equality", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

	  expect(actualValue === expectedValue).toBeTruthy();
  });

  //Some ways of asserting equality are better than others.
  it("should assert equality a better way", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = 1 + 1;

  // toEqual() compares using common sense equality.
	  expect(actualValue).toEqual(expectedValue);
  });

  //Sometimes you need to be really exact about what you "type".
  it("should assert equality with ===", function () {
	  var expectedValue = FILL_ME_IN;
	  var actualValue = (1 + 1).toString();

  // toBe() will always use === to compare.
	  expect(actualValue).toBe(expectedValue);
  });

  //Sometimes we will ask you to fill in the values.
  it("should have filled in values", function () {
	  expect(1 + 1).toEqual(FILL_ME_IN);
  });
});
<commit_msg>Fixed all of AboutExpects by filling in all of the values. Fill_Me_In was changed to either the value 2 or the string "2".<commit_after>describe("About Expects", function() {

  //We shall contemplate truth by testing reality, via spec expectations.
  it("should expect true", function() {
    expect(true).toBeTruthy(); //This should be true
  });

  //To understand reality, we must compare our expectations against reality.
  it("should expect equality", function () {
	  var expectedValue = 2;
	  var actualValue = 1 + 1;

	  expect(actualValue === expectedValue).toBeTruthy();
  });

  //Some ways of asserting equality are better than others.
  it("should assert equality a better way", function () {
	  var expectedValue = 2;
	  var actualValue = 1 + 1;

  // toEqual() compares using common sense equality.
	  expect(actualValue).toEqual(expectedValue);
  });

  //Sometimes you need to be really exact about what you "type".
  it("should assert equality with ===", function () {
	  var expectedValue = "2";
	  var actualValue = (1 + 1).toString();

  // toBe() will always use === to compare.
	  expect(actualValue).toBe(expectedValue);
  });

  //Sometimes we will ask you to fill in the values.
  it("should have filled in values", function () {
	  expect(1 + 1).toEqual(2);
  });
});
<|endoftext|>