12.2. Watching Stocks

Nothing changed the stock market quite like the Web. What used to be the domain of financial experts has become something that the everyman can now take part in. Online trading companies like E*Trade have changed the face of stock trading forever. Regardless of this change, the Web is first, and foremost, a vast repository of information, and stock price information is no exception.

12.2.1. Getting Yahoo! Finance Information

While not as feature filled as some Yahoo! premium services, basic stock reporting with Yahoo! Finance can easily be used in any Web application. The 15-minute delay certainly isn't desirable for anyone wanting up-to-the-second information, but this free service could satisfy anyone who wants to casually watch certain stocks during the day.

Yahoo! Finance stock information comes in comma-separated value (CSV) format, and you use the following URL to download information on any stock:

http://finance.yahoo.com/d/quotes.csv?s=stock_symbols&f=special_tags

Stock symbols should be separated by a plus sign (+). There's a host of information available for each individual stock, and you can specify what pieces of information you want with special tags. For example, the URL to get the day's opening price and the last trade's price for Microsoft's and GE's stock would look like this:

http://finance.yahoo.com/d/quotes.csv?s=MSFT+GE&f=ol1

For a complete list of available tags, visit www.gummy-stuff.org/Yahoo-data.htm.

The widget built in this section retrieves the stock symbol (tag: s), the company's name (tag: n), the last trade price (tag: l1), and the change since opening (tag: c1).

12.2.2. The Stock Quote Proxy

You'll use PHP to retrieve the stock quotes in CSV format and create a JSON structure (to send to the browser), which looks like this:

{
    "error"     : true || false,
    "stocks"    :
    [
        {
            "symbol"      : "stock symbol",
            "companyName" : "company's name",
            "lastTrade"   : "23.54",
            "change"      : "0.03"
        }
    ]
}

The first property in the object is error, which will hold a Boolean value (false means no error occurred). Next is stocks, an array of objects containing the requested data for each stock.

12.2.2.1. Organizing Stock Data

The structure of the stock objects comes from a PHP class called Stock, contained in the stock.class.php file.

class Stock {
    var $symbol;
    var $companyName;
    var $lastTrade;
    var $change;

    //More code to come.
}

The Stock class has four properties: symbol, companyName, lastTrade, and change. The constructor accepts one argument, $stock_data, which is a string containing the stock quote data.

class Stock {
    var $symbol;
    var $companyName;
    var $lastTrade;
    var $change;

    function Stock($stock_data) {
        //Split the data by commas.
        $split_data = explode(",", $stock_data);

        //Add the data to the properties
        $this->symbol       = $split_data[0];
        $this->companyName  = $split_data[1];
        $this->lastTrade    = $split_data[2];
        $this->change       = $split_data[3];
    }
}

$stock_data is in CSV format, so use the explode() function to split the string into an array and assign the class's properties their corresponding values. You can rely upon this order of assignment, as the data received from Yahoo! is in symbol, companyName, lastTrade, change format.

12.2.2.2. Retrieving the Stock Quotes

Aside from the JSON PHP class introduced in Chapter 8, the remainder of PHP code is contained in stockproxy.php, which is primarily responsible for retrieving data from Yahoo! Finance. Because JSON is used in this widget, the first two lines of this file set the required headers:

header("Content-Type: text/plain; charset=UTF-8");
header("Cache-Control: no-cache");

//more to come

The first line sets the Content-Type header to text/plain with a UTF-8 character set, and the second line sets the Cache-Control header, forcing browsers to not cache the information. Next, include the needed PHP files.

header("Content-Type: text/plain; charset=UTF-8");
header("Cache-Control: no-cache");

require_once("inc/stock.class.php");
require_once("inc/JSON.php");

class JSONObject {}

$SYMBOLS = array(
    "MSFT",
    "GE"
);

//more to come

Also, a generic class called JSONObject is defined; an instance of this class holds data in a structured format until it is time to serialize it into a JSON string. And $SYMBOLS is an array that contains stock symbols. These symbols are used to retrieve quotes from Yahoo! Finance.

The workhorse of this PHP application is the get_stock_quotes() function. This function attempts to retrieve information from http://finance.yahoo.com, and if successful, structures the data for JSON serialization.

//Header, required files, and JSONObject

$SYMBOLS = array(
    "MSFT",
    "GE"
);

function get_stock_quotes() {
    global $SYMBOLS;

    //Get the symbols in the format we need.

$symbol_string = implode("+", $SYMBOLS);

    //Build the URL
    $url = "http://finance.yahoo.com/d/quotes.csv?s=". $symbol_string ."&f=snl1c1";

    //Get the data.
    $data = file_get_contents($url);

    //Create the JSON object.
    $json = new Services_JSON ();

    //Create the JSONObject
    $object_to_serialize = new JSONObject();

    //more to come
}

The first step of this function is to transform the $SYMBOLS array into the format that you need (SYMBOL1+SYMBOL2+SYMBOL3, etc). The implode() function does this and returns the string to $symbol_string. Next, build the URL with $symbol_string, and use file_get_contents() to retrieve the data from Yahoo!. The last lines of this code create a Services_JSON object called $json, and a JSONObject object called $object_to_serialize.

If file_get_contents() failed to retrieve data from Yahoo!'s server, the $data variable will be false. So first, check to see if the request was successful:

function get_stock_quotes() {
    //Code cut for space......

    //Create the JSON object.
    $json = new Services_JSON ();

    //Create the JSONObject
    $object_to_serialize = new JSONObject();

    if (!$data) {
        $object_to_serialize->error = true;
    } else {
        //more to come
    }

    //more to come
}

If not, then add a property to $object_to_serialize called error, and set it to true. This information is utilized by the client, allowing the user to know that an error occurred. If the request was successful, then the data needs to be parsed.

The data received looks something like this:

"MSFT","MICROSOFT CP",27.87,-0.05
"GE","GEN ELECTRIC CO",36.14,-0.15

Each quote is followed by " ", and string data is contained within double quotation marks. The code needs to take these factors into account.

if (!$data) {
        $object_to_serialize->error = true;
    } else {
        //Start to populate our JSON object.
        $object_to_serialize->error = false;
        $object_to_serialize->stocks = array();

        //Remove the quotes that we get from Yahoo!
        $data = preg_replace('/"/', "", $data);

        //Create an array.
        $split_data = explode("
", $data);

        //The last element is just 
. Pop it off.
        array_pop($split_data);

        foreach($split_data as $stock_data)
            $object_to_serialize->stocks[] = new Stock($stock_data);

        //more code here
    }

The first step is to add error and stocks properties to $object_to_serialize. The error property is set to false, and stocks is an array. Next, use the preg_replace() function to remove the double quotation marks from the string values.

The next goal is to remove the carriage returns and separate the quotes from each other. To do this, use the explode() function. The result is an array with each element containing one quote. The last quote in the CSV, however, has a carriage return at the end of the line, which gives $split_data an extra array element in the last position of the array. By using array_pop(), you can remove the last element in the array, leaving nothing but stock quote data contained in $split_data.

Last, parse the data by looping through the $split_data array and adding Stock instances to the $object_to_serialize->stock property.

The final step of get_stock_quotes(), and the stockproxy.php file, is to output the serialized $object_to_serialize object and call the get_stock_quotes() function:

header("Content-Type: text/plain; charset=UTF-8");
header("Cache-Control: no-cache");

require_once("inc/stock.class.php");
require_once("inc/JSON.php");

class JSONObject {}

$SYMBOLS = array(
    "MSFT",
    "GE"

);

function get_stock_quotes() {
    global $SYMBOLS;

    //Get the symbols in a format we can use.
    $symbol_string = implode("+", $SYMBOLS);

    //Build the URL
    $url = "http://finance.yahoo.com/d/quotes.csv?s=". $symbol_string ."&f=snl1c1";

    //Get the data.
    $data = file_get_contents($url);

    //Create the JSON object.
    $json = new Services_JSON ();

    //Create the JSONObject
    $object_to_serialize = new JSONObject();

    if (!$data) {
        $object_to_serialize->error = true;
    } else {
        //Remove the quotes that we get from Yahoo!
        $data = preg_replace('/"/',"",$data);

        //Start to populate our JSON object.
        $object_to_serialize->error = false;
        $object_to_serialize->stocks = array();

        //Create an array.
        $split_data = explode("
", $data);

        //The last element is just 
. Pop it off.
        array_pop($split_data);

        foreach($split_data as $stock_data)
            $object_to_serialize->stocks[] = new Stock($stock_data);
    }

    //Echo the serialized data, yo!
    echo $json->encode($object_to_serialize);
}

get_stock_quotes();

12.2.3. Client Component: The AjaxStockWatcher Class

The client portion of this widget isn't overly complex; it is a class designed to poll data from the server, build an HTML table, populate it with data, and update it every so often. Before digging into the JavaScript, take a look at the HTML structure the script creates.

12.2.3.1. The User Interface

The table is a three-column table; the first cell contains the company name, followed by the last trade price, and finishing off with the change.

<table class="ajaxStockWatcher-Table">
  <tr>
    <td class="ajaxStockWatcher-stockName">
      <a href="http://finance.yahoo.com/q?s=[STOCK_SYMBOL]>Company Name</a>
    </td>
    <td class="ajaxStockWatcher-lastTrade">10.00</td>
    <td class="ajaxStockWatcher-change ajaxStockWatcher-change-up">+1.00</td>
  </tr>
  <tr>
    <td class="ajaxStockWatcher-stockName">
      <a href="http://finance.yahoo.com/q?s=[STOCK_SYMBOL2]>Company Name2</a>
    </td>
    <td class="ajaxStockWatcher-lastTrade">8.00</td>
    <td class="ajaxStockWatcher-change ajaxStockWatcher-change-down">-2.00</td>
  </tr>
</table>

Each quote exists in its own table row, and the company's name is used in a hyperlink to take the user to Yahoo! Finance report on the stock. Notice that the third cell in each row uses two CSS classes. This allows anyone who applies this widget to their web site greater flexibility when writing the CSS rules. For example, they could use the ajaxStockWatcher-change class to apply styling to the cell, and use the ajaxStockWatcher-change-up and -down classes to color the text green or red, depending upon if the stock's change is up or down.

12.2.3.2. The Class Constructor

The AjaxStockWatcher class constructor accepts one optional argument: the HTMLElement you append the HTML built by the widget to:

function AjaxStockWatcher(oElement) {
    //Get the element we'll append to.
    //If one's not specified, use the document's <body/>
    this.toAppend = (oElement) ? oElement : document.body;

    //Create the table that'll house our data
    this.table = document.createElement("table");

    //Assign its CSS class
    this.table.className = "ajaxStockWatcher-table";

    //and append it to toAppend
    this.toAppend.appendChild(this.table);

    //more code here
}

The first line of code defines the toAppend property. This property's value points to the HTMLElement specified in the oElement argument. If no value is passed to the constructor, then toAppend assumes the value of document.body. The next few lines of code create the <table/> element, assign its CSS class to ajaxStockWatcher-table, and append it toAppend.

The class preparation is nearly complete. All that remains is to set up the automatic polling with this next code:

function AjaxStockWatcher(oElement) {
    //Get the element we'll append to.
    //If one's not specified, use the document's <body/>
    this.toAppend = (oElement) ? oElement : document.body;

    //Create the table that'll house our data
    this.table = document.createElement("table");

    //Assign its CSS class
    this.table.className = "ajaxStockWatcher-table";

    //and append it to toAppend
    this.toAppend.appendChild(this.table);

    //For the timeout
    this.timer = null;

    //Begin polling.
    this.poll();
}

The first new line of code initializes the timer property to null. The AjaxStockWatcher class uses this property to keep track of the timeouts used for automatic polling. The last line of the constructor calls the poll() method, which starts the polling cycle.

12.2.3.3. Polling for Data

The poll() method is responsible for requesting and receiving stock information from the server component with an XHR object.

AjaxStockWatcher.prototype.poll = function() {
    //Pointer to the current object.
    var oThis = this;

    //Create the XHR object and handle the o.r.s.c. event
    var oReq = zXmlHttp.createRequest();
    oReq.onreadystatechange = function () {
        if (oReq.readyState == 4) {
            if (oReq.status == 200 || oReq.status == 304) {
                oThis.handleResponse(oReq.responseText);
            }
        }
    };

    //Send the request
    oReq.open("GET", "stockproxy.php", true);
    oReq.send(null);
};

The method first begins with creating a variable called oThis, a pointer to the current object. Next, an XHR object is created, and the onreadystatechange event handler is assigned. On a successful request, the XHR object's responseText is passed to the handleResponse() method, and finally, the request is sent to stockproxy.php.

12.2.3.4. Handling the Server's Response

On a successful request, the AjaxStockWatcher object needs to take the received data and populate the table. This job is delegated to the handleResponse() method. This method accepts one argument called sJson, the serialized data from the server component.

AjaxStockWatcher.prototype.handleResponse = function (sJson) {
    //Parse the JSON string
    var oResult = sJson.parseJSON();

    //more code here
}

The first line takes the serialized data and parses it into a JavaScript object by using the JSON library. Next, the contents of the table are cleared by looping through the rows collection and removing each row from the table. Then, the method determines if a server-side error occurred. This is accomplished by checking the error property of the oResult object:

AjaxStockWatcher.prototype.handleResponse = function (sJson) {
    //Parse the JSON string
    var oResult = sJson.parseJSON();

    //Delete the existing stocks shown.
    while (this.table.rows.length > 0)
        this.table.deleteRow(0);

    if (!oResult.error) {
        //No error. Display the information.
        for (var i = 0; i < oResult.stocks.length; i++) {
            var oStock = oResult.stocks[i];

            //Insert a new row
            var oRow = this.table.insertRow(i);

            //more code here

        }
    }
}

If the server component had no errors, then handleResponse() loops through the stocks array and create the table row for the current stock with the insertRow() method.

12.2.3.4.1. Adding the Data Cells and Data

Next, you add in the data cells as follows:

AjaxStockWatcher.prototype.handleResponse = function (sJson) {
    //Parse the JSON string
    var oResult = sJson.parseJSON();

    //Delete the existing stocks shown.
    while (this.table.rows.length > 0)
        this.table.deleteRow(0);

    if (!oResult.error) {
        //No error. Display the information.
        for (var i = 0; i < oResult.stocks.length; i++) {
            var oStock = oResult.stocks[i];

            //Insert a new row
            var oRow = this.table.insertRow(i);

            //Add a cell for the stock's name
            var tdName = oRow.insertCell(0);
            tdName.className = "ajaxStockWatcher-stockName";

            //And the last trade amount.
            var tdLastTrade = oRow.insertCell(1);
            tdLastTrade.className = "ajaxStockWatcher-lastTrade";

            //And the change
            var tdChange = oRow.insertCell(2);
            tdChange.className = "ajaxStockWatcher-change";
            tdChange.className += (parseFloat(oStock.change) > 0) ?
                " ajaxStockWatcher-change-up" : " ajaxStockWatcher-change-down";

            //more code here
        }
    }
}

By using the insertCell() method, you can easily add cells to an existing <tr/> elements, as this code illustrates. This code is straightforward, except for perhaps the className assignment for the tdChange element. The first assignment statement sets the cell's CSS class to ajaxStockWatcher-change. Then, the className is appended according to oStock.change's value. If it is a positive value, the "up" class is used; otherwise, the "down" class is added to the className property.

With the cells added to the table, you can now add data to them.

AjaxStockWatcher.prototype.handleResponse = function (sJson) {
    //Parse the JSON string
    var oResult = sJson.parseJSON();

    //When we should call poll() again.

//30 seconds is the default.
    var iTimeForNextPoll = 30;

    //Delete the existing stocks shown.
    while (this.table.rows.length > 0)
        this.table.deleteRow(0);

    if (!oResult.error) {
        //No error. Display the information.
        for (var i = 0; i < oResult.stocks.length; i++) {
            var oStock = oResult.stocks[i];

            //Insert a new row
            var oRow = this.table.insertRow(i);

            //Add a cell for the stock's name
            var tdName = oRow.insertCell(0);
            tdName.className = "ajaxStockWatcher-stockName";

            //And the last trade amount.
            var tdLastTrade = oRow.insertCell(1);
            tdLastTrade.className = "ajaxStockWatcher-lastTrade";

            //And the change
            var tdChange = oRow.insertCell(2);
            tdChange.className = "ajaxStockWatcher-change";
            tdChange.className += (parseFloat(oStock.change) > 0) ?
                " ajaxStockWatcher-change-up" : " ajaxStockWatcher-change-down";

            //Create the link used as
            var aLinkToYahoo = document.createElement("a");
            aLinkToYahoo.appendChild(document.createTextNode(oStock.companyName));
            aLinkToYahoo.href = "http://finance.yahoo.com/q?s=" + oStock.symbol;


            //Append the data to the <td/>s
            tdName.appendChild(aLinkToYahoo);
            tdLastTrade.appendChild(document.createTextNode(oStock.lastTrade));
            tdChange.appendChild(document.createTextNode(oStock.change));
        }
    }
    //more code here
}

This new code first creates the hyperlink that takes the user to Yahoo! Finance's web page for this particular stock. Then, the link and remaining stock data (the last trade and price change) are appended to their corresponding cells in the table row.

12.2.3.4.2. Handling Errors

If the server component returns an error, for whatever reason, the user should know why stock data is not displayed. This is easily accomplished with the following code:

AjaxStockWatcher.prototype.handleResponse = function (sJson) {
    //Parse the JSON string
    var oResult = sJson.parseJSON();

    //When we should call poll() again.
    //30 seconds is the default.
    var iTimeForNextPoll = 30;

    //Delete the existing stocks shown.
    while (this.table.rows.length > 0)
        this.table.deleteRow(0);

    if (!oResult.error) {
        //No error. Display the information.
        for (var i = 0; i < oResult.stocks.length; i++) {
            var oStock = oResult.stocks[i];

            //Insert a new row
            var oRow = this.table.insertRow(i);

            //Add a cell for the stock's name
            var tdName = oRow.insertCell(0);
            tdName.className = "ajaxStockWatcher-stockName";

            //And the last trade amount.
            var tdLastTrade = oRow.insertCell(1);
            tdLastTrade.className = "ajaxStockWatcher-lastTrade";

            //And the change
            var tdChange = oRow.insertCell(2);
            tdChange.className = "ajaxStockWatcher-change";
            tdChange.className += (parseFloat(oStock.change) > 0) ?
                " ajaxStockWatcher-change-up" : " ajaxStockWatcher-change-down";

            var aLinkToYahoo = document.createElement("a");
            aLinkToYahoo.appendChild(document.createTextNode(oStock.companyName));
            aLinkToYahoo.href = "http://finance.yahoo.com/q?s=" + oStock.symbol;


            //Append the data to the <td/>s
            tdName.appendChild(aLinkToYahoo);
            tdLastTrade.appendChild(document.createTextNode(oStock.lastTrade));
            tdChange.appendChild(document.createTextNode(oStock.change));
        }

    } else { //An error occurred. Probably network related.
        //Insert a new row
        var oRow = this.table.insertRow(0);

        //Add a cell, and text to tell the user
        //an error occurred
        var tdError = oRow.insertCell(0)
        tdError.colSpan = 3;
        tdError.appendChild(

document.createTextNode(
                "An error occurred. Attempting to reconnect..."
            )
        );
    }

    //more code here
}

This code creates a new row in the table (if an error occurred, it'll be the only row in the table). Then a data cell is created, and a text node is appended to it stating that an error occurred and that the widget is attempting to retrieve the information again. This approach allows you to keep the user informed in a graceful way (as opposed to using an alert box every 30 seconds).

12.2.3.4.3. Retrieving Updated Information

The handleResponse() method should do one more thing: call poll() again in 30 seconds to retrieve updated information, and this is easily accomplished with the following code.

AjaxStockWatcher.prototype.handleResponse = function (sJson) {
    //Parse the JSON string
    var oResult = sJson.parseJSON();

    //When we should call poll() again.
    //30 seconds is the default.
    var iTimeForNextPoll = 30;

    //Delete the existing stocks shown.
    while (this.table.rows.length > 0)
        this.table.deleteRow(0);


    if (!oResult.error) {
        //No error. Display the information.
        for (var i = 0; i < oResult.stocks.length; i++) {
            var oStock = oResult.stocks[i];

            //Insert a new row
            var oRow = this.table.insertRow(i);

            //Add a cell for the stock's name
            var tdName = oRow.insertCell(0);
            tdName.className = "ajaxStockWatcher-stockName";

            //And the last trade amount.
            var tdLastTrade = oRow.insertCell(1);
            tdLastTrade.className = "ajaxStockWatcher-lastTrade";

            //And the change
            var tdChange = oRow.insertCell(2);
            tdChange.className = "ajaxStockWatcher-change";
            tdChange.className += (parseFloat(oStock.change) > 0) ?
                " ajaxStockWatcher-change-up" : " ajaxStockWatcher-change-down";

            var aLinkToYahoo = document.createElement("a");

aLinkToYahoo.appendChild(document.createTextNode(oStock.companyName));
            aLinkToYahoo.href = "http://finance.yahoo.com/q?s=" + oStock.symbol;


            //Append the data to the <td/>s
            tdName.appendChild(aLinkToYahoo);
            tdLastTrade.appendChild(document.createTextNode(oStock.lastTrade));
            tdChange.appendChild(document.createTextNode(oStock.change));
        }

    } else { //An error occurred. Probably network related.
        //Insert a new row
        var oRow = this.table.insertRow(0);

        //Add a cell, and text to tell the user
        //an error occurred
        var tdError = oRow.insertCell(0)
        tdError.colSpan = 3;
        tdError.appendChild(
            document.createTextNode(
                "An error occurred. Attempting to reconnect..."
            )
        );
    }

    //Pointer to the current object.
    var oThis = this;

    //For the timeout
    var doSetTimeout = function () {
        oThis.poll();
    };

    //Run doSetTimeout() in 30 seconds.
    this.timer = setTimeout(doSetTimeout, 30000);
}

This new code assigns a pointer to the current object to the oThis variable. Next, a function called doSetTimeout() is created to call the poll() method. Then, by using setTimeout(), the enclosed method is set to be called in 30 seconds, thus facilitating the automatic update feature.

12.2.3.5. Stop Automatic Updates

Of course, there are times when it's desirable to stop a script from automatically updating itself; therefore, the widget needs a mechanism to do just that. The last method of the AjaxStockWatcher class, and the simplest one, is the stopPoll() method.

AjaxStockWatcher.prototype.stopPoll = function () {
    //Stop the polling
    clearTimeout(this.timer);
};

The one line of code in this method uses the clearTimeout() method to stop the timeout created in handleResponse().

12.2.4. Customizing the Stock Quotes

The style sheet provided in the code download styles this widget to fit into a sidebar (see Figure 12-7).

Figure 12.7. Figure 12-7

Giving the HTML this look and feel relies upon a simple CSS style sheet. Only the <table/> and <td/> elements have associated CSS classes, but you can easily style other HTML elements used in the widget.

/* The stock watcher table */
.ajaxStockWatcher-table
{
    font: 13px Arial;
    border: 2px solid #0066CC;
}

/* The table rows */
.ajaxStockWatcher-table tr
{
    background-color: #D4E2F7;
}

The first rule of the style sheet is the ajaxStockWatcher-table class. The text inside the table is 13 pixels in height and uses the Arial font face. A blue border, two pixels in width, also surrounds the table. Next, the <tr/> elements within the table are styled to have a light-blue background. This makes the information easier to read.

The next step is to style the data cells.

/* <td/> with the stock company's name */
.ajaxStockWatcher-stockName {}

/* <td/> with the last trade price */
.ajaxStockWatcher-lastTrade
{
    padding-left: 5px;
    padding-right: 5px;
    text-align: right;
}

/* <td/> with the change amount */
.ajaxStockWatcher-change
{
    text-align: right;
}

The first rule in this code is ajaxStockWatcher-stockName. It currently has no CSS properties, but you can easily style it, the hyperlink, and the text if you so desire. Following the stock name is the ajaxStockWatcher-lastTrade class. The left and right portions of this cell are padded with 5 pixels, and the right alignment of the text is how currency is typical displayed. The next rule, the ajaxStockWatcher-change class, aligns the text to the right for the same reasons for the -lastTrade class.

Two classes remain in the style sheet, and they are the ajaxStockWatcher-change-up and -down classes. These classes merely change the color of the text:

/* Used when the stock is up */
.ajaxStockWatcher-change-up
{
    color: green;
}

/* Used when the stock is down */
.ajaxStockWatcher-change-down
{
    color: red;
}

When the stock change is positive, the text color is green. When the stock's down, the color is red. This visual effect allows the viewer to easily correlate up and down stocks quickly, because one can identify color quicker than one can read plain text.

12.2.5. Using the Stock Watcher Widget

To add the widget to a web page, you must reference the required files. The widget relies upon the zXml and JSON libraries, so make sure they're added.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Stock Watcher</title>
        <link rel="stylesheet" type="text/css" href="css/ajaxstocktracker.css" />
        <script type="text/javascript" src="js/zxml.js"></script>
        <script type="text/javascript" src="js/json.js"></script>
        <script type="text/javascript" src="js/ajaxStockTracker.js"></script>
    </head>
    <body>
    </body>
</html>

Also required are the ajaxStockTracker.css and ajaxStockTracker.js files. Next, create an instance of the AjaxStockWatcher class:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Stock Watcher</title>
        <link rel="stylesheet" type="text/css" href="css/ajaxstocktracker.css" />
        <script type="text/javascript" src="js/zxml.js"></script>
        <script type="text/javascript" src="js/json.js"></script>
        <script type="text/javascript" src="js/ajaxStockTracker.js"></script>

        <script type="text/javascript">
            var stockWatcher;
            onload = function() {
                stockWatcher = new AjaxStockWatcher();
            };
        </script>
    </head>
    <body>
    </body>
</html>

This code appends the table to the document's body. If you wanted to add it to a preexisting element, your code might look like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Stock Watcher</title>
        <link rel="stylesheet" type="text/css" href="css/ajaxstocktracker.css" />

<script type="text/javascript" src="js/zxml.js"></script>
        <script type="text/javascript" src="js/json.js"></script>
        <script type="text/javascript" src="js/ajaxStockTracker.js"></script>
        <script type="text/javascript">
            var stockWatcher;
            onload = function() {
                var divStocks = document.getElementById("divStocks");
                stockWatcher = new AjaxStockWatcher(divStocks);
            };
        </script>
    </head>
    <body>
        <div id="divStocks"></div>
    </body>
</html>

This approach allows you to easily add the widget into a predesigned location.

The Stock Watcher widget in this section varies from the Weather widget discussed earlier in the chapter. Both the client- and server-side components have a level of complexity; the server retrieved the information, parsed it, and formatted it into JSON. The client code took the JSON, parsed it, and dynamically created the HTML to display the information.

The next widget follows along the same lines as the Stock Watcher: the server retrieves information and returns it as JSON, and the client displays that information. The major difference is the following widget, the Site Search widget, uses the .NET Framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset