var windowLoaded = false, scriptsLoaded = false;
function loaded() { if (/*windowLoaded && */scriptsLoaded) $(".modal .loading:first").addClass("loading-done").text($("body").hasClass("pl") ? "Dane gry pobrane pomyślnie." : "Data loaded successfully."); }
//$(window).load(function() { windowLoaded = true; loaded(); });


$(".scores .tables a").click(function(event, leaveTimer)
{
	if ($(this).hasClass("selected"))
		return false;

	$($(this).hrefId()).show();
	$(this).addClass("selected").parent().siblings().children("a").removeClass("selected").each(function() { $($(this).hrefId()).hide(); });

	if (typeof leaveTimer == "undefined" && scoresTimer)
	{
		clearInterval(scoresTimer);
		scoresTimer = false;
	}

	return false;
});


if ($("form.comment").length)
{
	$("p.toggle-comment-form a,a.toggle-comment-form").click(function() { $("form.comment").animate({ opacity: "toggle", height: "toggle", paddingBottom: "toggle", paddingTop: "toggle" }, 750, function() { if ($.browser.msie && $.browser.version == 7) this.style.removeAttribute("filter"); }); return false; });
	$("form.comment :reset").click(function() { $("form.comment").animate({ opacity: "hide", height: "hide", paddingBottom: "hide", paddingTop: "hide" }, 750).find("input").val(""); return false; });
	$("form.comment textarea").bind("change keyup mousemove", function() { $(this).parent().find("label[for=" + $(this).attr("id") + "] strong").text($(this).val().length); }).change();
	setTimeout(function() { $("form.comment p:last").append("<input type=\"hidden\" name=\"dobry-soczek-to-marchwiowy-soczek\" value=\"mniam-mniam\">"); }, 3000);
}


if ($(".scores-full:first").length)
	$(".scores-full:first").find("div[id^=scores-]").each(function()
	{
		var $tableBodiesLeft = $(this).find(">table.left>tbody"), $tableBodiesRight = $(this).find(">table.right>tbody");
		var $list = $("<div class=\"pages\"><p>Show results:</p><ul></ul></div>").appendTo(this).find("ul");
		$tableBodiesLeft.each(function(i) { i++; i *= 100; $list.append("<li><a>" + (i - 99) + "-" + i + "</a></li>"); });

		$list.find("a").click(function()
		{
			if ($(this).hasClass("selected"))
				return false;

			$(this).addClass("selected").parent().siblings().find("a").removeClass("selected");
			var index = $(this).parent().parent().find("a").index(this);

			$tableBodiesLeft.filter(":visible").hide();
			$tableBodiesRight.filter(":visible").hide();
			$tableBodiesLeft.eq(index).show();
			$tableBodiesRight.eq(index).show();
			return false;
		}).eq(0).click();
	});


Cufon.replace(".game-status>p>span,.mblocks .window h1");

$game = $(".mblocks>.game:first");
if ($game.length)
{
	$game.find(".mblocks-no-js").remove();

	function random(min, max) { return min + Math.floor((max - min + 1) * Math.random()); }
	Number.prototype.toMinutes = function() { return Math.floor(this / 60) + ":" + ((this % 60) < 10 ? "0" : "") + (this % 60); }
	Math.mid = function(min, mid, max) { return Math.max(min, Math.min(mid, max)); };

	Number.prototype.separateDigits = function(separator)
	{
		if (typeof separator != "string" && typeof separator != "number")
			separator = " ";
		var strCopy = "";
		var str = this.toString();
		var w = str.length;
		for (var i = 0; i < w / 3; i++)
			strCopy = str.substring(w - (i + 1) * 3, w - (i + 1) * 3 + 3) + (i <= 0 ? "" : separator) + strCopy;
		return strCopy;
	}

	function clone(source)
	{
		for (i in source)
			this[i] = typeof source[i] == "source" ? new clone(source[i]) : source[i];
	}

	$.fn.extend
	({
		startBlinking: function(delay)
		{
			return this.each(function()
			{
				if ($(this).data("blinkTimer"))
					clearInterval($(this).data("blinkTimer"));
				var $this = $(this);
				$this.data("blinkTimer", setInterval(function() { $this.toggle(); }, delay));
			});
		}
	,	stopBlinking: function(leaveVisible)
		{
			return this.each(function()
			{
				if ($(this).data("blinkTimer"))
					clearInterval($(this).data("blinkTimer"));
				if (typeof leaveVisible != "undefined")
				{
					if (leaveVisible)
						$(this).show();
					else
						$(this).hide();
				}
			});
		}
	});


	$.fn.extend({ mBlocksText: function(text, clear)
	{
		if (clear === true)
			$(this).empty();
		return this.each(function()
		{
			if (typeof text != "string")
				text = text.toString();

			var t = text.length, i = t - 1, $this = $(this), $input = $this.children(), l = $input.length, c, className;
			do
			{
				c = text.charAt(i);
				className = (c == " ") ? "space" : (c == ":" ? "colon" : c);

				if (l - t + i >= 0)
				{
					var element = $input.eq(l - t + i)[0];
					element.className = "l-" + className;
					element.childNodes[1].data = c;
				}
				else
					$this.prepend("<span class=\"l-" + className + "\"><img src=\"images/mblocks/letters.png\" alt=\"\">" + c + "</span>");
			}
			while (i--);
		});
	}});


	/*
	** mBlocks v1.3
	** (c) 2009 Merix Studio
	**
	** Based on the original idea "Tetris" by Alexey Pajitnov
	**
	** Warning! Own naming conventions for game elements!
	*/
	$.fn.extend({ mBlocks: function(options)
	{
		var defaults =
		{
			w: 10
		,	h: 20
		,	brickW: 24
		,	brickH: 24
		,	blockW: 4
		,	blockH: 4
		,	scoring: [ 3, 300, 900, 2700, 8100 ]
		,	blocks:
			[
				[  1632,  1632, 1632,  1632 ] // O
			,	[  1584,   612, 3168,  9792 ] // Z
			,	[ 17504,  1856, 1570,   736 ] // L
			,	[  8800,  1136, 1604,  3616 ] // J
			,	[  3840,  8738,  240, 17476 ] // I
			,	[  1248,  1124,  228,  1220 ] // T
			,	[  1728, 17952,  864,  1122 ] // S
			]
		,	pointsSeparator: " "
		,	gameOverCallback: null
		,	pauseCallback: null
		,	newGameCallback: null
		,	playDemoCallback: null
		,	updateDemoCallback: null
		,	timedModeTime: 300
		,	demoSpeeds: [ 320, 160, 80, 40, 20 ]
		,	$score: ".score strong"
		,	$time: ".time strong"
		,	$nextBlock: ".next-block"
		,	$level: ".level strong"
		,	$lines: ".lines strong"
		,	$lines1X: ".lines-1x strong"
		,	$lines2X: ".lines-2x strong"
		,	$lines3X: ".lines-3x strong"
		,	$lines4X: ".lines-4x strong"
		,	$debug: ".debug strong"
		};

		defaults.keys = ($.browser.opera || $.browser.mozilla)
		?	{ moveLeft: 37, moveRight: 39, moveDown: 40, rotateLeft: 90, rotateRight: 38, rotateRightAlt: 88, hardDrop: 32 }
		:	{ moveLeft: 74, moveRight: 76, moveDown: 75, rotateLeft: 90, rotateRight: 73, rotateRightAlt: 88, hardDrop: 32 };

		options = new clone($.extend(defaults, options));

		return this.each(function()
		{
			// write HTML and assign variables
			var
				version = 135
			,	w = options.w
			,	h = options.h
			,	brickW = options.brickW
			,	brickH = options.brickH
			,	blockW = options.blockW
			,	blockH = options.blockH
			,	blocks = options.blocks
			,	r		// current block's rotation
			,	b		// current block
			,	nb		// next block
			,	nr		// next block's rotation
			,	matrix = []
			,	x		// current position in matrix, not in pixels
			,	y
			,	modeTypes = { none: 0, playing: 1, demo: 2 }
			,	mode = modeTypes.none
			,	pause
			,	score
			,	scoringType = { hardDrop: 0, fullRows: 1 }
			,	lines = []
			,	linesPerLevel = []
			,	level
			,	gameTypes = { standard: 0, timed: 1, meteor: 2 }
			,	gameOverReasons = { fullMatrix: 0, timesUp: 1 }
			,	gameType
			,	timeLeft
			,	timer = false
			,	$score = $(options.$score)
			,	$time = $(options.$time)
			,	$nextBlock = $(options.$nextBlock)
			,	$level = $(options.$level)
			,	$lines = $(options.$lines)
			,	$lines1X = $(options.$lines1X)
			,	$lines2X = $(options.$lines2X)
			,	$lines3X = $(options.$lines3X)
			,	$lines4X = $(options.$lines4X)
			,	$debug = $(options.$debug)
			,	timeFlashing = false
			,	demo = new Demo()
			,	demoPosition = 0
			,	demoTimer = false
			,	demoFrames = []
			,	demoFirstFrame = false
			,	demoFast = false
			,	demoBugged = false
			,	demoSpeed = 1
			,	moveLeft = false
			,	moveRight = false
			,	meteors = []
			,	blocksCount = 0
			;

			/*
			** demo recording class
			*/
			function Demo()
			{
				var frames, f;

				Demo.operations =
				{
					headerEnd: 0
				,	newBlock: 1
				,	newNextBlock: 2
				,	moveLeft: 3
				,	moveRight: 4
				,	drop: 5
				,	dropFast: 6
				,	dropInstant: 7
				,	rotateLeft: 8
				,	rotateRight: 9
				,	newMeteor: 10
				,	dropMeteor: 11
				};

				this.reset = function() { frames = [ version, 0 ]; f = frames.length; };
				this.get = function() { return frames; };

				this.addFrame = function(frame)
				{
					if (mode != modeTypes.playing)
						return;

					switch (frame.operation)
					{
						case Demo.operations.newBlock:
							frames[f++] = frame.operation;
							frames[f++] = frame.block + (frame.rotation << 3);
							break;
						case Demo.operations.newNextBlock:
							frames[f++] = frame.block + (frame.rotation << 3);
							break;
						case Demo.operations.newMeteor:
							frames[f++] = frame.operation;
							frames[f++] = frame.id + (frame.x << 4);
							frames[f++] = frame.color;
							break;
						case Demo.operations.dropMeteor:
							frames[f++] = frame.operation;
							frames[f++] = frame.id;
							break;
						default:
							frames[f++] = frame.operation;
							break;
					}
				};

				this.toString = function() { return frames.toString(); }

				this.reset();
			}

			var meteorPosition = function()
			{
				var x, max = 20;
				do
				{
					var found = true;
					x = random(0, w - 1);
					for (var i = 0; i < meteors.length; i++)
						if (matrix[x] || (x == meteors[i].x && meteors[i].y <= 4)) // this position is too close to an existing meteor
						{
							found = false;
							break;
						}
					max--;
				}
				while (!found && max);

				if (max <= 0)
					return -1;

				return x;
			};

			function Meteor(meteor)
			{
				if (typeof Meteor.count == "undefined")
					Meteor.count = 0;
				Meteor.count++;

				// put meteor block on top of the matrix
				this.reset = function(meteor)
				{
					if (typeof meteor != "undefined")
					{
						this.id = meteor.id;
						this.x = meteor.x;
						this.y = this.speed = -1;
						this.color = meteor.color;
					}
					else
					{
						this.x = meteorPosition();
						this.y = -1;
						this.speed = random(750, 1000) - level * 50;
						this.color = random(1, blocks.length);
					}

					if (typeof this.$ == "undefined")
						this.$ = $("<div class=\"meteor brick brick-" + this.color + "\"></div>").prependTo($matrix);
					else
						this.$[0].className = "meteor brick brick-" + this.color;
					this.$.css({ left: (this.x * brickW) + "px", top: ((this.y - 1) * brickH) + "px" });

					if (!demoFast && mode == modeTypes.playing)
					{
						var m = this;
						this.$.animate({ top: (this.y * brickH) + "px" }, this.speed, "linear", function() { m.update(); });
						demo.addFrame({ operation: Demo.operations.newMeteor, id: this.id, x: this.x, color: this.color });
					}
				};

				this.putOnMatrix = function()
				{
					matrix[this.y * w + this.x] = this.color;
					if (mode == modeTypes.playing)
						this.reset();

					var l = findFullRows();
					updateMatrix(l[0]);
					if (l[1])
						removeFullRows(l[0]);
				};

				this.update = function()
				{
					if (this.x < 0)
						return;

					if (matrix[this.y * w + this.x])
					{
						if (mode == modeTypes.playing)
							this.reset();
					}
					else if ((this.y >= h - 1) || (this.y >= 0 && matrix[(this.y + 1) * w + this.x]))
						this.putOnMatrix();
					else
					{
						this.y++;
						if (mode == modeTypes.demo)
						{
							if (!demoFast)
								this.$.css({ left: (this.x * brickW) + "px", top: (this.y * brickH) + "px" });
						}
						else if (mode == modeTypes.playing)
						{
							if (!demoFast)
							{
								var m = this;
								this.$.animate({ top: (this.y * brickH) + "px" }, this.speed, "linear", function() { m.update(); });
								demo.addFrame({ operation: Demo.operations.dropMeteor, id: this.id });
							}
						}
					}
				};

				this.id = Meteor.count - 1;
				this.reset(meteor);
			}

			var updateScore = function(type, rows)
			{
				if (mode == modeTypes.none)
					return;

				// add points earned
				if (type == scoringType.hardDrop)
				{
					score += options.scoring[0] * rows * level;
					linesPerLevel[level - 1][0] += rows;
				}
				else
				{
					score += options.scoring[rows] * level;
					lines[rows - 1]++;
					linesPerLevel[level - 1][rows]++;
				}

				// output scores in fancy font
				$score.mBlocksText(score.separateDigits(options.pointsSeparator));

				// output lines made
				var sum = 0;
				for (var i = 0; i < Math.max(blockW, blockH); sum += lines[i] * (i + 1), i++)
					;
				$lines.mBlocksText(sum.separateDigits(options.pointsSeparator));
				$lines1X.text(lines[0]);
				$lines2X.text(lines[1]);
				$lines3X.text(lines[2]);
				$lines4X.text(lines[3]);

				// level advance
				if (sum >= (level * 10))
				{
					linesPerLevel[level++] = [ 0, 0, 0, 0, 0 ];
					if (gameType == gameTypes.meteor && meteors.length < w - 1)
						meteors.push(new Meteor());
				}

				$level.mBlocksText(level.separateDigits(options.pointsSeparator));
			};

			// update HTML matrix - assign appropriate classes
			var updateMatrix = function(lines, force)
			{
				if (demoFast)
					return;

				if ($rows.is(":animated") && force !== true)
				{
					$rows.filter(":animated:last").queue(function() { updateMatrix(lines, true); });
					return;
				}

				if (typeof lines == "object" && lines.length)
				{
					$bricks.each(function(i) { this.className = "brick brick-" + matrix[i]; });
					$rows.each(function(i) { if (lines[i] === true) this.className = "row full"; });

					$rows.filter(".full").stop(true).animate({ opacity: 0 }, 250, "swing", function() { $(this).removeClass("full").removeAttr("style"); $bricks.each(function(i) { this.className = "brick brick-" + matrix[i]; }); updateGhost(); });
				}
				else
				{
					$bricks.each(function(i) { this.className = "brick brick-" + matrix[i]; });
					updateGhost();
				}
			};

			var findFullRows = function()
			{
				if (mode == modeTypes.none)
					return [];
				var fullRows = [], count = 0;

				for (var y = 0; y < h; y++)
				{
					var x = y * w;
					while (matrix[x] && (x + 1) % w)
						x++;

					// is this line a full line?
					if (!((x + 1) % w) && matrix[x])
					{
						fullRows[y] = true;
						count++;
					}
				}

				return [ fullRows, count ];
			};

			// Check matrix for full lines. TODO: Please optimize me!
			var removeFullRows = function(fullRows)
			{
				if (mode == modeTypes.none)
					return 0;

				for (var y = 1; y < fullRows.length; y++)
					if (fullRows[y] === true)
					{
						for (var i = y * w + w - 1; i >= w; i--)
							matrix[i] = matrix[i - w];

						// clear first row
						var x = w - 1;
						do matrix[x] = 0; while (x--);
					}
			};

			var checkCollision = function(x, y)
			{
				if (y <= -blockH)
					return false;

				var i = blockW * blockH;
				y += blockH;
				do
				{
					x--;

					if (!(i % blockW))
					{
						x += blockW;
						y--;
					}

					if (!(y < 0 && x >= 0 && x < w) && (x < 0 || y >= h || x >= w || matrix[y * w + x]) && (blocks[b][r] & 1 << (i - 1)))
						return true;
				}
				while (--i);

				return false;
			};

			var updateGhost = function()
			{
				if (demoFast)
					return;

				$ghostBricks.each(function(i) { this.className = "brick brick-" + (blocks[b][r] & (1 << i) ? b + 1 : 0); });

				var yy = y;
				while (yy < h && !checkCollision(x, yy))
					yy++;
				$ghost.css({ left: (x * brickW) + "px", top: ((yy - 1) * brickH) + "px"});
			};

			var putBlockOnMatrix = function()
			{
				if (mode == modeTypes.none)
					return false;

				var i = blockW * blockH;
				y += blockH;
				var result = true;
				do
				{
					x--;

					if (!(i % blockW))
					{
						x += blockW;
						y--;
					}

					if (blocks[b][r] & (1 << (i - 1)))
					{
						if (x >= 0 && y >= 0 && x < w && y < h)
							matrix[y * w + x] = b + 1;
						else
							result = false;
					}
				}
				while (--i);

				var l = findFullRows();
				updateMatrix(l[0]);
				if (l[1])
				{
					updateScore(scoringType.fullRows, l[1]);
					removeFullRows(l[0]);
				}

				return result;
			};

			var gameOver = function(reason)
			{
				if (mode != modeTypes.playing)
					return;

				$currentBlock.stop(true);
				mode = modeTypes.none;

				for (var i = 0; i < meteors.length; i++)
					meteors[i].$.stop(true);

				if (timer)
				{
					clearTimeout(timer);
					timer = false;
				}

				if (typeof options.gameOverCallback == "function")
					options.gameOverCallback(gameType, level, score, linesPerLevel, blocksCount, reason, demo.toString());
			};

			var newBlock = function(isNewGame, forcedBlock, forcedRotation, forcedNextBlock, forcedNextRotation)
			{
				if (mode == modeTypes.none || (mode == modeTypes.demo && typeof forcedBlock == "undefined"))
					return;

				var force = typeof forcedBlock == "number" && typeof forcedRotation == "number";
				if (isNewGame)
				{
					nb = force ? forcedBlock : random(0, blocks.length - 1);
					nr = force ? forcedRotation : random(0, 3);
				}

				b = nb;
				r = nr;
				demo.addFrame({ operation: Demo.operations.newBlock, block: b, rotation: r });

				nr = force ? forcedNextRotation : random(0, 3);
				if (force)
					nb = forcedNextBlock;
				else
					do nb = random(0, blocks.length - 1); while (nb == b);
				demo.addFrame({ operation: Demo.operations.newNextBlock, block: nb, rotation: nr });

				x = (w - blockW) / 2;
				y = -blockH;

				if (!demoFast)
				{
					$currentBlock.stop(true).show().css({ left: (x * brickW) + "px", top: (y * brickH) + "px"});
					$currentBlockBricks.each(function(i) { this.className = "brick brick-" + (blocks[b][r] & (1 << i) ? b + 1 : 0); });
					$nextBlockBricks.each(function(i) { this.className = "brick brick-" + (blocks[nb][nr] & (1 << i) ? nb + 1 : 0); });
					updateGhost();
				}

				blocksCount++;
			};

			var currentSpeed = function() { return Math.max(10, (gameType == gameTypes.standard ? 1000 : 500) - ((level % 50) - 1) * 20); };

			var updateGame = function()
			{
				if (mode != modeTypes.playing || pause)
					return;

				if (moveLeft && !checkCollision(x - 1, y))
				{
					x--;
					demo.addFrame({ operation: Demo.operations.moveLeft });
				}
				else if (moveRight && !checkCollision(x + 1, y))
				{
					x++;
					demo.addFrame({ operation: Demo.operations.moveRight });
				}

				if (moveLeft || moveRight)
				{
					$currentBlock.css({ left: (x * brickW) + "px" });
					updateGhost();
					moveLeft = moveRight = false;
				}

				if (checkCollision(x, y) || checkCollision(x, y + 1))
					setTimeout(function()
					{
						if (checkCollision(x, y) || checkCollision(x, y + 1))
						{
							if (putBlockOnMatrix())
							{
								newBlock();
								updateGame();
							}
							else
								gameOver(gameOverReasons.fullMatrix);
						}
						else
							updateGame();
					}, currentSpeed() / 2);
				else
				{
					y++;
					if (!demoFast)
					{
						$currentBlock.animate({ top: (y * brickH) + "px" }, currentSpeed(), "linear", updateGame);
						demo.addFrame({ operation: Demo.operations.drop });
					}
				}
			};

			var resetGame = function()
			{
				if (demoTimer)
				{
					clearInterval(demoTimer);
					demoTimer = false;
				}

				Meteor.count = 0;
				for (var i = 0; i < meteors.length; i++)
				{
					meteors[i].$.stop(true).remove();
					delete meteors[i];
				}
				meteors = [];

				var i = w * h - 1;
				do matrix[i] = 0; while (i--);
				$rows.attr("class", "row").removeAttr("style");
				$bricks.attr("class", "brick brick-0").removeAttr("style");

				$currentBlock.stop(true).hide();
				$time.stop(true);

				blocksCount = 0;
				score = 0;
				linesPerLevel = [ [ 0, 0, 0, 0, 0 ] ];
				lines = [ 0, 0, 0, 0 ];
				level = 1;
				timeFlashing = pause = false;
				if (mode == modeTypes.playing)
					demo.reset();

				if (timer)
				{
					clearTimeout(timer);
					timer = false;
				}

				$level.mBlocksText("1", true);
				$score.mBlocksText("0", true);
				$time.stop(true).animate({ opacity: 1 }, 0).mBlocksText(options.timedModeTime.toMinutes(), true);
				$lines.mBlocksText("0", true);
				$lines1X.text(lines[0]);
				$lines2X.text(lines[1]);
				$lines3X.text(lines[2]);
				$lines4X.text(lines[3]);
			};

			var flashTime = function() { timeFlashing = true; $time.animate({ opacity: .1 }, 500, "linear", function() { $time.animate({ opacity: 1 }, 250, "linear", flashTime); }); };

			var decreaseTime = function()
			{
				if (mode != modeTypes.playing)
					return;

				timeLeft--;
				$time.mBlocksText(timeLeft.toMinutes());

				if (timeLeft <= 0)
				{
					if (timer)
					{
						clearTimeout(timer);
						timer = false;
					}

					putBlockOnMatrix();
					gameOver(gameOverReasons.timesUp);
				}
				else if (timeLeft < 30 && !timeFlashing)
					flashTime();
			};

			var startStandardGame = function(forceMode)
			{
				mode = typeof forceMode == "undefined" ? modeTypes.playing : forceMode;
				resetGame();
				gameType = gameTypes.standard;

				newBlock(true);
				updateGame();

				if (!demoFast && mode != modeTypes.demo && typeof options.newGameCallback == "function")
					options.newGameCallback(gameType);
			};

			var startTimedGame = function()
			{
				mode = modeTypes.playing;
				resetGame();
				timeLeft = options.timedModeTime;
				gameType = gameTypes.timed;

				newBlock(true);
				updateGame();

				$time.mBlocksText(timeLeft.toMinutes());

				timer = setInterval(decreaseTime, 999);

				if (mode == modeTypes.playing && typeof options.newGameCallback == "function")
					options.newGameCallback(gameType);
			};

			var startMeteorGame = function()
			{
				mode = modeTypes.playing;
				resetGame();
				gameType = gameTypes.meteor;

				newBlock(true);
				updateGame();

				meteors.push(new Meteor());

				if (mode == modeTypes.playing && typeof options.newGameCallback == "function")
					options.newGameCallback(gameType);
			};

			var togglePause = function(p)
			{
				if (mode == modeTypes.none)
					return;

				pause = (typeof p == "boolean" ? p : !pause);

				if (typeof options.pauseCallback == "function")
					options.pauseCallback(pause);

				if (pause)
				{
					if (mode == modeTypes.demo)
					{
						if (demoTimer)
						{
							clearInterval(demoTimer);
							demoTimer = false;
						}
					}
					else
					{
						if (gameType == gameTypes.timed && timer)
						{
							clearTimeout(timer);
							timer = false;
						}

						$currentBlock.stop(true);
						$rows.stop(true).animate({ opacity: .02 }, 500);

						for (var i = 0; i < meteors.length; i++)
							meteors[i].$.stop(true);
					}
				}
				else
				{
					if (mode == modeTypes.demo)
					{
						if (!demoTimer)
							demoTimer = setInterval(updateDemo, options.demoSpeeds[demoSpeed]);
					}
					else
					{
						if (gameType == gameTypes.timed)
						{
							timer = setInterval(decreaseTime, 999);
							timeLeft--;
						}

						$rows.stop(true).animate({ opacity: 1 }, 500);
						updateGame();

						for (var i = 0; i < meteors.length; i++)
							meteors[i].update();
					}
				}
			};


			var wallKick = function()
			{
				for (var i = 1; i < blockW; i++)
					if (!checkCollision(x + i, y))
					{
						x += i;
						return true;
					}
					else if (!checkCollision(x - i, y))
					{
						x -= i;
						return true;
					}
				return false;
			};

			// handle keypresses
			var handleKeypress = function(e)
			{
				if (mode != modeTypes.playing)
					return true;

				switch (e.which)
				{
					case 80: case 112: case 27: case 19: togglePause(); break; // pause/unpause game
					default: break;
				}

				if (pause)
					return true;

				var prevR = r, prevB = b, prevX = x, prevY = y, key = (e.which) ? e.which : e.keyCode, keyAlt = (key >= 65 && key <= 90 ? key + 32 : (key >= 97 && key <= 122 ? key - 32 : 0));

				moveLeft = moveRight = false;

				if (options.keys.moveLeft && key == options.keys.moveLeft || keyAlt == options.keys.moveLeft)
				{
					if (x > -3 && !checkCollision(x - 1, y))
					{
						if ($currentBlock.is(":animated") && checkCollision(x - 1, y - 1))
							moveLeft = true;
						else
							x--;
					}
					else if (!checkCollision(x - 1, y))
						moveLeft = true;
				}
				else if (options.keys.moveRight && key == options.keys.moveRight || keyAlt == options.keys.moveRight)
				{
					if (x + 1 < w && !checkCollision(x + 1, y))
					{
						if ($currentBlock.is(":animated") && checkCollision(x + 1, y - 1))
							moveRight = true;
						else
							x++;
					}
					else if (!checkCollision(x + 1, y))
						moveRight = true;
				}
				else if (options.keys.moveDown && key == options.keys.moveDown || keyAlt == options.keys.moveDown)
				{
					if (y + 1 < h && !checkCollision(x, y + 1))
					{
						y++;
						demo.addFrame({ operation: Demo.operations.drop });
						$currentBlock.stop(true).animate({ top: (y * brickH) + "px" }, currentSpeed() / 5, "linear", updateGame);
					}
				}
				else if ((options.keys.rotateRight && key == options.keys.rotateRight || keyAlt == options.keys.rotateRight)
				|| (options.keys.rotateRightAlt && key == options.keys.rotateRightAlt || keyAlt == options.keys.rotateRightAlt))
				{
					(r < 3) ? r++ : r = 0;
					if (checkCollision(x, y) && !wallKick())
						r = prevR;
				}
				else if (options.keys.rotateLeft && key == options.keys.rotateLeft || keyAlt == options.keys.rotateLeft)
				{
					(r > 0) ? r-- : r = 3;
					if (checkCollision(x, y) && !wallKick())
						r = prevR;
				}
				else if (options.keys.hardDrop && key == options.keys.hardDrop || keyAlt == options.keys.hardDrop)
				{
					while (!checkCollision(x, y + 1))
						y++;
					if (prevY != y)
					{
						updateScore(scoringType.hardDrop, y - prevY);
						demo.addFrame({ operation: Demo.operations.dropInstant });
						$currentBlock.stop(true).animate({ top: (y * brickH) + "px" }, 100, "swing", updateGame);
					}
				}
				else
					return true;

				if (prevX != x)
				{
					for (var i = 0; i < Math.abs(prevX - x); i++)
						demo.addFrame({ operation: x < prevX ? Demo.operations.moveLeft : Demo.operations.moveRight });
					$currentBlock.css({ left: (x * brickW) + "px" });
				}

				if (prevR != r)
				{
					if (mode == modeTypes.playing)
						demo.addFrame(((r < prevR || (r == 3 && prevR == 0)) && !(r == 0 && prevR == 3)) ?{ operation: Demo.operations.rotateLeft } : { operation: Demo.operations.rotateRight });
					$currentBlockBricks.each(function(i) { this.className = "brick brick-" + (blocks[b][r] & (1 << i) ? b + 1 : 0); });
				}

				if (prevR != r || prevX != x)
					updateGhost();

				return false;
			};

			var handleDemoKeypress = function(o)
			{
				if (mode != modeTypes.demo)
					return;

				var prevR = r, prevB = b, prevX = x, prevY = y;

				switch (o)
				{
					case Demo.operations.moveLeft: if (x > -3) x--; break; // move left
					case Demo.operations.moveRight: if (x + 1 < w) x++; break; // move right
					case Demo.operations.drop:
					case Demo.operations.dropFast: if (y + 1 < h) y++; break; // move down
					case Demo.operations.rotateRight: (r < 3) ? r++ : r = 0; if (demoBugged && checkCollision(x, y) && !wallKick()) r = prevR; break; // rotate clockwise
					case Demo.operations.rotateLeft: (r > 0) ? r-- : r = 3; if (demoBugged && checkCollision(x, y) && !wallKick()) r = prevR; break; // rotate counterclockwise
					case Demo.operations.dropInstant: // hard drop
						while (!checkCollision(x, y + 1))
							y++;
						if (prevY != y)
							updateScore(scoringType.hardDrop, y - prevY);
						break;
					default:
						alert("Wrong operation: " + o);
						break;
				}

				if (!demoFast)
				{
					if (prevX != x)
						$currentBlock.css({ left: (x * brickW) + "px" });
					if (prevY != y)
						$currentBlock.css({ top: (y * brickH) + "px" });
					if (prevR != r)
						$currentBlockBricks.each(function(i) { this.className = "brick brick-" + (blocks[b][r] & (1 << i) ? b + 1 : 0); });
					if (prevR != r || prevX != x)
						updateGhost();
				}
			};

			var updateDemo = function()
			{
				if (mode != modeTypes.demo)
					return;

				if (demoPosition >= demoFrames.length)
				{
					putBlockOnMatrix();
					clearInterval(demoTimer);
					demoTimer = false;
					mode = modeTypes.none;
					return;
				}

				switch (demoFrames[demoPosition])
				{
					case Demo.operations.newBlock:
						if (!demoFirstFrame)
							putBlockOnMatrix();
						newBlock(true, demoFrames[demoPosition + 1] & 7, (demoFrames[demoPosition + 1] >> 3) & 3, demoFrames[demoPosition + 2] & 7, (demoFrames[demoPosition + 2] >> 3) & 3);
						demoPosition += 3;
						demoFirstFrame = false;
						break;
					case Demo.operations.newMeteor:
						var m = { id: demoFrames[demoPosition + 1] & 15, x: (demoFrames[demoPosition + 1] >> 4) & 15, color: demoFrames[demoPosition + 2] & 7 };
						if (m.id < meteors.length)
						{
							meteors[m.id].update();
							meteors[m.id].reset(m);
						}
						else
							meteors.push(new Meteor(m));
						demoPosition += 3;
						break;
					case Demo.operations.dropMeteor:
						meteors[demoFrames[demoPosition + 1] & 15].update();
						demoPosition += 2;
						break;
					default:
						handleDemoKeypress(demoFrames[demoPosition]);
						demoPosition++;
						break;
				}

				if (!demoFast && typeof options.updateDemoCallback == "function")
					options.updateDemoCallback(demoFrames, demoPosition);
			};

			var playDemo = function(frames, position)
			{
				if (typeof options.playDemoCallback == "function")
					options.playDemoCallback();

				if (demoTimer)
					clearInterval(demoTimer);

				demoFrames = typeof frames != "undefined" ? frames : demo.get();
				startStandardGame(modeTypes.demo);

				// skip header data
				demoBugged = false;
				demoPosition = 0;
				var v = "";
				while (demoFrames[demoPosition] != Demo.operations.headerEnd)
				{
					v += demoFrames[demoPosition];
					demoPosition++;
				}

				if (parseInt(v) < version)
					demoBugged = true;

				demoPosition++;
				demoFirstFrame = true;

				// seek to specified position
				if (typeof position == "number")
				{
					demoFast = true;
					var dstFrame = demoFrames.length * position - 1;
					do updateDemo(); while (demoPosition <= dstFrame);
					demoFast = false;
					updateMatrix();
				}

				// play demo
				demoTimer = setInterval(updateDemo, options.demoSpeeds[demoSpeed]);
			};

			var setDemoSpeed = function(speed)
			{
				demoSpeed = speed;
				if (demoTimer)
				{
					clearInterval(demoTimer);
					demoTimer = setInterval(updateDemo, options.demoSpeeds[demoSpeed]);
				}
			};

			var toggleGhost = function(g)
			{
				if (g)
					$ghost.show();
				else
					$ghost.hide();
			};

			/*
			** public functions
			*/
			$.fn.extend
			({
				mBlocks:
				{
					isPlaying: function() { return mode == modeTypes.playing; }
				,	isDemoPlaying: function() { return mode == modeTypes.demo; }
				,	startStandardGame: function() { startStandardGame(); }
				,	startTimedGame: function() { startTimedGame(); }
				,	startMeteorGame: function() { startMeteorGame(); }
				,	togglePause: togglePause
				,	toggleGhost: toggleGhost
				,	isGhostOn: function() { return $ghost.is(":visible"); }
				,	playDemo: function(frames) { playDemo(typeof frames == "undefined" ? demo.get() : frames); }
				,	setDemoPosition: function(position) { position = Math.mid(0, position, 1); playDemo(demoFrames.length ? demoFrames : demo.get(), position); }
				,	setDemoSpeed: function(speed) { setDemoSpeed(Math.mid(0, speed, options.demoSpeeds.length - 1)); }
				,	getScore: function() { return score; }
				,	resetControls: function() { return new clone(options.keys = new clone(defaults.keys)); }
				,	getControls: function() { return new clone(options.keys); }
				,	getDefaultControls: function() { return new clone(defaults.keys); }
				,	setControls: function(controls) { options.keys = new clone(controls); }
				}
			});

			$(this).append("<div class=\"matrix\"><div class=\"current-block\"></div><div class=\"ghost\"></div></div>");
			var $matrix = $(this).find(".matrix").css({ width: (w * brickW) + "px", height: (h * brickH) + "px" });
			var $currentBlock = $matrix.find(".current-block");
			var $ghost = $matrix.find(".ghost");

			$currentBlock.add($ghost).css({ width: (blockW * brickW) + "px", height: (blockH * brickH) + "px", marginRight: (-blockW * brickW) + "px", marginBottom: (-blockH * brickH) + "px" });
			$nextBlock.css({ width: (blockW * brickW) + "px", height: (blockH * brickH) + "px" });

			var i = h;
			do
			{
				var $row = $("<div class=\"row\"1></div>").appendTo($matrix), j = w;
				do $row.append("<div class=\"brick brick-0\"></div>"); while (--j);
			}
			while (i--);

			var $rows = $matrix.find(".row");
			var $bricks = $matrix.find(".brick");
			//$bricks.each(function(i) { $(this).css({ position: "absolute", left: (i % w) + "px", top: (i / h) + "px" }); });

			i = blockW * blockH;
			var $t = $currentBlock.add($ghost).add($nextBlock);
			do $t.append("<div class=\"brick brick-0\"></div>"); while (--i);

			var $currentBlockBricks = $currentBlock.children();
			var $nextBlockBricks = $nextBlock.children();
			var $ghostBricks = $ghost.children();

			$score.mBlocksText("0");
			$lines.mBlocksText("0");
			$level.mBlocksText("1");
			$time.mBlocksText(options.timedModeTime.toMinutes());

			$(document).bind("keypress", handleKeypress);
		});
	}});


	function bindDemoAnchorsClick()
	{
		$(".scores:not(.scores-full) td.demo a").unbind().click(function()
		{
			$.getJSON("mblocks/demo/" + $(this).attr("href").substr($(this).attr("href").lastIndexOf("/")), function(data)
			{
				if (data.status)
					playDemo({ name: data.name, countryCode: data.countryCode, countryName: data.countryName, level: data.level, date: data.date, score: data.score, demo: data.demo });
			});
			return false;
		});
	}

	function gameOverCallback(gameType, level, score, linesPerLevel, blocksCount, reason, demo)
	{
		$message.removeAttr("style");

		if (reason == 1)
		{
			$message[0].className = "message message-times-up";
			$message.stop(true).hide().fadeIn(250);
		}
		else
		{
			$message[0].className = "message message-game-over";
			$message.stop(true).hide().fadeIn(250);
		}

		if (score <= 0)
			return;

		modal.open("window-submit-form");

		$demo.find(".name strong").text(submitScore.getName());
		$demo.find(".country img").attr("src", submitScore.getCountryFile()).attr("alt", submitScore.getCountryCode()).attr("title", submitScore.getCountryName());
		$demo.find(".level strong").text(level);
		$demo.find(".score strong").text(score.separateDigits());

		var send = function()
		{
			$demo.find(".name strong").text(submitScore.getName());

			$demo.find(".country img").attr("src", submitScore.getCountryFile()).attr("alt", submitScore.getCountryCode()).attr("title", submitScore.getCountryName());
			$.post("mblocks/submit", { type: gameType, name: submitScore.getName(), country: submitScore.getCountryCode(), level: level, score: score, "lines[]": linesPerLevel, blocks: blocksCount, demo: demo }, function(data)
			{
				var $w = modal.getWindow("window-submit-result");

				$w.find(data.status ? ".submit-error" : ".submit-success").addClass("hidden");

				if (data.status)
				{
					data.position = parseInt(data.position);
					$w.find(".submit-success em").text(data.position > 0 ? "#" + data.position : "unknown");

					var updateTable = function($tableRows, tableData)
					{
						if (tableData)
							$tableRows.each(function(i)
							{
								if (!tableData[i])
									return;
								$(this).children("td.name").html((i + 1) + ". " + tableData[i].name);
								$(this).find("img:first").attr("alt", tableData[i].countryCode).attr("title", tableData[i].countryName).attr("src", "images/flags/" + tableData[i].countryCode.toLowerCase() + ".gif");
								$(this).children("td").eq(2).text(tableData[i].level);
								$(this).children("td.score").text(tableData[i].score.separateDigits());
								if (tableData[i].demo === false)
									$(this).find("td.demo").empty();
								else
									$(this).find("td.demo").html("<a href=\"mblocks/demo/" + tableData[i].demo + "\"><img src=\"images/mblocks/button_replay.png\" alt=\"\">R</a>");
							});
					}

					updateTable($("#scores-standard tr"), data.standard);
					updateTable($("#scores-timed tr"), data.timed);
					updateTable($("#scores-meteor tr"), data.meteor);
					updateTable($("#scores-recent tr"), data.recent);

					bindDemoAnchorsClick();
				}

				modal.open("window-submit-result");
			}, "json");

			modal.open("window-submit-sending");
			return false;
		};

		submitScore.onSubmit(send);
	}


	function pauseCallback(pause)
	{
		$message.removeAttr("style");

		if (pause)
		{
			$message[0].className = "message message-game-paused";
			$message.stop(true).hide().fadeIn(250);
		}
		else
			$message.stop(true).fadeOut(250);
	}

	function newGameCallback(gameType)
	{
		$status.removeAttr("style");

		$message.stop(true).fadeOut(250);

		if (gameType == 1)
			$time.animate({ opacity: 1, height: "show", paddingTop: "show", paddingBottom: "show" }, 1000);
		else
			$time.animate({ opacity: 0, height: "hide", paddingTop: "hide", paddingBottom: "hide" }, 1000, "swing", function() { if ($.browser.msie && $.browser.version == 7) this.style.removeAttribute("filter"); });

		$demo.animate({ height: "hide", paddingTop: "hide", paddingBottom: "hide" }, 500);

		$game.find(".demo-indicator:first").stopBlinking(false);
	}


	function playDemoCallback()
	{
		$status.removeAttr("style");

		$message.stop(true).fadeOut(250);

		$time.animate({ opacity: 0, height: "hide", paddingTop: "hide", paddingBottom: "hide" }, 1000, "swing", function() { if ($.browser.msie && $.browser.version == 7) this.style.removeAttribute("filter"); });

		$demo.animate({ height: "show", paddingTop: "show", paddingBottom: "show" }, 500);
		$game.find(".demo-indicator:first").startBlinking(250);
	}


	function updateDemoCallback(frames, position)
	{
		if (!demoProgressDragging)
			$demo.find(".progress a").trigger("drag", position / frames.length);
	}



	$game = $(".mblocks>.game:first");
	$game.mBlocks
	({
		gameOverCallback: gameOverCallback
	,	pauseCallback: pauseCallback
	,	newGameCallback: newGameCallback
	,	playDemoCallback: playDemoCallback
	,	updateDemoCallback: updateDemoCallback
	,	$score: ".game-status .score strong"
	,	$time: ".game-status .time strong"
	,	$nextBlock: ".game-status .next-block"
	,	$level: ".game-status .level strong"
	,	$lines: ".game-status .lines strong"
	,	$lines1X: ".game-status .lines-1x strong"
	,	$lines2X: ".game-status .lines-2x strong"
	,	$lines3X: ".game-status .lines-3x strong"
	,	$lines4X: ".game-status .lines-4x strong"
	});

	var modal = new function()
	{
		var
			$modal = $(".modal:first")
		,	$overlay = $modal.children(".overlay:first")
		,	$windows = $modal.children(".window")
		,	modal = this
		;

		this.open = function(window)
		{
			if (typeof window != "undefined")
				$windows.addClass("hidden").filter("." + window).removeClass("hidden");
			$windows.filter(":not(.hidden)").trigger("open");
			$modal.height(540 + ($demo.is(":visible") ? $demo.outerHeight() : 0)).fadeIn(250, function() { if ($.browser.msie && $.browser.version == 7) this.style.removeAttribute("filter"); });
		};

		this.close = function() { $modal.fadeOut(250, function() { $windows.filter(":not(.hidden)").trigger("close"); $windows.addClass("hidden"); }); };

		this.getWindows = function() { return $windows; };
		this.getWindow = function(className) { return $windows.filter("." + className); };
		this.isOpened = function() { return $modal.is(":visible"); };

		$windows.find("p.close>a,a.close").click(function() { modal.close(); return false; });
	};

	var $demo = $("div.demo");
	modal.open("window-welcome");

	var settings = new function()
	{
		var keyNames =
		[
		/*000*/		"None"
		/*001*/,	"0x01"
		/*002*/,	"0x02"
		/*003*/,	"0x03"
		/*004*/,	"0x04"
		/*005*/,	"0x05"
		/*006*/,	"0x06"
		/*007*/,	"0x07"
		/*008*/,	"Backspace"
		/*009*/,	"Tab"
		/*010*/,	"0x0A"
		/*011*/,	"0x0B"
		/*012*/,	"0x0C"
		/*013*/,	"Enter"
		/*014*/,	"0x0E"
		/*015*/,	"0x0F"
		/*016*/,	"Shift"
		/*017*/,	"Ctrl"
		/*018*/,	"Alt"
		/*019*/,	"Pause Break"
		/*020*/,	"Caps Lock"
		/*021*/,	"0x15"
		/*022*/,	"0x16"
		/*023*/,	"0x17"
		/*024*/,	"0x18"
		/*025*/,	"0x19"
		/*026*/,	"0x1A"
		/*027*/,	"Esc"
		/*028*/,	"0x1C"
		/*029*/,	"0x1D"
		/*030*/,	"0x1E"
		/*031*/,	"0x1F"
		/*032*/,	"Space"
		/*033*/,	"Page Up"
		/*034*/,	"Page Down"
		/*035*/,	"End"
		/*036*/,	"Home"
		/*037*/,	"Left Arrow"
		/*038*/,	"Up Arrow"
		/*039*/,	"Right Arrow"
		/*040*/,	"Down Arrow"
		/*041*/,	"0x29"
		/*042*/,	"*"
		/*043*/,	"+"
		/*044*/,	","
		/*045*/,	"Insert"
		/*046*/,	"Delete"
		/*047*/,	"/"
		/*048*/,	"0"
		/*049*/,	"1"
		/*050*/,	"2"
		/*051*/,	"3"
		/*052*/,	"4"
		/*053*/,	"5"
		/*054*/,	"6"
		/*055*/,	"7"
		/*056*/,	"8"
		/*057*/,	"9"
		/*058*/,	"0x3A"
		/*059*/,	";"
		/*060*/,	"0x3C"
		/*061*/,	"="
		/*062*/,	"0x3E"
		/*063*/,	"0x3F"
		/*064*/,	"0x40"
		/*065*/,	"A"
		/*066*/,	"B"
		/*067*/,	"C"
		/*068*/,	"D"
		/*069*/,	"E"
		/*070*/,	"F"
		/*071*/,	"G"
		/*072*/,	"H"
		/*073*/,	"I"
		/*074*/,	"J"
		/*075*/,	"K"
		/*076*/,	"L"
		/*077*/,	"M"
		/*078*/,	"N"
		/*079*/,	"O"
		/*080*/,	"P"
		/*081*/,	"Q"
		/*082*/,	"R"
		/*083*/,	"S"
		/*084*/,	"T"
		/*085*/,	"U"
		/*086*/,	"V"
		/*087*/,	"W"
		/*088*/,	"X"
		/*089*/,	"Y"
		/*090*/,	"Z"
		/*091*/,	"["
		/*092*/,	"\\"
		/*093*/,	"]"
		/*094*/,	"0x5E"
		/*095*/,	"0x5F"
		/*096*/,	"`"
		/*097*/,	"A"
		/*098*/,	"B"
		/*099*/,	"C"
		/*100*/,	"D"
		/*101*/,	"E"
		/*102*/,	"F"
		/*103*/,	"G"
		/*104*/,	"H"
		/*105*/,	"I"
		/*106*/,	"J"
		/*107*/,	"K"
		/*108*/,	"L"
		/*109*/,	"M"
		/*110*/,	"N"
		/*111*/,	"O"
		/*112*/,	"P"
		/*113*/,	"Q"
		/*114*/,	"R"
		/*115*/,	"S"
		/*116*/,	"T"
		/*117*/,	"U"
		/*118*/,	"V"
		/*119*/,	"W"
		/*120*/,	"X"
		/*121*/,	"Y"
		/*122*/,	"Z"
		/*123*/,	"0x7B"
		/*124*/,	"0x7C"
		/*125*/,	"0x7D"
		/*126*/,	"0x7E"
		/*127*/,	"0x7F"
		/*128*/,	"0x80"
		/*129*/,	"0x81"
		/*130*/,	"0x82"
		/*131*/,	"0x83"
		/*132*/,	"0x84"
		/*133*/,	"0x85"
		/*134*/,	"0x86"
		/*135*/,	"0x87"
		/*136*/,	"0x88"
		/*137*/,	"0x89"
		/*138*/,	"0x8A"
		/*139*/,	"0x8B"
		/*140*/,	"0x8C"
		/*141*/,	"0x8D"
		/*142*/,	"0x8E"
		/*143*/,	"0x8F"
		/*144*/,	"Num Lock"
		/*145*/,	"Scroll Lock"
		/*146*/,	"0x92"
		/*147*/,	"0x93"
		/*148*/,	"0x94"
		/*149*/,	"0x95"
		/*150*/,	"0x96"
		/*151*/,	"0x97"
		/*152*/,	"0x98"
		/*153*/,	"0x99"
		/*154*/,	"0x9A"
		/*155*/,	"0x9B"
		/*156*/,	"0x9C"
		/*157*/,	"0x9D"
		/*158*/,	"0x9E"
		/*159*/,	"0x9F"
		/*160*/,	"0xA0"
		/*161*/,	"0xA1"
		/*162*/,	"0xA2"
		/*163*/,	"0xA3"
		/*164*/,	"0xA4"
		/*165*/,	"0xA5"
		/*166*/,	"0xA6"
		/*167*/,	"0xA7"
		/*168*/,	"0xA8"
		/*169*/,	"0xA9"
		/*170*/,	"0xAA"
		/*171*/,	"0xAB"
		/*172*/,	"0xAC"
		/*173*/,	"0xAD"
		/*174*/,	"0xAE"
		/*175*/,	"0xAF"
		/*176*/,	"0xB0"
		/*177*/,	"0xB1"
		/*178*/,	"0xB2"
		/*179*/,	"0xB3"
		/*180*/,	"0xB4"
		/*181*/,	"0xB5"
		/*182*/,	"0xB6"
		/*183*/,	"0xB7"
		/*184*/,	"0xB8"
		/*185*/,	"0xB9"
		/*186*/,	"0xBA"
		/*187*/,	"0xBB"
		/*188*/,	"0xBC"
		/*189*/,	"0xBD"
		/*190*/,	"0xBE"
		/*191*/,	"0xBF"
		/*192*/,	"0xC0"
		/*193*/,	"0xC1"
		/*194*/,	"0xC2"
		/*195*/,	"0xC3"
		/*196*/,	"0xC4"
		/*197*/,	"0xC5"
		/*198*/,	"0xC6"
		/*199*/,	"0xC7"
		/*200*/,	"0xC8"
		/*201*/,	"0xC9"
		/*202*/,	"0xCA"
		/*203*/,	"0xCB"
		/*204*/,	"0xCC"
		/*205*/,	"0xCD"
		/*206*/,	"0xCE"
		/*207*/,	"0xCF"
		/*208*/,	"0xD0"
		/*209*/,	"0xD1"
		/*210*/,	"0xD2"
		/*211*/,	"0xD3"
		/*212*/,	"0xD4"
		/*213*/,	"0xD5"
		/*214*/,	"0xD6"
		/*215*/,	"0xD7"
		/*216*/,	"0xD8"
		/*217*/,	"0xD9"
		/*218*/,	"0xDA"
		/*219*/,	"0xDB"
		/*220*/,	"0xDC"
		/*221*/,	"0xDD"
		/*222*/,	"0xDE"
		/*223*/,	"0xDF"
		/*224*/,	"0xE0"
		/*225*/,	"0xE1"
		/*226*/,	"0xE2"
		/*227*/,	"0xE3"
		/*228*/,	"0xE4"
		/*229*/,	"0xE5"
		/*230*/,	"0xE6"
		/*231*/,	"0xE7"
		/*232*/,	"0xE8"
		/*233*/,	"0xE9"
		/*234*/,	"0xEA"
		/*235*/,	"0xEB"
		/*236*/,	"0xEC"
		/*237*/,	"0xED"
		/*238*/,	"0xEE"
		/*239*/,	"0xEF"
		/*240*/,	"0xF0"
		/*241*/,	"0xF1"
		/*242*/,	"0xF2"
		/*243*/,	"0xF3"
		/*244*/,	"0xF4"
		/*245*/,	"0xF5"
		/*246*/,	"0xF6"
		/*247*/,	"0xF7"
		/*248*/,	"0xF8"
		/*249*/,	"0xF9"
		/*250*/,	"0xFA"
		/*251*/,	"0xFB"
		/*252*/,	"0xFC"
		/*253*/,	"0xFD"
		/*254*/,	"0xFE"
		/*255*/,	"0xFF"
		]
		,	$this = modal.getWindow("window-preferences")
		,	idToKey = { "move-left": "moveLeft", "move-right": "moveRight", "move-down": "moveDown", "rotate-right": "rotateRight", "rotate-right-2": "rotateRightAlt", "rotate-left": "rotateLeft", "hard-drop": "hardDrop" }
		,	controls
		;

		var resetKeys = function()
		{
			$this.find("li").each(function() { $(this).removeClass("selected").children("em").text(keyNames[controls[idToKey[$(this).attr("id")]]]).stopBlinking(true); });
			$(document).unbind("keypress", handleKeypress);
		};

		var handleKeypress = function(e)
		{
			var key = e.which ? e.which : e.keyCode;
			switch (key)
			{
				case 80: case 112: case 19: case 49: case 50: case 51:
					break;
				case 27:
					resetKeys();
					break;
				default:
					var keyAlt = (key >= 65 && key <= 90 ? key + 32 : (key >= 97 && key <= 122 ? key - 32 : 0));
					for (c in controls)
						if (controls[c] == key || controls[c] == keyAlt)
							controls[c] = 0;

					controls[idToKey[$this.find("li.selected:first").attr("id")]] = key;
					resetKeys();
					break;
			}
			return false;
		};

		$this
			.bind("open", function()
			{
				controls = $game.mBlocks.getControls();
				resetKeys();
			})
			.bind("close", function()
			{
				resetKeys();
				$this.find("#settings-images").attr("checked", $(document.body).hasClass("no-images"))
				$this.find("#settings-ghost").attr("checked", $game.mBlocks.isGhostOn())
				$game.mBlocks.togglePause(false);
			});

		$(document).click(function(e) { if ($(this).find("ul *").index(e.target) < 0) resetKeys(); });

		$this.find("li").click(function()
		{
			resetKeys();
			$(this).addClass("selected").children("em").text("Press any key").startBlinking(250);
			$(document).bind("keypress", handleKeypress);
		});

		$this.find("a.button.submit").click(function()
		{
			var noImages = $this.find("#settings-images").attr("checked");
			if (noImages)
				$(document.body).addClass("no-images");
			else
				$(document.body).removeClass("no-images");

			var ghost = $this.find("#settings-ghost").attr("checked");
			$game.mBlocks.toggleGhost(ghost);
			$game.mBlocks.setControls(controls);

			createCookie("mBlocks.noImages", noImages ? 1 : 0, 31);
			createCookie("mBlocks.ghost", ghost ? 1 : 0, 31);
			for (c in controls)
				createCookie("mBlocks." + c, controls[c], 31);

			modal.close();
			return false;
		});

		this.readFromCookie = function()
		{
			var noImages = readCookie("mBlocks.noImages");
			noImages = noImages === null ? false : !!parseInt(noImages);
			$this.find("#settings-images").attr("checked", noImages);
			if (noImages)
				$(document.body).addClass("no-images");

			var ghost = readCookie("mBlocks.ghost");
			ghost = ghost === null ? true : !!parseInt(ghost);
			$this.find("#settings-ghost").attr("checked", ghost);
			$game.mBlocks.toggleGhost(ghost);

			controls = $game.mBlocks.getControls();
			for (c in controls)
			{
				var k = readCookie("mBlocks." + c);
				if (k !== null)
					controls[c] = parseInt(k);
			}
			$game.mBlocks.setControls(controls);
		};

		$this.find("a.button.reset").click(function()
		{
			controls = $game.mBlocks.resetControls();
			resetKeys();
			$this.find("#settings-images").attr("checked", false);
			$this.find("#settings-ghost").attr("checked", true);
			return false;
		});
	};
	settings.readFromCookie();

	var submitScore = new function()
	{
		var
			$this = modal.getWindow("window-submit-form")
		,	$name = $this.find("input#submit-score-name")
		,	$country = $this.find("ul.country:first")
		,	countryCode = ""
		,	countryFile = ""
		,	countryName = ""
		;

		// close overlay and form
		$this
			.bind("close", function()
			{
				$this.unbind("submit");
				$this.find("a.button.submit").unbind("click");
				$game.mBlocks.playDemo();
			})
			.bind("open", function()
			{
				$this.find("h2:first>strong").text($game.mBlocks.getScore().separateDigits());
				Cufon.replace(".window-submit-form:first h2:first");
				$name.focus();
			})
		;

		// show country list
		$this.find("input.country").focus(function() { $country.show(); });

		// country list has lost focus, so hide it
		$name.focus(function() { $country.hide(); }).val(readCookie("mBlocks.name"));
		$(document).bind("click", function(event)
		{
			if ($this.find("input.country,ul.country,ul.country *").index(event.target) < 0)
				$country.hide();
		});

		// user has selected a country
		$country.find("a").click(function()
		{
			$country.hide().find("a.current").removeClass("current");
			$(this).addClass("current");
			var $img = $(this).children("img");
			countryFile = $img.attr("src");
			countryCode = $img.attr("alt");
			countryName = $(this).text();
			$this.find("input.country").val(countryName).css("background-image", "url(\"" + countryFile + "\")");
			return false;
		}).filter(".current").click();

		this.onSubmit = function(callback)
		{
			createCookie("mBlocks.name", $name.val());
			$this.find("form:first").unbind("submit").submit(callback);
			$this.find("a.button.submit:first").unbind("click").click(callback);
		};

		this.getCountryName = function() { return countryName; };
		this.getCountryCode = function() { return countryCode; };
		this.getCountryFile = function() { return countryFile; };
		this.getName = function() { return $name.val().length ? $name.val() : "Anonymous"; };
	};

	modal.getWindow("window-submit-sending").add(modal.getWindow("window-submit-result")).bind("close", function()
	{
		$game.mBlocks.playDemo();
	});


	var $message = $(".mblocks>.message:first").hide();
	var $time = $(".mblocks>.game-status>p.time:first").hide();
	var $status = $(".mblocks>.game-status").css(($.browser.msie ? ({ filter: "alpha(opacity=15)" }) : ({ opacity: ".15" })));


	$game.click(function() { if (!$game.mBlocks.isPlaying() && !$game.mBlocks.isDemoPlaying()) $game.mBlocks.startStandardGame(); });

	if ($game.length)
		$(document).keypress(function(e)
		{
			if (modal.isOpened())
				return true;

			if (e.which == 49) // 1
				$game.mBlocks.startStandardGame();
			else if (e.which == 50) // 2
				$game.mBlocks.startTimedGame();
			else if (e.which == 51) // 3
				$game.mBlocks.startMeteorGame();
			else
				return true;
			return false;
		});


	function playDemo(game)
	{
		$demo.find(".name strong").html(game.name);
		$demo.find(".country img").attr("src", "images/flags/" + game.countryCode.toLowerCase() + ".gif").attr("alt", game.countryCode).attr("title", game.countryName);
		$demo.find(".level strong").text(game.level);
		$demo.find(".date strong").text(game.date);
		$demo.find(".score strong").text(game.score.separateDigits());
		$game.mBlocks.playDemo(game.demo);
		modal.close();
	}


	$demo.find(".speed a").click(function()
	{
		if ($(this).hasClass("selected"))
			return false;
		$(this).addClass("selected").parent().siblings().find("a").removeClass("selected");
		$game.mBlocks.setDemoSpeed(parseInt($(this).text()) - 1);
		return false;
	}).eq(1).click();

	var demoProgressDragging = false;

	$demo.find(".progress a").click(function() { return false; })
		.bind("mousedown", function() { $game.mBlocks.togglePause(true); demoProgressDragging = true; })
		.bind("mouseup", function() { demoProgressDragging = false; $game.mBlocks.togglePause(false); })
		.bind("dragend", function() { $game.mBlocks.setDemoPosition(parseInt($(this).text()) / 100); demoProgressDragging = false; })
		.bind("drag", function(event, position)
			{
				var pw = $(this).parent().width(), tw = $(this).outerWidth(), pos = $(this).parent().offset(), x = typeof position == "number" ? position * (pw - tw) : Math.mid(0, event.offsetX - pos.left, pw - tw);
				$(this).css("left", x + "px").text((x / (pw - tw) * 100).toFixed(1) + "%");
			})
	;


	$("a.standard").click(function() { $game.mBlocks.startStandardGame(); return false; });
	$("a.timed").click(function() { $game.mBlocks.startTimedGame(); return false; });
	$("a.meteor").click(function() { $game.mBlocks.startMeteorGame(); return false; });

	$("a.preferences").click(function() { $game.mBlocks.togglePause(true); modal.open("window-preferences"); return false; });
	$("a.pause").click(function() { $game.mBlocks.togglePause(); return false; });


	var scoresTimer = false;
	if (!$(".scores-full").length)
		scoresTimer = setInterval(function() { var $n = $(".scores:not(.scores-full) .tables a.selected").parent().next().find("a"); if (!$n.length) $n = $(".scores:not(.scores-full) .tables a:first"); $n.trigger("click", true); }, 20000);

	$(".scores:not(.scores-full) .tables a:first").trigger("click", true);
	bindDemoAnchorsClick();
}


// fixes for IE
if ($.browser.msie)
{
	$("a:not([href])").attr("href", "#");
	if ($.browser.version < 7)
		$(".modal .window-settings li").bind("mouseenter mouseleave", function() { $(this).toggleClass("hover"); });
}


scriptsLoaded = true;
loaded();
