XQuery/Incremental Search of the Chemical Elements

Introduction

edit

In this example of AJAX in its AHAH form, an incremental search of the chemical elements is implemented. This is also an example of the use of matching with a regular expression.

The raw data is taken from a file [1]provided by Elliotte Rusty Harold.

Execution

edit

Search the Elements

The main page

edit

The main page is a simple HTML file. The div element with id list is where the generated contents will be placed. The JavaScript function getList() is called when any of several interface events occur.

declare option exist:serialize "method=xhtml media-type=text/html
        doctype-public=-//W3C//DTD HTML 4.01 Transitional//EN
        doctype-system=http://www.w3.org/TR/loose.dtd";

<html xmlns="http://www.w3.org/1999/xhtml" >
   <head>
     <title>Chemical Elements</title>
      <script language="javascript" src="ajaxelement.js"/>
      <style type="text/css">
       td {{background-color: #efe; font-size:14px;}}
       th {{background-color: #ded; text-align: right; font-variant:small-caps;padding:3px; font-size:12px;}}
       </style> 
   </head>
    <body>
    <h1>Chemical Elements</h1>
    <table class="page">
    <tr>
    <td valign="top" width="30%"><form onSubmit="getList(); return false">
      <span><label for="name">Element Name </label> 
                     <input type="text" size="5" name="name" id="name" title="e.g. Silver"
                                   onkeyup="getList();" onfocus="getList();" />
       </span>
    </form>
    </td>
    <td id="list"/>
    </tr>
    </table>
    </body>
</html>

The JavaScript

edit

The JavaScript implements the simple functionality of calling the server-side script getElement.xq with the string entered in the search box, and in the callback, pasting the returned XHTML into the div. [ This JavaScript uses no supporting libraries although it would be simplified by using e.g. jQuery ]

function updateList() {
  if (http.readyState == 4) {
      var divlist = document.getElementById('list');
      divlist.innerHTML = http.responseText;
      isWorking = false;
  }
}

function getList() {
  if (!isWorking && http) {
    var name = document.getElementById("name").value;
    http.open("GET", "getElement.xq?name=" + name);
    http.onreadystatechange = updateList;  
          // this sets the call-back function to be invoked when a response from the HTTP request is returned
    isWorking = true;
    http.send(null);
  }
}

function getHTTPObject() {
  var xmlhttp;
  /*@cc_on
  @if (@_jscript_version >= 5)
    try {
      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
      try {
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      } catch (E) {
        xmlhttp = false;
      }
    }
  @else
  xmlhttp = false;
  @end @*/
  if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    try {
      xmlhttp = new XMLHttpRequest();
      xmlhttp.overrideMimeType("text/xml"); 
    } catch (e) {
      xmlhttp = false;
    }
  }
  return xmlhttp;
}

var http = getHTTPObject(); //  create the HTTP Object
var isWorking = false;
edit

This script is called by the getlist() function when a partial atom name has been entered. The string is converted to a simple regular expression and used in the eXist free text matching function to retrieve matching atoms. The response depends on the number of matches found:

  • if only one match, return a table of details
  • if more than one match, return with a list of matches
  • if none return "no matches"

A function turns the ATOM node into a table,mapping each child node to a row with the node name as the legend.

declare function local:atom-to-table($element ) {
   <table class="element">
      {for $node in $element/*
      let $label := replace(name($node),"_"," ") 
      return 
            <tr>
                 <th>{$label}</th> 
                 <td>
                   { $node/text()   }
                   </td>
             </tr>       
        }
    </table>
 };
  
let $name := request:get-parameter("name",())
return
  if ($name != "")
  then
      let $search := concat('^',$name)  (: anchor the term to the start of the string :)
      let $elements := doc("periodicTable.xml")/PERIODIC_TABLE
      let $matches := $elements/ATOM[matches(NAME,$search,"i")]
      return 
         if (count($matches) = 0)
         then 
            <span>No matches</span>
         else 
         if (count($matches) =1)
          then 
             local:atom-to-table($matches)
         else 
         (: multiple matches :)
             <table class="list">
               <tr>
                <th>Name</th>
                <th>Symbol</th>
                <th>Atomic Weight</th>
               </tr>
               {for $match in $matches
                order by $match/NAME
                return 
                 <tr>
                    <th>{string($match/NAME)}</th>
                    <td>{string($match/SYMBOL)}</td>
                    <td>{string($match/ATOMIC_WEIGHT)} </td>
                 </tr>
              }
            </table>
  else ()

To do

edit

Some naming problems here - needs tidying. Units need to be included