Chapter

4

More fun with scripts

his chapter describes some special concepts and applications that extend the power and flexibility of Navigator JavaScript.

Creating arrays

An array is an ordered set of values that you reference through a name and an index. For example, you could have an array called emp that contains employees' names indexed by their employee number. So emp[1] would be employee number one, emp[2] employee number two, and so on.

Defining an array

In Navigator 2.0, JavaScript does not have an explicit array data type, but because of the intimate relationship between arrays and object properties, it is easy to create arrays in JavaScript. You can define an array object type, as follows:

function MakeArray(n) {
	this.length = n
	for (var i = 1; i <= n; i++) 
		this[i] = 0 
	return this
}
This defines an array so that the first property, length (with index of zero), represents the number of elements in the array. The remaining properties have an integer index of one or greater and are initialized to zero.

You can then create an array by a call to new with the array name, specifying the number of elements it has. For example,

emp = new MakeArray(20)
creates an array called emp with twenty elements and initializes the elements to zero.

Populating an array

You can populate an array simply by assigning values to its elements. For example,

emp[1] = "Casey Jones"
emp[2] = "Phil Lesh"
emp[3] = "August West"
You can also create arrays of objects. For example, suppose you define an object type named Employees, as follows:

function Employee(empno, name, dept) {
	this.empno = empno
	this.name = name
	this.dept = dept
}
The following statements define an array of these objects and populate it:

emp = new MakeArray(3)
emp[1] = new Employee(1, "Casey Jones", "Engineering")
emp[2] = new Employee(2, "Phil Lesh", "Music")
emp[3] = new Employee(3, "August West", "Admin")
To display the properties of an object, use a function such as the following:

function show_props(obj, obj_name) {
	var result = ""
	for (var i in obj)
		result += obj_name + "." + i + " = " + obj[i] + " "
	return result
}
You can display the objects and their properties in this array using the show_props function as follows:

for (var n =1; n <= 3; n++) {
	document.write("<P>Employee #" n + ":<BR>" 
	+ show_props(emp[n], "emp"))
}
The result is:

Employee #1:emp.empno=1 emp.name=Casey Jones emp.dept=Engineering

Employee #2:emp.empno=2 emp.name=Phil Lesh emp.dept=Music

Employee #3:emp.empno=3 emp.name=August West emp.dept=Admin

Using cookies

For a complete description of cookies, see Appendix C, "Netscape cookies.".
Netscape cookies are a mechanism for storing persistent data on the client in a file called cookies.txt. Because HyperText Transport Protocol (HTTP) is a stateless protocol, cookies provide a way to maintain information between client requests. This section discusses basic uses of cookies and illustrates with a simple example.

Each cookie is a small item of information with an optional expiration date and is added to the cookie file in the following format:

name=value;expires=expDate;
For more information on escape and unescape, see the topics in the reference section.
name is the name of the datum being stored, and value is its value. If name and value contain any semicolon, comma, or blank (space) characters, you must use the escape function to encode them and the unescape function to decode them.

expDate is the expiration date, in GMT date format:

Wdy, DD-Mon-YY HH:MM:SS GMT
Although it's slightly different from this format, the date string returned by the Date method toGMTString can be used to set cookie expiration dates.

The expiration date is an optional parameter indicating how long to maintain the cookie. If expDate is not specified, the cookie expires when the user exits the current Navigator session. Navigator maintains and retrieves a cookie only if its expiration date has not yet passed.

Limitations

Cookies have these limitations

Cookies can be associated with one or more directories. If your files are all in one directory, then you need not worry about this. If your files are in multiple directories, you may need to use an additional path parameter for each cookie. For more information, see Appendix C, "Netscape cookies."

Using cookies with JavaScript

The document.cookie property is a string that contains all the names and values of Navigator cookies. You can use this property to work with cookies in Java-Script.

Here are some basic things you can do with cookies:

It is convenient to define functions to perform these tasks. Here, for example, is a function that sets cookie values and expiration:

// Sets cookie values. Expiration date is optional
//
function setCookie(name, value, expire) {
	document.cookie = name + "=" + escape(value)
	+ ((expire == null) ? "" : ("; expires=" + expire.toGMTString()))
}
Notice the use of escape to encode special characters (semicolons, commas, spaces) in the value string. This function assumes that cookie names do not have any special characters.

The following function returns a cookie value, given the name of the cookie:

function getCookie(Name) {
	var search = Name + "="
	if (document.cookie.length > 0) { // if there are any cookies
		offset = document.cookie.indexOf(search) 
		if (offset != -1) { // if cookie exists 
			offset += search.length 
			// set index of beginning of value
			end = document.cookie.indexOf(";", offset) 
			// set index of end of cookie value
			if (end == -1) 
				end = document.cookie.length
			return unescape(document.cookie.substring(offset, end))
		} 
	}
}
Notice the use of unescape to decode special characters in the cookie value.

Using cookies: an example

Using the cookie functions defined in the previous section, you can create a simple page users can fill in to "register" when they visit your page. If they return to your page within a year, they will see a personal greeting.

You need to define one additional function in the HEAD of the document. This function, register, creates a cookie with the name TheCoolJavaScriptPage and the value passed to it as an argument.

function register(name) { 
	var today = new Date()
	var expires = new Date()
	expires.setTime(today.getTime() + 60*60*24*365)
	setCookie("TheCoolJavaScriptPage", name, expires)
}
The BODY of the document uses getCookie (defined in the previous section) to check whether the cookie for TheCoolJavaScriptPage exists and displays a greeting if it does. Then there is a form that calls register to add a cookie. The onClick event handler also calls history.go(0) to redraw the page.

<BODY>
<H1>Register Your Name with the Cookie-Meister</H1>
<P>
<SCRIPT>
var yourname = getCookie("TheCoolJavaScriptPage") 
if (yourname != null)
	document.write("<P>Welcome Back, ", yourname)
else
	document.write("<P>You haven't been here in the last year...")
</SCRIPT>
<P> Enter your name.  When you return to this page within a year, you 
will be greeted with a personalized greeting. 
<BR>
<FORM onSubmit="return false">
Enter your name: <INPUT TYPE="text" NAME="username" SIZE= 10><BR>
<INPUT TYPE="button" value="Register"
	onClick="register(this.form.username.value); history.go(0)">
</FORM>

Using JavaScript URLs

You should be familiar with the standard types of URLS: http, ftp, file, and so on. With Navigator you can also use URLs of type "javascript:" to execute JavaScript statements instead of loading a document. You simply use a string beginning with "javascript:" as the value for the HREF attribute of anchor tags. For example, you can define a hyperlink as follows:

<A HREF="javascript:history.go(0)">Reload Now</A>
to reload the current page when the user clicks it. In general, you can put any statements or function calls after the "javascript:" URL prefix.

You can use JavaScript URLs in many ways to add functionality to your applications. For example, you could increment a counter p1 in a parent frame whenever a user clicks a link, using the following function:

function countJumps() {
	parent.p1++
	window.location=page1
}
To call the function, use a JavaScript URL in a standard HTML hyperlink:

<A HREF="javascript:countJumps()">Page 1</A>
This example assumes page1 is a string representing a URL.

Using client-side image maps

A client-side image map is defined with the MAP tag. You can define areas within the image that are hyperlinks to distinct URLs; the areas can be rectangles, circles, or polygons.

Instead of standard URLs, you can also use JavaScript URLs in client-side image maps, for example,

<MAP NAME="buttonbar">
<AREA SHAPE="RECT" COORDS="0,0,16,14" 
	HREF ="javascript:top.close(); window.location = newnav.html">
<AREA SHAPE="RECT" COORDS="0,0,85,46" 
	HREF="contents.html" target="javascript:alert(`Loading 
	Contents.'); top.location = contents.html">
</MAP>

Using standard image maps

Client-side image maps provide functionality to perform most tasks, but standard (sometimes called server-side) image maps provide even more flexibility. You specify a standard image map with the ISMAP attribute of an IMG tag that is a hyperlink. For example,

The "about:logo" image is built in to Navigator and displays the Netscape logo.
<A HREF="img.html"><IMG SRC="about:logo" BORDER=0 ISMAP></A>
When you click an image with the ISMAP attribute, Navigator requests a URL of the form

URL?x,y
where URL is the document specified by the value of the HREF attribute, and x and y are the horizontal and vertical coordinates of the mouse pointer (in pixels from the top-left of the image) when you clicked.

Traditionally, image-map requests are sent to servers, and a CGI program performs a database lookup function. With Navigator JavaScript, however, you can perform the lookup on the client. You can use the search property of the location object to parse the x and y coordinates and perform an action accordingly. For example, suppose you have a file named img.html with the following content:

<H1>Click on the image</H1>
<P>
<A HREF="img.html"><IMG SRC="about:logo" BORDER=0 ISMAP></A>
<SCRIPT>
str = location.search
if (str == "") 
	document.write("<P>No coordinates specified.")
else {
	commaloc = str.indexOf(",") // the location of the comma
	document.write("<P>The x value is " + str.substring(1, commaloc))
	document.write("<P>The y value is " + str.substring(commaloc+1, 
str.length))
}
</SCRIPT>
When you click a part of the image, Navigator reloads the page (because the HREF attribute specifies the same document), adding the x and y coordinates of the mouse-click to the URL. The statements in the else clause then display the x and y coordinates. In practice, you could redirect to another page (by setting location) or perform some other action based on the values of x and y.

Using the status bar

You can use two window properties, status and defaultStatus, to display messages in the Navigator status bar at the bottom of the window. Navigator normally uses the status bar to display such messages as "Contacting Host..." and "Document: Done." The defaultStatus message appears when nothing else is in the status bar. The status property displays a transient message in the status bar, such as when the user moves the mouse pointer over a link.

You can set these properties to display custom messages. For example, to display a custom message after the document has finished loading, simply set defaultStatus. For example,

defaultStatus = "Some rise, some fall, some climb...to get to Terrapin"

Creating hints with onMouseOver

By default, when you move the mouse pointer over a hyperlink, the status bar displays the destination URL of the link. You can set status in the onMouseOver event handler of a hyperlink to display hints in the status bar instead. The event handler must return true to set status. For example,

<A HREF="contents.html" onMouseOver="window.status='Click to display 
contents'; return true">Contents</A>
This example will display the hint "Click to display contents" in the status bar when you move the mouse pointer over the link.

Using the Date object: an example

The JavaScript Date object lets you manipulate dates and times. For more information, see "Date object".

The following example shows a simple application of Date: it displays a continuously-updated digital clock in an HTML text field. This is possible because you can dynamically change the contents of a text field with JavaScript (in contrast to ordinary text, which you can't update without reloading the document).

The display in Navigator looks like this:

The current time is
(Note: reload page if clock is not running.)

The BODY of the document is:

<BODY ONLOAD="JSClock()">
<FORM NAME="clockForm">
The current time is <INPUT TYPE="text" NAME="digits" SIZE=12 VALUE="">
</FORM>
</BODY>
The BODY tag includes an onLoad event handler. When the page loads, the event handler calls the function JSClock, defined in the HEAD. A form called clockForm includes a single text field named digits, whose value is initially an empty string.

The HEAD of the document defines JSClock as follows:

<HEAD>
<SCRIPT language="JavaScript">
<!--
function JSClock() {
	var time = new Date()
	var hour = time.getHours()
	var minute = time.getMinutes()
	var second = time.getSeconds()
	var temp = "" + ((hour > 12) ? hour - 12 : hour)
	temp += ((minute < 10) ? ":0" : ":") + minute
	temp += ((second < 10) ? ":0" : ":") + second
	temp += (hour >= 12) ? " P.M." : " A.M."
	document.clockForm.digits.value = temp
	id = setTimeout("JSClock()",1000)
}
//-->
</SCRIPT>
</HEAD>
The JSClock function first creates a new Date object called time; since no arguments are given, time is created with the current date and time. Then calls to the getHours, getMinutes, and getSeconds methods assign the value of the current hour, minute and seconds to hour, minute, and second.

The next four statements build a string value based on the time. The first statement creates a variable temp, assigning it a value using a conditional expression; if hour is greater than twelve, (hour - 13), otherwise simply hour.

The next statement appends a minute value to temp. If the value of minute is less than ten, the conditional expression adds a string with a preceding zero; otherwise it adds a string with a demarcating colon. Then a statement appends a seconds value to temp in the same way.

Finally, a conditional expression appends "PM" to temp if hour is twelve or greater; otherwise, it appends "AM" to temp.

The next statement assigns the value of temp to the text field:

document.aform.digits.value = temp
This displays the time string in the document.

The final statement in the function is a recursive call to JSClock:

id = setTimeout("JSClock()", 1000)
The built-in JavaScript setTimeout function specifies a time delay to evaluate an expression, in this case a call to JSClock. The second argument indicates a a delay of 1,000 milliseconds (one second). This updates the display of time in the form at one-second intervals.

Note that the function returns a value (assigned to id), used only as an identifier (which can be used by the clearTimeout method to cancel the evaluation).