This past week I think I’ve had a couple breakthroughs in terms of understanding how xQuery works. The rubicon was when Christian Moser from the eXist forum explained iterative variables to me. I know, of course, what iterative variables are, but I didn’t know exactly how they worked in XQL–in my head, creating a variable $n and not tying it to a TEI element n is strange. But there’s no reason for that–what we call something in XQL has nothing but convention to do with what we call something in XML. So, there was that, and also the fact–which had not occurred to me–that you can define variables in for expressions as well as let expressions. Also, the iterative variable can be bound to the position in the iterative cycle. I don’t know if this makes any sense, but it helped me tremendously.
For instance, here’s how I made the TEI statement of responsibility <respStmt> display, accounting for more than one editor. Let’s say we have two people collaborating on a document:
<respStmt>
<resp>Transcription and correction</resp>
<name>Elizabeth Ricketts</name>
</respStmt>
<respStmt>
<resp>Correction, editorial commentary, and markup</resp>
<name>Tonya Howe</name>
</respStmt>
We want to render them on separate lines, connecting the <resp> and the <name> with ” by ” for readability. Here are two things I tried:
{
for $respStmt in $header
let $respCount := count($header//tei:respStmt)
return
if ($respCount > 1) then
<p>{string-join(($titleStmt/tei:respStmt/tei:resp, $titleStmt/tei:respStmt/tei:name), ‘ by ‘)}</p>
else
concat($titleStmt/tei:respStmt/tei:resp, ‘ by’, $titleStmt/tei:respStmt/tei:name)
}
{for $resp in $respsreturn<p>{string-join(($titleStmt/tei:respStmt/tei:resp, $titleStmt/tei:respStmt/tei:name), ‘ by ‘)}</p>}
These bits gave me the following results, respectively:
Transcription and correction by Correction, editorial commentary, and markup by Elizabeth Ricketts by Tonya Howe
andTranscription and correction by Correction, editorial commentary, and markup by Elizabeth Ricketts by Tonya HoweTranscription and correction by Correction, editorial commentary, and markup by Elizabeth Ricketts by Tonya Howe
Here’s what I ended up with, through the magic of iteration.
{
for $n in $resps
return
<p>{concat($n//$resps/tei:resp, ‘ by ‘, $n//$resps/tei:name)}</p>
}Transcription and correction by Elizabeth Ricketts
Correction, editorial commentary, and markup by Tonya Howe
It was the $n in front of the //$resps/tei:resp that made no sense to me–but you can think of it as the iterative variable and its position in the cycle of respStmts. Pretty cool, huh? I thought so.
And then, today, that breakthrough led me to another: I had been struggling to get the source information in <imprint> to display properly, especially since I want there to be multiple sources, including but not requiring a first edition, an online edition, and any other edition used for the project. The online imprint should display a live link. Here’s the catch–I’m using <date type=”firstEd”> or <date type=”accessed”> to identify dates associated with print versus online, and <extent type=”physical”> or <extent type=”onlineLink”> to identify the extent of the sources. This seems to be the most consistent in terms of TEI P5 guidelines. So, I wanted to test something (I went with date, but could have gone with extent) to see if it were a web source, and if so, display different information. Here’s what I came up with, in tei2html.xql:
<h3><b>Sources:</b></h3>
{
for $n in $imprints
return
<li>
{$n//$imprints/tei:pubPlace}: {$n//$imprints/tei:publisher}. {$n//$imprints/tei:date}.
{
if ($n//$imprints/tei:date/@type = “accessed”) then
<a href=”{$n//$imprints/tei:extent}”>{$n//$imprints/tei:extent}</a>
else
$n//$imprints/tei:extent
}
{$n//$imprints/tei:note}
</li>
}
And it did exactly what I wanted it to do!
Then, from this whole collection of moments, I could fix my image display problem. I had already gotten the <pb facs=”image.png”> elements to display inline with the text using a typeswitch in the main XQL display functions page that called a little function in the same page each time it met with a <pb> element. Like this, in the tei2html.xql:
declare function tei2:tei2html($nodes as node()*) {
for $node in $nodes
return
typeswitch ($node)case element(tei:pb) return
tei2:pageImages($node)
};declare function tei2:pageImages($pb as element (tei:pb)) {
let $facsPage := $pb/@facs
for $pb in “work”
return
<img src=”../images/{$facsPage}”/>
};
But this seemed clunky to me in the page display as a whole–in addition to being an inelegant solution generally. I wanted to put the page images in a sidebar, but translating that interdependent function to a standalone function and then calling it with <div data-template=”tei2:pageImages”/> kept returning errors. tei2:pageImages can’t be called as is with <div data-template=”tei2:pageImages”/> in the sidebar location, for reasons having to do, I think, with what information is being called in the node and the work as a whole. There are probably other reasons, too; suffice to say, it didn’t work. Using what I learned here and above, though, I came up with this, and put it not in tei2html.xql, but in app.xql:
declare function app:pageImages($node as node(), $model as map(*)) {
for $pb in $model(“work”)//tei:pb
let $facsPage := $pb/@facs
return
<p><a href=”../images/{$facsPage}”><img src=”../images/{$facsPage}” width=”100%”/></a></p>
};
Success!

As that worked like a such a charm, I wanted to try to move the source information elsewhere–possibly the sidebar, maybe a footer, who knows. For the moment, I went with sidebar, since I was on a roll. Here’s how I adapted it from above, putting it instead into app.xql, which is then called through templating:
declare function app:sources($node as node(), $model as map(*)) {
for $n in $model(“work”)//tei:imprint
return
<li>
{$n//tei:pubPlace}: {$n//tei:publisher}. {$n//tei:date}.
{
if ($n//tei:date/@type = “accessed”) then
<a href=”{$n//tei:extent}”>{$n//tei:extent}</a>
else
$n//tei:extent
}
{$n//tei:note}
</li>
};
As you can see, I was also able to streamline some of the code, making it more compact. I’m doing my happy dance right now, though you can’t see it.