JAVA BASICS FOR ARCIMS - JAVA SCRIPTS AND THE REQUEST/RESPONSE CYCLE

The Basic Task

The Java Scripts in an ArcIMS website, both in the website's javascripts  folder and embedded in the html, all perform one basic function - to take client input (the request), form it into an XML string, send that XML string to the Web Server and take the XML response from the web server and process it into a format the browser can handle.  To put more briefly, every time you pan, zoom, click on a button or pick up a tool a Java Script (or two, three or more) sends an XML request and processes the response.

That is about the end of the simplicity.  We are going to go through the process of what happens when you click on the Zoom to Full Extent button to get an idea of how Java Scripting works in the Request/Response cycle of an ArcIMS web site.

Building an XML String

Start Internet Explorer and go to one of your websites.  Using Windows Explorer, go to that website folder,  get into the javascripts folder, right click on aimsMap.js and Open With Word Pad.  Notice in the first five lines or so that this script is dependent on three other Java Script files.  What this means is that if you make changes in this script there is a possibility you will have to make changes in the others.

// aimsMap.js
/*
* JavaScript template file for ArcIMS HTML Viewer
* dependent on aimsXML.js, ArcIMSparam.js, aimsCommon.js
*/
 

That small section is followed by the setting of a lot of variables.  Some of the variables are conditional,  based on variables set in arcimsParams.js.  Those variables are global variables that any script can access.  In aimsMap.js look for this section of code:

MapUnits = MapUnits.toUpperCase();
ScaleBarUnits = ScaleBarUnits.toUpperCase();
if (drawScaleBar2) {
switch (ScaleBarUnits) {
case "MILES":
ScaleBar2Units = "KILOMETERS";
break;
case "KILOMETERS":
ScaleBar2Units = "MILES";
break;
case "FEET":
ScaleBar2Units = "METERS";
break;
case "METERS":
ScaleBar2Units = "FEET";
break;

}
}
 

The first two lines take variables set in aimsParams.js and change them to upper case.  Open aimsParams.js in another instance of Word Pad and find those two variables.  In the website I was using they were:

var MapUnits = "Degrees";
var ScaleBarUnits = "Miles";

Then it checks another variable in aimsParams.js, drawScaleBar2.  Look for this variable in your aimsParams.js file.  If this variable is set to "true" the rest of this conditional statement will be executed.  If it is set to "false" this section of code will not execute.  Let's assume it is true although it probably is not.

The next is a switch section based on case statements.  So if the ScaleBarUnits (these are on the first scalebar) are MILES then the units on the second scale bar are "KILOMETERS."  If the units on the first are "FEET", the units on the second are "METERS." The whole purpose is to allow two scale bars - one metric and one English units.

hat section of code completes the variable setting section.  It is followed here by a set of functions.  The function we are interested in is fullExtent.  How is this function called?  Well, you may remember from our discussion of framesets, the toolbar.htm file is placed in the place where the blank.htm file kept a a location for it and the tool and button images you checked on in Designer were placed in two columns there.  One of those images is the full extent tool.  That tool was put on the interface by this piece of script in toolbar.htm

if (parent.MapFrame.useFullExtent) {
// Full Extennt . . . requires amisLayers.js
document.write('<td align="center" valign="middle">');
document.write('<img src="images/fullextent_1.gif" width=16 height=16 hspace=1 vspace=1 border=0 alt=" ' + t.buttonList[18] + '" onmousedown="parent.MapFrame.clickFunction(\'fullextent\');" onmouseover="window.status=\' ' + t.buttonList[18] + '\'">');
isSecond = !isSecond
document.writeln('</td>');
if (isSecond) document.write('</tr><tr>');
}

The document.write Java function writes some html code to toolbar.htm.  In that code is what is called an "event handler."  It calls a function when an event occurs.  When the mouse goes down, the function clickFunction is called from the parent document MapFrame.  One of the Java script files referenced in MapFrame.htm is aimsClick.js.   The variable "fullextent" is passed to the clickFunction in aimsClickl.js as a case variable and since the case = "fullextent" it does what the code below tells it to do:

case "fullextent":
fullExtent();
break;

Remember that Java is case sensitive so "fullextent" is not the same as "fullExtent." Java will look in any script referenced in MapFrame.htm (eight of them) until it finds the function fullExtent. It finds it in aimsMap.js.

In aimsMap.js look for this function:

// zoom out to full extent
function fullExtent() {
if (aimsDHTMLPresent) moveLayer("theMap",hspc,vspc);
window.scrollTo(0,0);
saveLastExtent();
eLeft = fullLeft;
eRight = fullRight;
eTop = fullTop;
eBottom = fullBottom;
//var theString = writeXML();
sendMapXML();
}
 

First the function is named -  fullExtent.  Then a conditional statement is executed and if it is true (aimsDHTMLPresent) a function in aimsDHTML  called moveLayer is executed.  Question:  how does this script know whether this variable is true or false?  It is not present in any of the dependent scripts listed at the top of this script.  There is an aimsDHTML.js script and in it is a varaible aimsDHTMLPresent and it is set to true, so this portion is executed.

Then it calls a standard Java Script function to scroll the window, a specific function in this script to save the LastExtent (so when you click on the Zoom to Last Extent button there are some stored variables to tell the script what that extent was) then it sets some variables it pulls from the screen and calls the sendMapXML script.

That script, however, is not in this script.  It is in the aimsXML.js script.  Open the instance of Word Pad that has that script in it and find the sendMapXML function.  It should look like this:

function sendMapXML() {
// ask for the Main map
//window.onerror=clearError;
beforeMapRefresh();
//window.onerror=resetError;
showRetrieveMap();
var theText = writeXML();
if (debugOn==2) alert(msgList[12] + theText);
sendToServer(imsURL,theText,1);
}

This function basically takes a variable called theText and sends it to the writeXML function which is also in the aimsXML script.  This variable, theText, is constructed by a lot of different tools and buttons and consists of different text depending on what the tool or button does.  But it all goes into a variable called theText.  So the content of theText changes a lot.  The final sendToServer function is the last function called in a request.  It contains the URI for the server, the XML and a marke for the kind of request it is.  In this case it is a getImage request (type 1).  This ensures the correct kind of processing by the server.

To see the result of any writeXML request you can turn debugging on which will place the XML text you are sending in a box.  Go into the ArcIMSParams.js script and look for this text

function checkParams() {
appDir = getPath(document.location.pathname);
// global for overview map. . . change if not on same frame as Map
ovImageVar = document.ovImage;
debugOn = 0;
.
.
.

Change the bolded 0 to 2.  Save the ArcIMSParams.js file, get into your website and refresh with Ctrl + F5.  Pick up any geographic tool like the zoom in tool and drag a box.  A new window should open containing the contexnts of the XML that was sent by that tool.  Make one of the layers in the table of contents not visible and click the Refresh Map button.  You should see in that layers <LAYERDEF> element visible = "false."  The <LAYERDEF> elements are children to the <LAYERLIST> element.  Play around with some of the other variables you can set in the ArcIMSParams.  For example, set the drawCopyright variable to "false."  Remember, after each change to any of the .js files you must save the file and refresh your web site.  See how the requests change as you change variables in ArcIMSParams.js.  After you are done playing around set the debugOn variable to 4, save the ArcIMSParam.js file and refresh your web site.  By setting debugOn = 4 you are going to see all the XML that gets passed around between the server and client.

Handling the Response

Software on the server goes through much the same process except instead of constructing a string of XML is parses the string it received and based on information contained in that string processes the request. In the case of a fullExtent request (a <GET_IMAGE> request it takes all the layer information received, e.g. what is visible, what is on the acetate layer, style of north arrow, etc.. and creates a new image which it places in the output folder on the server. 

Click the fullExtent button. The first window you will see contains the XML request that got formed and sent.  Click OK to close that box and the image will appear in the MapFrame and a box  like the one below will appear on the screen:

The guts of this response are the bounding coordinates which get saved as the last extent and the URL for where to find the image.

If you remember the sendtoServer request that generated this response looked like this:

sendToServer(imsURL,theText,1);
 

Here is where my knowledge breaks down.  If you look at the XML above - the ArcXML response the URL value is in there as is something resembling theText in the sendtoServer function.  What is missing is the Type variable.  However, we already know the Type value, 1.  So when the processXML function is called to deal with the reply, it goes to the case: =1 section of the script.  Get back into the aimsXML script and look for the processXML function.  It is a lengthy function and the section you want looks like this; this is the first part of  the code for the case = 1 section of processXML:

// process the response xml
function processXML(theReplyIn) {
if (doURLencode) {
theReplyIn = replacePlus(theReplyIn);
var theReply = unescape(theReplyIn);
} else {
var theReply = theReplyIn;
}

lastXMLResponse = theReply;
okToSend = true;
if (debugOn>2) alert(msgList[13] + theReply);
var theError = getXMLErrorMessage(theReply);
switch(XMLMode) {
case 1:
//alert ("Received:\n\n" + theReply);
var theURL = "";
theURL = getURL(theReply);
//alert(theURL);
if (theURL != "") {
getXYs(theReply);
document.theImage.src = theURL;
afterMapRefresh();
}
.
.
.

The first "if" statement checks for URL encoding - I have no clue what that means.  But after the reply (the XML in the window above) passes that filter and several other checks, it gets to case 1:  It remembers this because it was part of the request.  A function getURL is called and theReply is passed to that function.  It is in aimsXML.js as well.  Use the find function (getURL) and get to the function:

function getURL(theReply) {
var theURL = "";
var startpos = 0;
var endpos = 0;
var pos = theReply.indexOf("OUTPUT");
if (pos != -1) {
theURL = getInsideString(theReply,'url="',dQuote,pos,0,false);
}
legendImage = getLegendURL(theReply);
return theURL;
}

This is an example of a parsing function.  The key line is

theURL = getInsideString(theReply,'url="',dQuote,pos,0,false);

This finds, in theReply XML string, the information between double quotes right after the text url= and places that in a variable called theURL. That value is passed back to the case: 1 portion of processXML where it becomes part of this line which is the line that actually goes out and gets the image from the server and puts it in the MapFrame.htm file which is rebuilt to hold the image.


document.theImage.src = theURL;

Note:  At this time as I was working on the project I tried to refresh my web site and I got an "error on page" message.  Obviously, as I was cutting and pasting to create this file I must have cut instead of copied something.  And by this time I had about 6 .js files open in instances of Word Pad so I was clueless as to which one I had screwed up.  To fix the problem, I copied the entire contents of the javascript folder of another web site into the first web site, overwriting the existing scripts and the web site refreshed itself.

The Entire Request/Response Cycle when you Refresh the Web Site

Let's wrap this up with the following activity.  Make sure that debugOn = 4 in the ArcIMSParams.js file and refresh your website.  The windows of XML code that you are going to see are all written out and processed by many Java scripts in the javascripts folder of the server.


 

Request a small image of the overview map.  It does not come back right away but this is how big it will be.  Server java scripts will develop the overview map and send the URL back (below)

Response from the server with the URL for the overview map. It has been created but is just sitting in the output folder until it will be placed in the map frame.

Client sends a <GET_SERVICE_INFO> to the server.  This requests gives

A really large window with lots of info (not reproduced here)

This is the response to the <GET_SERVICE_INFO> request.  The server needs this information passed to the scripts so that the full extent image can be created and its location sent to the client.

This is a <GET_IMAGE> request that goes to the server detailing what should be on the image.  The additional information needed to create the image, e.g. the width of lines, types of point symbols, etc.. is contained in the AXL file which the map server gets to through the service.  I generates the image in response to the request and using the map configuration information in the AXL file and returns the extent values and the URL in the response (below)

This is processed by the processXML script and the URL is located and placed in the map frame.  Your website is built and complete.  Make sure you set the debugOn variable back to 0 in the ArcIMSParams.js file.