function parser() {

	// Unfortunately commentaries have to be parsed for the whole PGN file and stored in this class
	// because it is necessary to split the file into games correctly
	var commentaries = [];
	this.games = [];

	// Loads the PGN file, splits it for every game and creates game objects
	this.loadPGN = 
	function(PGN) {
		this.games = undefined;
		commentaries = [];
		this.games = [];
		// IE uses \r\n, Gecko browsers use \n
		PGN = PGN.replace(/\r\n/g,"\n");
		// Replace bracket {} commentaries because the can mess up how we determine the game edges, store them in array
		PGN = PGN.replace(/\{([\W\w]*?)\}/g, function($1) {
			commentaries.push($1.substring(1, $1.length - 1));
			return "{" + (commentaries.length - 1) + "}";
		});

		// Replace strings (because they can contain the semicolon comments,
		// which could once again cripple our ability to parse the games correctly) and store them in array
		var tagStrings = [];
		PGN = PGN.replace(/"([^"\\\r\n]*(?:\\.[^"\\\r\n]*)*)"/g, function($1) {
			// Also attempt to replace back "nested" {} comments because they can appear inside these strings
			tagValue = $1.replace(/\{([\d]+)\}/g, function ($1) {
				return commentaries[$1.substring(1, $1.length - 1)];
			});
			tagStrings.push(tagValue.substring(1, tagValue.length - 1));
			return "\"" + (tagStrings.length - 1) + "\"";
		});

		// Replace semicolon this.commentaries and store them in array
		PGN = PGN.replace(/(;[\W\w]*?)\n/, function($1) {
			// Also attempt to replace back "nested" {} comments because they can appear inside semicolon commentaries
				commValue = $1.replace(/\{([\d]+)\}/g, function ($1) {
				return "{" + commentaries[$1.substring(1, $1.length - 1)] + "}";
			});
			commentaries.push(commValue.substring(1, commValue.length));
			return "{" + (commentaries.length - 1) + "}\n";
		});
		
		// Split the Games
		gamesArray = PGN.match(/(\[[\W\w]*?(?:0-1|1-0|1\/2-1\/2|\*))(?:[\s]+|$)/g);

		// Create Game object for every game in PGN file
		if (gamesArray != null) {
			for (var i = 0; i < gamesArray.length; i++) {
				game = new chessGame();
				// Add tags from the tag section to game object
				tagsArray = gamesArray[i].match(/(\[[\W\w]*?\])/g);
				for (var j = 0; j < tagsArray.length; j++) {
					tagMatch = tagsArray[j].match(/\[[\s]*([\w]+)[\s]*"([\d]+)"[\s]*\]/);
					game.tags[tagMatch[1]] = tagStrings[tagMatch[2]];
				}

				movetext = gamesArray[i].replace(/(\[[\W\w]*?\])/g, "");
				// Strip the numbers notation 
				// (this doesn't quite follow the PGN import format, as the numbers MUST end with at least one dot)
				// But virtually every PGN file has dot after the number, and its so much easier to create execute
				// regular expression, mainly because JavaScript has no lookbehind support for regular expressions
				movetext = movetext.replace(/\b[\d]+[\s]*[\.]+/g, "");

				// Recursive variations are just that, recursive and require a loop to be parsed, mainly because 
				// JavaScript's regular expressions do not support the recursion
				// While having recursive variations,it looks for deepest one and replaces it
				while(/\([^\(\)]*?\)/.test(movetext)) {
					movetext = movetext.replace(/\([^\(\)]*?\)/, function ($1) {
						game.variations.push($1.substring(1, $1.length - 1));
						return "<" + (game.variations.length - 1) + ">";
					});
				}
				// Assign movetext to game object
				game.movetext = movetext;
				this.games.push(game);
			}
		}
	}

	// Loads (e. g. parses the movetext) the game
	this.loadGame = 
	function (game) {
		game.initBoard();
		
		movetext = game.movetext;

		// Strip the result (ending)
		movetext = movetext.replace(/[\s]+(?:0-1|1-0|1\/2-1\/2|\*)[\s]+/, "");

		// Split the movetext to tokens
		//document.write(movetext);
		this.loadMoves(game, movetext, false, 0);
	}

	// Move Parsing
	this.loadMoves =
	function (game, movetext, variation, varNum) {
		var promotion;
		var promoteTo;
		var moveTokens = movetext.split(/[\s]+/);
		var token;
		for(var i = 0; i < moveTokens.length; i++) {
			token = moveTokens[i];
			if (/[RBQKPN]?[a-h]?[1-8]?[x]?[a-h][1-8][=]?[QNRB]?[+#]?/.test(token)) {
				moveArray = token.match(/([RBQKPN])?([a-h])?([1-8])?([x])?([a-h])([1-8])([=]?)([QNRB]?)([+#]?)/);
				if (moveArray[1]) 
					switch (moveArray[1].toLowerCase()) {
					case "r":
						piece = "rook";
						break;
					case "b":
						piece = "bishop";
						break;
					case "q":
						piece = "queen";
						break;
					case "n":
						piece = "knight";
						break;
					case "k":
						piece = "king";
						break;
					default:
						break;
				} else 
					piece = "pawn";

				if(moveArray[4])
					capture = true;
				else 
					capture = false;

				if(moveArray[8]) {
					promotion = true;
					switch (moveArray[8].toLowerCase()) {
					case "r":
						promoteTo = "rook";
						break;
					case "b":
						promoteTo = "bishop";
						break;
					case "q":
						promoteTo = "queen";
						break;
					case "n":
						promoteTo = "knight";
						break;
					default:
						break;
					}
				} else {
					promotion = false;
					promoteTo = '';
				}
				game.moveHandler(piece, moveArray[2], moveArray[3], moveArray[5], moveArray[6], capture, promotion, promoteTo, variation, varNum);
			} else if (/O-O-O|O-O/.test(token)) {
				game.castle(token, variation, varNum);
			} else if (/<[\d]+>/.test(token)) {
				vN = parseInt(token.match(/<([\d]+)>/)[1]);
				if (variation)
						varMovesNum = varNum + 1;
					else
						varMovesNum = varNum;
					game.moves[varMovesNum][game.moves[varMovesNum].length - 1].variated.push(vN);
				if (!game.varFENs[vN]) {
					game.varFENs[vN] = [];
				} if (!game.moves[vN + 1]) {
					game.moves[vN + 1] = [];
				}
				currFEN = game.currFEN;
				if (!variation) {
					game.loadFEN(game.FENs[game.FENs.length - 2]);
					game.varFENs[vN][0] = game.FENs[game.FENs.length - 2];
					game.moves[vN + 1][0] = game.moves[0][game.moves[0].length - 2]
				} else {
					game.loadFEN(game.varFENs[varNum][game.varFENs[varNum].length - 2]);
					game.varFENs[vN][0] = game.varFENs[varNum][game.varFENs[varNum].length - 2];
					game.moves[vN + 1][0] = game.moves[varNum + 1][game.moves[varNum + 1].length - 2]
				}
				this.loadMoves(game, game.variations[vN], true, vN);

				if (!variation)
					game.loadFEN(game.FENs[game.FENs.length - 1]);
				else
					game.loadFEN(game.varFENs[varNum][game.varFENs[varNum].length - 1]);
			} else if (/\{[\d]+\}/.test(token)) {
				cM = token.match(/\{([\d]+)\}/)[1];
				var comm = commentaries[cM];
				if (comm.match(/\%16/)) {
					if (variation)
						varMovesNum = varNum + 1;
					else
						varMovesNum = varNum;
					game.moves[varMovesNum][game.moves[varMovesNum].length - 1].guess = true;
					game.commentaries[cM] = "";
					game.guesses++;
				} else if (comm.match(/\%17/)) {
					var markers = comm.match(/\#MARKERS\s-\s+([\w\W]+?)\s+MARKERS\#/);
					if (variation)
						varMovesNum = varNum + 1;
					else
						varMovesNum = varNum;
					if (markers)
						game.moves[varMovesNum][game.moves[varMovesNum].length - 1].markers = markers[1];
					game.commentaries[cM] = "";
				} else if (comm.match(/\%05/)) {
					game.commentaries[cM] = "";
				} else
					game.commentaries[cM] = comm;
				
			} else if (/Z0/.test(token)) {
				game.saveFEN();
				game.Z0 = true;
			}
		}
	}
}
