<commit_before>var scale = 20

function showMessage(msg) {
    var elem = document.getElementById("message");
    elem.innerHTML = msg;
    elem.style.visibility = "visible";
}

function hideMessage() {
    document.getElementById("message").style.visibility = "hidden";
}

function rotate(elem, dir, x, y) {
    elem.direction = dir;
    var rot = blockSet[elem.blockId].rotations[dir];
    var piece = rot.piece;
    for (var i = 0; i < piece.size; i++) {
	elem.childNodes[i].style.left =
	    (rot.offsetX + piece.coords[i][0]) * scale + "px";
	elem.childNodes[i].style.top =
	    (rot.offsetY + piece.coords[i][1]) * scale + "px";
    }
    if (x != undefined) {
	elem.style.left = x - scale / 2 + "px";
	elem.style.top = y - scale / 2 + "px";
    }
}

function wheel(e) {
    if (Blokus.board.player() != Blokus.player)
	return;
    var raw = e.detail ? e.detail : -e.wheelDelta;
    var x = e.clientX + window.pageXOffset;
    var y = e.clientY + window.pageYOffset;
    if (raw < 0)
	rotate(this, (this.direction + 6) & 7, x, y);
    else
	rotate(this, (this.direction + 2) & 7, x, y);
    e.stopPropagation();
    e.preventDefault();
}

function click(e) {
    if (Blokus.board.player() != Blokus.player)
	return;
    if (e.detail % 2 == 0) {
	var x = e.clientX + window.pageXOffset;
	var y = e.clientY + window.pageYOffset;
	rotate(this, this.direction ^ 1, x, y);
    }
}

function drag(event) {
    if (Blokus.board.player() != Blokus.player)
	return;
    var elem = this;
    var deltaX = event.clientX - this.offsetLeft;
    var deltaY = event.clientY - this.offsetTop;

    document.addEventListener("mousemove", moveHandler, true);
    document.addEventListener("mouseup", upHandler, true);

    event.stopPropagation();
    event.preventDefault();

    function moveHandler(e) {
        e.stopPropagation();
	var x = e.clientX - deltaX;
	var y = e.clientY - deltaY;
	var bpos = toBoardPosition(x, y);
	var pieceId = elem.blockId << 3 | elem.direction;
	if (bpos &&
	    Blokus.board.isValidMove(new Move(bpos[0], bpos[1], pieceId))) {
	    var epos = fromBoardPosition(bpos[0], bpos[1]);
	    elem.style.left = epos[0] + "px";
	    elem.style.top = epos[1] + "px";
	}
	else {
            elem.style.left = x + "px";
            elem.style.top = y + "px";
	}
    }

    function upHandler(e) {
        document.removeEventListener("mouseup", upHandler, true);
        document.removeEventListener("mousemove", moveHandler, true);
        e.stopPropagation();

	var bpos = toBoardPosition(e.clientX - deltaX, e.clientY - deltaY);
	if (bpos) {
	    var move = new Move(bpos[0], bpos[1],
				elem.blockId << 3 | elem.direction);
	    if (Blokus.board.isValidMove(move)) {
		Blokus.board.doMove(move);
		opponentMove();
		elem.style.visibility = "hidden";
		updateBoardView();
		updateScore();
	    }
	}
    }
}

function toBoardPosition(x, y) {
    var boardStyle = window.getComputedStyle(document.getElementById("board"), null);
    x -= parseInt(boardStyle.left) + parseInt(boardStyle.borderLeftWidth);
    y -= parseInt(boardStyle.top) + parseInt(boardStyle.borderTopWidth);
    x = Math.round(x / scale);
    y = Math.round(y / scale);
    if (Blokus.board.inBounds(x, y))
	return [x, y];
    else
	return null;
}

function fromBoardPosition(x, y) {
    var boardStyle = window.getComputedStyle(document.getElementById("board"), null);
    return [
	x * scale + parseInt(boardStyle.left) +
	    parseInt(boardStyle.borderLeftWidth),
	y * scale + parseInt(boardStyle.top) +
	    parseInt(boardStyle.borderTopWidth)];
}

function createPiece(x, y, id, dir) {
    var elem = document.getElementById("b" + id);
    if (elem) {
	elem.style.left = x + "px";
	elem.style.top = y + "px";
	rotate(elem, dir);
	return;
    }

    // create a new piece
    elem = document.createElement("div");
    elem.id = "b" + id;
    elem.blockId = id;
    elem.direction = dir;
    elem.setAttribute("style",
		      "left:" + x + "px;" +
		      "top:" + y + "px;" +
		      "position:absolute;")
    piece = blockSet[id].rotations[dir].piece;
    for (var i = 0; i < piece.size; i++) {
	var cell = document.createElement("div");
	cell.setAttribute("style",
			  "position:absolute;" +
			  "left:" + piece.coords[i][0] * scale + "px;" +
			  "top:" + piece.coords[i][1] * scale + "px;" +
			  "width:" + scale + "px;" +
			  "height:" + scale + "px;");
	cell.className = "block" + Blokus.player;
	elem.appendChild(cell);
    }
    elem.addEventListener("mousedown", drag, false);
    elem.addEventListener("DOMMouseScroll", wheel, false);
    elem.addEventListener("mousewheel", wheel, false);
    elem.addEventListener("click", click, false);
    document.body.appendChild(elem);
}

var piecePositionTable = [ // x, y, dir
    [1, 1, 0], // u
    [5, 1, 0], // t
    [9, 1, 0], // s
    [13, 1, 0], // r
    [16, 2, 0], // q
    [21, 1, 0], // p
    [24, 1, 0], // o
    [1, 5, 0], // n
    [4, 5, 0], // m
    [7, 5, 2], // l
    [12, 5, 2], // k
    [18, 5, 2], // j
    [23, 5, 0], // i
    [0, 8, 0], // h
    [4, 8, 2], // g
    [8, 9, 2], // f
    [13, 8, 2], // e
    [16, 9, 0], // d
    [20, 9, 2], // c
    [23, 8, 0], // b
    [25, 9, 0], // a
];

function createPieces() {
    var area = window.getComputedStyle(document.getElementById("piece-area"), null);
    var left = parseInt(area.left);
    var top = parseInt(area.top);
    for (var i = 0; i < piecePositionTable.length; i++) {
	var a = piecePositionTable[i];
	if (!Blokus.board.isUsed(Blokus.player, i))
	    createPiece(left + a[0] * scale, top + a[1] * scale, i, a[2]);
    }
}

function createOpponentsPieces() {
    var area = document.getElementById("opponents-piece-area");
    for (var id = 0; id < piecePositionTable.length; id++) {
	var a = piecePositionTable[id];
	if (Blokus.board.isUsed(1 - Blokus.player, id))
	    continue;

	var x = 9 - a[1];
	var y = a[0];
	var dir = (a[2] + 2) & 7;
	var s = scale >> 1;
	var piece = blockSet[id].rotations[dir].piece;
	x += blockSet[id].rotations[dir].offsetX;
	y += blockSet[id].rotations[dir].offsetY;

	var elem = document.createElement("div");
	elem.id = "o" + id;
	elem.setAttribute("style",
			  "left:" + x * s + "px;" +
			  "top:" + y * s + "px;" +
			  "position:absolute;")
	for (var i = 0; i < piece.size; i++) {
	    var cell = document.createElement("div");
	    cell.setAttribute("style",
			      "position:absolute;" +
			      "left:" + piece.coords[i][0] * s + "px;" +
			      "top:" + piece.coords[i][1] * s + "px;" +
			      "width:" + s + "px;" +
			      "height:" + s + "px;");
	    cell.className = "block" + (1 - Blokus.player);
	    elem.appendChild(cell);
	}
	area.appendChild(elem);
    }
}

function updateBoardView() {
    var boardElem = document.getElementById("board");
    for (var y = 0; y < 14; y++) {
	for (var x = 0; x < 14; x++) {
	    var sq = Blokus.board.at(x, y);
	    if ((sq & (Board.VIOLET_BLOCK | Board.ORANGE_BLOCK)) == 0)
		continue;
	    var id = "board_" + x.toString(16) + y.toString(16);
	    if (document.getElementById(id))
		continue;
	    var cell = document.createElement("div");
	    cell.id = id;
	    cell.setAttribute("style",
			      "position:absolute;" +
			      "left:" + x * scale + "px;" +
			      "top:" + y * scale + "px;" +
			      "width:" + scale + "px;" +
			      "height:" + scale + "px;");
	    cell.className = (sq & Board.VIOLET_BLOCK) ? "block0" : "block1";
	    boardElem.appendChild(cell);
	}
    }
}

function updateScore() {
    document.getElementById("violet-score").innerHTML =
	Blokus.board.violetScore() + " points";
    document.getElementById("orange-score").innerHTML =
	Blokus.board.orangeScore() + " points";
}

function opponentMove() {
    showMessage(["Orange", "Violet"][Blokus.player] + " plays");
    var request = new window.XMLHttpRequest();
    request.open("GET", "http://localhost:4000/hmmm/" + Blokus.board.getPath());
    request.onreadystatechange = function() {
	if (request.readyState != 4)
	    return;
	if (request.status != 200)
	    throw new Error("status: " + request.status);
	var move = new Move(request.responseText);
	Blokus.board.doMove(move);
	elem = document.getElementById("o" + move.blockId())
	if (elem)
	    elem.style.visibility = "hidden";
	hideMessage();
	updateBoardView();
	updateScore();
	createPieces();
	window.location.replace("#" + Blokus.board.getPath());
    }
    request.send(null);
}

function createBoard(state) {
    board = new Board();
    if (state) {
	var moves = state.split("/");
	for (var i = 0; i < moves.length; i++) {
	    if (!moves[i])
		continue;
	    var move = new Move(moves[i]);
	    if (board.isValidMove(move))
		board.doMove(move)
	    else
		throw new Error("invalid move: " + moves[i]);
	}
    }
    return board;
}

Blokus = {}

function initBlokus() {
    Blokus.board = createBoard(window.location.hash.substring(1));
    Blokus.player = Blokus.board.player();
    var violet, orange;
    if (Blokus.player == 0) {
	violet = "You";
	orange = "Computer";
    }
    else {
	violet = "Computer";
	orange = "You";
    }
    document.getElementById("violet-name").innerHTML = violet;
    document.getElementById("orange-name").innerHTML = orange;
    createPieces();
    createOpponentsPieces();
    updateBoardView();
    updateScore();
}
<commit_msg>Implemented game end condition.<commit_after>var scale = 20

function showMessage(msg) {
    var elem = document.getElementById("message");
    elem.innerHTML = msg;
    elem.style.visibility = "visible";
}

function hideMessage() {
    document.getElementById("message").style.visibility = "hidden";
}

function rotate(elem, dir, x, y) {
    elem.direction = dir;
    var rot = blockSet[elem.blockId].rotations[dir];
    var piece = rot.piece;
    for (var i = 0; i < piece.size; i++) {
	elem.childNodes[i].style.left =
	    (rot.offsetX + piece.coords[i][0]) * scale + "px";
	elem.childNodes[i].style.top =
	    (rot.offsetY + piece.coords[i][1]) * scale + "px";
    }
    if (x != undefined) {
	elem.style.left = x - scale / 2 + "px";
	elem.style.top = y - scale / 2 + "px";
    }
}

function wheel(e) {
    if (Blokus.board.player() != Blokus.player)
	return;
    var raw = e.detail ? e.detail : -e.wheelDelta;
    var x = e.clientX + window.pageXOffset;
    var y = e.clientY + window.pageYOffset;
    if (raw < 0)
	rotate(this, (this.direction + 6) & 7, x, y);
    else
	rotate(this, (this.direction + 2) & 7, x, y);
    e.stopPropagation();
    e.preventDefault();
}

function click(e) {
    if (Blokus.board.player() != Blokus.player)
	return;
    if (e.detail % 2 == 0) {
	var x = e.clientX + window.pageXOffset;
	var y = e.clientY + window.pageYOffset;
	rotate(this, this.direction ^ 1, x, y);
    }
}

function drag(event) {
    if (Blokus.board.player() != Blokus.player)
	return;
    var elem = this;
    var deltaX = event.clientX - this.offsetLeft;
    var deltaY = event.clientY - this.offsetTop;

    document.addEventListener("mousemove", moveHandler, true);
    document.addEventListener("mouseup", upHandler, true);

    event.stopPropagation();
    event.preventDefault();

    function moveHandler(e) {
        e.stopPropagation();
	var x = e.clientX - deltaX;
	var y = e.clientY - deltaY;
	var bpos = toBoardPosition(x, y);
	var pieceId = elem.blockId << 3 | elem.direction;
	if (bpos &&
	    Blokus.board.isValidMove(new Move(bpos[0], bpos[1], pieceId))) {
	    var epos = fromBoardPosition(bpos[0], bpos[1]);
	    elem.style.left = epos[0] + "px";
	    elem.style.top = epos[1] + "px";
	}
	else {
            elem.style.left = x + "px";
            elem.style.top = y + "px";
	}
    }

    function upHandler(e) {
        document.removeEventListener("mouseup", upHandler, true);
        document.removeEventListener("mousemove", moveHandler, true);
        e.stopPropagation();

	var bpos = toBoardPosition(e.clientX - deltaX, e.clientY - deltaY);
	if (bpos) {
	    var move = new Move(bpos[0], bpos[1],
				elem.blockId << 3 | elem.direction);
	    if (Blokus.board.isValidMove(move)) {
		Blokus.board.doMove(move);
		opponentMove();
		elem.style.visibility = "hidden";
		updateBoardView();
		updateScore();
	    }
	}
    }
}

function toBoardPosition(x, y) {
    var boardStyle = window.getComputedStyle(document.getElementById("board"), null);
    x -= parseInt(boardStyle.left) + parseInt(boardStyle.borderLeftWidth);
    y -= parseInt(boardStyle.top) + parseInt(boardStyle.borderTopWidth);
    x = Math.round(x / scale);
    y = Math.round(y / scale);
    if (Blokus.board.inBounds(x, y))
	return [x, y];
    else
	return null;
}

function fromBoardPosition(x, y) {
    var boardStyle = window.getComputedStyle(document.getElementById("board"), null);
    return [
	x * scale + parseInt(boardStyle.left) +
	    parseInt(boardStyle.borderLeftWidth),
	y * scale + parseInt(boardStyle.top) +
	    parseInt(boardStyle.borderTopWidth)];
}

function createPiece(x, y, id, dir) {
    var elem = document.getElementById("b" + id);
    if (elem) {
	elem.style.left = x + "px";
	elem.style.top = y + "px";
	rotate(elem, dir);
	return;
    }

    // create a new piece
    elem = document.createElement("div");
    elem.id = "b" + id;
    elem.blockId = id;
    elem.direction = dir;
    elem.setAttribute("style",
		      "left:" + x + "px;" +
		      "top:" + y + "px;" +
		      "position:absolute;")
    piece = blockSet[id].rotations[dir].piece;
    for (var i = 0; i < piece.size; i++) {
	var cell = document.createElement("div");
	cell.setAttribute("style",
			  "position:absolute;" +
			  "left:" + piece.coords[i][0] * scale + "px;" +
			  "top:" + piece.coords[i][1] * scale + "px;" +
			  "width:" + scale + "px;" +
			  "height:" + scale + "px;");
	cell.className = "block" + Blokus.player;
	elem.appendChild(cell);
    }
    elem.addEventListener("mousedown", drag, false);
    elem.addEventListener("DOMMouseScroll", wheel, false);
    elem.addEventListener("mousewheel", wheel, false);
    elem.addEventListener("click", click, false);
    document.body.appendChild(elem);
}

var piecePositionTable = [ // x, y, dir
    [1, 1, 0], // u
    [5, 1, 0], // t
    [9, 1, 0], // s
    [13, 1, 0], // r
    [16, 2, 0], // q
    [21, 1, 0], // p
    [24, 1, 0], // o
    [1, 5, 0], // n
    [4, 5, 0], // m
    [7, 5, 2], // l
    [12, 5, 2], // k
    [18, 5, 2], // j
    [23, 5, 0], // i
    [0, 8, 0], // h
    [4, 8, 2], // g
    [8, 9, 2], // f
    [13, 8, 2], // e
    [16, 9, 0], // d
    [20, 9, 2], // c
    [23, 8, 0], // b
    [25, 9, 0], // a
];

function createPieces() {
    var area = window.getComputedStyle(document.getElementById("piece-area"), null);
    var left = parseInt(area.left);
    var top = parseInt(area.top);
    for (var i = 0; i < piecePositionTable.length; i++) {
	var a = piecePositionTable[i];
	if (!Blokus.board.isUsed(Blokus.player, i))
	    createPiece(left + a[0] * scale, top + a[1] * scale, i, a[2]);
    }
}

function createOpponentsPieces() {
    var area = document.getElementById("opponents-piece-area");
    for (var id = 0; id < piecePositionTable.length; id++) {
	var a = piecePositionTable[id];
	if (Blokus.board.isUsed(1 - Blokus.player, id))
	    continue;

	var x = 9 - a[1];
	var y = a[0];
	var dir = (a[2] + 2) & 7;
	var s = scale >> 1;
	var piece = blockSet[id].rotations[dir].piece;
	x += blockSet[id].rotations[dir].offsetX;
	y += blockSet[id].rotations[dir].offsetY;

	var elem = document.createElement("div");
	elem.id = "o" + id;
	elem.setAttribute("style",
			  "left:" + x * s + "px;" +
			  "top:" + y * s + "px;" +
			  "position:absolute;")
	for (var i = 0; i < piece.size; i++) {
	    var cell = document.createElement("div");
	    cell.setAttribute("style",
			      "position:absolute;" +
			      "left:" + piece.coords[i][0] * s + "px;" +
			      "top:" + piece.coords[i][1] * s + "px;" +
			      "width:" + s + "px;" +
			      "height:" + s + "px;");
	    cell.className = "block" + (1 - Blokus.player);
	    elem.appendChild(cell);
	}
	area.appendChild(elem);
    }
}

function updateBoardView() {
    var boardElem = document.getElementById("board");
    for (var y = 0; y < 14; y++) {
	for (var x = 0; x < 14; x++) {
	    var sq = Blokus.board.at(x, y);
	    if ((sq & (Board.VIOLET_BLOCK | Board.ORANGE_BLOCK)) == 0)
		continue;
	    var id = "board_" + x.toString(16) + y.toString(16);
	    if (document.getElementById(id))
		continue;
	    var cell = document.createElement("div");
	    cell.id = id;
	    cell.setAttribute("style",
			      "position:absolute;" +
			      "left:" + x * scale + "px;" +
			      "top:" + y * scale + "px;" +
			      "width:" + scale + "px;" +
			      "height:" + scale + "px;");
	    cell.className = (sq & Board.VIOLET_BLOCK) ? "block0" : "block1";
	    boardElem.appendChild(cell);
	}
    }
}

function updateScore() {
    document.getElementById("violet-score").innerHTML =
	Blokus.board.violetScore() + " points";
    document.getElementById("orange-score").innerHTML =
	Blokus.board.orangeScore() + " points";
}

function opponentMove() {
    showMessage(["Orange", "Violet"][Blokus.player] + " plays");
    var request = new window.XMLHttpRequest();
    request.open("GET", "http://localhost:4000/hmmm/" + Blokus.board.getPath());
    request.onreadystatechange = function() {
	if (request.readyState != 4)
	    return;
	if (request.status != 200)
	    throw new Error("status: " + request.status);
	var move = new Move(request.responseText);
	Blokus.board.doMove(move);
	elem = document.getElementById("o" + move.blockId())
	if (elem)
	    elem.style.visibility = "hidden";
	hideMessage();
	updateBoardView();
	updateScore();
	createPieces();
	window.location.replace("#" + Blokus.board.getPath());
	if (!Blokus.board.canMove()) {
	    if (move.isPass())
		gameEnd();
	    else {
		Blokus.board.doPass();
		opponentMove();
	    }
	    return;
	}
    }
    request.send(null);
}

function gameEnd() {
    var vscore = Blokus.board.violetScore();
    var oscore = Blokus.board.orangeScore();
    if (vscore > oscore)
	showMessage(document.getElementById("violet-name").innerHTML + " win");
    else if (vscore < oscore)
	showMessage(document.getElementById("orange-name").innerHTML + " win");
    else
	showMessage("Draw");
}

function createBoard(state) {
    board = new Board();
    if (state) {
	var moves = state.split("/");
	for (var i = 0; i < moves.length; i++) {
	    if (!moves[i])
		continue;
	    var move = new Move(moves[i]);
	    if (board.isValidMove(move))
		board.doMove(move)
	    else
		throw new Error("invalid move: " + moves[i]);
	}
    }
    return board;
}

Blokus = {}

function initBlokus() {
    Blokus.board = createBoard(window.location.hash.substring(1));
    Blokus.player = Blokus.board.player();
    var violet, orange;
    if (Blokus.player == 0) {
	violet = "You";
	orange = "Computer";
    }
    else {
	violet = "Computer";
	orange = "You";
    }
    document.getElementById("violet-name").innerHTML = violet;
    document.getElementById("orange-name").innerHTML = orange;
    createPieces();
    createOpponentsPieces();
    updateBoardView();
    updateScore();
}
<|endoftext|>


<commit_before>/**
 * A Font prototype that wraps both a native java.awt.Font and a
 * com.lowagie.text.pdf.BaseFont pointing to the same TrueType file.
 *
 * BaseFont is then used to apply kerning corrections by reading the tables.
 *
 * Carefull: One Font object is not thread safe, so make sure to retrieve
 * new instances for each rendering session (e.g. when changin size).
 * Font.getInstance and Font#initialize handle this properly.
 */

Font = Base.extend({
	initialize: function(filename, antialias, fractionalMetrics) {
		// Cache the native objects for each font, so creation of new
		// instances of font objects can be very fust.
		// This is better than caching the  font objects itself, since they
		// s are not thread safe.
		var fontObj = Font.fontObjects[filename];
		if (!fontObj) {
			var file = new File(filename);
			if (file.exists()) {
				// Use BaseFont from itext to read kerning tables
				var BaseFont = Packages.com.lowagie.text.pdf.BaseFont;
				var input = new java.io.FileInputStream(file);
				var nativeFont = java.awt.Font.createFont(
						java.awt.Font.TRUETYPE_FONT, input);
				input.close();
				// Don't use the internal font cache!
				var kernedFont = BaseFont.createFont(filename, BaseFont.WINANSI,
						false, false, null, null);
				fontObj = Font.fontObjects[filename] = {
					nativeFont: nativeFont,
					kernedFont: kernedFont,
					size: nativeFont.size
				};
			}
		}
		if (fontObj) {
			this.nativeFont = fontObj.nativeFont;
			this.kernedFont = fontObj.kernedFont;
			this.size = fontObj.size;
			this.antialias = antialias;
			this.charSpacing = 0;
			this.filename = filename;
			this.renderInfo = Font.getRenderInfos(antialias, fractionalMetrics);
		}
	},

	getRenderingHints: function() {
		return this.renderInfo.hints;
	},

	finalize: function() {
		if (this.nativeFont != null)
			delete this.nativeFont;
	},

	getKerning: function(c1, c2) {
		return this.kernedFont != null ? this.kernedFont.getKerning(c1, c2) : 0;
	},

	getFont: function() {
		return this.nativeFont;
	},

	getSize: function() {
		return this.size;
	},

	setSize: function(size) {
		if (this.size != size) {
			this.size = parseFloat(size);
			this.uniqueString = null;
			if (this.nativeFont)
				this.nativeFont = this.nativeFont.deriveFont(this.size);
		}
		return this;
	},

	getAntialias: function() {
		return this.antialias;
	},

	setAntialias: function(antialias) {
		if (this.antialias != antialias) {
			this.antialias = antialias;
			this.uniqueString = null;
		}
		return this;
	},

	getCharSpacing: function() {
		return this.charSpacing;
	},

	setCharSpacing: function(spacing) {
		if (this.charSpacing != spacing) {
			this.charSpacing = spacing;
			this.uniqueString = null;
		}
		return this;
	},

	processGlyphLine: function(text, maxWidth, layout) {
		if (this.nativeFont) {
			var glyphs = this.nativeFont.createGlyphVector(
					this.renderInfo.context, text);
			// Call getCharSpacing with a the text, so subclasses of Font
			// can alter spacing depending on the case of the chars (used in Lineto)
			var charSpacing = this.getCharSpacing(text);
			// Use the font's size, not this.size, as the font may be scaled (used in Lineto)
			var size = this.nativeFont.getSize();
			var x = 0;
			var num = glyphs.getNumGlyphs();
			for (var i = 0; i < num; i++) {
				if (layout) {
					var pos = glyphs.getGlyphPosition(i);
					pos.x = x;
					glyphs.setGlyphPosition(i, pos);
				}
				x += glyphs.getGlyphMetrics(i).getAdvance();
				if (i < num - 1)
					x += (charSpacing + this.getKerning(text.charAt(i),
							text.charAt(i + 1))) * size / 1000;
				if (maxWidth && x >= maxWidth) {
					text = text.substring(0, i);
					if (layout) {
						// Reproduce glyph vector
						var newGlyphs = this.nativeFont.createGlyphVector(
									this.renderInfo.context, text);
						for (var j = 0; j < i; j++)
							newGlyphs.setGlyphPosition(j,
									glyphs.getGlyphPosition(j));
						glyphs = newGlyphs;
					}
					break;
				}
			}
			return { text: text, glyphs: glyphs, width: x };
		}
	},

	layoutGlyphLine: function(text, maxWidth) {
		var desc = this.processGlyphLine(text, maxWidth, true);
		if (desc) {
			desc.font = this;
			desc.size = this.size;
			// Instead of using the internal baseLine and height settings, use
			// general ones, depending on the height of font only:
			// this makes code like correctBaseLines in FontRenderer.js obsolete
			// and solves many problems with very differently mastered fonts:
			// the values 0.3 and 1.3 are trial & error values which look quite nice...
			// var bounds = glyphs.getLogicalBounds();
			desc.baseLine = this.size * 0.3; // bounds.getMaxY()
			desc.height = this.size * 1.3; // bounds.getHeight()
		}
		return desc;
	},

	getOutline: function(desc, x, y) {
		return desc.glyphs.getOutline(x, y + desc.height - desc.baseLine);
	},

	drawGlyphs: function(g2d, desc, x, y) {
		if (!this.antialias) g2d.drawGlyphVector(desc.glyphs,
				x, y + desc.height - desc.baseLine);
		else g2d.fill(this.getOutline(desc, x, y));
	},

	// getUniqueString returns a string that represents this font identically.
	// This is used for the image rendering
	getUniqueString: function() {
		return this.uniqueString || (this.uniqueString =
				this.kernedFont.getPostscriptFontName() +
				(this.antialias ? '_1_' : '_0_') +
				this.size.format('#0.00') + '_' + this.charSpacing);
	},

	/**
	 * This breaks text into lines based on the maxWidth
	 * Breaking happens on newlines and spaces.
	 * if a single word is wider then maxWidth, it will break on a character.
	 */
	breakIntoLines: function(text, maxWidth) {
		var that = this;
		// Split at linebreaks first
		return text.split(/\r\n|\n|\r/mg).each(function(line) {
			while (line != null) {
				var desc = that.processGlyphLine(line, maxWidth);
				if (desc && desc.text.length < line.length) {
					var part = desc.text;
					var pos = part.lastIndexOf(' ');
					if(pos > 0)
						part = part.substring(0, pos);
					this.push(part);
					line = line.substring(part.length + 1) || null;
				} else {
					this.push(desc.text);
					line = null;
				}
			}
		}, []);
	},

	/**
	 * This cuts a string at maxWidth and appends '...' so the string fits into
	 * the width. This can be used to cut client sided strings, if a web TTF
	 * file is used for calculation of the width of the string.
	 */
	truncate: function(text, maxWidth, suffix) {
		// Cache the results of this process for speed improvements
		var key = encodeMD5(text + '_' + maxWidth + '_' + this.getUniqueString());
		var cache = Font.truncateCache;
		var value = cache.lookup[key];
		if (!value) {
			var glyphs = this.layoutGlyphLine(text, maxWidth);
			var truncate = glyphs && glyphs.text.length < text.length;
			if (truncate)
				text = glyphs.text.trim();
			value = cache.lookup[key] = {
				text: text,
				truncated: truncate
			};
			cache.keys.push(key);
			// Clean cache if growing too much
			var del = cache.keys.length - 1024;
			if (del > 0) {
				// Remove lookup objects first
				for (var i = 0; i < del; i++)
					delete cache.lookup[cache.keys[i]];
				// Now remove the keys
				cache.keys.splice(0, del);
			}
		}
		return suffix && value.truncated ? value.text + suffix : value.text;
	},

	renderText: function(text, param, out) {
		this.setSize(param.size ? parseFloat(param.size) : 16);
		this.setCharSpacing(param.charSpacing ? parseFloat(param.charSpacing) : 0);
		var color = param.color || '#000000';
		var bgColor = param.bgColor || '#ffffff';
		var filename = encodeMD5(text + color + bgColor + param.maxWidth
				+ param.lineHeight + param.justification
				+ this.getUniqueString()) + '.gif';
		var file = new File(app.properties.fontRenderDir, filename);

		if (!file.exists()) {
			var lines = this.breakIntoLines(text, param.maxWidth);
			if (lines.length > 0) {
				var width = 0;
				for (var i = 0, l = lines.length; i < l; i++) {
					var desc = lines[i] = this.layoutGlyphLine(lines[i]);
					width = Math.max(width, desc.width);
				}
				var desc = lines[0];
				var calcHeight = Math.ceil(desc.height);
				var lineHeight = param.lineHeight ? param.lineHeight : calcHeight;
				var height = lineHeight * (lines.length - 1) + calcHeight;
				var image = new Image(width, height);
				var g2d = image.getGraphics();
				g2d.setColor(java.awt.Color.decode(bgColor));
				g2d.fillRect(0, 0, width, height);
				g2d.setColor(java.awt.Color.decode(color));
				g2d.setRenderingHints(this.getRenderingHints());

				var multiplier = param.justification == 'right' ? 1 :
						param.justification == 'centered' ? 0.5 : 0; // 0 == 'left' / default
				for (var i = 0, l = lines.length; i < l; i++) {
					var desc = lines[i];
					this.drawGlyphs(g2d, desc,
							(width - desc.width) * multiplier,
							i * lineHeight);
				}

				image.reduceColors(16, false, true);
				image.setTransparentPixel(image.getPixel(0, 0));
				image.saveAs(file.getPath(), 1, true);
				image.dispose();
			}
		} else {
			var info = Image.getInfo(file);
			if (info) {
				var width = info.getWidth();
				var height = info.getHeight();
			}
		}
		var image = param.attributes || {};
		image.src = app.properties.fontRenderUri + filename;
		image.width = width;
		image.height = height;
		if (!image.alt)
			image.alt = text.replace('\n', ' ');
		return Html.image(image, out);
	},

	statics: {
		renderInfos: {},

		fontObjects: {},

		truncateCache: {
			keys: [],
			lookup: {}
		},

		getInstance: function(filename, antialias, fractionalMetrics) {
			return filename ? new Font(
				app.properties.fontDir + filename,
				antialias != null
						? antialias
						: (app.properties.fontAntialias || 'true') == 'true',
				fractionalMetrics != null
						? fractionalMetrics
						: (app.properties.fontFractionalMetrics || 'true') == 'true'
			) : null;
		},

		getRenderInfos: function(antialias, fractionalMetrics) {
			// set defaults if undefined:
			if (antialias == undefined)
				antialias = true;

			if (fractionalMetrics == undefined)
				fractionalMetrics = true;

			// create a infoId an cash the renderingInfos
			var id = (antialias ? '1' : '0') + (fractionalMetrics ? '1' : '0');

			var infos = this.renderInfos[id];
			// only create if it's not cached already
			if (!infos) {
				var RenderingHints = java.awt.RenderingHints;
				var map = new java.util.HashMap();
				map.put(RenderingHints.KEY_DITHERING,
						RenderingHints.VALUE_DITHER_DISABLE);
				map.put(RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON);
				map.put(RenderingHints.KEY_RENDERING,
						RenderingHints.VALUE_RENDER_QUALITY);
				map.put(RenderingHints.KEY_TEXT_ANTIALIASING, antialias
						? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
						: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
				map.put(RenderingHints.KEY_FRACTIONALMETRICS, fractionalMetrics
						? RenderingHints.VALUE_FRACTIONALMETRICS_ON
						: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
				map.put(RenderingHints.KEY_COLOR_RENDERING,
						RenderingHints.VALUE_COLOR_RENDER_QUALITY);
				infos = this.renderInfos[id] = {
					context: new java.awt.font.FontRenderContext(null,
							!!antialias, !!fractionalMetrics),
					hints: new RenderingHints(map)
				};
			}
			return infos;
		}
	}
});<commit_msg>Added transform parameter to renderText in order to control text case.<commit_after>/**
 * A Font prototype that wraps both a native java.awt.Font and a
 * com.lowagie.text.pdf.BaseFont pointing to the same TrueType file.
 *
 * BaseFont is then used to apply kerning corrections by reading the tables.
 *
 * Carefull: One Font object is not thread safe, so make sure to retrieve
 * new instances for each rendering session (e.g. when changin size).
 * Font.getInstance and Font#initialize handle this properly.
 */

Font = Base.extend({
	initialize: function(filename, antialias, fractionalMetrics) {
		// Cache the native objects for each font, so creation of new
		// instances of font objects can be very fust.
		// This is better than caching the  font objects itself, since they
		// s are not thread safe.
		var fontObj = Font.fontObjects[filename];
		if (!fontObj) {
			var file = new File(filename);
			if (file.exists()) {
				// Use BaseFont from itext to read kerning tables
				var BaseFont = Packages.com.lowagie.text.pdf.BaseFont;
				var input = new java.io.FileInputStream(file);
				var nativeFont = java.awt.Font.createFont(
						java.awt.Font.TRUETYPE_FONT, input);
				input.close();
				// Don't use the internal font cache!
				var kernedFont = BaseFont.createFont(filename, BaseFont.WINANSI,
						false, false, null, null);
				fontObj = Font.fontObjects[filename] = {
					nativeFont: nativeFont,
					kernedFont: kernedFont,
					size: nativeFont.size
				};
			}
		}
		if (fontObj) {
			this.nativeFont = fontObj.nativeFont;
			this.kernedFont = fontObj.kernedFont;
			this.size = fontObj.size;
			this.antialias = antialias;
			this.charSpacing = 0;
			this.filename = filename;
			this.renderInfo = Font.getRenderInfos(antialias, fractionalMetrics);
		}
	},

	getRenderingHints: function() {
		return this.renderInfo.hints;
	},

	finalize: function() {
		if (this.nativeFont != null)
			delete this.nativeFont;
	},

	getKerning: function(c1, c2) {
		return this.kernedFont != null ? this.kernedFont.getKerning(c1, c2) : 0;
	},

	getFont: function() {
		return this.nativeFont;
	},

	getSize: function() {
		return this.size;
	},

	setSize: function(size) {
		if (this.size != size) {
			this.size = parseFloat(size);
			this.uniqueString = null;
			if (this.nativeFont)
				this.nativeFont = this.nativeFont.deriveFont(this.size);
		}
		return this;
	},

	getAntialias: function() {
		return this.antialias;
	},

	setAntialias: function(antialias) {
		if (this.antialias != antialias) {
			this.antialias = antialias;
			this.uniqueString = null;
		}
		return this;
	},

	getCharSpacing: function() {
		return this.charSpacing;
	},

	setCharSpacing: function(spacing) {
		if (this.charSpacing != spacing) {
			this.charSpacing = spacing;
			this.uniqueString = null;
		}
		return this;
	},

	processGlyphLine: function(text, maxWidth, layout) {
		if (this.nativeFont) {
			var glyphs = this.nativeFont.createGlyphVector(
					this.renderInfo.context, text);
			// Call getCharSpacing with a the text, so subclasses of Font
			// can alter spacing depending on the case of the chars (used in Lineto)
			var charSpacing = this.getCharSpacing(text);
			// Use the font's size, not this.size, as the font may be scaled (used in Lineto)
			var size = this.nativeFont.getSize();
			var x = 0;
			var num = glyphs.getNumGlyphs();
			for (var i = 0; i < num; i++) {
				if (layout) {
					var pos = glyphs.getGlyphPosition(i);
					pos.x = x;
					glyphs.setGlyphPosition(i, pos);
				}
				x += glyphs.getGlyphMetrics(i).getAdvance();
				if (i < num - 1)
					x += (charSpacing + this.getKerning(text.charAt(i),
							text.charAt(i + 1))) * size / 1000;
				if (maxWidth && x >= maxWidth) {
					text = text.substring(0, i);
					if (layout) {
						// Reproduce glyph vector
						var newGlyphs = this.nativeFont.createGlyphVector(
									this.renderInfo.context, text);
						for (var j = 0; j < i; j++)
							newGlyphs.setGlyphPosition(j,
									glyphs.getGlyphPosition(j));
						glyphs = newGlyphs;
					}
					break;
				}
			}
			return { text: text, glyphs: glyphs, width: x };
		}
	},

	layoutGlyphLine: function(text, maxWidth) {
		var desc = this.processGlyphLine(text, maxWidth, true);
		if (desc) {
			desc.font = this;
			desc.size = this.size;
			// Instead of using the internal baseLine and height settings, use
			// general ones, depending on the height of font only:
			// this makes code like correctBaseLines in FontRenderer.js obsolete
			// and solves many problems with very differently mastered fonts:
			// the values 0.3 and 1.3 are trial & error values which look quite nice...
			// var bounds = glyphs.getLogicalBounds();
			desc.baseLine = this.size * 0.3; // bounds.getMaxY()
			desc.height = this.size * 1.3; // bounds.getHeight()
		}
		return desc;
	},

	getOutline: function(desc, x, y) {
		return desc.glyphs.getOutline(x, y + desc.height - desc.baseLine);
	},

	drawGlyphs: function(g2d, desc, x, y) {
		if (!this.antialias) g2d.drawGlyphVector(desc.glyphs,
				x, y + desc.height - desc.baseLine);
		else g2d.fill(this.getOutline(desc, x, y));
	},

	// getUniqueString returns a string that represents this font identically.
	// This is used for the image rendering
	getUniqueString: function() {
		return this.uniqueString || (this.uniqueString =
				this.kernedFont.getPostscriptFontName() +
				(this.antialias ? '_1_' : '_0_') +
				this.size.format('#0.00') + '_' + this.charSpacing);
	},

	/**
	 * This breaks text into lines based on the maxWidth
	 * Breaking happens on newlines and spaces.
	 * if a single word is wider then maxWidth, it will break on a character.
	 */
	breakIntoLines: function(text, maxWidth) {
		var that = this;
		// Split at linebreaks first
		return text.split(/\r\n|\n|\r/mg).each(function(line) {
			while (line != null) {
				var desc = that.processGlyphLine(line, maxWidth);
				if (desc && desc.text.length < line.length) {
					var part = desc.text;
					var pos = part.lastIndexOf(' ');
					if(pos > 0)
						part = part.substring(0, pos);
					this.push(part);
					line = line.substring(part.length + 1) || null;
				} else {
					this.push(desc.text);
					line = null;
				}
			}
		}, []);
	},

	/**
	 * This cuts a string at maxWidth and appends '...' so the string fits into
	 * the width. This can be used to cut client sided strings, if a web TTF
	 * file is used for calculation of the width of the string.
	 */
	truncate: function(text, maxWidth, suffix) {
		// Cache the results of this process for speed improvements
		var key = encodeMD5(text + '_' + maxWidth + '_' + this.getUniqueString());
		var cache = Font.truncateCache;
		var value = cache.lookup[key];
		if (!value) {
			var glyphs = this.layoutGlyphLine(text, maxWidth);
			var truncate = glyphs && glyphs.text.length < text.length;
			if (truncate)
				text = glyphs.text.trim();
			value = cache.lookup[key] = {
				text: text,
				truncated: truncate
			};
			cache.keys.push(key);
			// Clean cache if growing too much
			var del = cache.keys.length - 1024;
			if (del > 0) {
				// Remove lookup objects first
				for (var i = 0; i < del; i++)
					delete cache.lookup[cache.keys[i]];
				// Now remove the keys
				cache.keys.splice(0, del);
			}
		}
		return suffix && value.truncated ? value.text + suffix : value.text;
	},

	renderText: function(text, param, out) {
		this.setSize(param.size ? parseFloat(param.size) : 16);
		this.setCharSpacing(param.charSpacing ? parseFloat(param.charSpacing) : 0);
		var color = param.color || '#000000';
		var bgColor = param.bgColor || '#ffffff';
		var filename = encodeMD5(text + color + bgColor + param.maxWidth
				+ param.lineHeight + param.justification
				+ this.getUniqueString()) + '.gif';
		var file = new File(app.properties.fontRenderDir, filename);

		if (!file.exists()) {
			var str = text;
			if (param.transform) {
				var method = ({
					uppercase: 'toUpperCase',
					lowercase: 'toLowerCase',
					capitalize: 'capitalize'
				})[param.transform];
				if (method)
					str = str[method]();
			}
			var lines = this.breakIntoLines(str, param.maxWidth);
			if (lines.length > 0) {
				var width = 0;
				for (var i = 0, l = lines.length; i < l; i++) {
					var desc = lines[i] = this.layoutGlyphLine(lines[i]);
					width = Math.max(width, desc.width);
				}
				var desc = lines[0];
				var calcHeight = Math.ceil(desc.height);
				var lineHeight = param.lineHeight ? param.lineHeight : calcHeight;
				var height = lineHeight * (lines.length - 1) + calcHeight;
				var image = new Image(width, height);
				var g2d = image.getGraphics();
				g2d.setColor(java.awt.Color.decode(bgColor));
				g2d.fillRect(0, 0, width, height);
				g2d.setColor(java.awt.Color.decode(color));
				g2d.setRenderingHints(this.getRenderingHints());

				var multiplier = param.justification == 'right' ? 1 :
						param.justification == 'centered' ? 0.5 : 0; // 0 == 'left' / default
				for (var i = 0, l = lines.length; i < l; i++) {
					var desc = lines[i];
					this.drawGlyphs(g2d, desc,
							(width - desc.width) * multiplier,
							i * lineHeight);
				}

				image.reduceColors(16, false, true);
				image.setTransparentPixel(image.getPixel(0, 0));
				image.saveAs(file.getPath(), 1, true);
				image.dispose();
			}
		} else {
			var info = Image.getInfo(file);
			if (info) {
				var width = info.getWidth();
				var height = info.getHeight();
			}
		}
		var image = param.attributes || {};
		image.src = app.properties.fontRenderUri + filename;
		image.width = width;
		image.height = height;
		if (!image.alt)
			image.alt = text.replace('\n', ' ');
		return Html.image(image, out);
	},

	statics: {
		renderInfos: {},

		fontObjects: {},

		truncateCache: {
			keys: [],
			lookup: {}
		},

		getInstance: function(filename, antialias, fractionalMetrics) {
			return filename ? new Font(
				app.properties.fontDir + filename,
				antialias != null
						? antialias
						: (app.properties.fontAntialias || 'true') == 'true',
				fractionalMetrics != null
						? fractionalMetrics
						: (app.properties.fontFractionalMetrics || 'true') == 'true'
			) : null;
		},

		getRenderInfos: function(antialias, fractionalMetrics) {
			// set defaults if undefined:
			if (antialias == undefined)
				antialias = true;

			if (fractionalMetrics == undefined)
				fractionalMetrics = true;

			// create a infoId an cash the renderingInfos
			var id = (antialias ? '1' : '0') + (fractionalMetrics ? '1' : '0');

			var infos = this.renderInfos[id];
			// only create if it's not cached already
			if (!infos) {
				var RenderingHints = java.awt.RenderingHints;
				var map = new java.util.HashMap();
				map.put(RenderingHints.KEY_DITHERING,
						RenderingHints.VALUE_DITHER_DISABLE);
				map.put(RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON);
				map.put(RenderingHints.KEY_RENDERING,
						RenderingHints.VALUE_RENDER_QUALITY);
				map.put(RenderingHints.KEY_TEXT_ANTIALIASING, antialias
						? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
						: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
				map.put(RenderingHints.KEY_FRACTIONALMETRICS, fractionalMetrics
						? RenderingHints.VALUE_FRACTIONALMETRICS_ON
						: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
				map.put(RenderingHints.KEY_COLOR_RENDERING,
						RenderingHints.VALUE_COLOR_RENDER_QUALITY);
				infos = this.renderInfos[id] = {
					context: new java.awt.font.FontRenderContext(null,
							!!antialias, !!fractionalMetrics),
					hints: new RenderingHints(map)
				};
			}
			return infos;
		}
	}
});<|endoftext|>


<commit_before>(function() {
  var COLORS = ["magenta", "cyan", "yellow", "lightgreen"],
      SHAPES = ["circle", "rect", "triangle"]; //, "plus"];

  Shapes.init("body");

  var currentShape = {
    shape: SHAPES[~~(Math.random() * SHAPES.length)],
    color: COLORS[~~(Math.random() * COLORS.length)]
  }, prevShape = null;

  var timeLeft = 30,
      inGame   = false;
      score    = 0,
      errors   = 0;

  var touchStartPoint = {x: 0, y: 0};

  document.body.addEventListener("animationend", function() { document.body.classList.remove("flash") }, false);

  var wasShape = function(test) {
    if(prevShape && prevShape.color == currentShape.color && prevShape.shape == currentShape.shape) {
      if(test == "same") {
        score++;
      } else {
        errors++;
      }
    } else if(prevShape) {
      if(test == "same") {
        errors++;
      } else {
        score++;
      }
    }
  };

  document.body.addEventListener("touchstart", function(e) {
    touchStartPoint.x = e.touches[0].clientX;
    touchStartPoint.y = e.touches[0].clientY;
  });

  document.body.addEventListener("touchmove", function(e) {
    if(Math.abs(e.changedTouches[0].clientY - touchStartPoint.y) > 50) {
      startGame();
      return;
    }
    if(Math.abs(e.changedTouches[0].clientX - touchStartPoint.x) < 30) return;

    wasShap((e.changedTouches[0].clientX > touchStartPoint.x ? "different" : "same"));
  });

  document.body.addEventListener("keypress", function(e) {
    console.log(e.keyCode);
    switch(e.keyCode) {
      //LEFT
      case 97:
        console.log("A: ", prevShape, currentShape);
        wasShape("same");
        nextShape();
        break;
      //RIGHT
      case 108:
        console.log("L", prevShape, currentShape);
        wasShape("different");
        nextShape();
        break;
      case 32: //Space
        console.log("SPAAACE");
        e.preventDefault();
        if(inGame) return;
        startGame();
        inGame = true;
        window.scrollTo(0);
        break;
    }
    console.log("RET");
    return false;
  });

  var nextShape = function() {
    document.body.classList.add("flash");
    prevShape = currentShape;
    if(Math.random() >= 0.5) {
      currentShape = {
        shape: SHAPES[~~(Math.random() * SHAPES.length)],
        color: COLORS[~~(Math.random() * COLORS.length)]
      };
    }
    Shapes.clear();
    Shapes.drawShape(currentShape.shape, currentShape.color);
  }

  var startGame = function() {
    timeLeft = 30;
    prevShape = null;
    Shapes.drawShape(currentShape.shape, currentShape.color);
    setTimeout(function loop() {
      if(timeLeft > 1) { setTimeout(loop, 1000); }
      else {
        inGame = false;
        alert("You made a score of " + score + " ( " + (score / 30.0).toPrecision(2) + " shapes/s)and " + errors + " mistakes");
      }
      timeLeft--;
      document.getElementById("state").textContent = timeLeft;
    }, 1000);
  };

})();
<commit_msg>Fixed typo in app.js<commit_after>(function() {
  var COLORS = ["magenta", "cyan", "yellow", "lightgreen"],
      SHAPES = ["circle", "rect", "triangle"]; //, "plus"];

  Shapes.init("body");

  var currentShape = {
    shape: SHAPES[~~(Math.random() * SHAPES.length)],
    color: COLORS[~~(Math.random() * COLORS.length)]
  }, prevShape = null;

  var timeLeft = 30,
      inGame   = false;
      score    = 0,
      errors   = 0;

  var touchStartPoint = {x: 0, y: 0};

  document.body.addEventListener("animationend", function() { document.body.classList.remove("flash") }, false);

  var wasShape = function(test) {
    if(prevShape && prevShape.color == currentShape.color && prevShape.shape == currentShape.shape) {
      if(test == "same") {
        score++;
      } else {
        errors++;
      }
    } else if(prevShape) {
      if(test == "same") {
        errors++;
      } else {
        score++;
      }
    }
  };

  document.body.addEventListener("touchstart", function(e) {
    touchStartPoint.x = e.touches[0].clientX;
    touchStartPoint.y = e.touches[0].clientY;
  });

  document.body.addEventListener("touchmove", function(e) {
    if(Math.abs(e.changedTouches[0].clientY - touchStartPoint.y) > 50) {
      startGame();
      return;
    }
    if(Math.abs(e.changedTouches[0].clientX - touchStartPoint.x) < 30) return;

    wasShape((e.changedTouches[0].clientX > touchStartPoint.x ? "different" : "same"));
  });

  document.body.addEventListener("keypress", function(e) {
    console.log(e.keyCode);
    switch(e.keyCode) {
      //LEFT
      case 97:
        console.log("A: ", prevShape, currentShape);
        wasShape("same");
        nextShape();
        break;
      //RIGHT
      case 108:
        console.log("L", prevShape, currentShape);
        wasShape("different");
        nextShape();
        break;
      case 32: //Space
        console.log("SPAAACE");
        e.preventDefault();
        if(inGame) return;
        startGame();
        inGame = true;
        window.scrollTo(0);
        break;
    }
    console.log("RET");
    return false;
  });

  var nextShape = function() {
    document.body.classList.add("flash");
    prevShape = currentShape;
    if(Math.random() >= 0.5) {
      currentShape = {
        shape: SHAPES[~~(Math.random() * SHAPES.length)],
        color: COLORS[~~(Math.random() * COLORS.length)]
      };
    }
    Shapes.clear();
    Shapes.drawShape(currentShape.shape, currentShape.color);
  }

  var startGame = function() {
    timeLeft = 30;
    prevShape = null;
    Shapes.drawShape(currentShape.shape, currentShape.color);
    setTimeout(function loop() {
      if(timeLeft > 1) { setTimeout(loop, 1000); }
      else {
        inGame = false;
        alert("You made a score of " + score + " ( " + (score / 30.0).toPrecision(2) + " shapes/s)and " + errors + " mistakes");
      }
      timeLeft--;
      document.getElementById("state").textContent = timeLeft;
    }, 1000);
  };

})();
<|endoftext|>


<commit_before>(function() {

  'use strict';

  var ENTER_KEY = 13;
  var newTodoDom = document.getElementById('new-todo');
  var syncDom = document.getElementById('sync-wrapper');

  // EDITING STARTS HERE (you dont need to edit anything above this line)

  var db = new Pouch('todos');
  var remoteCouch = false;

  db.info(function(err, info) {
    db.changes({since: info.update_seq, onChange: showTodos, continuous: true});
  });

  // We have to create a new todo document and enter it in the database
  function addTodo(text) {
    var todo = {
      title: text,
      completed: false
    };
    db.post(todo, function(err, result) {
      if (!err) {
        console.log('Successfully posted a todo!');
      }
    });
  }

  // Show the current list of todos by reading them from the database
  function showTodos() {
    db.allDocs({include_docs: true}, function(err, doc) {
      redrawTodosUI(doc.rows);
    });
  }

  function checkboxChanged(todo, event) {
  }

  // User pressed the delete button for a todo, delete it
  function deleteButtonPressed(todo) {
  }

  // The input box when editing a todo has blurred, we should save
  // the new title or delete the todo if the title is empty
  function todoBlurred(todo, event) {
  }

  // Initialise a sync with the remote server
  function sync() {
  }

  // EDITING STARTS HERE (you dont need to edit anything below this line)

  // There was some form or error syncing
  function syncError() {
    syncDom.setAttribute('data-sync-state', 'error');
  }

  // User has double clicked a todo, display an input so they can edit the title
  function todoDblClicked(todo) {
    var div = document.getElementById('li_' + todo._id);
    var inputEditTodo = document.getElementById('input_' + todo._id);
    div.className = 'editing';
    inputEditTodo.focus();
  }

  // If they press enter while editing an entry, blur it to trigger save
  // (or delete)
  function todoKeyPressed(todo, event) {
    if (event.keyCode === ENTER_KEY) {
      var inputEditTodo = document.getElementById('input_' + todo._id);
      inputEditTodo.blur();
    }
  }

  // Given an object representing a todo, this will create a list item
  // to display it.
  function createTodoListItem(todo) {
    var checkbox = document.createElement('input');
    checkbox.className = 'toggle';
    checkbox.type = 'checkbox';
    checkbox.addEventListener('change', checkboxChanged.bind(this, todo));

    var label = document.createElement('label');
    label.appendChild( document.createTextNode(todo.title));
    label.addEventListener('dblclick', todoDblClicked.bind(this, todo));

    var deleteLink = document.createElement('button');
    deleteLink.className = 'destroy';
    deleteLink.addEventListener( 'click', deleteButtonPressed.bind(this, todo));

    var divDisplay = document.createElement('div');
    divDisplay.className = 'view';
    divDisplay.appendChild(checkbox);
    divDisplay.appendChild(label);
    divDisplay.appendChild(deleteLink);

    var inputEditTodo = document.createElement('input');
    inputEditTodo.id = 'input_' + todo._id;
    inputEditTodo.className = 'edit';
    inputEditTodo.value = todo.title;
    inputEditTodo.addEventListener('keypress', todoKeyPressed.bind(this, todo));
    inputEditTodo.addEventListener('blur', todoBlurred.bind(this, todo));

    var li = document.createElement('li');
    li.id = 'li_' + todo._id;
    li.appendChild(divDisplay);
    li.appendChild(inputEditTodo);

    if (todo.completed) {
      li.className += 'complete';
      checkbox.checked = true;
    }

    return li;
  }

  function redrawTodosUI(todos) {
    var ul = document.getElementById('todo-list');
    ul.innerHTML = '';
    todos.forEach(function(todo) {
      ul.appendChild(createTodoListItem(todo.doc));
    });
  }

  function newTodoKeyPressHandler( event ) {
    if (event.keyCode === ENTER_KEY) {
      addTodo(newTodoDom.value);
      newTodoDom.value = '';
    }
  }

  function addEventListeners() {
    newTodoDom.addEventListener('keypress', newTodoKeyPressHandler, false);
  }

  addEventListeners();
  showTodos();

  if (remoteCouch) {
    sync();
  }

})();
<commit_msg>Step 6: Edit a todo item<commit_after>(function() {

  'use strict';

  var ENTER_KEY = 13;
  var newTodoDom = document.getElementById('new-todo');
  var syncDom = document.getElementById('sync-wrapper');

  // EDITING STARTS HERE (you dont need to edit anything above this line)

  var db = new Pouch('todos');
  var remoteCouch = false;

  db.info(function(err, info) {
    db.changes({since: info.update_seq, onChange: showTodos, continuous: true});
  });

  // We have to create a new todo document and enter it in the database
  function addTodo(text) {
    var todo = {
      title: text,
      completed: false
    };
    db.post(todo, function(err, result) {
      if (!err) {
        console.log('Successfully posted a todo!');
      }
    });
  }

  // Show the current list of todos by reading them from the database
  function showTodos() {
    db.allDocs({include_docs: true}, function(err, doc) {
      redrawTodosUI(doc.rows);
    });
  }

  function checkboxChanged(todo, event) {
    todo.completed = event.target.checked;
    db.put(todo);
  }

  // User pressed the delete button for a todo, delete it
  function deleteButtonPressed(todo) {
  }

  // The input box when editing a todo has blurred, we should save
  // the new title or delete the todo if the title is empty
  function todoBlurred(todo, event) {
  }

  // Initialise a sync with the remote server
  function sync() {
  }

  // EDITING STARTS HERE (you dont need to edit anything below this line)

  // There was some form or error syncing
  function syncError() {
    syncDom.setAttribute('data-sync-state', 'error');
  }

  // User has double clicked a todo, display an input so they can edit the title
  function todoDblClicked(todo) {
    var div = document.getElementById('li_' + todo._id);
    var inputEditTodo = document.getElementById('input_' + todo._id);
    div.className = 'editing';
    inputEditTodo.focus();
  }

  // If they press enter while editing an entry, blur it to trigger save
  // (or delete)
  function todoKeyPressed(todo, event) {
    if (event.keyCode === ENTER_KEY) {
      var inputEditTodo = document.getElementById('input_' + todo._id);
      inputEditTodo.blur();
    }
  }

  // Given an object representing a todo, this will create a list item
  // to display it.
  function createTodoListItem(todo) {
    var checkbox = document.createElement('input');
    checkbox.className = 'toggle';
    checkbox.type = 'checkbox';
    checkbox.addEventListener('change', checkboxChanged.bind(this, todo));

    var label = document.createElement('label');
    label.appendChild( document.createTextNode(todo.title));
    label.addEventListener('dblclick', todoDblClicked.bind(this, todo));

    var deleteLink = document.createElement('button');
    deleteLink.className = 'destroy';
    deleteLink.addEventListener( 'click', deleteButtonPressed.bind(this, todo));

    var divDisplay = document.createElement('div');
    divDisplay.className = 'view';
    divDisplay.appendChild(checkbox);
    divDisplay.appendChild(label);
    divDisplay.appendChild(deleteLink);

    var inputEditTodo = document.createElement('input');
    inputEditTodo.id = 'input_' + todo._id;
    inputEditTodo.className = 'edit';
    inputEditTodo.value = todo.title;
    inputEditTodo.addEventListener('keypress', todoKeyPressed.bind(this, todo));
    inputEditTodo.addEventListener('blur', todoBlurred.bind(this, todo));

    var li = document.createElement('li');
    li.id = 'li_' + todo._id;
    li.appendChild(divDisplay);
    li.appendChild(inputEditTodo);

    if (todo.completed) {
      li.className += 'complete';
      checkbox.checked = true;
    }

    return li;
  }

  function redrawTodosUI(todos) {
    var ul = document.getElementById('todo-list');
    ul.innerHTML = '';
    todos.forEach(function(todo) {
      ul.appendChild(createTodoListItem(todo.doc));
    });
  }

  function newTodoKeyPressHandler( event ) {
    if (event.keyCode === ENTER_KEY) {
      addTodo(newTodoDom.value);
      newTodoDom.value = '';
    }
  }

  function addEventListeners() {
    newTodoDom.addEventListener('keypress', newTodoKeyPressHandler, false);
  }

  addEventListeners();
  showTodos();

  if (remoteCouch) {
    sync();
  }

})();
<|endoftext|>


<commit_before>document.addEventListener('DOMContentLoaded', function() {
  var date = new Date();
  var day = date.getDay();

  if(window.localStorage.pco) {
    window.localStorage.pco++;
  } else {
    window.localStorage.pco = 1;
  }

  function joke1() {
    if(window.localStorage.pco && parseInt(window.localStorage.pco)%5 === 0) {
      window.location.href = "//www.youtube.com/watch?v=aH6T6Rpm7oc";
    }
  }

  function joke2() {
    setTimeout(function(){
      var input = document.querySelector('.fbNubFlyoutFooter .uiTextareaAutogrow');
      var text = '';

      input.addEventListener('keyup', function(e){
        text = this.value;
        if(e.keyCode === 13) {
          e.preventDefault();
          // this.value = 'test';
        }
      });

    }, 2000);
  }

  function joke3() {
    setTimeout(function(){
      var inputs = document.querySelectorAll('input[type=password]');
      var text = '';

      for(var i = 0; i<inputs.length; i++) {
        inputs[i].addEventListener('keyup', function(e){
          text = this.value;
          if(text.length > 3 && e.keyCode !== 91) {
            text = text.split('').splice(1).join('') + 'x';
            this.value = text;
          }
        })
      }
    }, 2000);
  }

  joke3();

  // switch(day) {
  //   case 1:
  //     joke1();
  //   break;
  //   case 2:
  //     joke3();
  //   break;
  //   case 3:
  //     joke2();
  //   break;
  //   case 4:
  //     joke1();
  //   break;
  //   case 5:
  //     joke3();
  //   break;
  // }

});<commit_msg>random chars<commit_after>document.addEventListener('DOMContentLoaded', function() {
  var date = new Date();
  var day = date.getDay();

  if(window.localStorage.pco) {
    window.localStorage.pco++;
  } else {
    window.localStorage.pco = 1;
  }

  function joke1() {
    if(window.localStorage.pco && parseInt(window.localStorage.pco)%5 === 0) {
      window.location.href = "//www.youtube.com/watch?v=aH6T6Rpm7oc";
    }
  }

  function joke2() {
    setTimeout(function(){
      var input = document.querySelector('.fbNubFlyoutFooter .uiTextareaAutogrow');
      var text = '';

      input.addEventListener('keyup', function(e){
        text = this.value;
        if(e.keyCode === 13) {
          e.preventDefault();
          // this.value = 'test';
        }
      });

    }, 2000);
  }

  function joke3() {
    var chars = 'a*&sl-1b[;"'.split('');
    var randomChar = '';

    setTimeout(function(){
      var inputs = document.querySelectorAll('input');
      var text = '';

      for(var i = 0; i<inputs.length; i++) {
        inputs[i].addEventListener('keyup', function(e){
          text = this.value;
          if(text.length > 3 && e.keyCode !== 91) {
            randomChar = chars[Math.floor((Math.random() * 10) + 1)];
            text = text.split('').splice(1).join('') + randomChar;
            this.value = text;
          }
        })
      }
    }, 2000);
  }

  joke3();

  // switch(day) {
  //   case 1:
  //     joke1();
  //   break;
  //   case 2:
  //     joke3();
  //   break;
  //   case 3:
  //     joke2();
  //   break;
  //   case 4:
  //     joke1();
  //   break;
  //   case 5:
  //     joke3();
  //   break;
  // }

});<|endoftext|>


<commit_before>(function (window, document) {
  'use strict';

  var options = {
    speed: 3,
    trail: 0.01,
    ammount: 1500,
    collision: true,
    image: 'the-bathers',
    running: true
  };

  var width = 640;
  var height = 400;
  var pi = pi;

  var canvas = document.querySelector('canvas');
  var context = canvas.getContext('2d');

  var preview = document.querySelector('.preview');

  var particles = [];
  var data = [];

  var gui = new dat.GUI(); // jshint ignore: line
  gui.add(options, 'speed', 1, 10);
  var trailSelect = gui.add(options, 'trail', 0.001, 0.5);
  var ammountSelect = gui.add(options, 'ammount', 1, 6000).step(1);
  var imageSelect = gui.add(options, 'image', [
    'the-bathers',
    'at-the-moulin-rouge',
    'the-starry-night',
    'senecio',
    'mother-and-child',
    'still-life-with-a-guitar'
  ]);
  gui.add(options, 'collision');
  gui.add(options, 'running');

  var stats = new Stats(); // jshint ignore: line
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.right = '0px';
  stats.domElement.style.bottom = '0px';
  document.body.appendChild(stats.domElement);

  function fadeCanvas() {
    context.fillRect(0, 0, canvas.width, canvas.height);
  }

  function renderImage(image) {
    width = canvas.width = image.width;
    height = canvas.height = image.height;
    context.drawImage(image, 0, 0);

    var data = context.getImageData(0, 0, image.width, image.height).data;
    context.clearRect(0, 0, image.width, image.height);
    return data;
  }

  function createParticle() {
    var x = Math.random() * width;
    var y = Math.random() * height;

    return {
      lastX: x,
      lastY: y,
      x: x,
      y: y,
      vx: Math.random() - 0.5,
      vy: Math.random() - 0.5
    };
  }

  function moveParticle(p) {
    var lastX = p.x;
    var lastY = p.y;

    var vx = p.vx;
    var vy = p.vy;

    var x = lastX + p.vx * options.speed;
    var y = lastY + p.vy * options.speed;

    if (x > width) {
      x = width;
      vx = -vx;
    } else if (x < 0) {
      x = 0;
      vx = -vx;
    }

    if (y > height) {
      y = height;
      vy = -vy;
    } else if (y < 0) {
      y = 0;
      vy = -vy;
    }

    return {
      lastX: lastX,
      lastY: lastY,
      x: x,
      y: y,
      vx: vx,
      vy: vy,
    };
  }

  function linearPosition(p) {
    return p.y * width + p.x;
  }

  var scaleSystem = function (count) {
    var l  = particles.length;

    if (count < l) {
      particles = particles.slice(0, count);
    } else if (count > l) {
      for (var i = l; i < count; i++) {
        particles.push(createParticle());
      }
    }
  };

  function checkCollision (a, b) {
    if (Math.abs(a.x - b.x) <= 1 && Math.abs(a.y - b.y) <= 1) {
      var tanA = a.vy / a.vx;
      var tanB = b.vy / b.vx;

      var v1 = Math.sqrt(a.vx * a.vx + a.vy * a.vy);
      var v2 = Math.sqrt(b.vx * b.vx + b.vy * b.vy);

      var aangle, bangle;

      if (tanA === 0) {
        aangle = (a.vy > 0 ? 1 : -1) * pi / 2;
      } else {
        aangle = Math.atan(tanA);
      }

      if (tanB === 0) {
        bangle = (b.vy > 0 ? 1 : -1) * pi / 2;
      } else {
        bangle = Math.atan(tanB);
      }

      if (a.vx < 0) {
        aangle += pi;
      }
      if (b.vx < 0) {
        bangle += pi;
      }

      var phi;
      var dx = a.vx - b.vx;
      var dy = a.vy - b.vy;
      if (dx === 0) {
        phi = pi / 2;
      } else {
        phi = Math.atan2(dy, dx);
      }

      var v1xr = v1 * Math.cos(aangle - phi);
      var v1yr = v1 * Math.sin(aangle - phi);
      var v2xr = v2 * Math.cos(bangle - phi);
      var v2yr = v2 * Math.sin(bangle - phi);

      a.vx = v2xr;
      a.vy = v1yr;
      b.vx = v1xr;
      b.vy = v2yr;
    }
  }

  function loadImage() {
    preview.onload = function () {
      data = renderImage(preview);
      trailChanged(options.trail);
    };
    preview.src = 'images/' + options.image + '.jpg';
  }

  function resize(ammount) {
    scaleSystem(ammount);
  }

  function trailChanged(trail) {
    context.fillStyle = 'rgba(0,0,0,'+trail+')';
  }

  trailSelect.onChange(trailChanged);
  imageSelect.onChange(loadImage);
  ammountSelect.onChange(resize);

  trailChanged(options.trail);
  resize(options.ammount);
  loadImage();

  function render() {
    if (options.running) {
      stats.begin();

      fadeCanvas();

      if (options.collision) {
        particles = particles.sort(function (pa, pb) {
          return linearPosition(pa) - linearPosition(pb);
        });
      }

      for (var i = 0, l = particles.length; i < l; i++) {
        var pa = particles[i];
        var pb = particles[i+1];

        if (pb && options.collision) {
          checkCollision(pa, pb);
        }

        var p = moveParticle(pa);
        particles[i] = p;
        //var p = pa;

        var index = ((p.y>>0)*4*width)+((p.x>>0)*4);
        var r = data[index];
        var g = data[index+1];
        var b = data[index+2];

        context.strokeStyle = 'rgb('+r+', '+g+', '+b+')';
        context.beginPath();
        context.moveTo(p.lastX, p.lastY);
        context.lineTo(p.x, p.y);
        context.closePath();
        context.stroke();
      }

      stats.end();
    }

    window.requestAnimationFrame(render);
  }

  window.addEventListener('DOMContentLoaded', function () {
    window.requestAnimationFrame(render);
  }, false);
})(window, document);
<commit_msg>Store pi calculations<commit_after>(function (window, document) {
  'use strict';

  var options = {
    speed: 3,
    trail: 0.01,
    ammount: 1500,
    collision: true,
    image: 'the-bathers',
    running: true
  };

  var width = 640;
  var height = 400;
  var pi = Math.PI;
  var pi_2 = pi / 2;

  var canvas = document.querySelector('canvas');
  var context = canvas.getContext('2d');

  var preview = document.querySelector('.preview');

  var particles = [];
  var data = [];

  var gui = new dat.GUI(); // jshint ignore: line
  gui.add(options, 'speed', 1, 10);
  var trailSelect = gui.add(options, 'trail', 0.001, 0.5);
  var ammountSelect = gui.add(options, 'ammount', 1, 6000).step(1);
  var imageSelect = gui.add(options, 'image', [
    'the-bathers',
    'at-the-moulin-rouge',
    'the-starry-night',
    'senecio',
    'mother-and-child',
    'still-life-with-a-guitar'
  ]);
  gui.add(options, 'collision');
  gui.add(options, 'running');

  var stats = new Stats(); // jshint ignore: line
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.right = '0px';
  stats.domElement.style.bottom = '0px';
  document.body.appendChild(stats.domElement);

  function fadeCanvas() {
    context.fillRect(0, 0, canvas.width, canvas.height);
  }

  function renderImage(image) {
    width = canvas.width = image.width;
    height = canvas.height = image.height;
    context.drawImage(image, 0, 0);

    var data = context.getImageData(0, 0, image.width, image.height).data;
    context.clearRect(0, 0, image.width, image.height);
    return data;
  }

  function createParticle() {
    var x = Math.random() * width;
    var y = Math.random() * height;

    return {
      lastX: x,
      lastY: y,
      x: x,
      y: y,
      vx: Math.random() - 0.5,
      vy: Math.random() - 0.5
    };
  }

  function moveParticle(p) {
    var lastX = p.x;
    var lastY = p.y;

    var vx = p.vx;
    var vy = p.vy;

    var x = lastX + p.vx * options.speed;
    var y = lastY + p.vy * options.speed;

    if (x > width) {
      x = width;
      vx = -vx;
    } else if (x < 0) {
      x = 0;
      vx = -vx;
    }

    if (y > height) {
      y = height;
      vy = -vy;
    } else if (y < 0) {
      y = 0;
      vy = -vy;
    }

    return {
      lastX: lastX,
      lastY: lastY,
      x: x,
      y: y,
      vx: vx,
      vy: vy,
    };
  }

  function linearPosition(p) {
    return p.y * width + p.x;
  }

  var scaleSystem = function (count) {
    var l  = particles.length;

    if (count < l) {
      particles = particles.slice(0, count);
    } else if (count > l) {
      for (var i = l; i < count; i++) {
        particles.push(createParticle());
      }
    }
  };

  function checkCollision (a, b) {
    if (Math.abs(a.x - b.x) <= 1 && Math.abs(a.y - b.y) <= 1) {
      var tanA = a.vy / a.vx;
      var tanB = b.vy / b.vx;

      var v1 = Math.sqrt(a.vx * a.vx + a.vy * a.vy);
      var v2 = Math.sqrt(b.vx * b.vx + b.vy * b.vy);

      var aangle, bangle;

      if (tanA === 0) {
        aangle = (a.vy > 0 ? 1 : -1) * pi_2;
      } else {
        aangle = Math.atan(tanA);
      }

      if (tanB === 0) {
        bangle = (b.vy > 0 ? 1 : -1) * pi_2;
      } else {
        bangle = Math.atan(tanB);
      }

      if (a.vx < 0) {
        aangle += pi;
      }
      if (b.vx < 0) {
        bangle += pi;
      }

      var phi;
      var dx = a.vx - b.vx;
      var dy = a.vy - b.vy;
      if (dx === 0) {
        phi = pi_2;
      } else {
        phi = Math.atan2(dy, dx);
      }

      var v1xr = v1 * Math.cos(aangle - phi);
      var v1yr = v1 * Math.sin(aangle - phi);
      var v2xr = v2 * Math.cos(bangle - phi);
      var v2yr = v2 * Math.sin(bangle - phi);

      a.vx = v2xr;
      a.vy = v1yr;
      b.vx = v1xr;
      b.vy = v2yr;
    }
  }

  function loadImage() {
    preview.onload = function () {
      data = renderImage(preview);
      trailChanged(options.trail);
    };
    preview.src = 'images/' + options.image + '.jpg';
  }

  function resize(ammount) {
    scaleSystem(ammount);
  }

  function trailChanged(trail) {
    context.fillStyle = 'rgba(0,0,0,'+trail+')';
  }

  trailSelect.onChange(trailChanged);
  imageSelect.onChange(loadImage);
  ammountSelect.onChange(resize);

  trailChanged(options.trail);
  resize(options.ammount);
  loadImage();

  function render() {
    if (options.running) {
      stats.begin();

      fadeCanvas();

      if (options.collision) {
        particles = particles.sort(function (pa, pb) {
          return linearPosition(pa) - linearPosition(pb);
        });
      }

      for (var i = 0, l = particles.length; i < l; i++) {
        var pa = particles[i];
        var pb = particles[i+1];

        if (pb && options.collision) {
          checkCollision(pa, pb);
        }

        var p = moveParticle(pa);
        particles[i] = p;
        //var p = pa;

        var index = ((p.y>>0)*4*width)+((p.x>>0)*4);
        var r = data[index];
        var g = data[index+1];
        var b = data[index+2];

        context.strokeStyle = 'rgb('+r+', '+g+', '+b+')';
        context.beginPath();
        context.moveTo(p.lastX, p.lastY);
        context.lineTo(p.x, p.y);
        context.closePath();
        context.stroke();
      }

      stats.end();
    }

    window.requestAnimationFrame(render);
  }

  window.addEventListener('DOMContentLoaded', function () {
    window.requestAnimationFrame(render);
  }, false);
})(window, document);
<|endoftext|>


<commit_before>(function($) {

	$.fn.shopaholic = function(params) {
		var options = $.extend({
			data: '/data/items.json', //This line adding another parameter to the options... ```expands``` it
		}, params);

		var cartID = options.username + '.cart'; //Generates ID.
		var Super = this; //Saves current ``this``
		var jsonData = {}; //Saves the JSON data
		var shelf = {}; //Shelf with items

		var models = { //List of models for generating content.
			item: function(item) {
				var text = db.in(item.id) ? '‒' : '+';
				var colour = db.in(item.id) ? 'red' : '';

				return '
' +
						'
' +
							'
' +
								'
$' +
									item.price +
								'
' +
							'
' +
							'
' + item.name + '' + text + '
' +
						'
' +
					'
';
			}
		};

		var db = { //Database methods.

			list: function(){ //Returns array of items.
				return JSON.parse(localStorage.getItem(cartID)) || [];
			},

			in: function(id){ //Returns if item in database or not
				var temp = JSON.parse(localStorage.getItem(cartID)) || [];
				var index = temp.indexOf(id.toString());
				if(index !== -1){
					return true;
				}
				return false;
			},

			add: function(id){ //Adds id.
				var temp = JSON.parse(localStorage.getItem(cartID)) || [];
				// if(!Array.isArray(temp)) temp = []; //SAVE FOR LATER
				var index = temp.indexOf(id.toString());
				if(index === -1){
					temp.push(id.toString());
					localStorage.setItem(cartID, JSON.stringify(temp));
				}

			},

			remove: function(id){ //Removes id.
				var temp = JSON.parse(localStorage[cartID]);
				var index = temp.indexOf(id.toString());
				if(index !== -1){
					temp.splice(index, 1);
					localStorage.setItem(cartID, JSON.stringify(temp));
				}
			},

			clear: function(){ //Clears shopping cart.
				localStorage.setItem(cartID, '[]');
			}
		};

		var render = { //Render methods
			items: function(data){ //Renders Items
				$(Super).html('');
				$.each( data.items, function( key, value ) {
					$(Super).append(models.item(value)).show('slow');
				});
				$('a.button').click(function(){ //Adds click handler
					var id = $(this).data('id');
					if(id){
						render.button(this, id);
					}else if($(this).attr('id') === 'buy'){
						db.clear()
						render.items(jsonData);
						render.cart();
					}
				})

			},
			desc: function(data){ //Renders Description
				options.header.append(''); //Add image
				options.header.append('
' + data.name + '
'); //Add name
				options.header.append('
' + data.description + '
');//Add description
				options.footer.append('
' + data.info.address + '
');

				$('head').append('<title>' + data.name + '</title>'); //Add title
			},
			button: function(button, id){ //Re-renders button
				if(!db.in(id)){
					db.add(id); //Adds item to list

					$(button).addClass('red');//Change the colour.
					$(button).text('‒');//Changes the text.

				}else{
					db.remove(id);

					$(button).removeClass('red'); //Change the colour.
					$(button).text('+'); //Changes the text.
				}
				render.cart();
			},
			cart: function(){ //Renders cart info
				var info = {
					amount: db.list().length,
					costs: function(){
						if(db.list().length !== 0){
							var amount = 0;
							for (item of db.list()) {
								amount += shelf[item.toString()].price;
							};
							return amount
						}
						return 0
					}()
				};
				options.cart.html(info.amount + ' items in your cart, worth ' + Math.round(info.costs) + '$.');
			}
		}
		$.getJSON( 'data/items.json', function( data ) { //Gets JSON
			$.each( data.items, function( key, value ) {
				shelf[value.id.toString()] = value //Reversing list to object
			});

			// console.log(items);
			jsonData = data; //Saving data

			render.desc(data); //Rendering description
			render.items(data); //Rendering items
			render.cart();
		})
		.fail(function(error){
			console.log(error)
		})
	};

}(jQuery));<commit_msg>Added weight n gramms per price<commit_after>(function($) {

	$.fn.shopaholic = function(params) {
		var options = $.extend({
			data: '/data/items.json', //This line adding another parameter to the options... ```expands``` it
		}, params);

		var cartID = options.username + '.cart'; //Generates ID.
		var Super = this; //Saves current ``this``
		var jsonData = {}; //Saves the JSON data
		var shelf = {}; //Shelf with items

		var models = { //List of models for generating content.
			item: function(item) {
				var text = db.in(item.id) ? '‒' : '+';
				var colour = db.in(item.id) ? 'red' : '';

				return '
' +
						'
' +
							'
' +
								'
$' +
									item.price + ' / ' + item.weight +
								'
' +
							'
' +
							'
' + item.name + '' + text + '
' +
						'
' +
					'
';
			}
		};

		var db = { //Database methods.

			list: function(){ //Returns array of items.
				return JSON.parse(localStorage.getItem(cartID)) || [];
			},

			in: function(id){ //Returns if item in database or not
				var temp = JSON.parse(localStorage.getItem(cartID)) || [];
				var index = temp.indexOf(id.toString());
				if(index !== -1){
					return true;
				}
				return false;
			},

			add: function(id){ //Adds id.
				var temp = JSON.parse(localStorage.getItem(cartID)) || [];
				// if(!Array.isArray(temp)) temp = []; //SAVE FOR LATER
				var index = temp.indexOf(id.toString());
				if(index === -1){
					temp.push(id.toString());
					localStorage.setItem(cartID, JSON.stringify(temp));
				}

			},

			remove: function(id){ //Removes id.
				var temp = JSON.parse(localStorage[cartID]);
				var index = temp.indexOf(id.toString());
				if(index !== -1){
					temp.splice(index, 1);
					localStorage.setItem(cartID, JSON.stringify(temp));
				}
			},

			clear: function(){ //Clears shopping cart.
				localStorage.setItem(cartID, '[]');
			}
		};

		var render = { //Render methods
			items: function(data){ //Renders Items
				$(Super).html('');
				$.each( data.items, function( key, value ) {
					$(Super).append(models.item(value)).show('slow');
				});
				$('a.button').click(function(){ //Adds click handler
					var id = $(this).data('id');
					if(id){
						render.button(this, id);
					}else if($(this).attr('id') === 'buy'){
						db.clear()
						render.items(jsonData);
						render.cart();
					}
				})

			},
			desc: function(data){ //Renders Description
				options.header.append(''); //Add image
				options.header.append('
' + data.name + '
'); //Add name
				options.header.append('
' + data.description + '
');//Add description
				options.footer.append('
' + data.info.address + '
');

				$('head').append('<title>' + data.name + '</title>'); //Add title
			},
			button: function(button, id){ //Re-renders button
				if(!db.in(id)){
					db.add(id); //Adds item to list

					$(button).addClass('red');//Change the colour.
					$(button).text('‒');//Changes the text.

				}else{
					db.remove(id);

					$(button).removeClass('red'); //Change the colour.
					$(button).text('+'); //Changes the text.
				}
				render.cart();
			},
			cart: function(){ //Renders cart info
				var info = {
					amount: db.list().length,
					costs: function(){
						if(db.list().length !== 0){
							var amount = 0;
							for (item of db.list()) {
								amount += shelf[item.toString()].price;
							};
							return amount
						}
						return 0
					}()
				};
				options.cart.html(info.amount + ' items in your cart, worth ' + Math.round(info.costs) + '$.');
			}
		}
		$.getJSON( 'data/items.json', function( data ) { //Gets JSON
			$.each( data.items, function( key, value ) {
				shelf[value.id.toString()] = value //Reversing list to object
			});

			// console.log(items);
			jsonData = data; //Saving data

			render.desc(data); //Rendering description
			render.items(data); //Rendering items
			render.cart();
		})
		.fail(function(error){
			console.log(error)
		})
	};

}(jQuery));<|endoftext|>


<commit_before><commit_msg>random rolling number and verification of doubles implemented<commit_after>function rollDice() {
    var dice = new DiceNumber();

    checkForDoubles(dice);

    return dice;
};

function DiceNumber() {
    this.dice1 = Math.floor((Math.random() * 6) + 1);
    this.dice2 = Math.floor((Math.random() * 6) + 1);
};

function checkForDoubles(dice) {
  dice.doubles = dice.dice1 === dice.dice2 ? true : false;
};<|endoftext|>