Using JavaScript

avaScript is the scripting language for application development with LiveWire. It can be executed on the server or on the client. This chapter discusses JavaScript as it relates to LiveWire. For general information on the JavaScript language and on using JavaScript to create client applications for Netscape Navigator, see the JavaScript Guide.

Using client and server scripts

In general, a LiveWire application can contain JavaScript that is interpreted by the server (with the LiveWire runtime interpreter) and by the client, Netscape Navigator.

In source code HTML, client-side JavaScript is delimited by the SCRIPT tag and server-side JavaScript by the SERVER tag. LiveWire compiles the source code HTML into platform-independent bytecodes, parsing and compiling server-side JavaScript statements. At runtime, the Netscape server translates the bytecodes into HTML statements (possibly including client JavaScript) and sends them across the network to the client. Navigator interprets client JavaScript and performs standard HTML layout. This process is illustrated in Figure 5.1.

Client and server JavaScript

What really happens

When you run a complex LiveWire application, a variety of things occur, some on the server and some on the client. Although the end-user does not need to know the details, it is important for you, the application developer, to understand what happens "under the hood."

In an application that contains both client and server JavaScript

  1. You create an HTML page containing server and client JavaScript statements, compile it to a LiveWire web file, and install it on the server.
  2. A user accesses the application URL with the Navigator.
  3. LiveWire dynamically creates HTML based on server JavaScript statements, sending HTML and client JavaScript statements to the client.
  4. Navigator interprets client JavaScript statements, formats HTML output, and displays results to the user. Of course, the user must have Netscape Navigator (or some other JavaScript-capable client), for the client to be able to interpret client JavaScript statements. Likewise, if you create a page containing server JavaScript, it must be installed on a Netscape server to operate properly.

What to do where

There are usually a variety of ways to partition an application between client and server. Some tasks can be performed only on the client or on the server; others can be performed on either. Although there is no definitive way to know "what to do where," you can follow these general guidelines:

As a rule of thumb, use client scripts for

Using LiveWire functions

A number of JavaScript functions are built in to LiveWire. These functions are defined only for server JavaScript. These functions are summarized in the following table:




Javascript expression, converted to a string.

Generates dynamic HTML.



Flushes output buffer.


JavaScript string representing a URL

Redirects client to specified URL.


JavaScript expression, usually containing LiveWire properties

Displays value of expression to trace window or frame.


JavaScript string representing a URL

Appends client property values to URLs; use only if using URL encoding client maintenance.


JavaScript string representing a name or value to be added to a URL

Encodes special characters so they can be used in URLs.


Name or value string from a URL.

Decodes special characters from URLs.


String representing name of HTML SELECT object, number representing option number.

Gets values of individual options in an HTML SELECT form element


String representing name of HTML SELECT object.

Gets number of options in an HTML SELECT form element.

Generating dynamic HTML

The write function generates HTML based on the value of JavaScript expression given as its argument. For example, the statement

write("<P>Customer Name is:" + project.custname)
causes LiveWire to generate HTML including a paragraph tag and some text, concatenated with the value of the custname property of the project object, which, for example, might be "Fred's software company." The client would then receive the following HTML:

<P>Customer Name is: Fred's software company.
As far as the client is concerned, this is static HTML. However, it is actually generated dynamically by LiveWire.

Flushing the output buffer

To improve performance, LiveWire buffers write output and sends it to the client in 64 Kbyte blocks. The flush function sends data resulting from write functions from the internal buffer to the client. If you do not explicitly perform a flush, LiveWire will flush data after each 64 Kbytes of generated content in a document. Don't confuse the flush function with the flush method of File.

You can use flush to control when data are sent to the client, for example, before an operation that will create a delay, such as a database query. Also, if a database query retrieves a large number of rows, flushing the buffer every few rows prevents long delays in displaying data.

Note: Any changes to the client object should be done before flushing the buffer if the application is using a client-side technique for maintaining the client object.

For information on performing file input and output, see "Using files on the server." The following script shows how flush is used. This code fragment iterates through a text file and emits output for each line in the file, preceded by a line number and five spaces. Then flush causes the output to be displayed to the client.

while (!In.eof()) {
	AscLine = In.readln()
	if (!In.eof())
		write(LPad(LineCount + ": ", 5), AscLine, "n")

Redirecting the client

The redirect function redirects the client to the specified URL. For example,

sends the client to the indicated URL. The client immediately loads the indicated page, discarding any previous content. The client will not display any HTML or perform any JavaScript statements that follow the call to redirect.

You can use redirect to dynamically generate URLs. For example, if a page defines a request variable choice, you can redirect the client to a page based on the value of choice as follows:

+ request.choice ".html")
If your application is using client URL encoding or server URL encoding to maintain the client object, you must use addClient or writeURL to append client property values to generated URLs. For more information, see "Maintaining client properties with URL encoding."

Displaying debugging information

The debug function outputs its argument to the trace facility. Use this function to display the value of an expression for debugging purposes. For example, the statement

debug("Current Guess is ", request.guess)
displays the value of the guess property of the request object in the trace window or frame along with some identifying text.

For more information, see "Debugging applications."

Using select lists

The HTML SELECT tag allows multiple values to be associated with a single form element, with the MULTIPLE attribute. If your application requires select lists that allow multiple selected options, you must use the getOptionValue function to get the values in JavaScript.

The syntax of getOptionValue is

getOptionValue(name, index)
where name is specified by the NAME attribute of the SELECT tag, and index is the zero-based ordinal index of the selected option. The function returns the value of the selected item, as specified by the associated OPTION tag.

The associated function getOptionValueCount returns the number of options (specified by OPTION tags) in the select list. It requires only one argument, the name of the SELECT tag.

For example, suppose you have the following form element:

<SELECT NAME="what-to-wear" MULTIPLE SIZE=8>
	<OPTION>Wool Sweater
	<OPTION>Leather Jacket
	<OPTION>Running Shoes
You could process the input from this select list as follows:

var loopIndex = 0
var loopCount = getOptionValueCount("what-to-wear")
while ( loopIndex < loopCount ) {
	var optionValue = getOptionValue("what-to-wear",loopIndex)
	write("<br>Item #" + loopIndex + ": " + optionValue + "n")
If the user kept the default selections, this script would return

Item #1: JeansItem #3: SweatshirtItem #4: Socks

Maintaining client properties with URL encoding

When using client URL encoding or server URL encoding to maintain the client object, client property values are maintained in the URLs requested by the client, either directly or using a generated name. LiveWire automatically appends these values to standard HTML hyperlinks to other pages. For more information on URL encoding methods of maintaining the client object, see "Techniques for maintaining the client object."

If your application only uses HTML to link between its pages, you do not have to do anything special. However, if the application generates URLs dynamically or uses the redirect function, you must use the addClient function to append client property values to URLs.

Even though an application is initially installed to use one technique to maintain client, it may be modified later to use an URL encoding technique. Therefore, if your application generates dynamic URLs, you should always use addClient.

Suppose your application has a variable, nextPage, that you set to a string identifying the page to link to, based on user actions. You could generate a hyperlink using this property like this:

<A HREF=`nextPage`>
Assuming the value of nextPage is a string representing the desired URL, this would work fine. However, if you are using a URL-encoding method to preserve the client object, this URL would not contain the client properties. To do this, you must use addClient as follows:

<A HREF=`addClient(nextPage)`>
You must also use addClient when you use the redirect function. For example,


Encoding values in URLs

When generating a URL request string, certain characters are special characters, including ampersand (&), plus sign (+), equal sign (=), question mark (?), and blank ( ), among others. These characters must be encoded by escape sequences when used in URLs.

The escape and unescape functions are provided for encoding in a URL values that may include special characters. In general, if an application needs to generate its own name/value pairs in an URL request, you should use escape and unescape, to ensure that all values are interpreted properly.

The escape function returns the ASCII value in hexadecimal for a character, preceded by a percent sign (%), with the exception of a blank (space) character; it encodes this as a plus sign (+). For example, escape("a bc") returns the string "%61+%62%63".

Here is an example of using escape in server-side JavaScript:

<A HREF=\Q"mypage.html?val1=" + escape("the value")\Q)>Click Here</A>
This example allows the string "the value" to be assigned to the request property val1; if you did not use escape, this would be an illegal URL because it includes a space (blank) character.

The LiveWire implementation of escape and unescape are similar to their implementation in client JavaScript, except for the treatment of spaces. For more information see the JavaScript Guide.

Communicating between client and server

In developing a client-server application, keep in mind the strengths and weaknesses of client and server platforms. Servers are usually (though not always) high-performance workstations with fast processors and large storage capacities. Clients are often (though not always) desktop systems with less processor power and storage capacity. However, servers can become overloaded when accessed by thousands of clients, so it can be advantageous to offload processing to the client. Preprocessing data on the client can also reduce bandwidth requirements, if the client application can aggregate data.

The LiveWire object framework preserves information over time, but client JavaScript is more ephemeral. Navigator objects exist only as the user accesses a page. Also, servers can aggregate information from many clients and many applications and can store large amounts of data in databases. It is important to keep these characteristics in mind when partitioning functionality between client and server.

In developing applications, you might want to communicate between client and server JavaScript. In particular, you might want to

With JavaScript, you can easily accomplish both tasks.

Sending values from client to server

In HTML, you use form elements such as text fields and radio buttons to send values to the server. When the user clicks a submit button, the Navigator submits the values entered in the form to the server for processing. The ACTION attribute of the FORM tag determines the application to which the values are submitted. For example,

<FORM NAME="myform" ACTION="">
If you want to send user-entered values to a LiveWire application, you need do nothing special. Each form element corresponds to a request property.

For more information on the request object, see "The request object."

If you want to process data on the client first, you have to create a client-JavaScript function to perform processing on the form-element values and then assign the output of the client function to a form element. The element can be hidden, so it is not displayed to the user. This enables you to perform client preprocessing.

For example, say you have a client-JavaScript function named calc that performs some calculations based on the user's input. You want to pass the result of this function to your LiveWire application for further processing. You first need to define a hidden form element for the result, as follows:

<INPUT TYPE="hidden" NAME="result" SIZE=5>
Then you need to create an onClick event handler for the submit button that assigns the output of the function to the hidden element:

<INPUT TYPE="submit" VALUE="Submit" 
The value of result will be submitted along with any other form-element values. This value will be referenced as request.result in the LiveWire application.

Sending values from server to client

A LiveWire application communicates to the client through HTML and client-based JavaScript. If you simply want to display information to the user, then there is no subtlety: you create the dynamic HTML to format the information as you want it displayed.

However, you may want to send values to client scripts directly. You can do this in a variety of ways, including

To display an HTML form with default values set in the form elements, use the INPUT tag to create the desired form element, substituting a server-side JavaScript expression for the VALUE attribute. For example, say you want to display a text element and set the default value based on the value of client.custname. You could do this with the following statement:

<INPUT TYPE="text" NAME="customerName" SIZE="30" 
The initial value of this text field is set to the value of the LiveWire variable client.custname.

You can use a similar technique with hidden form elements if you do not want to display the value to the user. For example,

<INPUT TYPE="hidden" NAME="custID" SIZE=5 VALUE=`client.custID`>
In both cases, these values are reflected in client-side JavaScript in property values of Navigator objects. If these two elements are in a form named "entryForm," then these values are reflected into JavaScript properties document.entryForm.customerName and document.entryForm.custID, respectively. You can then perform client processing on these values in Navigator scripts, but only if the scripts occur after the definition of the form elements in the page. For more information, see the JavaScript Guide.

You can also use LiveWire to generate client-side scripts. This is the most straightforward way of sending values from the server to client JavaScript. These values can be used in subsequent statements on the client. As a simple example, you could initialize a client-side variable named budget based on the value of client.amount as follows:

write("<SCRIPT>var budget = " + client.amount + "</SCRIPT>")

Using cookies for client-server communication

Cookies are a mechanism Navigator uses to maintain information between requests using a file called cookie.txt (the cookie file). The contents of the cookie file are available through the client-JavaScript document.cookie property. If an application is using client cookies to maintain the client object, you can use the cookie file to communicate between client and server scripts.

The advantage of using cookies for client-server communication is that they provide a uniform mechanism for passing values between client and server and make it possible to maintain persistent values on the client.

For information on using cookies for maintaining the client object, see "Techniques for maintaining the client object."

When you use client cookies to maintain the client object, LiveWire adds the following entry for each property value:

where propName is the name of the property, and propValue is its value. Special characters in propValue are encoded in the cookie file, as described in

You can use the Navigator JavaScript functions escape to encode characters and unescape to decode them. Because these functions do not handle spaces, your application must manually encode and decode spaces, as well as forward slash (/) and the at-sign (@).

The following are examples of client-side JavaScript functions for getting and setting cookie values. These functions assume the use of some helper functions called encode and decode that perform the proper character encoding.

function getCookie(Name) {
	var search = "NETSCAPE_LIVEWIRE." + Name + "="
	var RetStr = ""
	var offset = 0
	var end    = 0
	if (document.cookie.length > 0) {
		offset = document.cookie.indexOf(search)
		if (offset != -1) {
			offset += search.length
			end = document.cookie.indexOf(";", offset)
			if (end == -1) 
				end = document.cookie.length
		RetStr = decode(document.cookie.substring(offset, end))
	return (RetStr)
function setCookie(Name, Value, Expire) {
	document.cookie = "NETSCAPE_LIVEWIRE." + Name + "=" 
	+ encode(Value)
	+ ((Expire == null) ? "" : ("; expires=" + Expire.toGMTString()))
These functions could be called in client JavaScript to get and set values of the client object, as in the following example:

var Kill = new Date()
Kill.setDate(Kill.getDate() + 7)
var value = getCookie("answer") 
if (value == "")
	setCookie("answer", "42", Kill)
	document.write("The answer is ", value)
This group of statements checks whether there is a client property called answer. If there is not, it creates it and sets its value to forty-two; if there is, it displays its value.

Using files on the server

LiveWire provides a File object that enables applications to write to the server's file system. This is useful for generating persistent HTML files and for storing information without using a database server. One of the main advantages of storing information in a file instead of in LiveWire objects is that the information is preserved even if the server goes down.

Important: Exercise caution when using the File object. An application can read or write files anywhere the operating system allows. You should be sure your application does not allow an attacker to read password files or other sensitive information or to write files at will. For more information, see "Security considerations."

For security reasons, Navigator does not provide automatic access to the file system of client machines. If needed, the user can save information directly to the client file system by making appropriate menu choices in Navigator.

Creating a File object

To create a File object, use the standard JavaScript syntax for object creation:

fileObjectName = new File("path")
where fileObjectName is the JavaScript object name by which you refer to the file, and path is the complete file path. The path should be in the format of the server's file system, not a URL path.

You can display the name of a file by using the write method, with the File object as its argument. For example, the following statement displays the filename:

x = new File("/path/file.txt")

Opening and closing a file

Once you have created a File object, you must open the file with the open method to read from it or write to it. The open method has the following syntax:

result ="mode")
This method will return true if the operation is a success and false if the operation is a failure. If the file is already open, the operation will fail and the original file will remain open.

The parameter mode is a string that specifies the mode in which to open the file. This table describes how the file is opened for each mode.




Opens the file, if it exists, as a text file for reading and returns true. If the file does not exist, returns false.


Opens the file as a text file for writing. Creates a new (initially empty) text file whether or not the file exists.


Opens the file as a text file for appending (writing at the end of the file). If the file does not already exist, creates it.


Opens the file as a text file for reading and writing. Reading and writing commence at the beginning of the file. If the file exists, returns true. If the file does not exist, returns false.


Opens the file as a text file for reading and writing. Creates a new (initially empty) file whether or not the file already exists.


Opens the file as a text file for reading and writing. Reading and writing commence at the end of the file. If the file does not exist, creates it.


Append to any of these modes to open the file as a binary file rather than a text file. Only applicable on Windows operating systems.

When an application is finished with a file, it can close the file by calling the close method. If the file is not open, close will fail. This method returns true if successful, false otherwise.

Locking files

Most applications can be accessed by many users simultaneously. In general, however, it is not recommended that different users simultaneously access the same file, because unexpected errors may result. For example, one user of an application could move the pointer to the end of the file when another user expected the pointer to be at the beginning of the file.

To prevent multiple users from accessing a file at the same time, use the locking facility of the project and server objects, as described in "Locking the project object." Use the lock method of project to ensure that other users cannot access a file that a user has locked. In general, this means you should precede all file operations with lock and follow them with unlock. If more than one application will access the same file, use the lock method of server. If one user has the file locked, other users of the application will wait until the file becomes unlocked.

For example, suppose you have created a file called myFile. Then you should use it as follows:

	// use the file as needed
In this way, only one user of the application has access to the file at one time.

Working with files

The File object has a number of methods that you can use once a file is opened:

Positioning within a file

A File object has a pointer, which indicates the current position in the file. When you open a file, the pointer is positioned at either the beginning or the end of the file, depending on the mode you used to open it. In an empty file, the beginning and end of the file are the same.

The setPosition method positions the pointer within the file, returning true if successful; false otherwise. The syntax is

fileObj.setPosition(position [,reference])
where fileObj is a File object, position is an integer indicating where to position the pointer, and reference indicates the reference point for position, as follows:

Reading from a file

Use these methods to read from a file: read, readln, and readByte.

The read method reads the specified number of bytes from a file and returns a string. The syntax is
where fileObj is a File object and count is an integer specifying the number of bytes to read. If count specifies more bytes than are left in the file, then the method reads to the end of the file.

The readln method reads the next line from the file and returns it as a string. The syntax is

where fileObj is a File object. The line-separator characters (either rn on Windows or just n on Unix or Macintosh) are not included in the string. The character r is skipped; n determines the actual end of the line. This compromise gets reasonable behavior on both Windows and Unix platforms.

The readByte method reads the next byte from the file and returns the numeric value of the next byte, or -1. The syntax is


Writing to a file

The methods for writing to a file, write, writeln, and writeByte, also include a flush method to flush internal buffers to disk.

The write method writes a string to the file. It returns true if successful; otherwise it returns false. The syntax is

where fileObj is a File object and string is a JavaScript string.

The writeln method writes a string to the file, followed by n (rn in text mode on Windows). It returns true if the write was successful; otherwise it returns false. The syntax is

The writeByte method writes a byte to the file. It returns true if successful; otherwise it returns false. The syntax is

where fileObj is a File object and number is a number.

When you use any of the File methods for writing to a file, they are buffered internally. The flush method writes the buffer to the file on disk. This method returns true if successful, and false otherwise. If fileObj is a File object, the syntax is


Converting data

There are two primary file formats: ASCII text and binary. The File object has two methods for converting data between these two formats: byteToString and stringToByte.

The byteToString method converts a number into a one-character string. This method is static, so no object is required. The syntax is

If the argument is not a number, the method will return the empty string.

The stringToByte method converts the first character of its argument, a string, into a number. This method is static, so no object is required. The syntax is

The method returns a numeric value of the first character, or zero.

Getting file information

There are several File methods you can use to get information on files and to work with the error status.

The getLength method returns the number of bytes (or characters for a text file) in the file, or -1 if there is an error. The syntax is

The exists method returns true if the file exists; otherwise it returns false. The syntax is

The error method returns the error status, or -1 if the file is not open or cannot be opened. The error status will be nonzero if an error occurred, zero otherwise (no error). Error status codes are platform dependent; refer to your operating system documentation. The syntax is

The clearError method clears both the error status (the value of error) and the value of eof:


Using the File object: an example

The Viewer sample is provided with LiveWire, but because it allows you to view any files on the server, it is not installed for security reasons. It is a good example of using the File object, but if you install it, be sure to restrict access so that unauthorized persons cannot view files on your server.

Note: For information on restricting access to an application, see "Restricting access to applications." The following code from the Viewer sample application creates a File object, opens it for reading, and generates HTML that echoes the lines in the file, with a hard line break after each line.

x = new File("/tmp/names.txt") // path name is platform dependent
fileIsOpen ="r")
if (fileIsOpen) {
	write("file name: " + x + "<BR>")
	while (!x.eof()) {
		line = x.readln()
		if (!x.eof())
	if (x.error() != 0)
		write("error reading file" + "<BR>")

Using external libraries

A LiveWire application can call functions written in languages such as C, C++, or Pascal and compiled into libraries on the server. Such functions are called external functions. Libraries are DLLs (dynamic link libraries) on Windows operating systems and SOs (shared objects) on Unix operating systems.

Note: Before using external functions, be sure to read "Security considerations." Using external functions in an application is useful if

The sample directory lwccall contains source and header files illustrating how to call functions in external native functions from a LiveWire application.

To use an external library in a LiveWire application

  1. Write and compile the library (DLL or SO) in a form compatible with LiveWire.
  2. With Application Manager, identify the library to be used by installing a new application or modifying installation parameters for an existing application. Once you identify an external library using Application Manager, all applications on the server can call external functions in that library.
  3. Restart the server to load the library with your application. The functions in the external library are now available to all applications on the server.
  4. In your application, use the JavaScript functions RegisterCFunction to identify the functions in the library to be called and callC to call the functions.
  5. Recompile and restart your application for the changes to take effect.
Important: You must restart your server to install a library to use with an application. You must restart the server anytime you add new library files or change the names of the library files used by an application.

Guidelines for writing external functions

Although you can write external libraries in any language, LiveWire uses C calling conventions. Your code must include the header file lwccall.h. Functions to be called from JavaScript must be exported and must conform to this typdef:

typedef void (*LivewireUserCFunction)
	( int argc, struct LivewireCCallData argv[], 
	struct LivewireCCallData *result);
The header file lwccall.h is provided in livewiresampleslwccall. This directory also includes the source code for a sample application that calls a C function defined in lwccall.c. Refer to these files for more specific guidelines on writing C functions for use with LiveWire.

Identifying library files

Before you can run an application that uses external libraries, you must identify the library files using Application Manager. You can identify libraries when you initially install an application (by clicking Add) or when you modify an application's installation parameters (by clicking Modify). For more information on identifying library files with Application Manager, see "Installing a new application."

Important: After you enter the paths of library files in Application Manager, you must restart your server for the changes to take effect. You must then be sure to compile and restart your application.

Once you have identified an external library using Application Manager, all applications running on the server can call functions in the library (by using registerCFunction and callC).

Registering external functions

Use the JavaScript function registerCFunction to register an external function for use with a LiveWire application, with this syntax:

registerCFunction(JSFunctionName, libraryPath, CFunctionName)
This function returns true if it registers the function successfully. If it does not register the function successfully, it returns false. This might happen if LiveWire could not find the library at the specified location or the specified function inside the library.

The parameters are

An application must register a function with registerCFunction before it can call it with callC. Once the application registers the function, it can call the function any number of times. A good place to register functions is in an application's initial startup page.

Using external functions in JavaScript

Once your application has registered a function with registerCFunction, it can call the function with the JavaScript function callC, using this syntax:

callC(JSFunctionName, arguments... )
This function returns a string value returned by the external function. The callC function can return only string values.

The parameters are

Calling external functions: an example

The directory lwccall in the LiveWire samples directory includes C source code (in lwccall.c) that defines a C function named mystuff_EchoCCallArguments. This function accepts any number of arguments and then returns a string that contains HTML listing the arguments. This sample illustrates calling C functions from a LiveWire application and returning values.

To run this sample application, you must compile lwccall.c with your C compiler. Command lines for several common compilers are provided in the comments in the file.

The following JavaScript statements would register the C function echoCCallArguments, call the function with some arguments, and then generate HTML based on the value returned by the function.

var isRegistered = registerCFunction("echoCCallArguments",
if (isRegistered == true) {
	var returnValue = callC("echoCCallArguments", 
		"first arg", 
		"last arg")
Then callC calls the function echoCCallArguments with the indicated arguments. The HTML generated by the write statement is

argc = 4<BR>
argv[0].tag: string; value = first arg<BR>
argv[1].tag: double; value = 42<BR>
argv[2].tag: boolean; value = true<BR>
argv[3].tag: string; value = last arg<BR>