Skip to content

Instantly share code, notes, and snippets.

@CoolOppo
Created January 12, 2015 18:25
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save CoolOppo/c52d0cef39fcc051c7c7 to your computer and use it in GitHub Desktop.
Save CoolOppo/c52d0cef39fcc051c7c7 to your computer and use it in GitHub Desktop.
/*
* Chemical equation balancer
*
* Copyright (c) 2014 Project Nayuki
* All rights reserved. Contact Nayuki for licensing.
* http://www.nayuki.io/page/chemical-equation-balancer-javascript
*/
"use strict";
/* Main functions, which are the entry points from the HTML code */
// Balances the given formula string and sets the HTML output on the page. Returns nothing.
function balance(formulaStr) {
// Clear output
setMessage("");
var balancedElem = document.getElementById("balanced");
var codeOutElem = document.getElementById("codeOutput");
removeAllChildren(balancedElem);
removeAllChildren(codeOutElem);
appendText(" ", codeOutElem);
// Parse equation
var eqn;
try {
eqn = parse(formulaStr);
} catch (e) {
if (typeof e == "string") { // Simple error message string
setMessage("Syntax error: " + e);
} else if ("start" in e) { // Error message object with start and possibly end character indices
setMessage("Syntax error: " + e.message);
var start = e.start;
var end = "end" in e ? e.end : e.start;
while (end > start && (formulaStr.charAt(end - 1) == " " || formulaStr.charAt(end - 1) == "\t"))
end--; // Adjust position to eliminate whitespace
if (start == end)
end++;
appendText(formulaStr.substring(0, start), codeOutElem);
var highlight = document.createElement("u");
if (end <= formulaStr.length) {
appendText(formulaStr.substring(start, end), highlight);
codeOutElem.appendChild(highlight);
appendText(formulaStr.substring(end, formulaStr.length), codeOutElem);
} else {
appendText(" ", highlight);
codeOutElem.appendChild(highlight);
}
} else {
setMessage("Assertion error");
}
return;
}
try {
var matrix = buildMatrix(eqn); // Set up matrix
solve(matrix); // Solve linear system
var coefs = extractCoefficients(matrix); // Get coefficients
checkAnswer(eqn, coefs); // Self-test, should not fail
balancedElem.appendChild(eqn.toHtml(coefs)); // Display balanced equation
} catch (e) {
setMessage(e.toString());
}
}
// Sets the input box to the given formula string and balances it. Returns nothing.
function demo(formulaStr) {
document.getElementById("inputFormula").value = formulaStr;
balance(formulaStr);
}
var RANDOM_DEMOS = [
"H2 + O2 = H2O",
"Fe + O2 = Fe2O3",
"NH3 + O2 = N2 + H2O",
"C2H2 + O2 = CO2 + H2O",
"C3H8O + O2 = CO2 + H2O",
"Na + O2 = Na2O",
"P4 + O2 = P2O5",
"Na2O + H2O = NaOH",
"Mg + HCl = MgCl2 + H2",
"AgNO3 + LiOH = AgOH + LiNO3",
"Pb + PbO2 + H^+ + SO4^2- = PbSO4 + H2O",
"HNO3 + Cu = Cu(NO3)2 + H2O + NO",
"KNO2 + KNO3 + Cr2O3 = K2CrO4 + NO",
"AgNO3 + BaCl2 = Ba(NO3)2 + AgCl",
"Cu(NO3)2 = CuO + NO2 + O2",
"Al + CuSO4 = Al2(SO4)3 + Cu",
"Na3PO4 + Zn(NO3)2 = NaNO3 + Zn3(PO4)2",
"Cl2 + Ca(OH)2 = Ca(ClO)2 + CaCl2 + H2O",
"CHCl3 + O2 = CO2 + H2O + Cl2",
"H2C2O4 + MnO4^- = H2O + CO2 + MnO + OH^-",
"H2O2 + Cr2O7^2- = Cr^3+ + O2 + OH^-",
"KBr + KMnO4 + H2SO4 = Br2 + MnSO4 + K2SO4 + H2O",
"K2Cr2O7 + KI + H2SO4 = Cr2(SO4)3 + I2 + H2O + K2SO4",
"KClO3 + KBr + HCl = KCl + Br2 + H2O",
"Ag + HNO3 = AgNO3 + NO + H2O",
"P4 + OH^- + H2O = H2PO2^- + P2H4",
"Zn + NO3^- + H^+ = Zn^2+ + NH4^+ + H2O",
"ICl + H2O = Cl^- + IO3^- + I2 + H^+",
"AB2 + AC3 + AD5 + AE7 + AF11 + AG13 + AH17 + AI19 + AJ23 = A + ABCDEFGHIJ",
];
var lastRandomIndex = -1;
function random() {
var index;
do {
index = Math.floor(Math.random() * RANDOM_DEMOS.length);
index = Math.max(Math.min(index, RANDOM_DEMOS.length - 1), 0);
} while (RANDOM_DEMOS.length >= 2 && index == lastRandomIndex);
lastRandomIndex = index;
demo(RANDOM_DEMOS[index]);
}
/* Core number-processing fuctions */
// Returns a matrix based on the given equation object.
function buildMatrix(eqn) {
var elems = eqn.getElements();
var rows = elems.length + 1;
var cols = eqn.getLeftSide().length + eqn.getRightSide().length + 1;
var matrix = new Matrix(rows, cols);
for (var i = 0; i < elems.length; i++) {
var j = 0;
for (var k = 0, lhs = eqn.getLeftSide() ; k < lhs.length; j++, k++)
matrix.set(i, j, lhs[k].countElement(elems[i]));
for (var k = 0, rhs = eqn.getRightSide(); k < rhs.length; j++, k++)
matrix.set(i, j, -rhs[k].countElement(elems[i]));
}
return matrix;
}
function solve(matrix) {
matrix.gaussJordanEliminate();
// Find row with more than one non-zero coefficient
var i;
for (i = 0; i < matrix.rowCount() - 1; i++) {
if (countNonzeroCoeffs(matrix, i) > 1)
break;
}
if (i == matrix.rowCount() - 1)
throw "All-zero solution"; // Unique solution with all coefficients zero
// Add an inhomogeneous equation
matrix.set(matrix.rowCount() - 1, i, 1);
matrix.set(matrix.rowCount() - 1, matrix.columnCount() - 1, 1);
matrix.gaussJordanEliminate();
}
function countNonzeroCoeffs(matrix, row) {
var count = 0;
for (var i = 0; i < matrix.columnCount(); i++) {
if (matrix.get(row, i) != 0)
count++;
}
return count;
}
function extractCoefficients(matrix) {
var rows = matrix.rowCount();
var cols = matrix.columnCount();
if (cols - 1 > rows || matrix.get(cols - 2, cols - 2) == 0)
throw "Multiple independent solutions";
var lcm = 1;
for (var i = 0; i < cols - 1; i++)
lcm = checkedMultiply(lcm / gcd(lcm, matrix.get(i, i)), matrix.get(i, i));
var coefs = [];
var allzero = true;
for (var i = 0; i < cols - 1; i++) {
var coef = checkedMultiply(lcm / matrix.get(i, i), matrix.get(i, cols - 1));
coefs.push(coef);
allzero &= coef == 0;
}
if (allzero)
throw "Assertion error: All-zero solution";
return coefs;
}
// Throws an exception if there's a problem, otherwise returns silently.
function checkAnswer(eqn, coefs) {
if (coefs.length != eqn.getLeftSide().length + eqn.getRightSide().length)
throw "Assertion error: Mismatched length";
var allzero = true;
for (var i = 0; i < coefs.length; i++) {
var coef = coefs[i];
if (typeof coef != "number" || isNaN(coef) || Math.floor(coef) != coef)
throw "Assertion error: Not an integer";
allzero &= coef == 0;
}
if (allzero)
throw "Assertion error: All-zero solution";
var elems = eqn.getElements();
for (var i = 0; i < elems.length; i++) {
var sum = 0;
var j = 0;
for (var k = 0, lhs = eqn.getLeftSide() ; k < lhs.length; j++, k++)
sum = checkedAdd(sum, checkedMultiply(lhs[k].countElement(elems[i]), coefs[j]));
for (var k = 0, rhs = eqn.getRightSide(); k < rhs.length; j++, k++)
sum = checkedAdd(sum, checkedMultiply(rhs[k].countElement(elems[i]), -coefs[j]));
if (sum != 0)
throw "Assertion error: Incorrect balance";
}
}
/* Chemical equation data types */
// A complete chemical equation. It has a left-hand side list of terms and a right-hand side list of terms.
// For example: H2 + O2 -> H2O.
function Equation(lhs, rhs) {
// Make defensive copies
lhs = cloneArray(lhs);
rhs = cloneArray(rhs);
this.getLeftSide = function() { return cloneArray(lhs); }
this.getRightSide = function() { return cloneArray(rhs); }
// Returns an array of the names all of the elements used in this equation.
// The array represents a set, so the items are in an arbitrary order and no item is repeated.
this.getElements = function() {
var result = new Set();
for (var i = 0; i < lhs.length; i++)
lhs[i].getElements(result);
for (var i = 0; i < rhs.length; i++)
rhs[i].getElements(result);
return result.toArray();
}
// Returns an HTML element representing this equation.
// 'coefs' is an optional argument, which is an array of coefficients to match with the terms.
this.toHtml = function(coefs) {
if (coefs !== undefined && coefs.length != lhs.length + rhs.length)
throw "Mismatched number of coefficients";
// Creates this kind of DOM node: <span class="className">text</span>
function createSpan(text, className) {
var span = document.createElement("span");
appendText(text, span);
span.className = className;
return span;
}
var node = document.createElement("span");
var j = 0;
function termsToHtml(terms) {
var head = true;
for (var i = 0; i < terms.length; i++, j++) {
var coef = coefs !== undefined ? coefs[j] : 1;
if (coef != 0) {
if (head) head = false;
else node.appendChild(createSpan(" + ", "plus"));
if (coef != 1)
node.appendChild(createSpan(coef.toString().replace(/-/, MINUS), "coefficient"));
node.appendChild(terms[i].toHtml());
}
}
}
termsToHtml(lhs);
node.appendChild(createSpan(" " + RIGHT_ARROW + " ", "rightarrow"));
termsToHtml(rhs);
return node;
}
}
// A term in a chemical equation. It has a list of groups or elements, and a charge.
// For example: H3O^+, or e^-.
function Term(items, charge) {
if (items.length == 0 && charge != -1)
throw "Invalid term"; // Electron case
items = cloneArray(items);
this.getItems = function() { return cloneArray(items); }
this.getElements = function(resultSet) {
resultSet.add("e");
for (var i = 0; i < items.length; i++)
items[i].getElements(resultSet);
}
// Counts the number of times the given element (specified as a string) occurs in this term, taking groups and counts into account, returning an integer.
this.countElement = function(name) {
if (name == "e") {
return -charge;
} else {
var sum = 0;
for (var i = 0; i < items.length; i++)
sum = checkedAdd(sum, items[i].countElement(name));
return sum;
}
}
// Returns an HTML element representing this term.
this.toHtml = function() {
var node = document.createElement("span");
if (items.length == 0 && charge == -1) {
appendText("e", node);
var sup = document.createElement("sup");
appendText(MINUS, sup);
node.appendChild(sup);
} else {
for (var i = 0; i < items.length; i++)
node.appendChild(items[i].toHtml());
if (charge != 0) {
var sup = document.createElement("sup");
var s;
if (Math.abs(charge) == 1) s = "";
else s = Math.abs(charge).toString();
if (charge > 0) s += "+";
else s += MINUS;
appendText(s, sup);
node.appendChild(sup);
}
}
return node;
}
}
// A group in a term. It has a list of groups or elements.
// For example: (OH)3
function Group(items, count) {
if (count < 1)
throw "Assertion error: Count must be a positive integer";
items = cloneArray(items);
this.getItems = function() { return cloneArray(items); }
this.getCount = function() { return count; }
this.getElements = function(resultSet) {
for (var i = 0; i < items.length; i++)
items[i].getElements(resultSet);
}
this.countElement = function(name) {
var sum = 0;
for (var i = 0; i < items.length; i++)
sum = checkedAdd(sum, checkedMultiply(items[i].countElement(name), count));
return sum;
}
// Returns an HTML element representing this group.
this.toHtml = function() {
var node = document.createElement("span");
appendText("(", node);
for (var i = 0; i < items.length; i++)
node.appendChild(items[i].toHtml());
appendText(")", node);
if (count != 1) {
var sub = document.createElement("sub");
appendText(count.toString(), sub);
node.appendChild(sub);
}
return node;
}
}
// A chemical element.
// For example: Na, F2, Ace, Uuq6
function Element(name, count) {
if (count < 1)
throw "Assertion error: Count must be a positive integer";
this.getName = function() { return name; }
this.getCount = function() { return count; }
this.getElements = function(resultSet) { resultSet.add(name); }
this.countElement = function(n) { return n == name ? count : 0; }
// Returns an HTML element representing this element.
this.toHtml = function() {
var node = document.createElement("span");
appendText(name, node);
if (count != 1) {
var sub = document.createElement("sub");
appendText(count.toString(), sub);
node.appendChild(sub);
}
return node;
}
}
/* Parser functions */
// Parses the given formula string and returns an equation object, or throws an exception.
function parse(formulaStr) {
var tokenizer = new Tokenizer(formulaStr);
return parseEquation(tokenizer);
}
// Parses and returns an equation.
function parseEquation(tok) {
var lhs = [];
var rhs = [];
lhs.push(parseTerm(tok));
while (true) {
var next = tok.peek();
if (next == "=") {
tok.consume("=");
break;
} else if (next == null) {
throw {message: "Plus or equal sign expected", start: tok.position()};
} else if (next == "+") {
tok.consume("+");
lhs.push(parseTerm(tok));
} else
throw {message: "Plus expected", start: tok.position()};
}
rhs.push(parseTerm(tok));
while (true) {
var next = tok.peek();
if (next == null)
break;
else if (next == "+") {
tok.consume("+");
rhs.push(parseTerm(tok));
} else
throw {message: "Plus or end expected", start: tok.position()};
}
return new Equation(lhs, rhs);
}
// Parses and returns a term.
function parseTerm(tok) {
var startPosition = tok.position();
// Parse groups and elements
var items = [];
while (true) {
var next = tok.peek();
if (next == null)
break;
else if (next == "(")
items.push(parseGroup(tok));
else if (/^[A-Za-z][a-z]*$/.test(next))
items.push(parseElement(tok));
else
break;
}
// Parse optional charge
var charge = 0;
var next = tok.peek();
if (next != null && next == "^") {
tok.consume("^");
next = tok.peek();
if (next == null)
throw {message: "Number or sign expected", start: tok.position()};
else
charge = parseOptionalNumber(tok);
next = tok.peek();
if (next == "+")
charge = +charge; // No-op
else if (next == "-")
charge = -charge;
else
throw {message: "Sign expected", start: tok.position()};
tok.take(); // Consume the sign
}
// Check if term is valid
var elems = new Set();
for (var i = 0; i < items.length; i++)
items[i].getElements(elems);
elems = elems.toArray(); // List of all elements used in this term, with no repeats
if (items.length == 0) {
throw {message: "Invalid term - empty", start: startPosition, end: tok.position()};
} else if (indexOf(elems, "e") != -1) { // If it's the special electron element
if (items.length > 1)
throw {message: "Invalid term - electron needs to stand alone", start: startPosition, end: tok.position()};
else if (charge != 0 && charge != -1)
throw {message: "Invalid term - invalid charge for electron", start: startPosition, end: tok.position()};
// Tweak data
items = [];
charge = -1;
} else { // Otherwise, a term must not contain an element that starts with lowercase
for (var i = 0; i < elems.length; i++) {
if (/^[a-z]+$/.test(elems[i]))
throw {message: 'Invalid element name "' + elems[i] + '"', start: startPosition, end: tok.position()};
}
}
return new Term(items, charge);
}
// Parses and returns a group.
function parseGroup(tok) {
var startPosition = tok.position();
tok.consume("(");
var items = [];
while (true) {
var next = tok.peek();
if (next == null)
throw {message: "Element, group, or closing parenthesis expected", start: tok.position()};
else if (next == "(")
items.push(parseGroup(tok));
else if (/^[A-Za-z][a-z]*$/.test(next))
items.push(parseElement(tok));
else if (next == ")") {
tok.consume(")");
if (items.length == 0)
throw {message: "Empty group", start: startPosition, end: tok.position()};
break;
} else
throw {message: "Element, group, or closing parenthesis expected", start: tok.position()};
}
return new Group(items, parseOptionalNumber(tok));
}
// Parses and returns an element.
function parseElement(tok) {
var name = tok.take();
if (!/^[A-Za-z][a-z]*$/.test(name))
throw "Assertion error";
return new Element(name, parseOptionalNumber(tok));
}
// Parses a number if it's the next token, returning a non-negative integer, with a default of 1.
function parseOptionalNumber(tok) {
var next = tok.peek();
if (next != null && /^[0-9]+$/.test(next))
return checkedParseInt(tok.take());
else
return 1;
}
/* Tokenizer object */
// Tokenizes a formula into a stream of token strings.
function Tokenizer(str) {
var i = 0;
// Returns the index of the next character to tokenize.
this.position = function() {
return i;
}
// Returns the next token as a string, or null if the end of the token stream is reached.
this.peek = function() {
if (i == str.length) // End of stream
return null;
var match = /^([A-Za-z][a-z]*|[0-9]+|[+\-^=()])/.exec(str.substring(i));
if (match == null)
throw {message: "Invalid symbol", start: i};
return match[0];
}
// Returns the next token as a string and advances this tokenizer past the token.
this.take = function() {
var result = this.peek();
if (result == null)
throw "Advancing beyond last token"
i += result.length;
skipSpaces();
return result;
}
// Takes the next token and checks that it matches the given string, or throws an exception.
this.consume = function(s) {
if (this.take() != s)
throw "Token mismatch";
}
function skipSpaces() {
var match = /^[ \t]*/.exec(str.substring(i));
i += match[0].length;
}
str = str.replace(/\u2212/g, "-");
skipSpaces();
}
/* Matrix object */
// A matrix of integers.
function Matrix(rows, cols) {
if (rows < 0 || cols < 0)
throw "Illegal argument";
// Initialize with zeros
var row = [];
for (var j = 0; j < cols; j++)
row.push(0);
var cells = []; // Main data (the matrix)
for (var i = 0; i < rows; i++)
cells.push(cloneArray(row));
row = null;
/* Accessor functions */
this.rowCount = function() { return rows; }
this.columnCount = function() { return cols; }
// Returns the value of the given cell in the matrix, where r is the row and c is the column.
this.get = function(r, c) {
if (r < 0 || r >= rows || c < 0 || c >= cols)
throw "Index out of bounds";
return cells[r][c];
}
// Sets the given cell in the matrix to the given value, where r is the row and c is the column.
this.set = function(r, c, val) {
if (r < 0 || r >= rows || c < 0 || c >= cols)
throw "Index out of bounds";
cells[r][c] = val;
}
/* Private helper functions for gaussJordanEliminate() */
// Swaps the two rows of the given indices in this matrix. The degenerate case of i == j is allowed.
function swapRows(i, j) {
if (i < 0 || i >= rows || j < 0 || j >= rows)
throw "Index out of bounds";
var temp = cells[i];
cells[i] = cells[j];
cells[j] = temp;
}
// Returns a new row that is the sum of the two given rows. The rows are not indices. This object's data is unused.
// For example, addRow([3, 1, 4], [1, 5, 6]) = [4, 6, 10].
function addRows(x, y) {
var z = cloneArray(x);
for (var i = 0; i < z.length; i++)
z[i] = checkedAdd(x[i], y[i]);
return z;
}
// Returns a new row that is the product of the given row with the given scalar. The row is is not an index. This object's data is unused.
// For example, multiplyRow([0, 1, 3], 4) = [0, 4, 12].
function multiplyRow(x, c) {
var y = cloneArray(x);
for (var i = 0; i < y.length; i++)
y[i] = checkedMultiply(x[i], c);
return y;
}
// Returns the GCD of all the numbers in the given row. The row is is not an index. This object's data is unused.
// For example, gcdRow([3, 6, 9, 12]) = 3.
function gcdRow(x) {
var result = 0;
for (var i = 0; i < x.length; i++)
result = gcd(x[i], result);
return result;
}
// Returns a new row where the leading non-zero number (if any) is positive, and the GCD of the row is 0 or 1. This object's data is unused.
// For example, simplifyRow([0, -2, 2, 4]) = [0, 1, -1, -2].
function simplifyRow(x) {
var sign = 0;
for (var i = 0; i < x.length; i++) {
if (x[i] > 0) {
sign = 1;
break;
} else if (x[i] < 0) {
sign = -1;
break;
}
}
var y = cloneArray(x);
if (sign == 0)
return y;
var g = gcdRow(x) * sign;
for (var i = 0; i < y.length; i++)
y[i] /= g;
return y;
}
// Changes this matrix to reduced row echelon form (RREF), except that each leading coefficient is not necessarily 1. Each row is simplified.
this.gaussJordanEliminate = function() {
// Simplify all rows
for (var i = 0; i < rows; i++)
cells[i] = simplifyRow(cells[i]);
// Compute row echelon form (REF)
var numPivots = 0;
for (var i = 0; i < cols; i++) {
// Find pivot
var pivotRow = numPivots;
while (pivotRow < rows && cells[pivotRow][i] == 0)
pivotRow++;
if (pivotRow == rows)
continue;
var pivot = cells[pivotRow][i];
swapRows(numPivots, pivotRow);
numPivots++;
// Eliminate below
for (var j = numPivots; j < rows; j++) {
var g = gcd(pivot, cells[j][i]);
cells[j] = simplifyRow(addRows(multiplyRow(cells[j], pivot / g), multiplyRow(cells[i], -cells[j][i] / g)));
}
}
// Compute reduced row echelon form (RREF), but the leading coefficient need not be 1
for (var i = rows - 1; i >= 0; i--) {
// Find pivot
var pivotCol = 0;
while (pivotCol < cols && cells[i][pivotCol] == 0)
pivotCol++;
if (pivotCol == cols)
continue;
var pivot = cells[i][pivotCol];
// Eliminate above
for (var j = i - 1; j >= 0; j--) {
var g = gcd(pivot, cells[j][pivotCol]);
cells[j] = simplifyRow(addRows(multiplyRow(cells[j], pivot / g), multiplyRow(cells[i], -cells[j][pivotCol] / g)));
}
}
}
// Returns a string representation of this matrix, for debugging purposes.
this.toString = function() {
var result = "[";
for (var i = 0; i < rows; i++) {
if (i != 0) result += "],\n";
result += "[";
for (var j = 0; j < cols; j++) {
if (j != 0) result += ", ";
result += cells[i][j];
}
result += "]";
}
return result + "]";
}
}
/* Set object */
function Set() {
// Storage for the set
var items = [];
// Adds the given object to the set. Returns nothing.
this.add = function(obj) {
if (indexOf(items, obj) == -1)
items.push(obj);
}
// Tests if the given object is in the set, returning a Boolean.
this.contains = function(obj) {
return items.indexOf(obj) != -1;
}
// Returns an array containing the elements of this set in an arbitrary order, with no duplicates.
this.toArray = function() {
return cloneArray(items);
}
}
/* Math functions (especially checked integer operations) */
var INT_MAX = 9007199254740992; // 2^53
// Returns the given string parsed into a number, or throws an exception if the result is too large.
function checkedParseInt(str) {
var result = parseInt(str, 10);
if (isNaN(result))
throw "Not a number";
if (result <= -INT_MAX || result >= INT_MAX)
throw "Arithmetic overflow";
return result;
}
// Returns the sum of the given integers, or throws an exception if the result is too large.
function checkedAdd(x, y) {
var z = x + y;
if (z <= -INT_MAX || z >= INT_MAX)
throw "Arithmetic overflow";
return z;
}
// Returns the product of the given integers, or throws an exception if the result is too large.
function checkedMultiply(x, y) {
var z = x * y;
if (z <= -INT_MAX || z >= INT_MAX)
throw "Arithmetic overflow";
return z;
}
// Returns the greatest common divisor of the given integers.
function gcd(x, y) {
if (typeof x != "number" || typeof y != "number" || isNaN(x) || isNaN(y))
throw "Invalid argument";
x = Math.abs(x);
y = Math.abs(y);
while (y != 0) {
var z = x % y;
x = y;
y = z;
}
return x;
}
/* Miscellaneous */
// Unicode character constants (because this script file's character encoding is unspecified)
var MINUS = "\u2212"; // Minus sign
var RIGHT_ARROW = "\u2192"; // Right arrow
// A JavaScript 1.6 function for Array, which every browser has except for Internet Explorer.
function indexOf(array, item) {
for (var i = 0; i < array.length; i++) {
if (array[i] == item)
return i;
}
return -1;
}
// Returns a shallow copy of the given array. Usually used for making defensive copies.
function cloneArray(array) {
return array.slice(0);
}
// Sets the page's message element to the given string. Returns nothing.
function setMessage(str) {
var messageElem = document.getElementById("message");
removeAllChildren(messageElem);
appendText(str, messageElem);
}
// Removes all the children of the given DOM node. Returns nothing.
function removeAllChildren(node) {
while (node.childNodes.length > 0)
node.removeChild(node.firstChild);
}
// Creates a new text node with the given text and appends it to the given DOM node. Returns nothing.
function appendText(text, node) {
node.appendChild(document.createTextNode(text));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment