PDA

View Full Version : Array.reverse() - Blocks my Script



Jesdisciple
08-11-2007, 10:24 PM
I'm making a simple Mancala application (using Firefox - IE6 only shows the holes at the ends) and reversing an array strangely causes the entire script to fail. I must reverse the array because Hole 1 is reacting to a click on 6, Hole 4 to a click on 3, etc. Here's the code (saved as mancala.js), with a comment next to the line where I toggle ".reverse()" to produce the error.


// JavaScript Document
var current = 0;
var players = new Array(2);
function Player(n){
this.hasTurn = false;
this.mancala = document.getElementById("p" + n);
this.holes = document.getElementsByName("p" + n);// <-line in question
this.turn = function(){
this.hasTurn = true;
this.mancala.style.backgroundColor = "red";
for(i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "red";
}
this.grab = function(n){
var where;
stones = this.holes[n - 1].innerHTML;
for(this.holes[n - 1].innerHTML = 0; stone > 0; stones--){
where = (stones - n) &#37; 13;
this.get(where).innerHTML++;
}
if(where != 0){
this.hasTurn = false;
this.mancala.style.backgroundColor = "white";
for(i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "white";
change();
}
}
this.get = function(n){
n = n % 13;
switch(n){
case 0:
return this.mancala;
default:
alert('uncaught: get(' + n + ')');
}
}
}
function change(){
if(current == 1) player[current = 0].turn();
else player[current = 1].turn();
}
function next(){
return current == 1 ? player[0] : player[1];
}
function init(s){
players[0] = new Player(1);
players[1] = new Player(2);
for(i1 = 0; i1 < 2; i1++){
players[i1].mancala.innerHTML = 0;
for(i2 = 0; i2 < players[i1].holes.length; i2++) players[i1].holes[i2].innerHTML = s;
}
players[0].turn();
}

And my page:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="mancala.css"/>
<title>Mancala</title>
<script type="text/javascript" src="mancala.js"></script>
</head>
<body onLoad="init(4)">
<table border="1" bordercolor="#000000" width="100%" height="100%">
<tr>
<td class="mancala" id="p2" rowspan="2"></td>
<td class="hole" name="p2" id="2-1" onClick="players[1].grab(1)"></td>
<td class="hole" name="p2" id="2-2" onClick="players[1].grab(2)"></td>
<td class="hole" name="p2" id="2-3" onClick="players[1].grab(3)"></td>
<td class="hole" name="p2" id="2-4" onClick="players[1].grab(4)"></td>
<td class="hole" name="p2" id="2-5" onClick="players[1].grab(5)"></td>
<td class="hole" name="p2" id="2-6" onClick="players[1].grab(6)"></td>
<td class="mancala" id="p1" rowspan="2"></td>
</tr>
<tr>
<td class="hole" name="p1" id="1-6" onClick="players[0].grab(6)"></td>
<td class="hole" name="p1" id="1-5" onClick="players[0].grab(5)"></td>
<td class="hole" name="p1" id="1-4" onClick="players[0].grab(4)"></td>
<td class="hole" name="p1" id="1-3" onClick="players[0].grab(3)"></td>
<td class="hole" name="p1" id="1-2" onClick="players[0].grab(2)"></td>
<td class="hole" name="p1" id="1-1" onClick="players[0].grab(1)"></td>
</tr>
</table>
</body>
</html>

EDIT: The question: How can I use reverse() while avoiding the failure?

Twey
08-12-2007, 03:44 AM
You've made the common mistake of thinking that DOM methods return arrays. They don't. That's not an array, it's a collection, and as such it doesn't have a lot of the methods arrays have. However, they can still be used with those methods; you just have to call them manually:
this.holes = Array.prototype.reverse.call(document.getElementsByName("p" + n));As for the rest of your script and page:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">This DOCTYPE has been unnecessary for a good decade or so. Use Strict.
<link rel="stylesheet" type="text/css" href="mancala.css"/>This XML-style ending syntax doesn't mean what you think it means in HTML, and what it does mean few browsers support.
<table border="1" bordercolor="#000000" width="100&#37;" height="100%">Avoid this presentational markup -- there's no reason not to use CSS.
<tr>
<td class="mancala" id="p2" rowspan="2"></td>
<td class="hole" name="p2" id="2-1" onClick="players[1].grab(1)"></td>
<td class="hole" name="p2" id="2-2" onClick="players[1].grab(2)"></td>
<td class="hole" name="p2" id="2-3" onClick="players[1].grab(3)"></td>
<!-- &c. -->Since the game depends on Javascript, it would be neater to have Javascript generate the necessary markup. Also, IDs may not begin with a number.
this.turn = function(){This results in the function being recreated each time a new instance is created, needlessly. Use prototypes:
Player.prototype.turn = function() {
for(i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "red";By omitting the var keyword, you've unwittingly made i public. You really do not want i to be public, since every man and his dog names their index variable i, and if someone else makes the same mistake and you start overwriting one another's index variables you're going to have a hell of a time debugging it. Personally I find it easiest to keep track of my index variables by declaring them at the top of the function, C-style, to avoid accidentally globalising them (with a missing var) or redefining them (with a superfluous var), but this is a matter of taste.
stones = this.holes[n - 1].innerHTML;There's no reason to use innerHTML here. Avoid it where possible, since it's non-standard, misrepresents the DOM, and has side effects that people often don't anticipate. Using DOM methods in this case is an equally simple endeavour.
function change(){
if(current == 1) player[current = 0].turn();
else player[current = 1].turn();
}
function next(){
return current == 1 ? player[0] : player[1];
}It would make more sense to store these on the Player function:
Player.change = function() {
/* ... */
};

Player.next = function() {
/* ... */
};The players array looks as though it would make sense in there as well -- avoid globals wherever possible, especially ones with generic names like "players" and "current." Since current seems to be used nowhere else, you could put it in a closure around these two functions:
(function() {
var current = 0;

Player.change = function() {
/* ... */
};

Player.next = function() {
/* ... */
};
})();

Jesdisciple
08-12-2007, 12:54 PM
You've made the common mistake of thinking that DOM methods return arrays. They don't. That's not an array, it's a collection, and as such it doesn't have a lot of the methods arrays have. However, they can still be used with those methods; you just have to call them manually:
this.holes = Array.prototype.reverse.call(document.getElementsByName("p" + n));I actually didn't know Javascript had collections... I'm guessing they're different from those in Java?



<link rel="stylesheet" type="text/css" href="mancala.css"/>This XML-style ending syntax doesn't mean what you think it means in HTML, and what it does mean few browsers support.I think I missed your point. Do you have an alternative? (My high school Webmastering teacher used link.)



<table border="1" bordercolor="#000000" width="100%" height="100%">Avoid this presentational markup -- there's no reason not to use CSS.I added the following to my CSS and now have no border:
table{
border-width: 1px;
border-color: #000000;
width: 100%;
}



<tr>
<td class="mancala" id="p2" rowspan="2"></td>
<td class="hole" name="p2" id="2-1" onClick="players[1].grab(1)"></td>
<td class="hole" name="p2" id="2-2" onClick="players[1].grab(2)"></td>
<td class="hole" name="p2" id="2-3" onClick="players[1].grab(3)"></td>
<!-- &c. -->Since the game depends on Javascript, it would be neater to have Javascript generate the necessary markup. Also, IDs may not begin with a number.For now, I'm just trying to get the mechanics to work. (And those IDs have been working fine, I actually use them to determine when to flip my "collection".)



this.turn = function(){This results in the function being recreated each time a new instance is created, needlessly. Use prototypes:
Player.prototype.turn = function() {Prototypes? Interesting and simple enough, but they're new to me.



stones = this.holes[n - 1].innerHTML;There's no reason to use innerHTML here. Avoid it where possible, since it's non-standard, misrepresents the DOM, and has side effects that people often don't anticipate. Using DOM methods in this case is an equally simple endeavour.??? What (W3C) DOM methods do you mean?



function change(){
if(current == 1) player[current = 0].turn();
else player[current = 1].turn();
}
function next(){
return current == 1 ? player[0] : player[1];
}It would make more sense to store these on the Player function:
Player.change = function() {
/* ... */
};

Player.next = function() {
/* ... */
};The players array looks as though it would make sense in there as well -- avoid globals wherever possible, especially ones with generic names like "players" and "current." Since current seems to be used nowhere else, you could put it in a closure around these two functions:
(function() {
var current = 0;

Player.change = function() {
/* ... */
};

Player.next = function() {
/* ... */
};
})();What-huh? What's wrong with globals? And generics? The usefulness of current and players is in their "globality"; they're essentially static and (I think) I want them that way. (In the JS below, I've changed "current" for "number" and added the "index" property to Player.)

Here's my Javascript:
// JavaScript Document
var number = 0;
var players = new Array(2);
function Player(n){
this.index = ++number;
this.hasTurn = false;
this.mancala = document.getElementById("p" + n);
this.holes = document.getElementsByName("p" + n);
if(this.holes[0].id != (n + '-' + 1)){
alert(this.holes[0].id);
this.holes = Array.prototype.reverse.call(document.getElementsByName("p" + n));
}
}
Player.prototype.turn = function(){
this.hasTurn = true;
this.mancala.style.backgroundColor = "red";
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "red";
}
Player.prototype.grab = function(n){
var where;
stones = this.holes[n - 1].innerHTML;
if(!this.hasTurn || stones == 0) return;
for(this.holes[n - 1].innerHTML = 0; stones > 0; stones--){
where = (stones - n) % 13;
this.drop(where);
}
if(where != 0){
this.hasTurn = false;
this.mancala.style.backgroundColor = "white";
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "white";
change();
}
}
Player.prototype.drop = function(n){
n = n % 13;
switch(n){
case -6: case -5: case -4: case -3: case -2: case -1:
next().holes[7 + n].innerHTML++;
break;
case 0:
this.mancala.innerHTML++;
break;
case 1: case 2: case 3: case 4: case 5: case 6:
this.holes[n - 1].innerHTML++;
break;
default:
alert('uncaught: drop(' + n + ')');
}
}
Player.prototype.change = function(){
this.hasTurn = false;
next().turn();
}
Player.prototype.next = function(){
return player[this.index == 1 ? 0 : 1];
}
function init(s){
players[0] = new Player(1);
players[1] = new Player(2);
for(var i1 = 0; i1 < 2; i1++){
players[i1].mancala.innerHTML = 0;
for(var i2 = 0; i2 < players[i1].holes.length; i2++) players[i1].holes[i2].innerHTML = s;
}
players[0].turn();
}

And my HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="mancala.css"/>
<title>Mancala</title>
<script type="text/javascript" src="mancala.js"></script>
</head>
<body onLoad="init(4)">
<table>
<tr>
<td class="mancala" id="p2" rowspan="2"></td>
<td class="hole" name="p2" id="2-1" onClick="players[1].grab(1)"></td>
<td class="hole" name="p2" id="2-2" onClick="players[1].grab(2)"></td>
<td class="hole" name="p2" id="2-3" onClick="players[1].grab(3)"></td>
<td class="hole" name="p2" id="2-4" onClick="players[1].grab(4)"></td>
<td class="hole" name="p2" id="2-5" onClick="players[1].grab(5)"></td>
<td class="hole" name="p2" id="2-6" onClick="players[1].grab(6)"></td>
<td class="mancala" id="p1" rowspan="2"></td>
</tr>
<tr>
<td class="hole" name="p1" id="1-6" onClick="players[0].grab(6)"></td>
<td class="hole" name="p1" id="1-5" onClick="players[0].grab(5)"></td>
<td class="hole" name="p1" id="1-4" onClick="players[0].grab(4)"></td>
<td class="hole" name="p1" id="1-3" onClick="players[0].grab(3)"></td>
<td class="hole" name="p1" id="1-2" onClick="players[0].grab(2)"></td>
<td class="hole" name="p1" id="1-1" onClick="players[0].grab(1)"></td>
</tr>
</table>
</body>
</html>

And my CSS:
/* CSS Document */
table{
border-width: 1px;
border-color: #000000;
width: 100%;
}
td.hole{
height: 100px;
text-align: center;
}
td.mancala{
width: 100px;
text-align: center;
}

Twey
08-12-2007, 06:28 PM
I actually didn't know Javascript had collections... I'm guessing they're different from those in Java?Yes. In ECMAScript terms, "collection" is basically any object that functions like an array but isn't actually an array.
I think I missed your point. Do you have an alternative? (My high school Webmastering teacher used link.)Not for <link>. I was talking about the XML-style self-closing syntax (/>) you've used for the element.
I added the following to my CSS and now have no border:You forgot to specify a border-style. You can combine these properties:
border: 1px solid black;
For now, I'm just trying to get the mechanics to work. (And those IDs have been working fine, I actually use them to determine when to flip my "collection".)They shouldn't, and probably won't in some browsers. Firefox tends to dislike invalid IDs, for a start.
Prototypes? Interesting and simple enough, but they're new to me.Definitely worth getting to know (http://www.devarticles.com/c/a/JavaScript/Object-Oriented-JavaScript-Using-the-Prototype-Property/). Prototypes are the basis of all OO in ECMAScript.
What-huh? What's wrong with globals? And generics? The usefulness of current and players is in their "globality"; they're essentially static and (I think) I want them that way.If you added them directly to Player (not to its prototype), they would still be "static" as Java uses the term. They just wouldn't be sitting in the global namespace where they're likely to conflict with other scripts (and besides, they look messy). You'd access them as, e.g., Player.players. Java forces this methodology on you. init() is another dreadfully generically-named function that you really don't want in the global namespace.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">If you're looking to support IE, then don't use XHTML. If you're not, make sure it's being sent with the correct content type header, application/xhtml+xml.

Jesdisciple
08-12-2007, 10:00 PM
Yes. In ECMAScript terms, "collection" is basically any object that functions like an array but isn't actually an array.*scratches head* It seems they'd make all of 'em arrays. Oh, well.


Not for <link>. I was talking about the XML-style self-closing syntax (/>) you've used for the element.Oh. I was told (W3Schools?) that carried over to XHTML, which is being adopted as standard.


You forgot to specify a border-style. You can combine these properties:
border: 1px solid black;I forget which property goes where and can't read my code except by referencing W3Schools (not for this combo, but many others).


They shouldn't, and probably won't in some browsers. Firefox tends to dislike invalid IDs, for a start.That's strange. I have it alerting the first collection element's ID in the constructor when the collection needs to be flipped, and FF does fine with it.


Definitely worth getting to know (http://www.devarticles.com/c/a/JavaScript/Object-Oriented-JavaScript-Using-the-Prototype-Property/). Prototypes are the basis of all OO in ECMAScript.That's very interesting. I'd wondered how JS was OOP. *reads* It's long but worth it, so far.


If you added them directly to Player (not to its prototype), they would still be "static" as Java uses the term. They just wouldn't be sitting in the global namespace where they're likely to conflict with other scripts (and besides, they look messy). You'd access them as, e.g., Player.players. Java forces this methodology on you. init() is another dreadfully generically-named function that you really don't want in the global namespace.My JS complies.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">If you're looking to support IE, then don't use XHTML. If you're not, make sure it's being sent with the correct content type header, application/xhtml+xml.The original DOCTYPE:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">is from my Dreamweaver 2004. You told me to use strict, and I only know (actually reference from W3Schools) the XHTML DOCTYPEs, but not what 'application/xhtml+xml' is.

Using the following codes, clicking has no effect, not even the finnicky one I was wrestling with earlier. :\

mancala.js:
// JavaScript Document
function Player(n){
this.index = ++Player.number;
this.hasTurn = false;
this.mancala = document.getElementById("p" + n);
this.holes = document.getElementsByName("p" + n);
if(this.holes[0].id != (n + '-' + 1)){
alert(this.holes[0].id);
this.holes = Array.prototype.reverse.call(document.getElementsByName("p" + n));
}
}
Player.number = 0;
Player.players = new Array(2);
Player.prototype.turn = function(){
this.hasTurn = true;
this.mancala.style.backgroundColor = "red";
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "red";
}
Player.prototype.grab = function(n){
var where;
stones = this.holes[n - 1].innerHTML;
if(!this.hasTurn || stones == 0) return;
for(this.holes[n - 1].innerHTML = 0; stones > 0; stones--){
where = (stones - n) % 13;
this.drop(where);
}
if(where != 0){
this.hasTurn = false;
this.mancala.style.backgroundColor = "white";
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = "white";
change();
}
}
Player.prototype.drop = function(n){
n = n % 13;
switch(n){
case -6: case -5: case -4: case -3: case -2: case -1:
next().holes[7 + n].innerHTML++;
break;
case 0:
this.mancala.innerHTML++;
break;
case 1: case 2: case 3: case 4: case 5: case 6:
this.holes[n - 1].innerHTML++;
break;
default:
alert('uncaught: drop(' + n + ')');
}
}
Player.prototype.change = function(){
this.hasTurn = false;
next().turn();
}
Player.prototype.next = function(){
return Player.players[this.index == 1 ? 0 : 1];
}
Player.init = function(s){
Player.players[0] = new Player(1);
Player.players[1] = new Player(2);
for(var i1 = 0; i1 < 2; i1++){
Player.players[i1].mancala.innerHTML = 0;
for(var i2 = 0; i2 < Player.players[i1].holes.length; i2++) Player.players[i1].holes[i2].innerHTML = s;
}
Player.players[0].turn();
}

mancala.css:
/* CSS Document */
table{
border: 1px solid black;
width: 100%;
}
td{
border: 1px solid black;
}
td.hole{
height: 100px;
text-align: center;
}
td.mancala{
width: 100px;
text-align: center;
}
td.minihole{
height: 50px;
text-align: center;
}
td.minimancala{
height: 50px;
text-align: center;
}

mancala.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="mancala.css">
<title>Mancala</title>
<script type="text/javascript" src="mancala.js"></script>
</head>
<body onLoad="Player.init(4)">
<table>
<tr>
<td class="mancala" id="p2" rowspan="2"></td>
<td class="hole" name="p2" id="2-1" onClick="players[1].grab(1)"></td>
<td class="hole" name="p2" id="2-2" onClick="players[1].grab(2)"></td>
<td class="hole" name="p2" id="2-3" onClick="players[1].grab(3)"></td>
<td class="hole" name="p2" id="2-4" onClick="players[1].grab(4)"></td>
<td class="hole" name="p2" id="2-5" onClick="players[1].grab(5)"></td>
<td class="hole" name="p2" id="2-6" onClick="players[1].grab(6)"></td>
<td class="mancala" id="p1" rowspan="2"></td>
</tr>
<tr>
<td class="hole" name="p1" id="1-6" onClick="players[0].grab(6)"></td>
<td class="hole" name="p1" id="1-5" onClick="players[0].grab(5)"></td>
<td class="hole" name="p1" id="1-4" onClick="players[0].grab(4)"></td>
<td class="hole" name="p1" id="1-3" onClick="players[0].grab(3)"></td>
<td class="hole" name="p1" id="1-2" onClick="players[0].grab(2)"></td>
<td class="hole" name="p1" id="1-1" onClick="players[0].grab(1)"></td>
</tr>
</table>
</body>
</html>

Twey
08-12-2007, 10:50 PM
*scratches head* It seems they'd make all of 'em arrays. Oh, well.DOM collections have some special abilities. For example, if you destroy an element, the element is automatically removed from all DOM collections you may have stored that contain it (causing no end of confusion to newbies with loops that destroy elements).
Oh. I was told (W3Schools?) that carried over to XHTML, which is being adopted as standard.It doesn't "carry over" to XHTML. It's primarily an XHTML construct. In SGML (of which HTML is technically a profile) it has a very different meaning: it's a "short tag" construct that no HTML parsers (of which I know) actually support, but <br /> should, by the rules of this construct, be rendered the same as <br>&gt;. Also beware XHTML. It does look to have a reasonable chance of becoming the next big markup language for the Web (HTML5 is still in the running, though), but IE doesn't yet support it. Until IE does get support, you should steer clear of it for general-purpose applications (i.e. anything that might be accessed with IE). W3Schools has a fairly bad reputation for mixing good information in with bad. It's not a bad reference overall, but take it with a pinch of salt.
I forget which property goes where and can't read my code except by referencing W3Schools (not for this combo, but many others).Shorthand vs. longhand properties are, of course, entirely a matter of personal preference. You still need border-style though. Longhand, it would be written:
border-style: solid;
border-color: black;
border-width: 1px;
My JS complies.This is again a matter of style, but since there are more than players involved in a game of mancala, I would suggest putting the whole script inside a namespacing object (called, perhaps, Mancala). You could then reference it as Mancala.Player, Mancala.Player.players, &c. I was raised on Java, and thus love namespacing; Dean Edwards would hate me :) Also, don't be afraid of creating small constructor functions for convenience methods. I would restructure this script entirely, dividing it into Mancala.Board, Mancala.Player, Mancala.Hole, and Mancala.Stone.
The original DOCTYPE:
Code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
is from my Dreamweaver 2004. You told me to use strict, and I only know (actually reference from W3Schools) the XHTML DOCTYPEs, but not what 'application/xhtml+xml' is.The HTML 4.01 Strict DOCTYPE (which you'd almost certainly be better off using) is:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">If you do wish to continue using XHTML, you need to serve it as XHTML: right now what you're actually sending is invalid HTML. When your webserver serves a document, it sends a series of headers containing meta-information about the document and request. One of these headers is called Content-Type, and it specifies what sort of content is being sent, in the form of a MIME type. Common values include, for example, image/jpeg for a JPEG image, text/html for an HTML document, or application/xhtml+xml for an XHTML document.
??? What (W3C) DOM methods do you mean?I apologise for not answering this earlier: I appear to have missed it. In this particular case, assuming you leave a spacer character in each hole, the only DOM property you would need to use would be .firstChild.nodeValue. You can set and get this just as you're currently doing with innerHTML (which, by the way, will cease to work if you switch to using XHTML).
stones = this.holes[n - 1].innerHTML;Wouldn't it be easier to just zero-index everything? Also, stones is unintentionally global.

Jesdisciple
08-14-2007, 12:17 AM
DOM collections have some special abilities. For example, if you destroy an element, the element is automatically removed from all DOM collections you may have stored that contain it (causing no end of confusion to newbies with loops that destroy elements).
I just realized that my collection isn't flipping in response to this.holes = Array.prototype.reverse.call(this.holes). See the code at bottom.


This is again a matter of style, but since there are more than players involved in a game of mancala, I would suggest putting the whole script inside a namespacing object (called, perhaps, Mancala). You could then reference it as Mancala.Player, Mancala.Player.players, &c. I was raised on Java, and thus love namespacing; Dean Edwards would hate me :) Also, don't be afraid of creating small constructor functions for convenience methods. I would restructure this script entirely, dividing it into Mancala.Board, Mancala.Player, Mancala.Hole, and Mancala.Stone.That'll take a while.


The HTML 4.01 Strict DOCTYPE (which you'd almost certainly be better off using) is:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

If you do wish to continue using XHTML, you need to serve it as XHTML: right now what you're actually sending is invalid HTML. When your webserver serves a document, it sends a series of headers containing meta-information about the document and request. One of these headers is called Content-Type, and it specifies what sort of content is being sent, in the form of a MIME type. Common values include, for example, image/jpeg for a JPEG image, text/html for an HTML document, or application/xhtml+xml for an XHTML document.I'm curious... How is it invalid HTML?


I apologise for not answering this earlier: I appear to have missed it. In this particular case, assuming you leave a spacer character in each hole, the only DOM property you would need to use would be .firstChild.nodeValue. You can set and get this just as you're currently doing with innerHTML (which, by the way, will cease to work if you switch to using XHTML).When innerHTML is replaced with firstChild.nodeValue in the below script, firstChild is null so nodeValue aborts the script.


// JavaScript Document
function Mancala(){

}
Mancala.number = 0;
Mancala.players;
Mancala.init = function(s, p){// stones/hole, players
if(p < 2) return;
Mancala.stonesPerHole = s;
Mancala.players = new Array(p);
for(var i = 0; i < p; i++) Mancala.players[i] = new Mancala.Player();
(Mancala.current = Mancala.players[0]).turn();
}
Mancala.change = function(){
(Mancala.current = Mancala.current.next()).turn();
}
Mancala.Player = function(){
this.index = Mancala.number++;

this.mancala = document.getElementById('p' + this.index);
this.mancala.innerHTML = 0;

this.holes = document.getElementsByName('p' + this.index);
if(this.holes[0].id != ('h' + this.index + '-' + 0)) this.holes = Array.prototype.reverse.call(this.holes);
for(var i = 0; i < this.holes.length; i++){
alert(this.holes[i].innerHTML);
this.holes[i].innerHTML = Mancala.stonesPerHole;
}
}
Mancala.Player.prototype.turn = function(){
Mancala.current = this;
this.mancala.style.backgroundColor = 'red';
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = 'red';
}
Mancala.Player.prototype.grab = function(n){
alert('grabbing ' + n);
var start = n;
var where = start - 1;
var stones = this.holes[n].innerHTML;
if(this != Mancala.current || stones == 0) return;
this.holes[n].innerHTML = 0;
while(stones > 0 && where > 0){
this.drop(where--);
stones--;
}
if(where != -1){
this.mancala.style.backgroundColor = 'white';
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = 'white';
Mancala.change();
}
}
Mancala.Player.prototype.drop = function(n){
n = n % 13;
alert('dropping ' + n);
switch(n){
case -7: case -6: case -5: case -4: case -3: case -2:
this.next().holes[6 + n].innerHTML++;
break;
case -1:
this.mancala.innerHTML++;
break;
case 0: case 1: case 2: case 3: case 4: case 5:
this.holes[n].innerHTML++;
break;
default:
alert('uncaught: drop(' + n + ')');
}
}
Mancala.Player.prototype.next = function(){
return Mancala.players[this.index == Mancala.players.length - 1 ? 0 : this.index + 1];
}


/* CSS Document */
table{
border-style: solid;
border-color: black;
border-width: 1px;
width: 100%;
}
td{
border-style: solid;
border-color: black;
border-width: 1px;
}
td.hole{
height: 100px;
text-align: center;
}
td.mancala{
width: 100px;
text-align: center;
}
td.minihole{
height: 50px;
text-align: center;
}
td.minimancala{
height: 50px;
text-align: center;
}


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="mancala.css">
<title>Mancala</title>
<script type="text/javascript" src="mancala.js"></script>
</head>
<body onLoad="Mancala.init(4, 2)">
<table>
<tr>
<td class="mancala" id="p1" rowspan="2"></td>
<td class="hole" name="p1" id="h1-0" onClick="Mancala.players[1].grab(0)"></td>
<td class="hole" name="p1" id="h1-1" onClick="Mancala.players[1].grab(1)"></td>
<td class="hole" name="p1" id="h1-2" onClick="Mancala.players[1].grab(2)"></td>
<td class="hole" name="p1" id="h1-3" onClick="Mancala.players[1].grab(3)"></td>
<td class="hole" name="p1" id="h1-4" onClick="Mancala.players[1].grab(4)"></td>
<td class="hole" name="p1" id="h1-5" onClick="Mancala.players[1].grab(5)"></td>
<td class="mancala" id="p0" rowspan="2"></td>
</tr>
<tr>
<td class="hole" name="p0" id="h0-5" onClick="Mancala.players[0].grab(5)"></td>
<td class="hole" name="p0" id="h0-4" onClick="Mancala.players[0].grab(4)"></td>
<td class="hole" name="p0" id="h0-3" onClick="Mancala.players[0].grab(3)"></td>
<td class="hole" name="p0" id="h0-2" onClick="Mancala.players[0].grab(2)"></td>
<td class="hole" name="p0" id="h0-1" onClick="Mancala.players[0].grab(1)"></td>
<td class="hole" name="p0" id="h0-0" onClick="Mancala.players[0].grab(0)"></td>
</tr>
</table>
</body>
</html>

Twey
08-14-2007, 12:32 AM
I just realized that my collection isn't flipping in response to this.holes = Array.prototype.reverse.call(this.holes).Really? Try:
this.holes = Array.prototype.slice.call(this.holes).reverse();
That'll take a while.Not that long -- it's not a particularly complex script. I'm surprised you didn't design it this way in the first place, given that you know Java.
I'm curious... How is it invalid HTML?It's using a non-HTML DOCTYPE, for a start. There are other differences between XHTML and HTML as well, like self-closing tags:
<link />is (syntactically) valid XHTML, but not HTML except with short tags, which, although they are a feature of SGML, are usually not considered a feature of HTML because few or no HTML parsers support them. When they are supported, as I said, they don't mean the same as they do in XHTML.
When innerHTML is replaced with firstChild.nodeValue in the below script, firstChild is null so nodeValue aborts the script.I said:
assuming you leave a spacer character in each holeTry putting an &nbsp; in each hole element.

Jesdisciple
08-15-2007, 12:19 AM
Really? Try:
this.holes = Array.prototype.slice.call(this.holes).reverse();Never mind on that. I decided to step through the IDs and not flip.


Not that long -- it's not a particularly complex script. I'm surprised you didn't design it this way in the first place, given that you know Java.I'm not very object-oriented in JS, I guess because I don't have to be.


It's using a non-HTML DOCTYPE, for a start. There are other differences between XHTML and HTML as well, like self-closing tags:
<link />is (syntactically) valid XHTML, but not HTML except with short tags, which, although they are a feature of SGML, are usually not considered a feature of HTML because few or no HTML parsers support them. When they are supported, as I said, they don't mean the same as they do in XHTML.I thought you meant my updated code was invalid.


I said:

Try putting an &nbsp; in each hole element.I took "spacer character" to mean the 0 that remains in the holes when the stones are gone.

And I've apparently made a new problem: Nothing shows up because I transfered the layout to JS and the script aborts before entering the first function.


// JavaScript Document
Mancala.number = 0;
Mancala.init = function(s, area){// stones/hole, display area
alert(0);
Mancala.board = new Mancala.Board(s);
Mancala.board.display(area);
Mancala.board.init();
}
Mancala.change = function(){
(Mancala.current = Mancala.current.next()).turn();
}
Mancala.Player = function(){
this.index = Mancala.number++;

this.mancala = document.getElementById('p' + this.index);
this.mancala.innerHTML = 0;

for(var i = 0; i < 6; i++) this.holes[i] = document.getElementById('h' + this.index + '-' i);
for(var i = 0; i < this.holes.length; i++){
alert(this.holes[i].innerHTML);
this.holes[i].innerHTML = Mancala.stonesPerHole;
}
}
Mancala.Player.prototype.turn = function(){
Mancala.current = this;
this.mancala.style.backgroundColor = 'red';
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = 'red';
}
Mancala.Player.prototype.grab = function(n){
var start = n;
var where = start - 1;
var stones = this.holes[n].innerHTML;
if(this != Mancala.current || stones == 0) return;
alert('grabbing ' + n);
this.holes[n].innerHTML = 0;
while(stones > 0 && where > 0){
this.drop(where--);
stones--;
}
if(where != -1){
this.mancala.style.backgroundColor = 'white';
for(var i = 0; i < this.holes.length; i++) this.holes[i].style.backgroundColor = 'white';
Mancala.change();
}
}
Mancala.Player.prototype.drop = function(n){
n = n &#37; 13;
alert('dropping ' + n);
switch(n){
case -7: case -6: case -5: case -4: case -3: case -2:
this.next().holes[6 + n].innerHTML++;
break;
case -1:
this.mancala.innerHTML++;
break;
case 0: case 1: case 2: case 3: case 4: case 5:
this.holes[n].innerHTML++;
break;
default:
alert('uncaught: drop(' + n + ')');
}
}
Mancala.Player.prototype.next = function(){
return Mancala.players[this.index == Mancala.players.length - 1 ? 0 : this.index + 1];
}
Mancala.Board = function(s){
this.stonesPerHole = s;
this.players = new Array(2);
}
Mancala.Board.prototype.init(){
for(var i = 0; i < p; i++) this.players[i] = new Mancala.Player();
(this.current = this.players[0]).turn();
}
Mancala.Board.prototype.display(area){
var table = document.createElement('table');
table.insertRow(0);
table.rows[0].insertCell(0);
table.rows[0].cells[0].rowSpan = 2;
table.rows[0].cells[0].className = 'mancala';
table.rows[0].cells[0].id = 'p' + 0;

for(var i2 = 0; i2 < 6; i2++){
table.rows[0].insertCell(i2);
table.rows[0].cells[i2].className = 'hole';
table.rows[0].cells[i2].id = 'h' + 0 + '-' i2;
table.rows[0].cells[i2].onClick='Mancala.board.players[0].grab(' + i2 + ')';
}
table.rows[0].insertCell(6);
table.rows[0].cells[6].rowSpan = 2;
table.rows[0].cells[6].className = 'mancala';
table.rows[0].cells[6].id = 'p' + 1;

table.insertRow(1);
for(var i2 = 0; i2 < 6; i2++){
table.rows[1].insertCell(i2);
table.rows[1].cells[i2].className = 'hole';
table.rows[1].cells[i2].id = 'h' + 1 + '-' (6 - i2);
table.rows[0].cells[i2].onClick='Mancala.board.players[1].grab(' + i2 + ')';
}
area.appendChild(table);
}


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" type="text/css" href="mancala.css">
<title>Mancala</title>
<script type="text/javascript" src="mancala.js"></script>
</head>
<body onLoad="Mancala.init(4, this)">
</body>
</html>


/* CSS Document */
table{
border-style: solid;
border-color: black;
border-width: 1px;
width: 100%;
}
td{
border-style: solid;
border-color: black;
border-width: 1px;
}
td.hole{
height: 100px;
text-align: center;
}
td.mancala{
width: 100px;
text-align: center;
}

td.minihole, td.minimancala{
height: 50px;
text-align: center;
}

EDIT: I will be uploading the page to http://www.auto-chat.net/ccarter/mancala.html from now on. (I might forget, just remind me.)

Twey
08-15-2007, 03:58 PM
You haven't defined Mancala, which you need to do before adding properties to it:
var Mancala = {};

Jesdisciple
08-16-2007, 01:04 AM
You haven't defined Mancala, which you need to do before adding properties to it:
var Mancala = {};

I just realized that Firefox has an error console. I'm still used to the automatic alerts of IE which I haven't used for one or more years. As it turned out, that was a good time for such a discovery, as I had made several stupid mistakes. Oddly, the error console doesn't work for me at the uploaded URL. (It might be that the JS is interpreted server-side; it's a friend's site and I'm not server-literate.)

Now I can't figure out why document.getElementById() has no properties and how this makes document.getElementById().appendChild() impossible. According to http://www.java2s.com/Code/JavaScriptReference/Javascript-Methods/appendChildSyntaxParametersandNote.htm, this should work.

http://www.auto-chat.net/ccarter/mancala.html

Twey
08-16-2007, 02:28 AM
Means the query didn't return anything: in this case, there's no element with ID "p1."

Jesdisciple
08-16-2007, 02:15 PM
Means the query didn't return anything: in this case, there's no element with ID "p1."

document.getElementById('p1') was a debugging attempt, and I've now changed my code to use an actual element rather than its ID. It should read:


area.appendChild(table);

area is the body element, passed here:


<body onLoad="Mancala.init(4, this)">

And yet...

Error: area.appendChild is not a function
Source File: mancala.js
Line: 100

I tried the id argument, but document.getElementById(id).appendChild(child) is apparently not allowed, either.