5.5. JavaScript Application Code

There are two ways to use JavaScript in the third, deepest level of application programming. The first is to organize your JavaScript into libraries so your functions can be reused, distributed, and perhaps collaborated upon.

The second way is to write a JavaScript component, create a separate interface for that component, and compile it as an XPCOM component whose methods and data can be accessed from XPConnect (using JavaScript). This kind of application programming is described in Chapter 8, which includes examples of creating new interfaces, implementing them in JavaScript or C++, and compiling, testing, and using the resulting component in the Mozilla interface.

This section introduces the library organization method of JavaScript application programming. The JSLib code discussed here is a group of JavaScript libraries currently being developed by Mozilla contributors and is especially useful for working with the XPFE and other aspects of the Mozilla application/package programming model. When you include the right source files at the top of your JavaScript and/or XUL file, you can use the functions defined in JSLib libraries as you would use any third-party library or built-in functions. You may even want to contribute to the JSLib project yourself if you think functionality is missing and as your Mozilla programming skills grow.

5.5.1. JavaScript Libraries

The open source JSLib project makes life easier for developers. The JSLib package implements some of the key XPCOM components just discussed and wraps them in simpler, JavaScript interfaces, which means that you can use the services of common XPCOM components without having to do any of the instantiation, interface selection, or glue code yourself. Collectively, these interfaces are intended to provide a general-purpose library for Mozilla application developers. To understand what JSLib does, consider the following short snippet from the JSLib source file jslib/io/file.js, which implements a close( ) function for open file objects and provides a handy way to clean up things when you finish editing a file in the filesystem.

/********************* CLOSE ********************************
* void close( )                                              *
*                                                           *
* void file close                                           *
* return type void(null)                                    *
* takes no arguments closes an open file stream and         *
* deletes member var instances of objects                   *
*   Ex:                                                     *
*     var p='/tmp/foo.dat';                                 *
*     var f=new File(p);                                    *
*     fopen( );                                              *
*     f.close( );                                            *
*                                                           *
*   outputs: void(null)                                     *
************************************************************/
File.prototype.close = function( ) 
{
  /***************** Destroy Instances *********************/
  if(this.mFileChannel)   delete this.mFileChannel;
  if(this.mInputStream)   delete this.mInputStream;
  if(this.mTransport)     delete this.mTransport;
  if(this.mMode)          this.mMode=null;
  if(this.mOutStream) {
    this.mOutStream.close( );
    delete this.mOutStream;
  }
  if(this.mLineBuffer)     this.mLineBuffer=null;
  this.mPosition           = 0;
  /***************** Destroy Instances *********************/
  return;
}

To use the close method as it's defined here, import the file.js source file into your JavaScript, create a file object (as shown in the examples below), and call its close( ) method.

The source files for JSLib are well annotated and easy to read. JSLib provide easy-to-use interfaces for creating instances of components (e.g., File objects), performing necessary error checking, and ensuring proper usage. To use a function like the one just shown, simply include the source file you need in your XUL:

<script type="application/x-JavaScript"
    src="chrome://jslib/content/jslib.js" />

Then you can include the specific library files you need in your JavaScript code by using the include method:

include("chrome://jslib/content/io/file.js");
include("chrome://jslib/content/zip/zip.js");

5.5.1.1. Installing JSLib

To use the JavaScript libraries, install the JSLib package in Mozilla. The package is available as a tarball, a zip file, or as CVS sources. The easiest way to obtain it is to install it from the Web using Mozilla's XPInstall technology, described in Chapter 6.

Using your Mozilla browser, go to http://jslib.mozdev.org/installation.html and click the installation hyperlink. The link uses XPInstall to install JSLIB and make it available to you in Mozilla. To test whether it is installed properly, type the following code in your shell:

./mozilla -chrome chrome://jslib/content/

You should see a simple window that says "welcome to jslib."

5.5.1.3. Using the File class

The JSLib File class exposes most local file routines from the nsIFile interface. The File class is part of the JSLib I/O module, and is defined in jslib/io/file.js. Here is how you load the library from xpcshell:

$ ./run-mozilla.sh ./xpcshell -w -s
js> load('chrome/jslib/jslib.js');
*********************
JS_LIB DEBUG IS ON
*********************
js>

Once JSLib is loaded, you can load the File module with an include statement:

js> include(`chrome://jslib/content/io/file.js');
*** Chrome Registration of package: Checking for contents.rdf at
resource:/chrome/jslib/
*** load: filesystem.js OK
*** load: file.js OK
true
js>

Note that file.js loads filesystem.js in turn. The class FileSystem in filesystem.js is the base class for the File object. You can also load file.js by using the top-level construct JS_LIB_PATH:

js> include(JS_LIB_PATH+'io/file.js');

Once you have the file.js module loaded, you can create an instance of a File object and call methods on it to manipulate the file and path it represents:

js> var f = new File('/tmp/foo'); 
js> f;
[object Object]
js> f.help; // listing of everything available to the object
. . .
js> f.path;
/tmp/foo
js> f.exists( );   // see if /tmp/foo exists
false
js> f.create( );   // it doesn't, so create it.
js> f.exists( );
true
js> f.isFile( );   // is it a file?
true
js> f.open('w');  // open the file for writing
true            
js> f.write('this is line #1\n');
true
js> f.close( );
js> f.open( );     // open the file again and
js> f.read( );     // read back the data
                  // you can also use default flag 'r' for reading
this is line #1
js> f.close( );

You can also assign the contents of the file to a variable for later use, iterative loops through the file contents, or updates to the data:

js> f.open( );            
true
js> var contents = f.read( );
js> f.close( );
js> print(contents);
this is line #1
js> 
// rename the file
js> f.move(`/tmp/foo.dat');
foo.dat
filesystem.js:move successful!
js> f.path;
/tmp/foo.dat

These examples show some ways the JSLib File object can manipulate local files. Using these interfaces can make life a lot easier by letting you focus on creating your Mozilla application without having to implement XPCOM nsIFile objects manually from your script.

5.5.1.4. Using the FileUtils class

To create an instance of the FileUtils class, use the FileUtils constructor:

js> var fu = new FileUtils( );
js> fu;
[object Object]

Then look at the object by calling its help method:

js> fu.help;

The difference between using the File and FileUtils interfaces is that methods and properties on the latter are singleton and require a path argument, while the FileUtils utilities are general purpose and not bound to any particular file. The FileUtils interface has several handy I/O utilities for converting, testing, and using URLs, of which this example shows a few:

js> fu.exists('/tmp');
true
// convert a chrome path to a url
js> fu.chromeToPath('chrome://jslib/content/');
/usr/src/mozilla/dist/bin/chrome/jslib/jslib.xul
// convert a file URL path to a local file path
js> fu.urlToPath('file:///tmp/foo.dat');
/tmp/foo.dat

Most methods on the FileUtils objects are identical to the methods found in file.js, except they require a path argument. Another handy method in the FileUtils class is spawn, which spawns an external executable from the operating system. It's used as follows:

js> fu.spawn('/usr/X11R6/bin/Eterm');

This command spawns a new Eterm with no argument. To open an Eterm with vi, you could also use this code:

js> fu.spawn('/usr/X11R6/bin/Eterm', ['-e/usr/bin/vi']);

Checking to see if three different files exist would take several lines when using the File class, but the FileUtils class is optimized for this type of check, as the following listing shows:

js> var fu=new FileUtils( );
js> fu.exists('/tmp');
true
js> fu.exists('/tmp/foo.dat');
true
js> fu.exists('/tmp/foo.baz');
false

You need to initialize the FileUtils class only once to use its members and handle local files robustly.