S
Steve Howell
I've written my first nontrivial JS program, and I have a few general
questions about it.
First of all, I've only been able to get it working on Firefox, and
I'd like advice on getting it to work under IE. I'm most interested
in specific mistakes that I've made that affect IE, but I also want to
learn how to debug IE in general.
The program allows a user to create a little world of walls in a 2d
grid. The user moves a robot around the world and builds walls.
Right now the program is just a toy, but I'm hoping to turn it into
something educational, and it's open source FWIW.
Some questions about the program:
1) I tried to make it OO, but I'm not sure I have all the JS idioms
down.
2) I had to do a strange workaround for Firefox. When I update the
DOM to give a table cell a right border, all the cells below it
inherit that border. Although it's partly a CSS question, it seems to
happen only when I update the DOM via JS.
3) In general, how can I improve this program?
Thanks in advance.
Here is the program, which is all self-contained. Sorry if it's a bit
long.
<html>
<style type="text/css">
table {border-collapse: collapse;}
table.wvr th {background: #EEEEEE}
table.wvr td {background: #FFFF00}
table.wvr td, th {height: 40px; width: 40px; text-align: center}
..heading {background: #FF00FF}
..north {border-top: solid red;}
..east {border-right: solid red;}
..no_north {border-top: solid white;}
..no_east {border-right: solid white;}
..robot {background: #FF8888}
..beeper {background: #00FFFF}
..tiny {font-size: 10px}
</style>
<body onload="create_world()">
<script type="text/javascript">
/*
Webster van Robot.
(c) GNU General Public license
*/
function showMsg(the_message) {
document.the_form.the_text.value += (the_message + "\n");
}
function clearMsg() {
document.the_form.the_text.value = ''
}
function World() {
this.robot_x = 1;
this.robot_y = 1;
this.robot_dir = "E";
this.north_walls = [];
this.east_walls = [];
this.num_aves = 12
this.num_streets = 10
function _coords() {
return this.robot_x + "," + this.robot_y;
}
this._coords = _coords;
function wall_on_north(x, y)
{
if (y == 0 || y == this.num_streets) {
return true
}
coords = x + "," + y;
return (this.north_walls[coords] == 1)
}
this.wall_on_north = wall_on_north
function wall_on_south(x, y)
{
return this.wall_on_north(x, y - 1)
}
this.wall_on_south = wall_on_south
function wall_on_east(x, y)
{
if (x == 0 || x == this.num_aves) {
return true
}
coords = x + "," + y;
return (this.east_walls[coords] == 1)
}
this.wall_on_east = wall_on_east
function wall_on_west(x, y)
{
return this.wall_on_east(x - 1, y)
}
this.wall_on_west = wall_on_west
function is_facing_wall() {
if (this.robot_dir == "N") {
return this.wall_on_north(this.robot_x, this.robot_y)
}
else if (this.robot_dir == "W") {
return this.wall_on_west(this.robot_x, this.robot_y)
}
else if (this.robot_dir == "S") {
return this.wall_on_south(this.robot_x, this.robot_y)
}
else {
return this.wall_on_east(this.robot_x, this.robot_y)
}
}
this.is_facing_wall = is_facing_wall
function move() {
if (this.is_facing_wall()) {
return 'blocked by wall'
}
if (this.robot_dir == "N") { this.robot_y += 1;}
else if (this.robot_dir == "W") { this.robot_x -= 1;}
else if (this.robot_dir == "S") { this.robot_y -= 1;}
else { this.robot_x += 1;}
}
this.move = move;
function turnleft() {
if (this.robot_dir == "N") { this.robot_dir = "W";}
else if (this.robot_dir == "W") { this.robot_dir = "S";}
else if (this.robot_dir == "S") { this.robot_dir = "E";}
else { this.robot_dir = "N";}
}
this.turnleft = turnleft;
function turnright() {
if (this.robot_dir == "N") { this.robot_dir = "E";}
else if (this.robot_dir == "W") { this.robot_dir = "N";}
else if (this.robot_dir == "S") { this.robot_dir = "W";}
else { this.robot_dir = "S";}
}
this.turnright = turnright;
function build_east_wall(x, y) {
if (this.wall_on_east(x, y)) {
return 'There is already a wall there'
}
coords = x + "," + y;
this.east_walls[coords] = 1;
}
this.build_east_wall = build_east_wall
function build_west_wall(x, y) {
return this.build_east_wall(x-1, y)
}
this.build_west_wall = build_west_wall
function build_north_wall(x, y) {
if (this.wall_on_north(x, y)) {
return 'There is already a wall there'
}
coords = x + "," + y;
this.north_walls[coords] = 1;
}
this.build_north_wall = build_north_wall
function build_south_wall(x, y) {
return this.build_north_wall(x, y-1)
}
this.build_south_wall = build_south_wall
function build_wall_on_left() {
x = this.robot_x;
y = this.robot_y;
if (this.robot_dir == "N") {
return this.build_west_wall(x, y)
}
else if (this.robot_dir == "S") {
return this.build_east_wall(x, y)
}
else if (this.robot_dir == "E") {
return this.build_north_wall(x, y)
}
else if (this.robot_dir == "W") {
return this.build_south_wall(x, y)
}
}
this.build_wall_on_left = build_wall_on_left;
function robot() {
if (this.robot_dir == "N") {
return '^'
}
else if (this.robot_dir == "S") {
return 'v'
}
else if (this.robot_dir == "E") {
return '>'
}
else if (this.robot_dir == "W") {
return '<'
}
}
this.robot = robot;
function render(avenue, street)
{
var data = ''
var klass = ''
var coords = avenue + ',' + street
if (this.north_walls[coords] == 1) {
klass += 'north'
}
else {
klass += 'no_north'
}
if (this.east_walls[coords] == 1) {
klass += ' east'
}
else {
klass += ' no_east'
}
if (this.robot_x == avenue && this.robot_y == street) {
data = '<div class="robot">' + this.robot() + '</div>'
}
else {
data = '<div class="tiny">' + coords + '</div>'
}
var text = '<td class="' + klass + '">' + data + '</td>'
return text
}
this.render = render
}
the_world = new World()
function start_over() {
the_world = new World()
redraw_grid()
}
function redraw_street(street)
{
/* showMsg('redraw' + street) */
row = document.getElementById("street"+street)
/* row = document.getElementById("street"+street) */
var text = '<th class="st east">S' + street + '</th>'
for (var ave = 1; ave <= the_world.num_aves; ++ave) {
text += the_world.render(ave, street)
}
row.innerHTML = text;
row.id = 'street' + street
}
function create_world() {
ave_row = document.getElementById("ave_heading")
text = '<th></th>'
for (var ave = 1; ave <= the_world.num_aves; ++ave) {
text += '<th class="ave">A' + ave + '</td>'
}
ave_row.innerHTML = text
table = document.getElementById("world")
for (var street = 1; street <= the_world.num_streets; ++street) {
x = table.insertRow(2)
x.id = "street"+street
}
redraw_grid()
}
function redraw_grid() {
for (var street = 1; street <= the_world.num_streets; ++street) {
redraw_street(street)
}
}
function redraw_to_bottom(street) {
for (var i = street; i >= 1; --i) {
redraw_street(i)
}
}
function move() {
old_street = the_world.robot_y
problem = the_world.move()
if (problem) {
alert(problem)
return problem
}
showMsg('move')
new_street = the_world.robot_y
redraw_street(old_street)
redraw_street(new_street)
}
function turnleft() {
showMsg('turnleft')
the_world.turnleft()
street = the_world.robot_y
redraw_street(street)
}
function turnright() {
showMsg('turnright')
the_world.turnright()
street = the_world.robot_y
redraw_street(street)
}
function build_wall_on_left() {
problem = the_world.build_wall_on_left()
if (problem) {
alert(problem)
return problem
}
showMsg('build_wall_on_left')
var street = the_world.robot_y
redraw_to_bottom(street) /* browsers are stupid */
}
</script>
<table>
<tr>
<td valign="top">
<a href="#" onClick="move(); return false;">move</a>
<a href="#" onClick="turnleft(); return false;">turnleft</a>
<a href="#" onClick="turnright(); return false;">turnright</a>
<a href="#" onClick="build_wall_on_left(); return
false;">build_wall_on_left</a>
<a href="#" onClick="start_over(); return false;">start_over</a>
<table id="world" class="wvr">
<tr>
<th colspan=13 class="heading">Webster van Robot's World</th>
</tr>
<tr id="ave_heading">
</tr>
<tr class="north"></tr>
</table>
</td>
<td>
<form name="the_form">
<textarea name="the_text" rows=30 cols=40>
</textarea>
</form>
</td>
</tr>
</table>
</body>
</html>
questions about it.
First of all, I've only been able to get it working on Firefox, and
I'd like advice on getting it to work under IE. I'm most interested
in specific mistakes that I've made that affect IE, but I also want to
learn how to debug IE in general.
The program allows a user to create a little world of walls in a 2d
grid. The user moves a robot around the world and builds walls.
Right now the program is just a toy, but I'm hoping to turn it into
something educational, and it's open source FWIW.
Some questions about the program:
1) I tried to make it OO, but I'm not sure I have all the JS idioms
down.
2) I had to do a strange workaround for Firefox. When I update the
DOM to give a table cell a right border, all the cells below it
inherit that border. Although it's partly a CSS question, it seems to
happen only when I update the DOM via JS.
3) In general, how can I improve this program?
Thanks in advance.
Here is the program, which is all self-contained. Sorry if it's a bit
long.
<html>
<style type="text/css">
table {border-collapse: collapse;}
table.wvr th {background: #EEEEEE}
table.wvr td {background: #FFFF00}
table.wvr td, th {height: 40px; width: 40px; text-align: center}
..heading {background: #FF00FF}
..north {border-top: solid red;}
..east {border-right: solid red;}
..no_north {border-top: solid white;}
..no_east {border-right: solid white;}
..robot {background: #FF8888}
..beeper {background: #00FFFF}
..tiny {font-size: 10px}
</style>
<body onload="create_world()">
<script type="text/javascript">
/*
Webster van Robot.
(c) GNU General Public license
*/
function showMsg(the_message) {
document.the_form.the_text.value += (the_message + "\n");
}
function clearMsg() {
document.the_form.the_text.value = ''
}
function World() {
this.robot_x = 1;
this.robot_y = 1;
this.robot_dir = "E";
this.north_walls = [];
this.east_walls = [];
this.num_aves = 12
this.num_streets = 10
function _coords() {
return this.robot_x + "," + this.robot_y;
}
this._coords = _coords;
function wall_on_north(x, y)
{
if (y == 0 || y == this.num_streets) {
return true
}
coords = x + "," + y;
return (this.north_walls[coords] == 1)
}
this.wall_on_north = wall_on_north
function wall_on_south(x, y)
{
return this.wall_on_north(x, y - 1)
}
this.wall_on_south = wall_on_south
function wall_on_east(x, y)
{
if (x == 0 || x == this.num_aves) {
return true
}
coords = x + "," + y;
return (this.east_walls[coords] == 1)
}
this.wall_on_east = wall_on_east
function wall_on_west(x, y)
{
return this.wall_on_east(x - 1, y)
}
this.wall_on_west = wall_on_west
function is_facing_wall() {
if (this.robot_dir == "N") {
return this.wall_on_north(this.robot_x, this.robot_y)
}
else if (this.robot_dir == "W") {
return this.wall_on_west(this.robot_x, this.robot_y)
}
else if (this.robot_dir == "S") {
return this.wall_on_south(this.robot_x, this.robot_y)
}
else {
return this.wall_on_east(this.robot_x, this.robot_y)
}
}
this.is_facing_wall = is_facing_wall
function move() {
if (this.is_facing_wall()) {
return 'blocked by wall'
}
if (this.robot_dir == "N") { this.robot_y += 1;}
else if (this.robot_dir == "W") { this.robot_x -= 1;}
else if (this.robot_dir == "S") { this.robot_y -= 1;}
else { this.robot_x += 1;}
}
this.move = move;
function turnleft() {
if (this.robot_dir == "N") { this.robot_dir = "W";}
else if (this.robot_dir == "W") { this.robot_dir = "S";}
else if (this.robot_dir == "S") { this.robot_dir = "E";}
else { this.robot_dir = "N";}
}
this.turnleft = turnleft;
function turnright() {
if (this.robot_dir == "N") { this.robot_dir = "E";}
else if (this.robot_dir == "W") { this.robot_dir = "N";}
else if (this.robot_dir == "S") { this.robot_dir = "W";}
else { this.robot_dir = "S";}
}
this.turnright = turnright;
function build_east_wall(x, y) {
if (this.wall_on_east(x, y)) {
return 'There is already a wall there'
}
coords = x + "," + y;
this.east_walls[coords] = 1;
}
this.build_east_wall = build_east_wall
function build_west_wall(x, y) {
return this.build_east_wall(x-1, y)
}
this.build_west_wall = build_west_wall
function build_north_wall(x, y) {
if (this.wall_on_north(x, y)) {
return 'There is already a wall there'
}
coords = x + "," + y;
this.north_walls[coords] = 1;
}
this.build_north_wall = build_north_wall
function build_south_wall(x, y) {
return this.build_north_wall(x, y-1)
}
this.build_south_wall = build_south_wall
function build_wall_on_left() {
x = this.robot_x;
y = this.robot_y;
if (this.robot_dir == "N") {
return this.build_west_wall(x, y)
}
else if (this.robot_dir == "S") {
return this.build_east_wall(x, y)
}
else if (this.robot_dir == "E") {
return this.build_north_wall(x, y)
}
else if (this.robot_dir == "W") {
return this.build_south_wall(x, y)
}
}
this.build_wall_on_left = build_wall_on_left;
function robot() {
if (this.robot_dir == "N") {
return '^'
}
else if (this.robot_dir == "S") {
return 'v'
}
else if (this.robot_dir == "E") {
return '>'
}
else if (this.robot_dir == "W") {
return '<'
}
}
this.robot = robot;
function render(avenue, street)
{
var data = ''
var klass = ''
var coords = avenue + ',' + street
if (this.north_walls[coords] == 1) {
klass += 'north'
}
else {
klass += 'no_north'
}
if (this.east_walls[coords] == 1) {
klass += ' east'
}
else {
klass += ' no_east'
}
if (this.robot_x == avenue && this.robot_y == street) {
data = '<div class="robot">' + this.robot() + '</div>'
}
else {
data = '<div class="tiny">' + coords + '</div>'
}
var text = '<td class="' + klass + '">' + data + '</td>'
return text
}
this.render = render
}
the_world = new World()
function start_over() {
the_world = new World()
redraw_grid()
}
function redraw_street(street)
{
/* showMsg('redraw' + street) */
row = document.getElementById("street"+street)
/* row = document.getElementById("street"+street) */
var text = '<th class="st east">S' + street + '</th>'
for (var ave = 1; ave <= the_world.num_aves; ++ave) {
text += the_world.render(ave, street)
}
row.innerHTML = text;
row.id = 'street' + street
}
function create_world() {
ave_row = document.getElementById("ave_heading")
text = '<th></th>'
for (var ave = 1; ave <= the_world.num_aves; ++ave) {
text += '<th class="ave">A' + ave + '</td>'
}
ave_row.innerHTML = text
table = document.getElementById("world")
for (var street = 1; street <= the_world.num_streets; ++street) {
x = table.insertRow(2)
x.id = "street"+street
}
redraw_grid()
}
function redraw_grid() {
for (var street = 1; street <= the_world.num_streets; ++street) {
redraw_street(street)
}
}
function redraw_to_bottom(street) {
for (var i = street; i >= 1; --i) {
redraw_street(i)
}
}
function move() {
old_street = the_world.robot_y
problem = the_world.move()
if (problem) {
alert(problem)
return problem
}
showMsg('move')
new_street = the_world.robot_y
redraw_street(old_street)
redraw_street(new_street)
}
function turnleft() {
showMsg('turnleft')
the_world.turnleft()
street = the_world.robot_y
redraw_street(street)
}
function turnright() {
showMsg('turnright')
the_world.turnright()
street = the_world.robot_y
redraw_street(street)
}
function build_wall_on_left() {
problem = the_world.build_wall_on_left()
if (problem) {
alert(problem)
return problem
}
showMsg('build_wall_on_left')
var street = the_world.robot_y
redraw_to_bottom(street) /* browsers are stupid */
}
</script>
<table>
<tr>
<td valign="top">
<a href="#" onClick="move(); return false;">move</a>
<a href="#" onClick="turnleft(); return false;">turnleft</a>
<a href="#" onClick="turnright(); return false;">turnright</a>
<a href="#" onClick="build_wall_on_left(); return
false;">build_wall_on_left</a>
<a href="#" onClick="start_over(); return false;">start_over</a>
<table id="world" class="wvr">
<tr>
<th colspan=13 class="heading">Webster van Robot's World</th>
</tr>
<tr id="ave_heading">
</tr>
<tr class="north"></tr>
</table>
</td>
<td>
<form name="the_form">
<textarea name="the_text" rows=30 cols=40>
</textarea>
</form>
</td>
</tr>
</table>
</body>
</html>