Take Command with Ajax

Do you want to build more dynamic, responsive, desktop-like web applications like Gmail and Google Maps? Then this article is for you! It guides you through the Ajax basics and through the process of building a simple Ajax application.

That application is named WebConsole, a browser interface for executing system commands for which you’d usually need shell access. There are also short examples of using the Ajax functionality of two popular JavaScript libraries – jQuery and YUI.

In this article, first published in 2005 and recently updated, I’ll explain the creation of one simple, reusable JavaScript function for making HTTP requests. Then, I’ll apply that function in the creation of a simple application.

--ADVERTISEMENT--

Although there are some YUI and jQuery examples, the article is not a tutorial on a specific Ajax library. Instead, it aims to give you more hands-on information about making HTTP requests, so that you’re in a better position when evaluating such libraries or deciding to go on your own.

A Simple HTTP Request Example

Let’s first revise the flow of making an HTTP request in JavaScript, and handling the response. This is just a quick example to refresh your memory. For all the spicy details, see SitePoint’s introductory article, “Ajax: Usable Interactivity with Remote Scripting.”

There are three basic steps:

Create an XMLHttpRequest object.

Assign a callback function to handle the HTTP response.

Make (send) the request.

Let’s see an example where we’ll request a simple HTML document, test.html, which only contains the text “I’m a test.” We’ll then alert() the contents of the test.html file:

The Problem

The above example worked just fine, but there’s one thing we need to improve before we’re ready for prime time. The improvement is to code a reusable request function that handles all the boring and repetitive object creation and request/response stuff, while leaving the presentational part to other functions, which are request-agnostic and deal with the result only, regardless of its source.

In the example above, we needed a global variable, http_request, that was accessible by both the makeRequest() and alertContents() functions, which is not good in terms of reusability and also risks naming collisions. Ideally, makeRequest() should perform the request and alertContents() should just present the result; neither function needs know about or require the other.

a flag if the callback function expects an XML document (true) or plain text (false, default)

This function relies on two JavaScript capabilities in order to wrap and isolate the request object nicely. The first is the ability to define new functions (called anonymous functions) on the fly, like this:
http_request.onreadystatechange = function() {...}

The other trick is the ability to invoke callback functions without knowing their names in advance; for example:

var callmeback = alert;
callmeback('test'); // alerts 'test'

Note how the name of the callback function is passed without any quotes.

You can easily make the function even more reusable by allowing the HTTP request method as well as any query string to be passed as parameters to the function and then used in calls to open() and send() methods. This will also allow you to make POST requests in addition to the GETs it was originally intended to perform.

Another capability of the function is the handling of response codes other than 200, which could be handy if you want to be more specific and take appropriate actions depending on the type of the success/error code returned.

The Simple Example Revisited

Now let’s redo the previous example in which the contents of a test.html file were alert()ed. This time, by employing our shiny new reusable request function, the revised versions of the two functions used will be much simpler:

function alertContents(text) {
alert(text);
}

function makeRequest(url) {
makeHttpRequest(url, alertContents);
}

As you can see, alertContents() is simply presentational: there are no states, readyStates, or HTTP requests flying around whatsoever.

Since these functions are now just one-liners, we can in fact get rid of them entirely, and change the function call instead. So the whole example will become:

Our Project: The WebConsole Application

Knowing the Ajax basics, and armed with a reusable way of making requests, let’s go deeper, to create a little something that can actually be used in real life.

The application we’ll create will allow you to execute any shell command on your web server, whether it’s Windows- or Linux-based. We’ll even put in a little CSS effort in an attempt to make the app feel more like a console window.

Interface-wise, we have one scrollable <div> that contains the results of the commands executed so far, and one <input> where we type the commands to be executed. They both have a black background and gray courier font. Here’s a screenshot.

That’s it: a <div> that gets updated with the results of the command being executed, and an <input> into which we can type commands. It’s a nice, clean interface, with no <iframe> wrestling, no page reloads – none of that!

The CSS

The style sheet webconsole.css defines the styles for the result <div> and the command <input>:

We make the <div> that shows the command execution results scrollable by setting its overflow property to auto. We also change the <pre> tag display property to inline (block is its default). There’s also the reusable .console class to make everything look “consoley:” gray monospaced font on black background.

The Server-side Code

Our application will make requests to a server-side script (exec.php), which receives a command through the GET parameter 'command'. This script simply checks that the command appears in the allowed list (you can edit this list to allow more commands), executes the command, and prints the result. The command is executed with the help of the native PHP function shell_exec(). PHP is used here, but it should be relatively easy to implement this functionality using your preferred server-side language.

WARNING!
The $allowed_commands array restricts the commands that users can execute through the console. You can add as many commands as you like to the array, but beware that any additional commands will really be executed on your web server: adding format c:: apachectl stop or rm â€“rf, for example, is not recommended!

The JavaScript

The first step in the JavaScript code is to define a namespace: a glorified label for what is essentially nothing more than just an empty object:

var WebConsole = {};

All other variables and functions we need will be defined as properties of this object. This allows us to keep the global namespace clean and the code self-contained.

The flow of the JavaScript code in the application is as follows:

The WebConsole.keyEvent() function is attached to the onkeyup event of the input field, and is called every time a key is pressed and released.

WebConsole.keyEvent() checks if the key with code 13 is pressed (this is the Enter/Return key).

If Enter is pressed, the URL for the request is constructed like so: exec.php?command=the-command-entered-by-the-user

The URL is passed to our reusable makeHttpRequest() function. Also, the name of the callback function – WebConsole.printResult – is provided as a parameter to makeHttpRequest().

After a successful server response, WebConsole.printResult() is called.

WebConsole.printResult() updates the result <div>, scrolls down the <div>, and clears the command text box to make room for the next command to be typed.

But the use of innerHTML to dynamically update web pages is discouraged, because it treats the HTML code as a string, while modern web design thinking prefers to treat the page as a document containing an XML tree of nodes, accessible through the DOM methods and properties. The DOM is the path we’ll now take, in order to update our <div>.

adds the command that was entered in the <input> to the result <div> by creating a new text node and adding it to the document tree

displays the result of the command execution. This is done by splitting the result into lines and adding each line to the document tree, while wrapping each of these lines in <pre> tags to preserve the spacing. We need to split the result because it may contain several lines (imagine the result if a 'ls -la' (or 'dir' on Windows) was executed)

adds a new cursor-like text node (:->)

scrolls down the <div>, using the scrollTop and scrollHeight properties (non-W3C-standard but supported by modern browsers)

clears the command <input> so that the next command can be entered

The last task in the JavaScript is to handle the events:

form submits are simply “muted” so that there’s no page refresh

the method WebConsole.keyEvent() is attached to the keyup event of the input where commands are typed.

And there you go! This was the last piece of the puzzle. What we have now is a working Ajax application, built from scratch.

A Little Extra

If you were curious enough to look at the source code of the previous example, you may have noticed that there is a bit more to this app than what we’ve discussed so far. The little extra is not really Ajax-related, but it makes the application feel more like a command prompt. The functionality in question involves the use of the up and down arrow keys to access the history of the commands used in a session.

Let’s say you executed 'ls -la', then 'ls'. If you hit the up-arrow key, the command <input> will be prefilled with the command that was used last; that is, 'ls'. Hit the up-arrow key again and the command input will show 'ls -la'. Hit the down-arrow key. You reach 'ls' again as you move through the history of commands. Try it out yourself.

The implementation of this feature is not difficult. We just need an array that will store all the commands executed so far:
WebConsole.commands_history = [];

…and an array pointer (an integer) that remembers where we were:

WebConsole.history_pointer = 0;

Here’s the listing of the WebConsole.keyEvent() function. The lines that deal with the history functionality are shown in bold.

Here are some notes on how the function works to provide the commands history:

When we hit Enter (key code 13) and we make a request, the executed command is added to the commands_history array, and the array pointer is reset to the new length of the array.

When hitting the up arrow (key code 38), which means “go back,” we decrement history_pointer and prefill the command <input> with the previous command in the history list.

Hitting the down arrow increments the pointer by one, and we see the next command.

Working with XML

So far, we haven’t discussed how to request and use XML documents – the X in Ajax! We were using the responseText property of the XMLHTTP object. Requesting the document is no different than what we’ve already seen: we just need to instruct our reusable request function to return responseXML, as opposed to responseText. We do that by setting the third parameter to true:

makeHttpRequest(the_url, 'printResult', true);

Then, we need to change our exec.php script to return valid XML, instead of plain text. Here’s the source code of the new script (exec_xml.php):

In order to update the result <div> with the data from the XML document, we follow the procedure:

Access a node from the source XML.

Get its value.

Create a new node.

Append it to the <div> target tree.

As you see in the code xmldoc.getElementsByTagName ('command') is used, and it returns a collection (an array-like list object) of all <command> nodes. In our case, there’s only one such node. We access its value with the following:

xmldoc.getElementsByTagName('command')[0].firstChild.nodeValue;

We take the node value, and create a new text node to append to the <div>, like this:

$ is a quick name for jQuery; you can also do this instead: jQuery.Ajax

We call the Ajax() method and pass an object containing a URL to request, a data object (which will be escaped and converted to a query string by jQuery), and a callback function to call once the response arrives.

There are more ways to Ajax with jQuery, as a look at the documentation will confirm. For example, an often-repeated task of updating a <div> (with id mydiv) using the contents of the file (test.html) could be as simple as:

an object that contains functions to handle success and failure scenarios

YUI passes XMLHttpRequest objects to the handler functions, so in this case we just take the contents of the responseText and forward it to printResult().

You can see how the URL was created by concatenating and escaping strings. In this case, there’s only one value we want to pass through the query string. But if there are more, it becomes pretty inconvenient. YUI helps you deal with such situations easily, by providing a setForm() method. Once you set the form, YUI will take the values from the form and take care of escaping and stitching together the query string:
YAHOO.util.Connect.setForm(document.forms[0]);
YAHOO.util.Connect.asyncRequest(
'GET',
'exec.php',
{
success: function(xhr){
WebConsole.printResult(xhr.responseText)
}
}
);

JSON (JavaScript Object Notation) is a popular data exchange format. It gives you another alternative to plain text or XML when it comes to communicating data from the server to the browser. JSON is extremely simple; in essence, it’s just JavaScript.

This literal notation is what JSON uses to pass data. The quotes around properties in JavaScript proper are not required most of the time, but by convention they are required in JSON.

Let’s change our console so it uses the JSON format to transfer data. The flow would be:

PHP on the server side creates an associative array with the result, then converts it to JSON using the built-in function json_encode() which is PHP5-only, but it would be trivial to encode the response even manually. The JSON string is returned.

JavaScript in the browser receives the JSON string and transforms it into a native JavaScript object. An unsecure way to do so is by using the eval() function. The better way is to use the free JSON library.

For example, if the command we execute is ls, the JSON response from the server will be something like the following (formatted and abridged for readability):

You can see how after the eval(), data becomes a normal JavaScript object and you can access its properties, such as data.result and data.command. As mentioned already, eval() is an less-than-secure way of transforming a JSON encoded string into an object. A better way is by using the JSON library which helps us replace the eval() call with this:

Security Reminder
For the purposes of the demonstration of this application, I allow only a predefined set of harmless commands to be executed on my web server. If you expand the list of commands or allow any command whatsoever, don’t forget to protect the directory on your server in which you’ll install application. Leaving this application accessible to strangers can have devastating results. It’s pretty powerful: it will allow the user to execute any command, including, but not limited to, deleting everything on your web server!

Conclusion

We’ve come to the end of our example Ajax application. You know the basics, you saw the action, and you’re armed with enough knowledge to start experimenting yourself. You can warm up by modifying and playing around with this article’s code – it’s all included in the downloadable code archive – then move to projects of your own.

These are exciting times: the face of the Web is undergoing big changes, thanks to remote scripting. We’ve passed the phase of early adopters (Google, Amazon, Flickr, Yahoo) and now remote scripting with Ajax is becoming more common when creating responsive and user-friendly web pages. Your visitors nowadays are already spoiled by using GMail and Flickr, and you cannot afford to insult them with static Web 1.0-style pages!

Stoyan is an engineer at Yahoo and the author the author of Object-Oriented
JavaScript and other books and articles about web development. Outside work, his time is spent
experimenting with random web technologies, blogging at phpied.com and playing the blues
guitar to his daughters' despite their preference for Mickey
Mouse CDs.