WEEK 3
In Review

In this third and final week, you have learned more about XSLT using XSLT in real-world applications. You also have learned about design issues involved in creating real-world applications. You learned that although XSLT isn’t a high-end programming language when it comes to computations, you can still do computations on source data, such as aggregating and performing statistical analysis.

You now have a solid grasp of all that XSLT has to offer. As you have seen, XSLT is very powerful in certain areas and can match any other language in that way. XSLT enables you to perform complex transformations with little effort. The same transformations would be very hard in most other languages. This is, of course, not surprising because XSLT is specifically designed to operate on XML, whereas languages such as C++, Java, and Visual Basic are more generic in their use of data sources.

Overview of Bonus Project 3

This week’s Bonus Project aims at combining several of the points you have learned over the last seven days. Of course, not everything covered will be used here because the Bonus Project is about a situation that may occur in a real-world application. It is unlikely that all that has been discussed will find its way into one small project. You will, however, see namespaces and computation.

Note

This Bonus Project requires Internet Explorer 6.0 or higher, or Internet Explorer 5.0 or higher with version 3.0 or higher of the MSXML component installed in Replace mode. The easiest way to make sure that you can run the Bonus Project is by updating Internet Explorer through Windows Update, or http://www.microsoft.com/windows/ie/default.asp.

Creating a Shopping Basket in XSLT

This week’s Bonus Project is a shopping basket that runs entirely in an XML- and XSLT-enabled browser. Because of that requirement, the shopping basket was written for Internet Explorer with MSXML 3.0 or higher. The code that runs the processor is written in JavaScript. When other browsers support XSLT processors it is easy to port to work on those browsers.

The end product of this Bonus Project is one large HTML document that consists of five major pieces:

• The HTML and JavaScript code that runs the shopping basket

• An XML island with the product XML

• An XML island with the shopping basket

• An XML island with the stylesheet responsible for displaying the shopping basket and the product file

• An XML island with the stylesheet performing calculations

NEW TERM

An XML island is an HTML element in Internet Explorer that can hold an XML document. In the HTML file, it looks as follows:

<xml id="myisland"><!--XML code here--></xml>

These XML islands hold all the data and XSLT. The rest of the code runs the processor, and so on.

The Product Data

The fact that this project is about a shopping basket means, of course, that there must be product data. The data chosen is closely related to the data from Bonus Project 2, with wines and cheeses. The product data is shown in Listing BP3.1.

Listing BP3.1 Product Data

1:  <prd:products xmlns:prd="http://www.aspnl.com/xmlns/products">
2:      <prd:product prd:ID="1" prd:description="Bordeaux"
3:            prd:price="9.95" prd:type="wine" prd:color="red" />
4:      <prd:product prd:ID="2" prd:description="Ruby Cabernet"
5:            prd:price="10.95" prd:type="wine" prd:color="red" />
6:       <prd:product prd:ID="3" prd:description="Soave"
7:           prd:price="8.95" prd:type="wine" prd:color="white" />
8:      <prd:product prd:ID="4" prd:description="Chianti"
9:            prd:price="11.95" prd:type="wine" prd:color="red" />
10:     <prd:product prd:ID="5" prd:description="Merlot"
11:           prd:price="7.95" prd:type="wine" prd:color="red" />
12:     <prd:product prd:ID="6" prd:description="Camembert"
13:           prd:price="3.95" prd:type="cheese" />
14:      <prd:product prd:ID="7" prd:description="Gouda"
15:           prd:price="4.95" prd:type="cheese" />
16:     <prd:product prd:ID="8" prd:description="Brie"
17:           prd:price="3.95" prd:type="cheese" />
18:     <prd:product prd:ID="9" prd:description="Mozzarella"
19:           prd:price="2.95" prd:type="cheese" />
20:      <prd:product prd:ID="10" prd:description="Feta"
21:           prd:price="1.95" prd:type="cheese" />
22: </prd:products>

ANALYSIS

Listing BP3.1 uses a namespace for the product data. The namespace prefix is prd, which is used for both elements and attributes. Each product has at least a unique identifier, description, and price. As you can see, it may have additional attributes. These additional attributes don’t have to be the same, and each product doesn’t need to have the same number of attributes. The wine on line 2 for instance has other attributes than the cheese on line 12. To keep the project simple, the additional data is ignored here, but the display stylesheet can easily be modified to show this information as well.

The Shopping Basket

The shopping basket’s data—in essence, the items that somebody has placed in the shopping basket—is stored in XML. The shopping basket is the heart of the application because this data changes while the user moves through the shop. All the other data and stylesheets are static. The shop starts off with an empty shopping basket, as shown in Listing BP3.2.

Listing BP3.2 Empty Shopping Basket

     <shop:basket xmlns:shop="http://www.aspnl.com/xmlns/shop"
                xmlns:prd="http://www.aspnl.com/xmlns/products">
     </shop:basket>

ANALYSIS

The empty shopping basket consists of only the shop:basket element. Note that two namespaces are declared, the first for the shopping basket data and the other for the product information. The latter is the same as the namespace used in the product data in Listing BP3.1. Suppose you put two bottles of Merlot and a Camembert cheese in the shopping basket; it would then look like Listing BP3.3.

Listing BP3.3 Basket with Several Items

1: <shop:basket xmlns:shop="http://www.aspnl.com/xmlns/shop"
2:              xmlns:prd="http://www.aspnl.com/xmlns/products">
3:   <prd:product prd:ID="5" prd:description="Merlot"
4:     prd:price="7.95" prd:type="wine" prd:color="red" shop:quantity="2" />
5:   <prd:product prd:ID="6" prd:description="Camembert"
6:     prd:price="3.95" prd:type="cheese" shop:quantity="1" />
7: </shop:basket>

ANALYSIS

Listing BP3.3 shows the shopping basket after several items have been placed in it. The product on line 10 of Listing BP3.1 has been copied over entirely, and the same goes for the product on line 12. Note that both elements have the additional attribute shop:quantity on lines 4 and 6. This attribute is part of the shopping basket data and therefore has a different namespace so that it can be distinguished from the product data itself. It is very important that all the product data is copied over. When the user is finished shopping, the whole result can be used in checking out, creating an invoice, shipping information, and so on. Using only product identifiers, this wouldn’t be possible. In effect, the shopping basket is independent of any other data sources.

Displaying the Data

With the two data files, let’s look at how this data will be displayed. Listing BP3.4 shows the stylesheet responsible for displaying the data.

Listing BP3.4 Stylesheet Responsible for Displaying the Product Data and the Shopping Basket

      1:  <xsl:stylesheet version=" 1.0"exclude-result-prefixes="shop prd"
      2:            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      3:           xmlns:shop="http://www.aspnl.com/xmlns/shop"
      4:            xmlns:prd="http://www.aspnl.com/xmlns/products">
      5:
      6:      <xsl:output method="html" omit-xml-declaration="yes" />
      7:
      8:      <xsl:template match="/">
      1:        <xsl:apply-templates />
      10:      </xsl:template>
      11:
      12:     <xsl:template match="prd:products">
      13:       <h1>Products</h1>
      14:       <table border="1">
      15:        <tr>
      16:          <th>ID</th>
      17:          <th>description</th>
      18:           <th>price</th>
      19:           <th></th>
      20:        </tr>
      21:        <xsl:for-each select="//prd:product">
      22:           <tr>
      23:             <td><xsl:value-of select="@prd:ID" /></td>
      24:             <td><xsl:value-of select="@prd:description" /></td>
      25:            <td>US $ <xsl:value-of select="@prd:price" /></td>
      26:             <td><a><xsl:attribute name="href">#</xsl:attribute>
      27:                    <xsl:attribute name="onclick">
      28:                     <xsl:text>return addProduct (</xsl:text>
      29:                      <xsl:value-of select="@prd:ID" />
      30:                     <xsl:text>, 1)</xsl:text>
      31:                    </xsl:attribute>
      32:                   <xsl:text>add to cart</xsl:text></a>
      33:            </td>
      34:           </tr>
      35:         </xsl:for-each>
      36:       </table>
      37:       <a href="#" onclick="return showBasket ()">show basket</a>
      38:     </xsl:template>
      39:
      40:    <xsl:template match="shop:basket">
      41:      <form id="basketform">
      42:         <h1>Basket</h1>
      43:        <table border="1">
      44:           <tr>
      45:             <th>Quantity</th>
      46:             <th>ID</th>
      47:             <th>Item</th>
      48:             <th>Price per item</th>
      49:             <th>Tax per item</th>
      50:             <th>Product total +tax</th>
      51:             <th>Delete</th>
      52:           </tr>
      53:          <xsl:for-each select="//prd:product">
      54:             <xsl:call-template name="basketproduct" />
      55:           </xsl:for-each>
      56:        </table>
      57:        <br />
      58:        <input type="submit" value="Update basket"
      59:                onclick="return updateBasket ()" />
      60:       </form>
      61:      <a href="#" onclick="return showProducts ()">show products</a>
      62:    </xsl:template>
      63:
      64:     <xsl:template name="basketproduct">
      65:      <xsl:variable name="taxrate" select="0.06" />
      66:      <xsl:variable name="tax" select="@prd:price * $taxrate" />
      67:       <xsl:variable name="producttotal"
      68:                    select="@shop:quantity *  (@prd:price + $tax)" />
      69:      <tr>
      70:         <td>
      71:          <input type="text" size="2" name="update{@prd:ID}"
      72:                 value="{@shop:quantity}" />
      73:         </td>
      74:        <td><xsl:value-of select="@prd:ID" /></td>
      75:        <td><xsl:value-of select="@prd:description" /></td>
      76:        <td><xsl:value-of select="@prd:price" /></td>
      77:         <td><xsl:value-of
      78:                 select="format-number ($tax, '#,##0.00')" /></td>
      79:         <td><xsl:value-of
      80:                 select="format-number ($producttotal, '#,##0.00')" />
      81:         </td>
      82:         <td>
      83:          <input type="checkbox">
      84:             <xsl:attribute name="name">
      85:              <xsl:text>delete</xsl:text>
      86:              <xsl:value-of select="@prd:ID" />
      87:            </xsl:attribute>
      88:           </input>
      89:         </td>
      90:      </tr>
      91:    </xsl:template>
      92:  </xsl:stylesheet>

ANALYSIS

Listing BP3.4 has a double function: It is used to display both the product data and the contents of the shopping basket. Because the whole thing is already embedded in an HTML file, you don’t have to worry about the outer HTML elements such as HTML and BODY. The template on line 12 is responsible for displaying the product data. It matches the prd:products element. Basically, it creates a table with each row showing a product. Each table cell shows product information. Line 21 iterates through the products and creates the table rows. Lines 26–32 are very important for the functionality of the output. These lines are responsible for creating a link that calls a JavaScript function that adds the product to the shopping basket. Line 28 tells the browser to call the addProduct JavaScript function, with line 29 inserting the identifier of the product that needs to be added to the shopping basket. Line 30 passes the quantity of the product to be added; for this project, the quantity is always 1. Listing BP3.5 shows what the HTML looks like when it is created for Listing BP3.1.

OUTPUT

Listing BP3.5 Partial Listing of HTML Displaying the Product Data

   1:  <H1>Products</H1>
   2:  <TABLE border=1>
   3:  <TBODY>
   4:  <TR>
   5:  <TH>ID</TH>
   6:  <TH>description</TH>
   7:  <TH>price</TH>
   8:  <TH></TH></TR>
   9:  <TR>
   10: <TD>1</TD>
   11: <TD>Bordeaux</TD>
   12: <TD>US $ 9.95</TD>
   13: <TD><A onclick="return addProduct (1, 1)"#">add to cart</A></TD></TR>
   14: <TR>
   15: <TD>2</TD>
   16: <TD>Ruby Cabernet</TD>
   17: <TD>US $ 10.95</TD>
   18: <TD><A onclick="return addProduct (2, 1)" href="#">add to cart</A>
    </TD></TR>
   19: <TR>
   20: <TD>3</TD>
   21: <TD>Soave</TD>
   22: <TD>US $ 8.95</TD>
   23: <TD><A onclick="return addProduct (3, 1)" href="#">add to cart</A>
    </TD></TR>
   24: <!--additional products left out-->
   25: </TBODY></TABLE><A onclick="return showBasket ()" href="#">show basket</A>

ANALYSIS

Listing BP3.5 shows only a partial result. For clarity, many of the products have been left out. Lines 4–8 display the table header row; then each product covers five lines. The last line, such as line 13, is of most interest. This is an a element, which is a hyperlink. It contains an attribute named onclick. The value of that attribute calls a JavaScript function in the HTML document that must add the product for which the link was clicked. Figure BP3.1 shows what this product list looks like in a browser.

Image

In Listing BP3.4, the template on line 40 is responsible for displaying the products in the shopping basket. This template doesn’t differ much from the template that displays the products in the product data. Line 54 invokes a called template for each product, to make the template a little shorter and handier to work with. The called template starts on line 59. Line 71 inserts a text box with the number of items you want to have. If you want to change the number of items, you can change the number in that text box and update the basket by using the button inserted on line 59. Just like the links for the products, this button invokes a script function in the HTML. Line 83 creates a check box that you can check if you want to remove a product altogether. The resulting HTML is shown in Listing BP3.6

FIGURE BP3.1 Listing BP3.5 when viewed in a browser.

OUTPUT

Listing BP3.6 HTML Displaying the Shopping Basket

<FORM id=basketform>
<H1>Basket</H1>
<TABLE border=1>
<TBODY>
<TR>
<TH>Quantity</TH>
<TH>ID</TH>
<TH>Item</TH>
<TH>Price per item</TH>
<TH>Tax per item</TH>
<TH>Product total +tax</TH>
<TH>Delete</TH></TR>
<TR>
<TD><INPUT size=2 value=2 name=update5></TD>
<TD>5</TD>
<TD>Merlot</TD>
<TD>7.95</TD>
<TD>0.48</TD>
<TD>16.85</TD>
<TD><INPUT type=checkbox name=delete5></TD></TR>
<TR>
<TD><INPUT size=2 value=1 name=update6></TD>
<TD>6</TD>
<TD>Camembert</TD>
<TD>3.95</TD>
<TD>0.24</TD>
<TD>4.19</TD>
<TD><INPUT type=checkbox name=delete6></TD></TR></TBODY></TABLE><BR>
<INPUT onclick="return updateBasket ()" type=submit value="Update basket">
</FORM>
<A onclick="return showProducts ()" href=" #">show products</A>

ANALYSIS

The HTML in Listing BP3.6 shows a table embedded in an HTML form. The form consists of a button to update the basket, text boxes to change the quantity of each product, and a check box to delete a product. If you have multiple items in the basket, you can change the quantities you want and possibly check items to be deleted. These changes are committed and the basket recalculated when you click the button to update the basket. You can see what this form looks like in FIGURE BP3.2.

Figure BP3.2 Listing BP3.6 when viewed in a browser.

Image

Updating the Basket

NEW TERM

When updating the basket, you have no way of knowing how many parameters you will need. To get around this problem, you use an updategram to actually update the shopping basket. An updategram is an XML tree fragment that is passed as such to the stylesheet, so you can pass any number of values. A sample updategram is shown in Listing BP3.7.

Listing BP3.7 Updategram to Update the Shopping Basket

<updates>
  delete ID="1" />
  delete ID="4" />
  <update ID="3" quantity="10" />
  <update ID="7" quantity="2" />
</updates>

Analysis

The delete elements in Listing BP3.7 indicate which elements need to be deleted. Those that need to be updated are indicated by the update elements. For each product, both the ID and quantity are passed.

If you add only one product, an add element will suffice, as follows:

<add ID="5" quantity="1" />

The preceding code is still a child element of the updates element, so theoretically you can add, delete, and update with one updategram. This capability is handy if you choose to create a different implementation where you register the changes and, for instance, post an updategram back to the server. Based on the updategram and the current shopping basket, a new basket is created with the stylesheet in Listing BP3.8.

Listing BP3.8 Stylesheet Creating New Shopping Basket

1:    <xsl:stylesheet version=″“1.0”"
2:      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3:      xmlns:shop="http://www.aspnl.com/xmlns/shop"
4:      xmlns:prd="http://www.aspnl.com/xmlns/products">
6:
7:      <xsl:output method="xml" omit-xml-declaration="yes" />
8:
9:      <xsl:param name="products" select="empty" />
10:     <xsl:param name="updates" select="empty" />
11:
12:      <xsl:template match="/">
13:       <shop:basket>
14:         <xsl:call-template name="addproducts" />
15:         <xsl:call-template name="updateproducts" />
16:       </shop:basket>
17:      </xsl:template>
18:
19:       <xsl:template name="addproducts">
20:        <xsl:for-each select="$updates//add">
21:          <xsl:variable name="ID" select="@ID" />
22:         <xsl:variable name="quantity" select="@quantity" />
23:         <prd:product>
24:           <xsl:choose>
25:             <xsl:when test="/shop:basket/prd:product/@prd:ID = $ID">
26:               <xsl:copy-of select="/shop:basket/prd:product[./@prd:ID
27:                                    = $ID]/@prd:*" />
28:               <xsl:attribute name="shop:quantity">
29:               <xsl:value-of select="/shop:basket/prd:product[./@prd:ID
30:                                     = $ID]/@shop:quantity + $quantity" />
31:               </xsl:attribute>
32:             </xsl:when>
33:             <xsl:otherwise>
34:               <xsl:copy-of select="$products//prd:product[./@prd:ID
35:                                     = $ID]/@*" />
36:               <xsl:attribute name="shop:quantity">
37:             <xsl:value-of select="$quantity" />
38:               </xsl:attribute>
39:             </xsl:otherwise>
40:           </xsl:choose>
41:         </prd:product>
42:        </xsl:for-each>
43:      </xsl:template>
44:
45:      <xsl:template name="updateproducts">
46:        <xsl:for-each select="/shop:basket/prd:product">
47:          <xsl:choose>
48:          <xsl:when test="@prd:ID = $updates//add/@ID" />
49:          <xsl:when test="@prd:ID = $updates//delete/@ID" />
50:          <xsl:when test="@prd:ID = $updates//update/@ID">
51:            <prd:product>
52:              <xsl:copy-of select="@prd:*" />
53:                <xsl:attribute name="shop:quantity">
54:                  <xsl:value-of select="$updates//update[@ID
55:                                        = current ()/@prd:ID]/@quantity" />
56:                </xsl:attribute>
57:              </prd:product>
58:            </xsl:when>
59:            <xsl:otherwise>
60:              <xsl:copy-of select="." />
61:            </xsl:otherwise>
62:          </xsl:choose>
63:        </xsl:for-each>
64:      </xsl:template>
65:    </xsl:stylesheet>

Analysis

Listing BP3.8 processes the shopping basket document. In addition, the product data and updategram are passed to the parameters on lines 9 and 10. The product data could have been accessed with the document () function as well, but the choice in this project was to create one large file that the browser could download. In other scenarios, using the document () function is probably a better option.

Note

MSXML allows you to pass an XML DOM document as a parameter. You can’t pass the text of a tree fragment as a parameter because it will be output escaped first. Most processors don’t allow passing DOM documents, so both the updategram and product data need to be loaded using the document () function when using those processors.

The template on line 12 creates the root element for the new shopping basket and then calls the templates responsible for adding products to the shopping basket or updating the current products in the basket. The template on line 19 iterates through all the add elements in the updategram and adds the corresponding product to the basket. Line 25 checks whether the product is already in the basket, in which case the product is altered instead of added. If the product exists, line 26 copies all the product data using the expression

/shop:basket/prd:product[./@prd:ID = $ID]/@prd:*"

This expression selects the product data from the shopping basket for the product that corresponds to the correct ID value. All the attributes are copied, only from the name-space of the product. Because the quantity has to be changed, the shop:quantity attribute is not copied, but handled by lines 28–31 instead. If the product doesn’t exist in the basket yet, line 34 copies the product data from the products parameter, and lines 36–38 create the shop:quantity attribute

The template on line 45 deals with the update and delete elements in the updategram. It iterates through all the elements in the shopping basket and checks whether the current element has a corresponding delete or update element. Line 48 checks whether there is a corresponding add element, in which case nothing happens because the element is already processed. Line 49 checks whether there is a delete element, in which case nothing happens because the element shouldn’t be inserted into the new shopping basket. Line 50 checks whether there is an update element, in which case line 52 copies all the product data from the old shopping basket, with line 53 updating the quantity. If no add, update, or delete element appears in the updategram, line 60 just copies the entire item in the shopping basket to the new shopping basket.

Invoking the Processor

This project so far is all about XML and XSLT. You don’t want the user messing around with a command-line processor, though, so you need to automate invoking the processor as well. This means you need to invoke the processor programmatically, as shown in Listing BP3.9.

Listing Bp3.9 Script Code Invoking the Processer

1:  <script language="javascript">
2:  var xslUpdate = null;
3:
4:  function body_onload () {
5:    var xslDoc = new ActiveXObject ("MSXML2.FreeThreadedDOMDocument.3.0");
6:    xslUpdate = new ActiveXObject ("MSXML2.XSLTemplate.3.0");
7:    xslDoc.async = false;
8:    xslDoc.load (update.XMLDocument);
9:    xslUpdate.stylesheet = xslDoc;
10:
11:    showProducts ();
12: }
13:
14: function doUpdate (strUpdates) {
15:   var xmlUpdates = new ActiveXObject (“MSXML2.DOM Document.3.0”);
16:   xmlUpdates.loadXML (strUpdates);
17:   var xmlInput = new ActiveXObject (“MSXML2.DOM Document.3.0”);
18:   xmlInput.load (basket);
19:
20:   var xslProc = xslUpdate.createProcessor ();
21:   xslProc.addParameter ("products", products);
22:   xslProc.addParameter ("updates", xmlUpdates);
23:   xslProc.input = xmlInput;
24:   xslProc.output = basket;
25:   xslProc.transform ();
26: }
27:
28: function addProduct (ID, qty) {
29:   var strUpdates = "<updates><add ID='" + ID + "' quantity='"
30:   strUpdates = strUpdates + qty + "' /></updates>"
31:   doUpdate (strUpdates);
32:   showBasket ();
33:   return false;
34: }
35:
36: function updateBasket () {
37:   var strUpdates = "<updates>";
38:   for (var i = 0; i < document.forms[0].length - 1; i++) {
39:     if (document.forms[0].elements[i].type = 'text') {
40:       var ID = parseInt (document.forms[0].elements[i].name.substring (6));
41:       if (document.forms[0].elements[i+1].checked) {
42:         strUpdates = strUpdates + "<delete ID='" + ID + "'/>";
43:       } else {
44:         strUpdates = strUpdates + "<update ID='" + ID + "' ";
45:         strUpdates = strUpdates + "quantity='"
46:         strUpdates = strUpdates + 
 parseInt (document.forms[0].elements[i].value);
47:         strUpdates = strUpdates + "'/>"
48:       }
49:       i++;
50:     }
51:   }
52:   strUpdates = strUpdates + "</updates>";
53:   doUpdate (strUpdates);
54:   showBasket ();
55:   return false;
56: }
57:
58: function showBasket () {
59:   result.innerHTML = basket.transformNode (display.XMLDocument);
60:    return false;
61: }
62:
63: function showProducts () {
64:   result.innerHTML = products.transformNode (display.XMLDocument);
65:   return false;
66: }
67: </script>

Analysis

Listing BP3.9, which is all JavaScript, uses several objects that are provided by the MSXML parser/processor component. It is beyond the scope of this book to really dive into the code because doing so requires good knowledge of JavaScript, but in broad terms you need to know what’s going on. The functions showBasket and showProducts, on lines 58 and 63, speak for themselves. To show the resulting HTML, they set the HTML inside an HTML div element named result. On line 59, basket refers to the XML island that contains the basket XML. The processor is invoked for this XML island using the transformNode method. The argument of this function is the stylesheet that is responsible for display. It is passed from its own XML island named display. Line 64 does exactly the same for the products. The addProduct and updateBasket functions on lines 28 and 36 create an updategram in the strUpdates string. The updategram string is passed to the doUpdate function on line 14. This function creates an XML DOM document from that string, which is passed as a parameter to the stylesheet on line 22. Line 21 passes the product data to the stylesheet. Line 24 makes sure that the output is sent to the basket object. Because this object can’t be processed and serve as a result container at the same time, line 18 creates a temporary object that is a copy of the current basket and becomes the document to be processed. Line 25 finally invokes the processor to perform the transformation.

Note

Listing BP3.10 shows the entire project file that you can run from the browser. You can download this file from the publisher’s Web site.

Listing BP3.10 Complete HTML Document

<script language="javascript">
var xslUpdate = null;

function body_onload () {
    var xslDoc = new ActiveXObject ("MSXML2.FreeThreadedDOMDocument");
    xslUpdate = new ActiveXObject ("MSXML2.XSLTemplate");
    xslDoc.async = false;
    xslDoc.load (update.XMLDocument);
    xslUpdate.stylesheet = xslDoc;

    showProducts ();
}

function doUpdate (strUpdates) {
    var xmlUpdates = new ActiveXObject (“MSXML2.DOM Document.3.0”);
    xmlUpdates.loadXML (strUpdates);
    var xmlInput = new ActiveXObject (“MSXML2.DOM Document.3.0”);
    xmlInput.load (basket);

    var xslProc = xslUpdate.createProcessor ();
    xslProc.addParameter ("products", products);
    xslProc.addParameter ("updates", xmlUpdates);
    xslProc.input = xmlInput;
    xslProc.output = basket;
    xslProc.transform ();
}

function addProduct (ID, qty) {
    var strUpdates = "<updates><add ID='" + ID + "' quantity='"
    strUpdates = strUpdates + qty + "' /></updates>"
    doUpdate (strUpdates);
    showBasket ();
    return false;
}<

function updateBasket () {
    var strUpdates = "<updates>";
    for (var i = 0; i < document.forms[0].length - 1; i++) {
       if (document.forms[0].elements[i].type = 'text') {
      var ID = parseInt (document.forms[0].elements[i].name.substring (6));
      if (document.forms[0].elements[i+1].checked) {
       strUpdates = strUpdates + "<delete ID='" + ID + "'/>";
      } else {
       strUpdates = strUpdates + "<update ID='" + ID + "' ";
       strUpdates = strUpdates + "quantity='"
       strUpdates = strUpdates + parseInt  (document.forms[0].elements[i].value);
       strUpdates = strUpdates + "'/>"
      }
      i++;
       }
    }
    strUpdates = strUpdates + "</updates>";
    doUpdate (strUpdates);
    showBasket ();
    return false;
}

function showBasket () {
    result.innerHTML = basket.transformNode (display.XMLDocument);
    return false;
}

function showProducts () {
    result.innerHTML = products.transformNode (display.XMLDocument);
    return false;
}
</script>
<html>
<body onload="body_onload ()">

<div id="result"></div>

<xml id="products">
    <prd:products xmlns:prd="http://www.aspnl.com/xmlns/products">
       <prd:product prd:ID="1" prd:description="Bordeaux"
        prd:price="9.95" prd:type="wine" prd:color="red" />
       <prd:product prd:ID="2" prd:description="Ruby Cabernet"
        prd:price="10.95" prd:type="wine" prd:color="red" />
       <prd:product prd:ID="3" prd:description="Soave"
        prd:price="8.95" prd:type="wine" prd:color="white" />
       <prd:product prd:ID="4" prd:description="Chianti"
        prd:price="11.95" prd:type="wine" prd:color="red" />
       <prd:product prd:ID="5" prd:description="Merlot"

        prd:price="7.95" prd:type="wine" prd:color="red" />
       <prd:product prd:ID="6" prd:description="Camembert"
        prd:price="3.95" prd:type="cheese" />
       <prd:product prd:ID="7" prd:description="Gouda"
        prd:price="4.95" prd:type="cheese" />
       <prd:product prd:ID="8" prd:description="Brie"
        prd:price="3.95" prd:type="cheese" />
       <prd:product prd:ID="9" prd:description="Mozzarella"
        prd:price="2.95" prd:type="cheese" />
       <prd:product prd:ID="10" prd:description="Feta"
        prd:price="1.95" prd:type="cheese" />
    </prd:products>
</xml>

<xml id="basket">
    <shop:basket xmlns:shop="http://www.aspnl.com/xmlns/shop"
              xmlns:prd="http://www.aspnl.com/xmlns/products">
    </shop:basket>
</xml>

<xml id="display">
    <xsl:stylesheet version=″“1.0”"exclude-result-prefixes="shop prd"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:shop="http://www.aspnl.com/xmlns/shop"
        xmlns:prd="http://www.aspnl.com/xmlns/products">

       <xsl:output method="html" omit-xml-declaration="yes" />

       <xsl:template match="/">
      <xsl:apply-templates />
       </xsl:template>

       <xsl:template match="prd:products">
      <h1>Products</h1>
      <table border="1">
       <tr>
         <th>ID</th>
         <th>description</th>
         <th>price</th>
         <th></th>
       </tr>
       <xsl:for-each select="//prd:product">
         <tr>
           <td><xsl:value-of select="@prd:ID" /></td>
           <td><xsl:value-of select="@prd:description" /></td>
           <td>US $ <xsl:value-of select="@prd:price" /></td>
           <td><a><xsl:attribute name="href">#</xsl:attribute>
                 <xsl:attribute name="onclick">
                   <xsl:text>return addProduct (</xsl:text>

               <xsl:value-of select="@prd:ID" />
               <xsl:text>, 1)</xsl:text>
              </xsl:attribute>
              <xsl:text>add to cart</xsl:text><>
       </td>
      </tr>
       </xsl:for-each>
    </table>
    <a href="#" onclick="return showBasket ()">show basket</a>
</xsl:template>

<xsl:template match="shop:basket">
    <form id="basketform">
       <h1>Basket</h1>
       <table border="1">
      <tr>
       <th>Quantity</th>
       <th>ID</th>
       <th>Item</th>
       <th>Price per item</th>
       <th>Tax per item</th>
       <th>Product total +tax</th>
       <th>Delete</th>
      </tr>
      <xsl:for-each select="//prd:product">
       <xsl:call-template name="basketproduct" />
      </xsl:for-each>
       </table>
       <br />
       <input type="submit" value="Update basket"
          onclick="return updateBasket ()" />
    </form>
    <a href="#" onclick="return showProducts ()">show products</a>
</xsl:template>

<xsl:template name="basketproduct">
    <xsl:variable name="taxrate" select="0.06" />
    <xsl:variable name="tax" select="@prd:price * $taxrate" />
    <xsl:variable name="producttotal"
              select="@shop:quantity *  (@prd:price + $tax)" />
    <tr>
       <td>
      <input type="text" size="2" name="update{@prd:ID}"
            value="{@shop:quantity}" />
       </td>
       <td><xsl:value-of select="@prd:ID" /></td>
       <td><xsl:value-of select="@prd:description" /></td>
       <td><xsl:value-of select="@prd:price" /></td>
       <td><xsl:value-of

               select="format-number ($tax, '#,##0.00')" /></td>
       <td><xsl:value-of
               select="format-number ($producttotal, '#,##0.00')" />
       </td>
       <td>
         <input type="checkbox">
           <xsl:attribute name="name">
             <xsl:text>delete</xsl:text>
             <xsl:value-of select="@prd:ID" />
           </xsl:attribute>
         </input>
       </td>
      </tr>
       </xsl:template>
    </xsl:stylesheet>
</xml>

<xml id="update">
  <xsl:stylesheet version=″“1.0”"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:shop="http://www.aspnl.com/xmlns/shop"
    xmlns:prd="http://www.aspnl.com/xmlns/products">

    <xsl:output method="xml" omit-xml-declaration="yes" />

    <xsl:param name="products" select="empty" />
    <xsl:param name="updates" select="empty" />

    <xsl:template match="/">
      <shop:basket>
        <xsl:call-template name="addproducts" />
        <xsl:call-template name="updateproducts" />
      </shop:basket>
    </xsl:template>

    <xsl:template name="addproducts">
     <xsl:for-each select="$updates//add">
       <xsl:variable name="ID" select="@ID" />
       <xsl:variable name="quantity" select="@quantity" />
       <prd:product>
         <xsl:choose>
           <xsl:when test="/shop:basket/prd:product/@prd:ID = $ID">
             <xsl:copy-of select="/shop:basket/prd:product[./@prd:ID
                                  = $ID]/@prd:*" />
             <xsl:attribute name="shop:quantity">
               <xsl:value-of select="/shop:basket/prd:product[./@prd:ID
                                  = $ID]/@shop:quantity + $quantity" />
             </xsl:attribute>
           </xsl:when>
                 <xsl:otherwise>
                   <xsl:copy-of select="$products//prd:product[./@prd:ID
                                        = $ID]/@*" />
                   <xsl:attribute name="shop:quantity">
                     <xsl:value-of select="$quantity" />
                   </xsl:attribute>
                 </xsl:otherwise>
               </xsl:choose>
             </prd:product>
           </xsl:for-each>
         </xsl:template>

         <xsl:template name="updateproducts">
           <xsl:for-each select="/shop:basket/prd:product">
             <xsl:choose>
               <xsl:when test="@prd:ID = $updates//add/@ID" />
               <xsl:when test="@prd:ID = $updates//delete/@ID" />
               <xsl:when test="@prd:ID = $updates//update/@ID">
                 <prd:product>
                   <xsl:copy-of select="@prd:*" />
                   <xsl:attribute name="shop:quantity">
                     <xsl:value-of select="$updates//update[@ID
                                           = current ()/@prd:ID]/@quantity" />
                   </xsl:attribute>
                 </prd:product>
               </xsl:when>
               <xsl:otherwise>
                 <xsl:copy-of select="." />
               </xsl:otherwise>
             </xsl:choose>
           </xsl:for-each>
         </xsl:template>
       </xsl:stylesheet>
     </xml>

     </body>
     </html>

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

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