Commits

Martin Tournoij committed 8555a58

Import

Comments (0)

Files changed (10)

+.hiawatha
+.sass-cache

graphics/classic.png

Added
New image

graphics/cybermen.png

Added
New image

graphics/dalek.png

Added
New image

graphics/sprites.xcf

Binary file added.
+###
+Robots!
+Copyright © 2012 Martin Tournoij
+http://arp242.net/robots/
+###
+
+
+# TODO Many of these should be user-definable settings
+_boxsize = 14
+_gridsizex = 59
+_gridsizey = 22
+
+# Various globals for convencience
+_gridheight = _gridsizey * _boxsize
+_gridwidth = _gridsizex * _boxsize
+_grid = document.getElementById 'grid'
+_gridcon = _grid.getContext '2d'
+_playerpos = [0, 0]
+_junk = []
+_robots = []
+_level = 0
+_numrobots = 10
+_maxlevels = 4
+_waiting = false
+_keybinds = null
+_spritesize = 14
+
+###
+Load options from localStorage or set defaults
+###
+LoadOptions = ->
+	window._showgrid = if localStorage.getItem('showgrid') == 'true' then true else false
+	window._hardcore = if localStorage.getItem('hardcore') == 'true' then true else false
+	window._autoteleport = if localStorage.getItem('autoteleport') == 'true' then true else false
+	window._graphics = localStorage.getItem 'graphics'
+	if !window._graphics then window._graphics = (parseInt(Math.random() * 3) + 1) + ''
+
+	window._sprite = new Image
+	window.loaded = false
+	window._sprite.onload = -> window.loaded = true
+
+	if window._graphics == '1'
+		window._sprite.src = 'graphics/classic.png'
+	else if window._graphics == '2'
+		window._sprite.src = 'graphics/dalek.png'
+	else if window._graphics == '3'
+		window._sprite.src = 'graphics/cybermen.png'
+
+	document.getElementById('graphics').selectedIndex = parseInt(window._graphics, 10) - 1
+	document.getElementById('showgrid').checked = window._showgrid
+	document.getElementById('autoteleport').checked = window._autoteleport
+	document.getElementById('hardcore').checked = window._hardcore
+
+	if localStorage.getItem('keybinds') == '1'
+		document.getElementById('keybinds0').style.display = 'none'
+		document.getElementById('keybinds1').style.display = 'block'
+		document.getElementById('keyset').selectedIndex = 1
+
+		window._keybinds = [
+			[121, -> MovePlayer ['up', 'left']], # y
+			[107, -> MovePlayer ['up']], # k
+			[117, -> MovePlayer ['up', 'right']], # u
+			[104, -> MovePlayer ['left']], # h
+			[108, -> MovePlayer ['right']], # l
+			[98,  -> MovePlayer ['down', 'left']], # b
+			[106, -> MovePlayer ['down']], # j
+			[110, -> MovePlayer ['down', 'right']], # n
+
+			[46, -> MovePlayer []], # .
+			[119, -> do Wait], # w
+			[116, -> do Teleport], # t
+		]
+	else
+		document.getElementById('keybinds0').style.display = 'block'
+		document.getElementById('keybinds1').style.display = 'none'
+		document.getElementById('keyset').selectedIndex = 0
+
+		window._keybinds = [
+			[55, -> MovePlayer ['up', 'left']], # 7
+			[56, -> MovePlayer ['up']], # 8
+			[57, -> MovePlayer ['up', 'right']], # 9
+			[52, -> MovePlayer ['left']], # 4
+			[54, -> MovePlayer ['right']], # 6
+			[49,  -> MovePlayer ['down', 'left']], # 1
+			[50, -> MovePlayer ['down']], # 2
+			[51, -> MovePlayer ['down', 'right']], # 3
+
+			[53, -> MovePlayer []], # 5
+			[119, -> do Wait], # w
+			[116, -> do Teleport], # t
+		]
+
+###
+Draw an empty grid aka playfield
+###
+DrawGrid = ->
+	_gridcon.fillStyle = '#fff'
+	_gridcon.fillRect 0, 0, _gridwidth, _gridheight
+
+	if _showgrid
+		for col in [0.._gridsizex * _boxsize] by _boxsize
+			_gridcon.moveTo col + 0.5, 0
+			_gridcon.lineTo col + 0.5, _gridheight
+
+		for row in [0.._gridsizey * _boxsize] by _boxsize
+			_gridcon.moveTo 0, row + 0.5
+			_gridcon.lineTo _gridwidth, row + 0.5
+
+		_gridcon.strokeStyle = '#b7b7b7'
+		_gridcon.lineWidth = 1
+		do _gridcon.stroke
+
+###
+Draw a bunch of robots at a random locations
+###
+InitRobots = ->
+	for i in [1.._numrobots]
+		while true
+			x = GetRandomCoord 'x'
+			y = GetRandomCoord 'y'
+
+			if not RobotAtPosition(x, y) and (x != _playerpos[0] and y != _playerpos[1])
+				break
+
+		DrawRobot null, x, y
+
+DrawSprite = (num, x, y) ->
+	_gridcon.drawImage _sprite, _spritesize * num, 0, _spritesize, _spritesize,
+		x * _boxsize, y * _boxsize, _boxsize, _boxsize
+
+###
+Draw a robot
+###
+DrawRobot = (num, x, y) ->
+	if _robots[num] == null
+		return
+	else if num == null
+		num = _robots.length
+		_robots.push [x, y]
+	else
+		ClearGrid _robots[num][0], _robots[num][1]
+
+	DrawSprite 1, x, y
+
+	#_gridcon.font = "bold 8px sans-serif";
+	#_gridcon.fillStyle = '#000'
+	#_gridcon.fillText num, x * _boxsize + 4, y * _boxsize + 12
+
+	_robots[num] = [x, y]
+
+###
+Two robots collided. BBBOOOOOMMM!!
+###
+DestroyRobots = (x, y) ->
+	ClearGrid x, y
+	DrawJunk x, y
+	_junk.push [x, y]
+
+	i = 0
+	for r in _robots
+		if r and r[0] == x and r[1] == y
+			_robots[i] = null
+			_numrobots -= 1
+			do UpdateScore
+		i += 1
+
+DrawJunk = (x, y) ->
+	DrawSprite 2, x, y
+
+###
+Move robots around
+###
+MoveRobots = ->
+	i = 0
+	for r, i in _robots
+		if r == null
+			continue
+
+		x = r[0]
+		y = r[1]
+
+		if _playerpos[0] > x
+			x += 1
+		else if _playerpos[0] < x
+			x -= 1
+
+		if _playerpos[1] > y
+			y += 1
+		else if _playerpos[1] < y
+			y -= 1
+
+		if RobotAtPosition _playerpos[0], _playerpos[1]
+			do Die
+			return
+		else if JunkAtPosition x, y
+			ClearGrid _robots[i][0], _robots[i][1]
+			_robots[i] = [x, y]
+			DestroyRobots x, y
+		else
+			DrawRobot i, x, y
+
+	# Check for collisions
+	for r, i in _robots
+		if r == null
+			continue
+
+		c = RobotAtPosition r[0], r[1], true
+		if c != false and c != i
+			DestroyRobots r[0], r[1]
+
+###
+Draw our handsome protagonist
+###
+DrawPlayer = (x, y) ->
+	ClearGrid _playerpos[0], _playerpos[1]
+	DrawSprite 0, x, y
+	_playerpos = [x, y]
+
+###
+Get random coordinates
+TODO: How random is Math.random()?
+###
+GetRandomCoord = (axis) ->
+	axis = if axis == 'x' then _gridsizex else _gridsizey
+
+	parseInt(Math.random() * (axis - 1) + 1, 10)
+
+###
+Set position of player or robot inside the grid
+###
+SetPosition = (obj, x, y) ->
+	obj.style.left = x + 'px'
+	obj.style.top = y + 'px'
+
+###
+Deal with keyboard events
+###
+HandleKeyboard = (event) ->
+	if event.ctrlKey or event.altKey
+		return
+
+	code = event.keyCode || event.charCode
+
+	# Escape key
+	if code == 27
+		do CloseAllWindows
+		return
+
+	for [keyCode, action] in _keybinds
+		if keyCode == code
+			do event.preventDefault
+			do action
+
+###
+Deal with mouse events
+###
+HandleMouse = (event) ->
+	if event.target.id == 'options'
+		ShowWindow 'options'
+	else if event.target.id == 'help'
+		ShowWindow 'help'
+	else if event.target.id == 'about'
+		ShowWindow 'about'
+	else if event.target.id == 'close'
+		do CloseAllWindows
+	else if event.target.id == 'save'
+		localStorage.setItem 'keybinds', document.getElementById('keyset').selectedIndex
+		localStorage.setItem 'graphics', document.getElementById('graphics').selectedIndex + 1
+		localStorage.setItem 'showgrid', document.getElementById('showgrid').checked
+		localStorage.setItem 'autoteleport', document.getElementById('autoteleport').checked
+		localStorage.setItem 'hardcore', document.getElementById('hardcore').checked
+		do LoadOptions
+		do CloseAllWindows
+		do DrawGrid
+
+		# Why doesn't Javascript have sleep() ? :-(
+		sleep = setInterval(->
+			if window.loaded
+				clearInterval sleep
+				DrawPlayer _playerpos[0], _playerpos[1]
+
+				for r, i in _robots
+					if r != null then DrawRobot i, r[0], r[1]
+
+				for j, i in _junk
+					DrawJunk i, j[0], j[1]
+		, 100)
+
+
+###
+Close all windows
+###
+CloseAllWindows = ->
+	l = document.getElementById 'layover'
+	if l then l.parentNode.removeChild l
+	for win in document.getElementsByClassName 'window'
+		win.style.display = 'none'
+
+###
+Show a window
+###
+ShowWindow = (name) ->
+	div = document.createElement 'div'
+	div.id = 'layover'
+	document.body.appendChild div
+
+	document.getElementById(name + 'window').style.display = 'block'
+
+###
+Our bold player decided to wait ... Let's see if that decision was wise ... or fatal!
+###
+Wait = ->
+	_waiting = true
+
+	while true
+		do MoveRobots
+
+		if RobotAtPosition _playerpos[0], _playerpos[1]
+			return
+
+		if _numrobots == 0
+			do NextLevel
+			break
+
+	_waiting = false
+
+###
+Teleport to a new location ... or to death!
+###
+Teleport = ->
+	x = GetRandomCoord 'x'
+	y = GetRandomCoord 'y'
+
+	if RobotAtPosition x, y or JunkAtPosition x, y
+		do Die
+		return
+
+	DrawPlayer x, y
+	do MoveRobots
+
+### Move the player around
+###
+MovePlayer = (dir) ->
+	x = _playerpos[0]
+	y = _playerpos[1]
+
+	if 'left' in dir
+		x -= 1
+	else if 'right' in dir
+		x += 1
+
+	if 'up' in dir
+		y -= 1
+	else if 'down' in dir
+		y += 1
+
+	if x < 0 or x > _gridsizex - 1 then return false
+	if y < 0 or y > _gridsizey - 1 then return false
+
+	dangerous = false
+	for i in [-1..1]
+		for j in [-1..1]
+			if x + i < 0 or x + i > _gridsizex - 1 then continue
+			if y + j < 0 or y + j > _gridsizey - 1 then continue
+			if RobotAtPosition x + i, y + j then dangerous = true
+
+	if not _hardcore and dangerous then return false
+
+	if JunkAtPosition x, y then return false
+	DrawPlayer x, y
+	do MoveRobots
+
+	if _numrobots <= 0
+		do NextLevel
+
+	if not _hardcore and _autoteleport and not MovePossible() then do Teleport
+
+###
+Check if there is a possible move left
+###
+MovePossible = ->
+	for x1 in [-1..1]
+		for y1 in [-1..1]
+			dangerous = false
+			if _playerpos[0] + x1 < 0 or _playerpos[0] + x1 > _gridsizex - 1
+				continue
+			if _playerpos[1] + y1 < 0 or _playerpos[1] + y1 > _gridsizey - 1
+				continue
+
+			for x2 in [-1..1]
+				for y2 in [-1..1]
+					if RobotAtPosition _playerpos[0] + x1 + x2, _playerpos[1] + y1 + y2
+						dangerous = true
+
+			if not dangerous
+				return true
+
+	return false
+
+###
+Check of there if a robot at the position
+###
+RobotAtPosition = (x, y, retnum) ->
+	for r, i in _robots
+		if r and r[0] == x and r[1] == y
+			return if retnum then i else true
+
+	return false
+
+###
+Check if there is "junk" at this position
+###
+JunkAtPosition = (x, y) ->
+	for j in _junk
+		if j[0] == x and j[1] == y then return true
+
+	return false
+
+###
+Clear (blank) this grid positon
+TODO: Redraw grid lines if grid is enabled
+###
+ClearGrid = (x, y) ->
+	_gridcon.fillStyle = '#fff'
+
+	_gridcon.fillRect(
+		x * _boxsize,
+		y * _boxsize,
+		_boxsize, _boxsize
+	)
+
+###
+Oh noes! Our brave hero has died! :-(
+###
+Die = ->
+	ClearGrid _playerpos[0], _playerpos[1]
+	DrawSprite 3, _playerpos[0], _playerpos[1]
+
+	curscore = parseInt document.getElementById('score').innerHTML, 10
+	scores = localStorage.getItem 'scores'
+	scores = JSON.parse scores
+	if not scores
+		scores = []
+
+	d = new Date
+	d = do d.toLocaleDateString
+	scores.push [curscore, d, true]
+	scores.sort (a, b) ->
+		if a[0] > b[0] then return -1
+		if a[0] < b[0] then return 1
+		return 0
+
+	scores = scores.slice 0, 5
+
+	restart = document.createElement 'div'
+	restart.id = 'restart'
+	restart.innerHTML = 'AARRrrgghhhh....<br><br>' +
+		'Your highscores:<br>'
+
+	for s in scores
+		restart.innerHTML += '<span class="row' + (if s[2] then ' cur' else '') + '">' +
+			'<span class="score">' + s[0] + '</span>' + s[1] + '</span>'
+
+	restart.innerHTML += '<br>Press any key to try again.'
+	document.body.appendChild restart
+
+	scores = scores.map (s) ->
+		s[2] = false
+		return s
+	
+	localStorage.setItem 'scores', JSON.stringify scores
+
+	document.body.removeEventListener 'keypress', HandleKeyboard, false
+
+	window.addEventListener 'keypress', ((e) ->
+		if e.ctrlKey or e.altKey
+			return
+
+		do e.preventDefault
+		do window.location.reload),
+		false
+
+###
+Woohoo! A robot is no more, so lets update the score.
+###
+UpdateScore = ->
+	score = parseInt(document.getElementById('score').innerHTML, 10)
+	score += 10
+	if _waiting then score += 1
+
+	document.getElementById('score').innerHTML = score
+
+###
+Keep IE happy (also shorter to type!)
+###
+log = (msg) ->
+	if console and console.log
+		console.log msg
+
+###
+Advance to the next level
+###
+NextLevel = ->
+	_level += 1
+	_numrobots = 10 + _level * 10
+	_waiting = false
+	_junk = []
+
+	do DrawGrid
+	DrawPlayer GetRandomCoord('x'), GetRandomCoord('y')
+	do InitRobots
+
+
+
+CheckBrowser = ->
+	old = false
+
+	# Opera
+	if window.opera
+		if parseFloat(do window.opera.version) < 11.60
+			old = true
+	# Chrome
+	else if window.chrome
+		if parseInt(navigator.appVersion.match(/Chrome\/(\d+)/)[1], 10) < 18
+			old = true
+	# Safari
+	else if navigator.vendor and navigator.vendor.match(/[aA]pple/)
+		if parseFloat(navigator.appVersion.match(/Version\/(\d+\.\d+)/)[1]) < 5
+			old = true
+	# Firefox
+	else if navigator.userAgent.match /Firefox\/\d+/
+		if parseFloat(navigator.userAgent.match(/Firefox\/([\d\.]+)/)[1]) < 10
+			old = true
+	# IE
+	else if  navigator.appName == 'Microsoft Internet Explorer'
+		if parseInt(navigator.appVersion.match(/MSIE (\d+)/)[1], 10) < 9
+			old = true
+
+	if old
+		div = document.createElement 'div'
+		div.id = 'oldbrowser'
+		div.innerHTML = 'Robots requires a fairly new browser with support for canvas, JSON, localStorage, etc.<br>' +
+			'Almost all modern browsers support this, but a few may not (IE8, for example, does not).<br>' +
+			'Tested versions are Opera 12, Firefox 14, Chrome 20, Internet Explorer 9'
+
+		document.body.insertBefore div, _grid
+
+###
+Start the game!
+###
+InitGame = ->
+	_numrobots = 10
+
+	do LoadOptions
+
+	# Why doesn't Javascript have sleep() ? :-(
+	sleep = setInterval(->
+		if window.loaded
+			clearInterval sleep
+			do InitGame2
+	, 100)
+
+InitGame2 = ->
+	do DrawGrid
+	DrawPlayer GetRandomCoord('x'), GetRandomCoord('y')
+	do InitRobots
+	window.addEventListener 'keypress', HandleKeyboard, false
+	window.addEventListener 'click', HandleMouse, false
+
+do CheckBrowser
+do InitGame
+html {
+  margin: 0;
+  padding: 0;
+  font-family: monospace;
+  font-size: 14px; }
+
+body {
+  margin: 0;
+  padding: 10px; }
+
+p {
+  text-align: justify; }
+
+a, a:visited {
+  display: inline-block;
+  background-color: #eeeeee;
+  color: black;
+  text-decoration: none;
+  border: 1px solid #666666;
+  padding: 0.2em 0.3em;
+  margin-bottom: 0.2em; }
+  a:hover, a:visited:hover {
+    background-color: #dddddd; }
+
+#aboutwindow p > a {
+  background-color: transparent;
+  text-decoration: underline;
+  border: none;
+  padding: 0; }
+
+label {
+  display: inline-block;
+  width: 10em; }
+
+input, select {
+  border: 1px solid #b7b7b7;
+  padding: 0.3em;
+  margin-bottom: 0.3em; }
+
+input.small {
+  width: 2em; }
+
+#grid {
+  z-index: 1;
+  border: 2px solid #666666; }
+
+#restart {
+  position: absolute;
+  left: 20px;
+  top: 20px;
+  border: 1px solid #666666;
+  background-color: white;
+  padding: 0.5em; }
+  #restart .score {
+    display: inline-block;
+    width: 5em; }
+  #restart .row {
+    display: block; }
+  #restart .cur {
+    font-weight: bold; }
+
+#menu .box {
+  float: left;
+  width: 160px; }
+#menu #keybinds1 {
+  display: none; }
+
+.window {
+  z-index: 20;
+  display: none;
+  position: absolute;
+  top: 40px;
+  left: 110px;
+  width: 626px;
+  background-color: white;
+  border: 1px solid #666666; }
+  .window p {
+    padding: 0.5em 1em;
+    margin: 0; }
+  .window .toolbar {
+    margin: 0;
+    padding: 0.3em;
+    border-top: 1px solid #b7b7b7;
+    background-color: #aaaaaa; }
+  .window .help {
+    display: block;
+    margin-left: 12.5em;
+    margin-bottom: 1em;
+    font-size: 0.9em;
+    font-style: italic; }
+
+#layover {
+  z-index: 10;
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background-color: black;
+  opacity: 0.5; }
+
+#oldbrowser {
+  padding: 0.5em;
+  border: 1px solid #666666;
+  background-color: #eeeeee;
+  margin-bottom: 1em;
+  width: 814px; }
+<!DOCTYPE html>
+<!--
+Robots!
+Copyright © 2012 Martin Tournoij
+http://arp242.net/robots/
+-->
+<html>
+	<head>
+		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+		<title>Robots!</title>
+		<link type="text/css" rel="stylesheet" href="robots.css">
+	</head>
+
+	<body>
+		<canvas id="grid" width="826" height="308"></canvas>
+
+		<div id="menu">
+			<div class="box">
+				Score: <span id="score">0</span><br>
+				<a id="options" href="#">Options</a><br>
+				<a id="help" href="#">Help</a><br>
+				<a id="about" href="#">About</a>
+			</div>
+
+			<div class="box" id="keybinds0">
+				Keys:<br>
+				7 8 9<br>
+				&nbsp;\|/<br>
+				4- -6<br>
+				&nbsp;/|\<br>
+				1 2 3<br>
+				<br>
+				5: Do nothing<br>
+				w: Wait for end<br>
+				t: Teleport
+			</div>
+
+			<div class="box" id="keybinds1">
+				Keys:<br>
+				y k u<br>
+				&nbsp;\|/<br>
+				h- -l<br>
+				&nbsp;/|\<br>
+				b j n<br>
+				<br>
+				.: Do nothing<br>
+				w: Wait for end<br>
+				t: Teleport
+			</div>
+
+			<div class="box">
+				Legend:<br>
+				+: robot<br>
+				*: junk heap<br>
+				@: you
+			</div>
+		</div>
+
+		<div id="helpwindow" class="window">
+				<p>
+					The robots game pits you against evil robots, who are trying to kill you
+					(which is why they are evil). Fortunately for you, even though they are
+					evil, they are not very bright and have a habit of bumping into each
+					other, thus destroying themselves. In order to survive, you must get
+					them to kill each other off, since you have no offensive weaponry.
+				</p>
+
+				<p>
+					Since you are stuck without offensive weaponry, you are endowed with one
+					piece of defensive weaponry: a teleportation device.  When two robots run
+					into each other or a junk pile, they die.  If a robot runs into you, you
+					die.  When a robot dies, you get 10 points, and when all the robots die,
+					you start on the next field.  This keeps up until they finally get you.
+				</p>
+
+				<p>
+					Robots are represented on the screen by a ‘+’, the junk heaps from their
+					collisions by a ‘*’, and you (the good guy) by a ‘@’.
+				</p>
+				<p>
+					You can move around with the numpad. You can also choose the classic hjkl keys (if you
+					don’t know what they are, you don’t want them).<br>
+					Unlike the original robots, you can *not* precede the commands with a count or “run” with
+					shift.
+				</p>
+
+				<p>
+					If you use the ‘w’ command and survive to the next level, you will get a
+					bonus of 10% for each robot which died after you decided to wait. If you
+					die, however, you get nothing.  For all other commands, the program will
+					save you from typos by stopping short of being eaten. However, with ‘w’
+					you take the risk of dying by miscalculation.
+				</p>
+
+
+			<div class="toolbar">
+				<a href="#" id="close">Close</a>
+			</div>
+		</div>
+
+		<div id="optionswindow" class="window">
+			<p>
+				<label for="keyset">Keyset</label>
+				<select id="keyset">
+					<option>Keyset 1 (Numpad)</option>
+					<option>Keyset 2 (Classic hjkl keys)</option>
+				</select><br>
+
+				<label for="graphics">Graphics</label>
+				<select id="graphics">
+					<!--<option name="0">Random for each level</option>-->
+					<option name="1">Classic "ASCII Art"</option>
+					<option name="2">Daleks</option>
+					<option name="3">Cybermen</option>
+				</select><br>
+
+				<label for="showgrid">Show grid</label>
+				<input type="checkbox" id="showgrid"><br>
+
+				<label for="autoteleport">Auto-teleport</label>
+				<input type="checkbox" id="autoteleport"><br>
+				<span class="help">Automatically teleport if there is no other option remaining.</span>
+
+				<label for="hardcore">Hardcode mode</label>
+				<input type="checkbox" id="hardcore"><br>
+				<span class="help">With this option enabled, the game will *not* prevent you from making a move that would mean
+					certain death. This significantly increases the difficulty.</span>
+
+				<!--
+				<label for="gridsizex">Grid size X</label>
+				<input type="text" id="gridsizex" class="small" value="59"><br>
+				
+				<label for="gridsizey">Grid size Y</label>
+				<input type="text" id="gridsizey" class="small" value="59">
+				-->
+			</p>
+
+			<div class="toolbar">
+				<a href="#" id="save">Save</a>
+				<a href="#" id="close">Close</a>
+			</div>
+		</div>
+
+		<div id="aboutwindow" class="window">
+			<p>
+				Robots is faithful reproduction of the 1980
+				<a href="http://en.wikipedia.org/wiki/Robots_(computer_game)">“classic” robots game for BSD UNIX</a>.
+			</p>
+
+			<p>
+				Copyright © 2012 Martin Tournoij &lt;<a href="mailto:martin@arp242.net">martin@arp242.net</a>&gt;<br>
+				Robots is free software licenced under the terms of the
+				<a href="http://opensource.org/licenses/MIT">MIT license</a>.<br>
+				<a href="https://bitbucket.org/Carpetsmoker/robots">Source code</a><br>
+				<br>
+				The original robots was created by Ken Arnold.
+			</p>
+
+			<div class="toolbar">
+				<a href="#" id="close">Close</a>
+			</div>
+		</div>
+
+		<script src="robots.js"></script>
+	</body>
+</html>
+// Generated by CoffeeScript 1.3.1
+/*
+Robots!
+Copyright © 2012 Martin Tournoij
+http://arp242.net/robots/
+*/
+
+var CheckBrowser, ClearGrid, CloseAllWindows, DestroyRobots, Die, DrawGrid, DrawJunk, DrawPlayer, DrawRobot, DrawSprite, GetRandomCoord, HandleKeyboard, HandleMouse, InitGame, InitGame2, InitRobots, JunkAtPosition, LoadOptions, MovePlayer, MovePossible, MoveRobots, NextLevel, RobotAtPosition, SetPosition, ShowWindow, Teleport, UpdateScore, Wait, log, _boxsize, _grid, _gridcon, _gridheight, _gridsizex, _gridsizey, _gridwidth, _junk, _keybinds, _level, _maxlevels, _numrobots, _playerpos, _robots, _spritesize, _waiting,
+  __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+_boxsize = 14;
+
+_gridsizex = 59;
+
+_gridsizey = 22;
+
+_gridheight = _gridsizey * _boxsize;
+
+_gridwidth = _gridsizex * _boxsize;
+
+_grid = document.getElementById('grid');
+
+_gridcon = _grid.getContext('2d');
+
+_playerpos = [0, 0];
+
+_junk = [];
+
+_robots = [];
+
+_level = 0;
+
+_numrobots = 10;
+
+_maxlevels = 4;
+
+_waiting = false;
+
+_keybinds = null;
+
+_spritesize = 14;
+
+/*
+Load options from localStorage or set defaults
+*/
+
+
+LoadOptions = function() {
+  window._showgrid = localStorage.getItem('showgrid') === 'true' ? true : false;
+  window._hardcore = localStorage.getItem('hardcore') === 'true' ? true : false;
+  window._autoteleport = localStorage.getItem('autoteleport') === 'true' ? true : false;
+  window._graphics = localStorage.getItem('graphics');
+  if (!window._graphics) {
+    window._graphics = (parseInt(Math.random() * 3) + 1) + '';
+  }
+  window._sprite = new Image;
+  window.loaded = false;
+  window._sprite.onload = function() {
+    return window.loaded = true;
+  };
+  if (window._graphics === '1') {
+    window._sprite.src = 'graphics/classic.png';
+  } else if (window._graphics === '2') {
+    window._sprite.src = 'graphics/dalek.png';
+  } else if (window._graphics === '3') {
+    window._sprite.src = 'graphics/cybermen.png';
+  }
+  document.getElementById('graphics').selectedIndex = parseInt(window._graphics, 10) - 1;
+  document.getElementById('showgrid').checked = window._showgrid;
+  document.getElementById('autoteleport').checked = window._autoteleport;
+  document.getElementById('hardcore').checked = window._hardcore;
+  if (localStorage.getItem('keybinds') === '1') {
+    document.getElementById('keybinds0').style.display = 'none';
+    document.getElementById('keybinds1').style.display = 'block';
+    document.getElementById('keyset').selectedIndex = 1;
+    return window._keybinds = [
+      [
+        121, function() {
+          return MovePlayer(['up', 'left']);
+        }
+      ], [
+        107, function() {
+          return MovePlayer(['up']);
+        }
+      ], [
+        117, function() {
+          return MovePlayer(['up', 'right']);
+        }
+      ], [
+        104, function() {
+          return MovePlayer(['left']);
+        }
+      ], [
+        108, function() {
+          return MovePlayer(['right']);
+        }
+      ], [
+        98, function() {
+          return MovePlayer(['down', 'left']);
+        }
+      ], [
+        106, function() {
+          return MovePlayer(['down']);
+        }
+      ], [
+        110, function() {
+          return MovePlayer(['down', 'right']);
+        }
+      ], [
+        46, function() {
+          return MovePlayer([]);
+        }
+      ], [
+        119, function() {
+          return Wait();
+        }
+      ], [
+        116, function() {
+          return Teleport();
+        }
+      ]
+    ];
+  } else {
+    document.getElementById('keybinds0').style.display = 'block';
+    document.getElementById('keybinds1').style.display = 'none';
+    document.getElementById('keyset').selectedIndex = 0;
+    return window._keybinds = [
+      [
+        55, function() {
+          return MovePlayer(['up', 'left']);
+        }
+      ], [
+        56, function() {
+          return MovePlayer(['up']);
+        }
+      ], [
+        57, function() {
+          return MovePlayer(['up', 'right']);
+        }
+      ], [
+        52, function() {
+          return MovePlayer(['left']);
+        }
+      ], [
+        54, function() {
+          return MovePlayer(['right']);
+        }
+      ], [
+        49, function() {
+          return MovePlayer(['down', 'left']);
+        }
+      ], [
+        50, function() {
+          return MovePlayer(['down']);
+        }
+      ], [
+        51, function() {
+          return MovePlayer(['down', 'right']);
+        }
+      ], [
+        53, function() {
+          return MovePlayer([]);
+        }
+      ], [
+        119, function() {
+          return Wait();
+        }
+      ], [
+        116, function() {
+          return Teleport();
+        }
+      ]
+    ];
+  }
+};
+
+/*
+Draw an empty grid aka playfield
+*/
+
+
+DrawGrid = function() {
+  var col, row, _i, _j, _ref, _ref1;
+  _gridcon.fillStyle = '#fff';
+  _gridcon.fillRect(0, 0, _gridwidth, _gridheight);
+  if (_showgrid) {
+    for (col = _i = 0, _ref = _gridsizex * _boxsize; 0 <= _ref ? _i <= _ref : _i >= _ref; col = _i += _boxsize) {
+      _gridcon.moveTo(col + 0.5, 0);
+      _gridcon.lineTo(col + 0.5, _gridheight);
+    }
+    for (row = _j = 0, _ref1 = _gridsizey * _boxsize; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; row = _j += _boxsize) {
+      _gridcon.moveTo(0, row + 0.5);
+      _gridcon.lineTo(_gridwidth, row + 0.5);
+    }
+    _gridcon.strokeStyle = '#b7b7b7';
+    _gridcon.lineWidth = 1;
+    return _gridcon.stroke();
+  }
+};
+
+/*
+Draw a bunch of robots at a random locations
+*/
+
+
+InitRobots = function() {
+  var i, x, y, _i, _results;
+  _results = [];
+  for (i = _i = 1; 1 <= _numrobots ? _i <= _numrobots : _i >= _numrobots; i = 1 <= _numrobots ? ++_i : --_i) {
+    while (true) {
+      x = GetRandomCoord('x');
+      y = GetRandomCoord('y');
+      if (!RobotAtPosition(x, y) && (x !== _playerpos[0] && y !== _playerpos[1])) {
+        break;
+      }
+    }
+    _results.push(DrawRobot(null, x, y));
+  }
+  return _results;
+};
+
+DrawSprite = function(num, x, y) {
+  return _gridcon.drawImage(_sprite, _spritesize * num, 0, _spritesize, _spritesize, x * _boxsize, y * _boxsize, _boxsize, _boxsize);
+};
+
+/*
+Draw a robot
+*/
+
+
+DrawRobot = function(num, x, y) {
+  if (_robots[num] === null) {
+    return;
+  } else if (num === null) {
+    num = _robots.length;
+    _robots.push([x, y]);
+  } else {
+    ClearGrid(_robots[num][0], _robots[num][1]);
+  }
+  DrawSprite(1, x, y);
+  return _robots[num] = [x, y];
+};
+
+/*
+Two robots collided. BBBOOOOOMMM!!
+*/
+
+
+DestroyRobots = function(x, y) {
+  var i, r, _i, _len, _results;
+  ClearGrid(x, y);
+  DrawJunk(x, y);
+  _junk.push([x, y]);
+  i = 0;
+  _results = [];
+  for (_i = 0, _len = _robots.length; _i < _len; _i++) {
+    r = _robots[_i];
+    if (r && r[0] === x && r[1] === y) {
+      _robots[i] = null;
+      _numrobots -= 1;
+      UpdateScore();
+    }
+    _results.push(i += 1);
+  }
+  return _results;
+};
+
+DrawJunk = function(x, y) {
+  return DrawSprite(2, x, y);
+};
+
+/*
+Move robots around
+*/
+
+
+MoveRobots = function() {
+  var c, i, r, x, y, _i, _j, _len, _len1, _results;
+  i = 0;
+  for (i = _i = 0, _len = _robots.length; _i < _len; i = ++_i) {
+    r = _robots[i];
+    if (r === null) {
+      continue;
+    }
+    x = r[0];
+    y = r[1];
+    if (_playerpos[0] > x) {
+      x += 1;
+    } else if (_playerpos[0] < x) {
+      x -= 1;
+    }
+    if (_playerpos[1] > y) {
+      y += 1;
+    } else if (_playerpos[1] < y) {
+      y -= 1;
+    }
+    if (RobotAtPosition(_playerpos[0], _playerpos[1])) {
+      Die();
+      return;
+    } else if (JunkAtPosition(x, y)) {
+      ClearGrid(_robots[i][0], _robots[i][1]);
+      _robots[i] = [x, y];
+      DestroyRobots(x, y);
+    } else {
+      DrawRobot(i, x, y);
+    }
+  }
+  _results = [];
+  for (i = _j = 0, _len1 = _robots.length; _j < _len1; i = ++_j) {
+    r = _robots[i];
+    if (r === null) {
+      continue;
+    }
+    c = RobotAtPosition(r[0], r[1], true);
+    if (c !== false && c !== i) {
+      _results.push(DestroyRobots(r[0], r[1]));
+    } else {
+      _results.push(void 0);
+    }
+  }
+  return _results;
+};
+
+/*
+Draw our handsome protagonist
+*/
+
+
+DrawPlayer = function(x, y) {
+  ClearGrid(_playerpos[0], _playerpos[1]);
+  DrawSprite(0, x, y);
+  return _playerpos = [x, y];
+};
+
+/*
+Get random coordinates
+TODO: How random is Math.random()?
+*/
+
+
+GetRandomCoord = function(axis) {
+  axis = axis === 'x' ? _gridsizex : _gridsizey;
+  return parseInt(Math.random() * (axis - 1) + 1, 10);
+};
+
+/*
+Set position of player or robot inside the grid
+*/
+
+
+SetPosition = function(obj, x, y) {
+  obj.style.left = x + 'px';
+  return obj.style.top = y + 'px';
+};
+
+/*
+Deal with keyboard events
+*/
+
+
+HandleKeyboard = function(event) {
+  var action, code, keyCode, _i, _len, _ref, _results;
+  if (event.ctrlKey || event.altKey) {
+    return;
+  }
+  code = event.keyCode || event.charCode;
+  if (code === 27) {
+    CloseAllWindows();
+    return;
+  }
+  _results = [];
+  for (_i = 0, _len = _keybinds.length; _i < _len; _i++) {
+    _ref = _keybinds[_i], keyCode = _ref[0], action = _ref[1];
+    if (keyCode === code) {
+      event.preventDefault();
+      _results.push(action());
+    } else {
+      _results.push(void 0);
+    }
+  }
+  return _results;
+};
+
+/*
+Deal with mouse events
+*/
+
+
+HandleMouse = function(event) {
+  var sleep;
+  if (event.target.id === 'options') {
+    return ShowWindow('options');
+  } else if (event.target.id === 'help') {
+    return ShowWindow('help');
+  } else if (event.target.id === 'about') {
+    return ShowWindow('about');
+  } else if (event.target.id === 'close') {
+    return CloseAllWindows();
+  } else if (event.target.id === 'save') {
+    localStorage.setItem('keybinds', document.getElementById('keyset').selectedIndex);
+    localStorage.setItem('graphics', document.getElementById('graphics').selectedIndex + 1);
+    localStorage.setItem('showgrid', document.getElementById('showgrid').checked);
+    localStorage.setItem('autoteleport', document.getElementById('autoteleport').checked);
+    localStorage.setItem('hardcore', document.getElementById('hardcore').checked);
+    LoadOptions();
+    CloseAllWindows();
+    DrawGrid();
+    return sleep = setInterval(function() {
+      var i, j, r, _i, _j, _len, _len1, _results;
+      if (window.loaded) {
+        clearInterval(sleep);
+        DrawPlayer(_playerpos[0], _playerpos[1]);
+        for (i = _i = 0, _len = _robots.length; _i < _len; i = ++_i) {
+          r = _robots[i];
+          if (r !== null) {
+            DrawRobot(i, r[0], r[1]);
+          }
+        }
+        _results = [];
+        for (i = _j = 0, _len1 = _junk.length; _j < _len1; i = ++_j) {
+          j = _junk[i];
+          _results.push(DrawJunk(i, j[0], j[1]));
+        }
+        return _results;
+      }
+    }, 100);
+  }
+};
+
+/*
+Close all windows
+*/
+
+
+CloseAllWindows = function() {
+  var l, win, _i, _len, _ref, _results;
+  l = document.getElementById('layover');
+  if (l) {
+    l.parentNode.removeChild(l);
+  }
+  _ref = document.getElementsByClassName('window');
+  _results = [];
+  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+    win = _ref[_i];
+    _results.push(win.style.display = 'none');
+  }
+  return _results;
+};
+
+/*
+Show a window
+*/
+
+
+ShowWindow = function(name) {
+  var div;
+  div = document.createElement('div');
+  div.id = 'layover';
+  document.body.appendChild(div);
+  return document.getElementById(name + 'window').style.display = 'block';
+};
+
+/*
+Our bold player decided to wait ... Let's see if that decision was wise ... or fatal!
+*/
+
+
+Wait = function() {
+  _waiting = true;
+  while (true) {
+    MoveRobots();
+    if (RobotAtPosition(_playerpos[0], _playerpos[1])) {
+      return;
+    }
+    if (_numrobots === 0) {
+      NextLevel();
+      break;
+    }
+  }
+  return _waiting = false;
+};
+
+/*
+Teleport to a new location ... or to death!
+*/
+
+
+Teleport = function() {
+  var x, y;
+  x = GetRandomCoord('x');
+  y = GetRandomCoord('y');
+  if (RobotAtPosition(x, y || JunkAtPosition(x, y))) {
+    Die();
+    return;
+  }
+  DrawPlayer(x, y);
+  return MoveRobots();
+};
+
+/* Move the player around
+*/
+
+
+MovePlayer = function(dir) {
+  var dangerous, i, j, x, y, _i, _j;
+  x = _playerpos[0];
+  y = _playerpos[1];
+  if (__indexOf.call(dir, 'left') >= 0) {
+    x -= 1;
+  } else if (__indexOf.call(dir, 'right') >= 0) {
+    x += 1;
+  }
+  if (__indexOf.call(dir, 'up') >= 0) {
+    y -= 1;
+  } else if (__indexOf.call(dir, 'down') >= 0) {
+    y += 1;
+  }
+  if (x < 0 || x > _gridsizex - 1) {
+    return false;
+  }
+  if (y < 0 || y > _gridsizey - 1) {
+    return false;
+  }
+  dangerous = false;
+  for (i = _i = -1; _i <= 1; i = ++_i) {
+    for (j = _j = -1; _j <= 1; j = ++_j) {
+      if (x + i < 0 || x + i > _gridsizex - 1) {
+        continue;
+      }
+      if (y + j < 0 || y + j > _gridsizey - 1) {
+        continue;
+      }
+      if (RobotAtPosition(x + i, y + j)) {
+        dangerous = true;
+      }
+    }
+  }
+  if (!_hardcore && dangerous) {
+    return false;
+  }
+  if (JunkAtPosition(x, y)) {
+    return false;
+  }
+  DrawPlayer(x, y);
+  MoveRobots();
+  if (_numrobots <= 0) {
+    NextLevel();
+  }
+  if (!_hardcore && _autoteleport && !MovePossible()) {
+    return Teleport();
+  }
+};
+
+/*
+Check if there is a possible move left
+*/
+
+
+MovePossible = function() {
+  var dangerous, x1, x2, y1, y2, _i, _j, _k, _l;
+  for (x1 = _i = -1; _i <= 1; x1 = ++_i) {
+    for (y1 = _j = -1; _j <= 1; y1 = ++_j) {
+      dangerous = false;
+      if (_playerpos[0] + x1 < 0 || _playerpos[0] + x1 > _gridsizex - 1) {
+        continue;
+      }
+      if (_playerpos[1] + y1 < 0 || _playerpos[1] + y1 > _gridsizey - 1) {
+        continue;
+      }
+      for (x2 = _k = -1; _k <= 1; x2 = ++_k) {
+        for (y2 = _l = -1; _l <= 1; y2 = ++_l) {
+          if (RobotAtPosition(_playerpos[0] + x1 + x2, _playerpos[1] + y1 + y2)) {
+            dangerous = true;
+          }
+        }
+      }
+      if (!dangerous) {
+        return true;
+      }
+    }
+  }
+  return false;
+};
+
+/*
+Check of there if a robot at the position
+*/
+
+
+RobotAtPosition = function(x, y, retnum) {
+  var i, r, _i, _len;
+  for (i = _i = 0, _len = _robots.length; _i < _len; i = ++_i) {
+    r = _robots[i];
+    if (r && r[0] === x && r[1] === y) {
+      if (retnum) {
+        return i;
+      } else {
+        return true;
+      }
+    }
+  }
+  return false;
+};
+
+/*
+Check if there is "junk" at this position
+*/
+
+
+JunkAtPosition = function(x, y) {
+  var j, _i, _len;
+  for (_i = 0, _len = _junk.length; _i < _len; _i++) {
+    j = _junk[_i];
+    if (j[0] === x && j[1] === y) {
+      return true;
+    }
+  }
+  return false;
+};
+
+/*
+Clear (blank) this grid positon
+TODO: Redraw grid lines if grid is enabled
+*/
+
+
+ClearGrid = function(x, y) {
+  _gridcon.fillStyle = '#fff';
+  return _gridcon.fillRect(x * _boxsize, y * _boxsize, _boxsize, _boxsize);
+};
+
+/*
+Oh noes! Our brave hero has died! :-(
+*/
+
+
+Die = function() {
+  var curscore, d, restart, s, scores, _i, _len;
+  ClearGrid(_playerpos[0], _playerpos[1]);
+  DrawSprite(3, _playerpos[0], _playerpos[1]);
+  curscore = parseInt(document.getElementById('score').innerHTML, 10);
+  scores = localStorage.getItem('scores');
+  scores = JSON.parse(scores);
+  if (!scores) {
+    scores = [];
+  }
+  d = new Date;
+  d = d.toLocaleDateString();
+  scores.push([curscore, d, true]);
+  scores.sort(function(a, b) {
+    if (a[0] > b[0]) {
+      return -1;
+    }
+    if (a[0] < b[0]) {
+      return 1;
+    }
+    return 0;
+  });
+  scores = scores.slice(0, 5);
+  restart = document.createElement('div');
+  restart.id = 'restart';
+  restart.innerHTML = 'AARRrrgghhhh....<br><br>' + 'Your highscores:<br>';
+  for (_i = 0, _len = scores.length; _i < _len; _i++) {
+    s = scores[_i];
+    restart.innerHTML += '<span class="row' + (s[2] ? ' cur' : '') + '">' + '<span class="score">' + s[0] + '</span>' + s[1] + '</span>';
+  }
+  restart.innerHTML += '<br>Press any key to try again.';
+  document.body.appendChild(restart);
+  scores = scores.map(function(s) {
+    s[2] = false;
+    return s;
+  });
+  localStorage.setItem('scores', JSON.stringify(scores));
+  document.body.removeEventListener('keypress', HandleKeyboard, false);
+  return window.addEventListener('keypress', (function(e) {
+    if (e.ctrlKey || e.altKey) {
+      return;
+    }
+    e.preventDefault();
+    return window.location.reload();
+  }), false);
+};
+
+/*
+Woohoo! A robot is no more, so lets update the score.
+*/
+
+
+UpdateScore = function() {
+  var score;
+  score = parseInt(document.getElementById('score').innerHTML, 10);
+  score += 10;
+  if (_waiting) {
+    score += 1;
+  }
+  return document.getElementById('score').innerHTML = score;
+};
+
+/*
+Keep IE happy (also shorter to type!)
+*/
+
+
+log = function(msg) {
+  if (console && console.log) {
+    return console.log(msg);
+  }
+};
+
+/*
+Advance to the next level
+*/
+
+
+NextLevel = function() {
+  _level += 1;
+  _numrobots = 10 + _level * 10;
+  _waiting = false;
+  _junk = [];
+  DrawGrid();
+  DrawPlayer(GetRandomCoord('x'), GetRandomCoord('y'));
+  return InitRobots();
+};
+
+CheckBrowser = function() {
+  var div, old;
+  old = false;
+  if (window.opera) {
+    if (parseFloat(window.opera.version()) < 11.60) {
+      old = true;
+    }
+  } else if (window.chrome) {
+    if (parseInt(navigator.appVersion.match(/Chrome\/(\d+)/)[1], 10) < 18) {
+      old = true;
+    }
+  } else if (navigator.vendor && navigator.vendor.match(/[aA]pple/)) {
+    if (parseFloat(navigator.appVersion.match(/Version\/(\d+\.\d+)/)[1]) < 5) {
+      old = true;
+    }
+  } else if (navigator.userAgent.match(/Firefox\/\d+/)) {
+    if (parseFloat(navigator.userAgent.match(/Firefox\/([\d\.]+)/)[1]) < 10) {
+      old = true;
+    }
+  } else if (navigator.appName === 'Microsoft Internet Explorer') {
+    if (parseInt(navigator.appVersion.match(/MSIE (\d+)/)[1], 10) < 9) {
+      old = true;
+    }
+  }
+  if (old) {
+    div = document.createElement('div');
+    div.id = 'oldbrowser';
+    div.innerHTML = 'Robots requires a fairly new browser with support for canvas, JSON, localStorage, etc.<br>' + 'Almost all modern browsers support this, but a few may not (IE8, for example, does not).<br>' + 'Tested versions are Opera 12, Firefox 14, Chrome 20, Internet Explorer 9';
+    return document.body.insertBefore(div, _grid);
+  }
+};
+
+/*
+Start the game!
+*/
+
+
+InitGame = function() {
+  var sleep;
+  _numrobots = 10;
+  LoadOptions();
+  return sleep = setInterval(function() {
+    if (window.loaded) {
+      clearInterval(sleep);
+      return InitGame2();
+    }
+  }, 100);
+};
+
+InitGame2 = function() {
+  DrawGrid();
+  DrawPlayer(GetRandomCoord('x'), GetRandomCoord('y'));
+  InitRobots();
+  window.addEventListener('keypress', HandleKeyboard, false);
+  return window.addEventListener('click', HandleMouse, false);
+};
+
+CheckBrowser();
+
+InitGame();
+// Robots!
+// Copyright © 2012 Martin Tournoij
+// http://arp242.net/robots/
+
+html
+  margin: 0
+  padding: 0
+  font-family: monospace
+  font-size: 14px
+
+body
+  margin: 0
+  padding: 10px
+
+p
+  text-align: justify
+
+a, a:visited
+  display: inline-block
+  background-color: #eee
+  color: #000
+  text-decoration: none
+
+  border: 1px solid #666
+  padding: .2em .3em
+  margin-bottom: .2em
+
+  &:hover
+    background-color: #ddd
+
+#aboutwindow p >  a
+  background-color: transparent
+  text-decoration: underline
+  border: none
+  padding: 0
+
+label
+  display: inline-block
+  width: 10em
+
+input, select
+  border: 1px solid #b7b7b7
+  padding: .3em
+  margin-bottom: .3em
+
+input.small
+  width: 2em
+
+#grid
+  z-index: 1
+  border: 2px solid #666
+
+#restart
+  position: absolute
+  left: 20px
+  top: 20px
+  border: 1px solid #666
+  background-color: #fff
+  padding: .5em
+
+  .score
+    display: inline-block
+    width: 5em
+
+  .row
+    display: block
+
+  .cur
+    font-weight: bold
+
+#menu
+  .box
+    float: left
+    width: 160px
+
+  #keybinds1
+    display: none
+
+.window
+  z-index: 20
+  display: none
+  position: absolute
+
+  top: 40px
+  left: 110px
+  width: 626px
+  background-color: #fff
+  border: 1px solid #666
+
+  p
+    padding: .5em 1em
+    margin: 0
+
+  .toolbar
+    margin: 0
+    padding: .3em
+    border-top: 1px solid #b7b7b7
+    background-color: #aaa
+
+  .help
+    display: block
+    margin-left: 12.5em
+    margin-bottom: 1em
+    font-size: .9em
+    font-style: italic
+
+
+#layover
+  z-index: 10
+  position: absolute
+  left: 0
+  top: 0
+  width: 100%
+  height: 100%
+  background-color: #000
+  opacity: 0.5
+
+#oldbrowser
+  padding: .5em
+  border: 1px solid #666
+  background-color: #eee
+  margin-bottom: 1em
+  width: 814px
+