Javascript Libraries

Contents

This page describes some of my Javascript libraries which are available at http://www.cs.bham.ac.uk/~pxc/js/.

Javascript Style

As many authors have noted, Javascript's object-oriented features are of limited value. One key issue is information hiding (i.e. creating the equivalent of Java's private for the fields of objects):

My preference is to take advantage of Javascript's strengths as a functional programming language. For example, the ability to construct functions 'on the fly' and pass them as arguments greatly simplifies the code for some algorithms. However, it is vital to have some mechanism for partioning name spaces, to avoid effectively creating a new set of keywords.

The approach that I have taken is to construct 'packages' as methods of global objects (similar to the methods of the Math object in Java or Javascript). Using Javascript's with, it is easier to write code based on this approach than it would be in Java.

As a concrete example, Matrix.js implements functions to handle matrices, stored as methods of the global object Matrix. Here is an annotated example of how this library might be used.

with (Matrix) // set up default source for otherwise unknown methods
{ // Create a new 'matrix object' A from a 2D array:
  var A = create([[1,2,4],[8,2,1],[-2,3,0]]);

  // Display A with 2 decimal places:
  display(A,2);

  // Invert A and show the values of inverse(A)*A and
  // inverse(A)*A - I:
  var Ainv = inverse(A);
  display(mult(Ainv,A), 2);
  display(sub(mult(Ainv,A), identity(A.n,A.m)), 2);

  // Solve the matrix equation A*X = B for X:
  var B = random(3,2);
  display(B,2);
  var X = solve(A,B);
  display(X,2);

  // Demonstrate that A*X = B:
  display(sub(mult(A,X), B), 2);
}

Personally I find expressions like sub(mult(inverse(A), A), mult(A, inverse(A))) much clearer than the mixed style which results from the object-oriented approach, namely A.inverse().mult(A).sub(A.mult(A.inverse())), but this is a matter of preference.

IOUtils.js

These are global functions, not methods of a global variable. Their purpose is to simplify 'writing' to a web page.

For details, see the source code of IOUtils.js.

The annotated example below shows how to set up a web page in which functions from IOUtils are used. The source for this web page is available online to use as a template.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html lang="en" xml:lang="en"
      xmlns="http://www.w3.org/1999/xhtml"> 
  <head> 
    <meta http-equiv="Content-Type"
          content="text/html; charset=UTF-8" />
    <meta http-equiv="cache-control" content="no-cache" />
    <title>TEST</title>
    <script type="text/javascript"
            src="http://www.cs.bham.ac.uk/~pxc/js/IOUtils.js">
    </script>
    <script type="text/javascript">

var outputBox; // location of output
// Initialize Javascript DOM objects when the page has loaded.
function init(inputID, outputID)
{ outputBox = document.getElementById(outputID);
}

// main() is where the main code is placed.
function main()
{ // YOUR CODE GOES HERE

  println('Hello world!');

  displayMat([[1,2,3],[2,1,5],[11,0,5]], null, null, 0);
}

// Standard function to run a Javascript program which outputs to the web page.
// The use of two different threads ensures that the output is cleared first.
// run can either be initiated by the user (e.g. via a button) or added
// to the body's onload actions so that it runs when the page is loaded.
function run()
{ startOutput(outputBox,'Running...');
  setTimeout(run_aux,100);
}
// Putting run_aux in a separate thread via setTimeout ensures that the
// output box is cleared first.
function run_aux()
{ try
  { main();
  }
  catch (e)
  { if (e != null) writeln('***ERROR: '+e);
    writeln('***Run terminated abnormally.');
  }
  endOutput(); // only now display the output in the window
}
</script>
  </head> 
  <body onload="init('input','output');"> 
    <h1>TEST</h1>
    <p>
      <input type="button" value="Run" onclick="run(); return false;" />
    </p>
    <div id="output"></div>
  </body> 
</html> 

Utils.js

This library (which is under development) provides a number of utility functions of various kinds, e.g. shuffling and sorting an array.

The code below shows how functions in Utils.js can be used. A web page containing this example is available online. The source code of Utils.js includes documentation for all the functions.

with (Utils)
{
  var A = [-3,1,8,74,101,192];
  println('Initial value of A:');
  displayMat([A],null,null,0);
  A = shuffle(A);
  println('After shuffling:');
  displayMat([A],null,null,0);
  A = sort(A);
  println('After sorting in ascending order:');
  displayMat([A],null,null,0);
  A = sort(shuffle(A), function (x,y) { return x > y; });
  println('After shuffling again and then sorting in descending order:');
  displayMat([A],null,null,0);
}

Set.js

This library provides functions to create and manipulate sets. The elements of the set must have values on which the operators ==, < and > can be used.

The code below shows how functions in Set.js can be used. A web page containing this example is available online. The source code of Sets.js includes documentation for all the functions.

with (Set)
{
  var s1 = addElem(8,addElem(6,addElem(1,create(3))));
  var s2 = addElem(4,addElem(1,null));
  println(union(s1,s2));
  println(intersection(s1,s2));
  println(intersectionR(s1,create(10)));
  
  var w1 = addElem('cat',addElem('dog',addElem('horse',addElem('donkey',null))));
  var w2 = addElem('horse',addElem('donkey',null));
  println(intersection(w1,w2));
  println(max(w1));
}

BTree0.js

This library provides functions to create and manipulate unordered binary trees, i.e. binary trees which do not keep the leaves of the tree in a particular order of their values.

he code below shows how functions in BTree0.js can be used. A web page containing this example is available online. The source code of BTree0.js includes documentation for all the functions.

with (BTree0)
{
  var t1 = createFromArray([[[4,5],[2,3]],1]);
  println('t1 = '+t1);
  display(t1);
  standardize(t1);
  println('After standardization');
  display(t1);
  var t2 = reroot(t1,2);
  println('After re-rooting at 2');
  display(t2);
  var forest = allExtended(t2,0);
  println('There are '+forest.length+' extended trees:');
  for (var i = 0; i < forest.length; i++) println(forest[i]);
}

Matrix.js

This library provides functions to create and manipulate matrices (2D arrays). Javascript versions of code from the JAMA library are used to provide operations such as inverse or determinant via LUDecomposition.js, QRDecomposition.js and EVDecomposition.js.

The code below shows how functions in Matrix.js can be used. A web page containing this example is available online. The source code of Matrix.js includes documentation for all the functions.

with (Matrix)
{ var A = create([[1,2,4],[8,2,1],[-2,3,0]]);
  println('A');
  display(A,0);

  var Ainv = inverse(A);
  nl(); println('inverse(A)*A');
  display(mult(Ainv,A));
  nl(); println('inverse(A)*A - I');
  display(sub(mult(Ainv,A),identity(A.n,A.m)));

  var B = random(3,2);
  nl(); println('B');
  display(B);
  var X = solve(A,B);
  nl(); println('X obtained by solving A*X = B');
  display(X);
  nl(); println('A*X - B');
  display(sub(mult(A,X),B));

  var es = eigenstructure(A);

  nl(); println('V (eigenvectors for A)');
  display(es.V);
  nl(); println('L (block diagonal eigenvalue matrix for A)');
  display(es.L);
  nl(); println('A*V - V*L');
  display(sub(mult(A,es.V),mult(es.V,es.L)));
  nl(); println('A - V*L*inverse(V)');
  display(sub(A,mult(es.V,mult(es.L,inverse(es.V)))));
}

LUDecomposition.js, QRDecomposition.js and EVDecomposition.js

These libraries provide Matrix.js with some advanced matrix operations. All three are based on the JAMA library. Functions in these libraries will not normally be used directly, but via methods of the Matrix global object.

The source code of LUDecomposition.js, QRDecomposition.js and EVDecomposition.js includes documentation for all the functions.

Developing Javascript

Two pieces of advice (learnt by experience!):

  1. Learn to make effective use of Firebug. This of course means using Firefox as your browser during code development as there are, as yet, no equivalents for Opera, Safari, etc.
  2. When Javascript is put into separate files, these have a tendency to get 'stuck' in caches, either locally or in your network. If you have changed a source file to correct a bug and the same error persists, the browser may well be loading the old file. Alerts displaying messages like "version 2.02 loaded" are a good way of checking that the right version is loading.

Page maintained by: Dr Peter Coxhead
Content last updated: 5 Jul 2011