XQuery/Incremental Search of the Chemical Elements
From Wikibooks, the open-content textbooks collection
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 the eXist free text index and matching with a limited regular expression.
The raw data is taken from a file [1]provided by Elliotte Rusty Harold.
Contents |
[edit] The main page
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.
<html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>Chemical Element Data</title> <script language="javascript" src="ajaxelement.js"/> </head> <body> <h1>Chemical Element Data</h1> <p> Data provided by <a href="http://www.cafeconleche.org/examples/periodic_table/allelements.xml"> Elliotte Rusty Harold </a> </p> <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> <div id="list"/> </body> </html>
[edit] The JavaScript
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 this value into the div.
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] The server-side search
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 generic function turns the elements of an XML node into rows in a table, using the element name as the legend. In the XML file, element names are uppercase, so a function is used to turn these into more readable mixed case roughly title-cased labels.
declare function local:string-to-title ( $string as xs:string?) as xs:string {
string-join(
for $w in tokenize(lower-case($string),"_")
return
if ($w = ("of","a","in","on","the"))
then $w
else
concat ( upper-case(substring($w,1,1)),
substring($w,2)
)
," ")
} ;
declare function local:element-to-table($element) as element(table) {
<table border="1">
{for $node in $element/*
let $label := local:string-to-title(name($node))
return
<tr>
<td>{$label}</td>
<td>
{ $node/text() }
</td>
</tr>
}
</table>
};
let $name := request:get-parameter("name",())
return
if ($name != "")
then
(: convert the partial string to a regexp so that it can be used in the eXist free text match &= :)
let $search := concat('^',$name,'*')
let $matches := /PERIODIC_TABLE/ATOM[NAME &= $search]
return
if (count($matches) = 0)
then
<span>No matches</span>
else
if (count($matches) =1)
then
local:element-to-table($matches)
else
(: multiple matches :)
<div>
{for $match in $matches
order by $match/NAME
return
<div>
{concat($match/NAME, " [", $match/SYMBOL, "], atomic weight ",$match/ATOMIC_WEIGHT)}
</div>
}
</div>
else ()
[edit] Discussion
Some naming problems here - needs tidying. Units need to be included