Search

The sailing story site : XQuery, JavaScript/ jQuery code and lessons learnt

I've learnt a lot, some of it rather painfully slowly,over the past few weeks working on the Sailing Association application with  its augmented trip reports. Here are a few notes of bits on XQuery,Javascript and the interface between them. 

The XQuery code is all in a module and can be browser using this graphical library module browser which requires SVG .   The call graph is generated using GraphViz  The main script merely does a call to the root function.  Here also is the currrent Javascript.

XQuery

index-of()

To render a trip report as HTML,  I wrote a recursive function using the typeswitch construct. In the trip report, the elements <wp> ( a wayppint or location/date/time marker) can occur anywhere in the body of the report. They need to be numbered seqentially to link to numbered markers on the map.  If $item is an <wp> element, I can find the Trip of which it is part and then the sequence of the wp markers with :



$item/ancestor::Trip//wp


Now I need to find the index of the current <wp> element in this sequence.  The function to find the index of a node in a sequence of nodes using identity seems to be a missing XPath function although its easy enough to write, though there are probably smarter ways to do this:



declare function wfn:index-of($nodes,$target) as xs:integer {    
  for $node at $i in $nodes
  return if ($node is $target) then $i else ()
};


and hence the index of $item in the sequence is



wfn:index-of($item/ancestor::Trip//wp, $item)


node-join()

string-join(strings, sep)  joins strings with a separator but I often want to join nodes, such as to join <span>'s with a separator character.  This function does this simple task:



declare function wfn:node-join($nodes,$sep) {
  if (count($nodes) > 1)
  then ($nodes[1],$sep, wfn:node-join(subsequence($nodes,2),$sep))
  else $nodes
};


JavaScript

 I'm currrently getting up to speed with jQuery.

Using Javascript with XQuery is rather painful. I initially generated Javascript functions with unrolled code using constructed variable names. When it was working, I factored the data from the code and wrote a set of general Javascript functions which are parameterised by global variables generated by XQuery. The difference in array bases (0 for Javascript, 1 for XQuery and humans, numbering marker icons for example) is a cause of off-by-one errors.  I find the XQuery approach more natural so to allow me to remain 1-based, I first hit on the idea of generated arrays with a dummy first element so the indexes could be the same. But this was too tricky so I had to accept that I would have to translate to and from the two index bases.

Generating HTML in JavaScript is especially painful.  I find it easer to generate an HTML div with display:none containing each item in an inner div with a numbered id. The HTML can then be accessed as for example $("#wp"+i) in JQuery. It also means the text is indexed by search engines.

GoogleMap API  V3

I hit two time-consuming snags which I could not have solved without Google search and forums.  I encountered the first when creating the content of infoWindows (popup boxes tied to a marker) in the unrolled code.  For example,



     var wp5 = new google.maps.Marker({
         position: latlong,
         map: map
        });


    google.maps.event.addListener(marker,'click',function() {
        var html = document.getElementById('wp5'');
        infoWindow.setContent(html);infoWindow.open(map,wp5)
     )


This worked but only for the first click on any marker. It took me a while to realise that the divs were being progressively deleted, when had I assumed they would be copied.  The fix was to use cloneNode(true) to force a copy.



google.maps.event.addListener(marker,'click',function() {
         var html = document.getElementById('wp5''). cloneNode(true);
         infoWindow.setContent(html);infoWindow.open(map,wp5)


     )


The second problem appeared when I factored the data from the code.  Simply iterating over the waypoints in a waypoints array with code like that above gave me only one infoWindow which popped up whichever marker was clicked.  After much time spent rather randomly mutating the code in the hope of  hitting on a version more fit for purpose, I found via the forum that this was a problem with closures and variable bindings, fixed by another level of indirection.

Initailly I had problems using JQuery with the Google Api but I finally figured out the key functions in this libary are those which transform between different node representations:

 

Annotation update

 

Shipping Forcasts

After struggling with this code, it was very pleasing to hear from a chap at the Met Office that shipping forecasts have been archived since 2003. Moreover he was willing to supply specific files to march the story dates. Of course it would be great if these were open data available via an API. Surely I'm not the only person interested in past forecasts. I wonder how we can encourage the Met Office to make them available.