Due to maintenance reasons, gitlab.gwdg.de will not be available on Saturday 2021-07-24 from 10:30 to approximately 11:30 CEST.

Commit 95925e70 authored by mrodzis's avatar mrodzis 🌎
Browse files

Merge branch 'feature/update-docs' into 'develop'

Feature/update docs

See merge request !67
parents 221c295a 68574855
......@@ -2,7 +2,7 @@ xquery version "3.1";
(:~
: This module is responsible for getting an XML version of the abbreviations
: index which can be converted to TeX. This XML version is appended to
: index which can be converted to TeX. The output XML version is appended to
: fontane-full.xml during the creation of the intermediate format in etTransfo.xqm.
:
: @author Michelle Weidling
......@@ -14,13 +14,12 @@ module namespace abbrev-index="http://fontane-nb.dariah.eu/abbrev-index";
import module namespace config="http://textgrid.de/ns/SADE/config" at "../../config/config.xqm";
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace tei="http://www.tei-c.org/ns/1.0";
declare namespace xhtml="http://www.w3.org/1999/xhtml";
declare function abbrev-index:main() as element(tei:div) {
let $docs-coll := collection("/db/sade-projects/textgrid/data/xml/doku/")
let $docs-coll := collection($config:data-root || "/doku/")
let $file := $docs-coll[matches(base-uri(.), "verzeichnis_der_abkuerzungen")]/*
let $transformed :=
abbrev-index:transform($file)
......
......@@ -7,6 +7,8 @@ xquery version "3.1";
: It creates all the single HTML files for each notebook as well as a complete
: intermediate format TEI/XML that is used for the print output.
:
: This module is used in production and is called by tgconnect.
:
: @author Michelle Weidling
: @version 1.0
:)
......@@ -24,22 +26,22 @@ import module namespace fsort="http://fontane-nb.dariah.eu/sort" at "sort.xqm";
import module namespace functx = "http://www.functx.com";
import module namespace prepCom="http://fontane-nb.dariah.eu/prepCom" at "prepcom.xqm";
import module namespace simple2xhtml="http://fontane-nb.dariah.eu/simple2xhtml" at "simple2xhtml.xqm";
import module namespace tidySimple ="http://fontane-nb.dariah.eu/tidysimple" at "tidysimple.xqm";
import module namespace tidy ="http://fontane-nb.dariah.eu/tidy" at "tidy.xqm";
declare variable $etTransfo:cases :=
(
"3qtcz.xml", (: case C :)
"3qtqv.xml", (: case A :)
"3qtqw.xml", (: case B :)
"3qtqx.xml", (: case D :)
"3qtqz.xml" (: case E :)
(: "3qtcz.xml", (: case C :):)
(: "3qtqv.xml", (: case A :):)
(: "3qtqw.xml", (: case B :):)
"3qtqx.xml" (: case D :)
(: "3qtqz.xml" (: case E :):)
);
declare variable $etTransfo:coll := "/db/apps/SADE/modules/fontane/edited-text/";
(:~
: The main function
: The main function. It is called by tgconnect.
:
: @author Michelle Weidling
: @return An XHTML page for each notebook in /db/sade-projects/textgrid/data/print/xhtml
......@@ -55,21 +57,24 @@ declare function etTransfo:complete() {
try {
etTransfo:create-print-tei()
} catch * {
error(QName("error", "ETTRANSFO02"), "An error occured while creating the whole TEI base for print.")
error(QName("error", "ETTRANSFO02"), "An error occurred while creating the whole TEI base for print.")
},
etTransfo:tidy-logs(),
try {
etTransfo:report-errors()
} catch * {
error(QName("error", "ETTRANSFO15"), "An error occured while reporting errors.")
error(QName("error", "ETTRANSFO15"), "An error occurred while reporting errors.")
}
};
(:~
: A function to trigger the creation of the edited text for a single notebook.
: Mainly used for parallel creation of the notebooks.
: Mainly used for parallel creation of the notebooks but can also be used for
: debugging.
:
: @param $uri The notebook's URI, e.g. "16b00" :)
: @param $uri The notebook's URI, e.g. "16b00"
:)
declare function etTransfo:transform-single-nb($uri as xs:string) as xs:string* {
let $assure-dir-available := etTransfo:assure-coll-available("print")
let $assure-dir-available := etTransfo:assure-coll-available("log")
......@@ -86,17 +91,23 @@ declare function etTransfo:transform-single-nb($uri as xs:string) as xs:string*
}
return
(try {
etTransfo:transform-tei($updated-notebook, $log)
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO08: Couldn't transform TEI. ")
},
etTransfo:tidy-logs())
(
try {
etTransfo:transform-tei($updated-notebook, $log)
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO08: Couldn't transform TEI.")
},
etTransfo:tidy-logs()
)
};
(:~
: Resolves all XIncludes in a given case file and copies all notebooks belonging to a case into one file.
: Resolves all XIncludes in a given case file and copies all notebooks belonging
: to a case into one XML file.
:
: This is mainly used to be able to serialize one case at a time which is
: a project requirement.
:
: @author Michelle Weidling
: @param $showcase The filename of a showcase, e.g. "12345.xml"
......@@ -124,7 +135,7 @@ declare function etTransfo:create-case($showcase as xs:string) as xs:string {
let $uri := $xi/@href/string()
let $tei-referred-to :=
try {
doc("/db/sade-projects/textgrid/data/xml/data/" || $uri || ".xml")/tei:TEI
doc($config:data-root || "/data/" || $uri || ".xml")/tei:TEI
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO04: Couldn't find the node tei:TEI of " || $uri || ".")
}
......@@ -133,7 +144,7 @@ declare function etTransfo:create-case($showcase as xs:string) as xs:string {
attribute id {$uri},
$tei-referred-to/@*,
$tei-referred-to/*
}
}
return update replace $xi with $updated-notebook
return
......@@ -142,14 +153,14 @@ declare function etTransfo:create-case($showcase as xs:string) as xs:string {
(:~
: Gets all notebooks of a showcase in-memory.
: Returns all notebooks of a showcase
:
: @author Michelle Weidling
: @param $showcase The filename of a showcase, e.g. "12345.xml"
: @return All notebooks of a showcase in-memory, i.e. a sequence of tei:TEI
:)
declare function etTransfo:get-teis($showcase as xs:string) as element(tei:TEI)* {
let $doc := doc("/db/sade-projects/textgrid/data/xml/data/" || $showcase)
let $doc := doc($config:data-root || "/data/" || $showcase)
let $summary-file-name := $doc//tei:title[1] => replace(" ", "-") || ".xml"
let $log := doc($etTransfo:coll || "logs/" || $doc//tei:title[1] => replace(" ", "-") || "-log.xml")
return
......@@ -187,7 +198,11 @@ declare function etTransfo:create-htmls($showcase as xs:string) as xs:string* {
: Handles the transformation to the intermediate format and the XHTML
: representation.
:
: The whole transformation is a five step process in order to keep different
: steps apart semantically and logically.
:
: @param $tei The current notebook as element(tei:TEI)
: @param $log The absolute path of the log file belonging to the current notebook
:)
declare function etTransfo:transform-tei($tei as element(tei:TEI), $log as xs:string) {
let $sorted :=
......@@ -216,7 +231,7 @@ declare function etTransfo:transform-tei($tei as element(tei:TEI), $log as xs:st
let $tidy-interform :=
try {
tidySimple:main($transform-to-interform, $tei/@id)
tidy:main($transform-to-interform, $tei/@id)
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO12: Error while tidying up the intermediate format for this notebook. Reason: 
" ||
concat("[", $err:line-number, ": ", $err:column-number, "] Error ", $err:code, ": ", $err:description))
......@@ -233,11 +248,14 @@ declare function etTransfo:transform-tei($tei as element(tei:TEI), $log as xs:st
(:~
: Assembles the intermediate format version of each notebook and copies them into one file called "fontane-full.xml".
: Assembles the intermediate format version of each notebook and copies it into one file.
: Furthermore, additional data needed for the print is appended.
:
: The resulting file is mainly needed for serializing the print.
: Attention: In case you change the file name, update the PERL script in the print repo as well.
:
: @author Michelle Weidling
: @return A string indicating the location where the full TEI/XML of the edition was stored to.
: @return A string indicating the location where the full TEI/XML of the edition was stored to, i.e. "/db/sade-projects/textgrid/data/xml/print/xml/fontane-full.xml"
:)
declare function etTransfo:create-print-tei() as xs:string {
let $xmls := etTransfo:get-all-xmls()
......@@ -248,30 +266,31 @@ declare function etTransfo:create-print-tei() as xs:string {
try {
etTransfo:get-literature()
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO06: An error occured while creating the bibliography.")
etTransfo:add-log-entry($log, "ETTRANSFO06: An error occurred while creating the bibliography.")
},
try {
abbrev-index:main()
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO07: An error occured while creating the index of abbreviations.")
etTransfo:add-log-entry($log, "ETTRANSFO07: An error occurred while creating the index of abbreviations.")
},
try {
etTransfo:create-overall-toc()
} catch * {
etTransfo:add-log-entry($log, "ETTRANSFO10: An error occured while creating the overall TOC..")
etTransfo:add-log-entry($log, "ETTRANSFO10: An error occurred while creating the overall TOC..")
}
}
return
xmldb:store($config:data-root || "/print/xml/", "fontane-full.xml", $complete-print-file)
};
(:~
: An auxiliary function. Returns all final intermediate format TEI/XML files.
:
: @author Michelle Weidling
: @return All available final intermediate format TEI/XML files as opened documents
: @return All available final intermediate format TEI/XML files as document nodes
:)
declare function etTransfo:get-all-xmls() as node()+ {
for $res in collection($config:data-root || "/print/xml/")
......@@ -288,7 +307,7 @@ declare function etTransfo:get-all-xmls() as node()+ {
: bibliography.
:
: @author Michelle Weidling
: @return a tei:div containing all bibliographic sections of the print
: @return A tei:div containing all bibliographic sections of the print
:)
declare function etTransfo:get-literature() as element(tei:div) {
element {QName("http://www.tei-c.org/ns/1.0", "text")} {
......@@ -311,14 +330,14 @@ declare function etTransfo:get-literature() as element(tei:div) {
(:~
: An auxiliary function to create the tei:div elements in the bibliography.
: An auxiliary function to create the tei:div elements/sections in the bibliography.
:
: @author Michelle Weidling
: @param $identifier An identifier for a subtype of scholarly literature
: @return a tei:div containing all bibliographic information of the literatur's subtype
: @param $identifier An identifier for a subtype of scholarly literature, e.g. "Briefausgaben"
: @return A tei:div containing all bibliographic information of the literature's subtype
:)
declare function local:make-bib-div($identifier as xs:string) as element(tei:div) {
element tei:div {
element {QName("http://www.tei-c.org/ns/1.0", "div")} {
attribute n {$identifier},
etTransfo:make-bib-entries($identifier)
}
......@@ -327,17 +346,18 @@ declare function local:make-bib-div($identifier as xs:string) as element(tei:div
(:~
: Creates a div containing all the scholarly literature ("Forschungsliteratur")
: Creates a tei:div containing all the scholarly literature ("Forschungsliteratur")
: of a kind that's listed in the respective index. Examples for this are
: "Allgemeine Nachschlagewerke" and "Werkausgaben"
:
: @author Michelle Weidling
: @param $type The type of a tei:listBibl, e.g. "Forschungsliteratur"
: @return a tei:div containing the scribal abbreviation as well as the full
: @return A tei:div containing the scribal abbreviation as well as the full
: bibliographic reference
:)
declare function etTransfo:make-bib-entries($type as xs:string) as element(tei:div)* {
for $entry in doc("/db/sade-projects/textgrid/data/xml/data/25547.xml")//tei:listBibl[@type = $type]//tei:bibl
(: 25547.xml is the file that contains all bibliographical information :)
for $entry in doc($config:data-root || "/data/25547.xml")//tei:listBibl[@type = $type]//tei:bibl
let $abbr :=
if($entry/tei:abbr) then
$entry/tei:abbr
......@@ -347,14 +367,14 @@ declare function etTransfo:make-bib-entries($type as xs:string) as element(tei:d
let $bib-no := count($entry/ancestor::tei:bibl)
return
element tei:div {
element {QName("http://www.tei-c.org/ns/1.0", "div")} {
attribute type {"entry"},
element tei:seg {
element {QName("http://www.tei-c.org/ns/1.0", "seg")} {
attribute type {"abbr"},
$abbr
},
if($full-ref) then
element tei:seg {
element {QName("http://www.tei-c.org/ns/1.0", "seg")} {
attribute type {"full"},
$full-ref
}
......@@ -366,19 +386,21 @@ declare function etTransfo:make-bib-entries($type as xs:string) as element(tei:d
let $same-as-loc := $referenced-entry/ancestor::tei:listBibl[1]/@type/string()
return
(element tei:seg {
attribute type {"same-as"},
$same-as
},
element tei:seg {
attribute type {"same-as-loc"},
$same-as-loc
})
(
element {QName("http://www.tei-c.org/ns/1.0", "seg")} {
attribute type {"same-as"},
$same-as
},
element {QName("http://www.tei-c.org/ns/1.0", "seg")} {
attribute type {"same-as-loc"},
$same-as-loc
}
)
else
(),
if($bib-no gt 0) then
element tei:seg {
element {QName("http://www.tei-c.org/ns/1.0", "seg")} {
attribute type {"sub-entry"},
$bib-no
}
......@@ -406,7 +428,7 @@ declare function etTransfo:create-log($uri as xs:string) as xs:string {
: Makes sure a requested collection is available.
:
: @author Michelle Weidling
: @param $flag a string indicating the target collection
: @param $flag A string indicating the target collection
: @return The location of the log collection, "/db/apps/sade/modules/fontane/edited-text/logs/"
:)
declare function etTransfo:assure-coll-available($flag as xs:string) {
......@@ -432,19 +454,21 @@ declare function etTransfo:assure-coll-available($flag as xs:string) {
: Adds a log entry to a given log file.
:
: @author Michelle Weidling
: @param $log-file The path to the log file, e.g. "/db/apps/sade/modules/fontane/edited-text/logs/12345-log.xml"
: @param $log-file The path to the log file, e.g. "/db/apps/sade/modules/fontane/edited-text/logs/12345-log.xml"
: @param $message The message of the log entry
: @return The location of the log collection, "/db/apps/sade/modules/fontane/edited-text/logs/"
:)
declare function etTransfo:add-log-entry($log-file as xs:string,
$message as xs:string) as empty-sequence() {
let $entry := <LogEntry timestamp="{util:system-time()}">{$message}</LogEntry>
return update insert $entry into doc($log-file)/*
return
update insert $entry into doc($log-file)/*
};
(:~
: Removes all log files that don't contain any information.
:
: @author Michelle Weidling
:)
declare function etTransfo:tidy-logs() as item()* {
......@@ -458,7 +482,8 @@ declare function etTransfo:tidy-logs() as item()* {
};
(:~
: Prints all errors found to stdout.
: Prints all errors found during the transformation to stdout.
:
: @author Michelle Weidling
:)
declare function etTransfo:report-errors() as item()* {
......@@ -482,43 +507,45 @@ declare function etTransfo:report-errors() as item()* {
(:~
: creates the overall TOC by collection information from all Überblickskommentare.
: this info is needed for a separate kind of index in the book.
: Creates the overall TOC by collecting information from all "Überblickskommentare".
: This info is needed for a separate kind of index in the book.
:
: TODO: fine tuning
: This feature is still in development and has to be extended to work with all
: notebooks, not just E1.
:
: @author Michelle Weidling:)
: @author Michelle Weidling
:)
declare function etTransfo:create-overall-toc() as element(tei:div) {
let $nbs := collection($config:data-root || "/data")
let $items := collection($config:data-root || "/data")//tei:TEI[descendant::tei:title = "Notizbuch E1"]/tei:teiHeader//tei:msContents/tei:ab/tei:list[@type="editorial"]/tei:item
let $items := $nbs//tei:TEI[descendant::tei:title = "Notizbuch E1"]/tei:teiHeader//tei:msContents/tei:ab/tei:list[@type="editorial"]/tei:item
let $sortPattern := "^\W+"
return element tei:div {
return element {QName("http://www.tei-c.org/ns/1.0", "div")} {
attribute type {"overall-toc"},
let $segs :=
for $doc in $nbs
return
let $nb-name := $doc//tei:fileDesc/tei:titleStmt/tei:title[1]/substring-after(., " ")
return
for $item in $doc//tei:msContents/tei:ab/tei:list[@type="editorial"]//tei:item return
let $first-ref := $item//tei:ref[@type = "first"]/@target/string()
let $last-ref := $item//tei:ref[@type = "last"]/@target/string()
let $refs :=
for $ref in ($first-ref, $last-ref) return
let $tokens := tokenize($ref, " ")
return $tokens[2]
return
element tei:seg {
attribute type {$nb-name},
attribute source {
let $string := for $ref in $refs return
substring-after($ref, "@xml:id='")
=> substring-before("']")
return
string-join($string, " ")
},
normalize-space(functx:substring-before-last($item, ")") || ")")
}
for $doc in $nbs
return
let $nb-name := $doc//tei:fileDesc/tei:titleStmt/tei:title[1]/substring-after(., " ")
return
for $item in $doc//tei:msContents/tei:ab/tei:list[@type="editorial"]//tei:item return
let $first-ref := $item//tei:ref[@type = "first"]/@target/string()
let $last-ref := $item//tei:ref[@type = "last"]/@target/string()
let $refs :=
for $ref in ($first-ref, $last-ref) return
let $tokens := tokenize($ref, " ")
return $tokens[2]
return
element {QName("http://www.tei-c.org/ns/1.0", "seg")} {
attribute type {$nb-name},
attribute source {
let $string := for $ref in $refs return
substring-after($ref, "@xml:id='")
=> substring-before("']")
return
string-join($string, " ")
},
normalize-space(functx:substring-before-last($item, ")") || ")")
}
return for $seg in $segs
order by replace($seg, $sortPattern, "") => substring(1, 1) => upper-case()
return $seg
......
......@@ -2,7 +2,7 @@ xquery version "3.1";
(:~
: This module is responsible for retrieving all relevant information from the
: different indicies in order to create a tei:index in the simpler TEI.
: different indices in order to create a tei:index in the simpler TEI.
:
: @author Michelle Weidling
: @version 0.1
......@@ -164,7 +164,7 @@ declare variable $index-info:lit-map :=
: @param $index-type the index type ("psn", "eve", ...)
: @param $term the current term, e.g. plc:Lueneburg
: @param $info the specific information to be retrieved, e.g. "regular-name"
: @return the information asked
: @return The information asked
:)
declare function index-info:get-info-about($index-type as xs:string,
$term as xs:string, $info as xs:string) as xs:string* {
......
xquery version "3.1";
(:~
:
: This modules serves for setting a tei:ptr right before each element that
: is referenced by an editorial commentary (tei:note[@type = 'editorial']).
:
......
xquery version "3.1";
(:~
: This modules runs the first and second process that transforms the Fontane
: TEI subset into a simpler TEI encoding.
: This modules runs the whole process that transforms the Fontane
: TEI subset into a "simpler" TEI encoding.
:
: This is used for development and debugging purposes only.
:
: @author Michelle Weidling
: @version 1.0
......
xquery version "3.1";
(:~
: This modules handles the conversion of the Fontante-TEI/XML into a TEI subset
: for the edited text. The resulting TEI is the basis for the "Edierter
: Text" (edited text) view on the website and the book. It represents the latest
: layer of text.
:
: Its main purpose is to tidy up the intermediate TEI that has been created by
: tei2teisimple.
:
: @author Michelle Weidling
: @version 0.1
: @since 0.0.0
:)
module namespace tidy ="http://fontane-nb.dariah.eu/tidy";
declare namespace tei="http://www.tei-c.org/ns/1.0";
declare namespace test="http://exist-db.org/xquery/xqsuite";
import module namespace config="http://textgrid.de/ns/SADE/config" at "../../config/config.xqm";
import module namespace functx="http://www.functx.com";
import module namespace index-info="http://fontane-nb.dariah.eu/index-info" at "index-info.xqm";
import module namespace simpleHelpers="http://fontane-nb.dariah.eu/teisimplehelpers" at "teisimplehelpers.xqm";
(: only contemporary hands (and selected posthumous hands) are considered for
: the edited text :)
declare variable $tidy:valid-hands :=
for $res in collection($config:data-root || "/data")
return
$res//tei:handNote[@script = "contemporary"]/@xml:id/string();
declare function tidy:main($tei as node()*, $uri as xs:string) {
let $tidy := tidy:enhance-handshifts($tei)
=> tidy:sort-out-surplus-elements()
=> tidy:sort-out-invalid-hands()
=> tidy:split-headings()
=> tidy:summarize()
=> tidy:summarize-headings()
=> tidy:summarize-notes()
=> tidy:summarize-hi()
=> tidy:sort-double-imgs()
=> tidy:tidy()
let $header :=
tidy:get-Fontanes-sources($tei//tei:teiHeader[parent::tei:TEI])
=> tidy:get-references-in-abstract()
(: tei:TEI/@id is always something like 'Notizbuch A1'.
for sorting we use the shelf number :)
let $id-parts := tokenize($tei//tei:TEI/@id, " ")
let $key1 := substring($id-parts[2], 1, 1)
let $key2 := substring($id-parts[2], 2)
let $final-tei := <TEI xmlns="http://www.tei-c.org/ns/1.0" id="{$tei//tei:TEI/@id}" key1="{$key1}" key2="{$key2}">{$header}{$tidy//tei:text}</TEI>
let $store := xmldb:store($config:data-root || "/print/xml/", $uri || ".xml", $final-tei)
return
$final-tei
};
(:~
: Returns the text that has been written by contemporary (or certain posthumous)
: hands. Up until this point, all encoded hands and their texts are still in
: place.
:)
declare function tidy:sort-out-invalid-hands($nodes as node()*)
as node()* {
for $node in $nodes return
let $prev-handshift := $node/preceding::tei:milestone[@unit = "handshift"][1]
let $is-hand-not-valid := not(simpleHelpers:is-hand-valid($tidy:valid-hands, $prev-handshift))
return
typeswitch ($node)
case text() return
if($prev-handshift
and $is-hand-not-valid) then
()
else
$node
(: All lines have to be preserved because of the editorial commentary
which references the lines in the notebooks. If we omitted @unit = "line",
referencing wouldn't work any longer :)
case element(tei:milestone) return
if($node/@unit = "handshift" and
simpleHelpers:is-hand-valid($tidy:valid-hands, $node)) then
tidy:construct-element($node, "post")
else if($node/@unit = "handshift") then
()
else if($prev-handshift
and $node/@unit = "line"
and $is-hand-not-valid) then
tidy:construct-element($node, "post")
else if($prev-handshift
and $is-hand-not-valid) then
()
else
tidy:construct-element($node, "post")
case element(tei:div) return
(: even though it's posthumous we want to keep the text written on
calendar pages by Friedrich Fontane. Unfortunately, Friedrich's
handshift is oftentimes not the first hand appearing on the page
but we want to keep the page nevertheless. :)
if($node/@type = "Kalenderblatt"
or $node/@type = "clipping") then
tidy:construct-element($node, "post")
else
tidy:invalid-hands-default-return($node)
default return
tidy:invalid-hands-default-return($node)
};
declare function tidy:invalid-hands-default-return($node as node()*)
as node()* {
let $prev-handshift := $node/preceding::tei:milestone[@unit = "handshift"][1]
let $first-child-handshift := $node/child::tei:milestone[@unit = "handshift"][1]
let $first-child-element := $node/child::*[1]
let $first-child-node := $node/child::node()[1]
return
(: in some cases the valid handshift is the first child node
instead of a previous node. of course we want to keep the element
then :)
if($first-child-element = $first-child-handshift
(: ensure there's no text before the handshift :)
and (normalize-space($first-child-node) = ""
or $first-child-element = $first-child-node)
and simpleHelpers:is-hand-valid($tidy:valid-hands, $first-child-handshift)) then
tidy:construct-element($node, "post")
else if($prev-handshift
and not(simpleHelpers:is-hand-valid($tidy:valid-hands, $prev-handshift))) then
()
else
tidy:construct-element($node, "post")
};
(:~
: Some elements aren't considered in the edited text. These encompass:
:
: * subsequent handshifts of the same type
: * certain line markers
: * empty elements that have lost their text nodes during the sorting process
:
:)
declare function tidy:sort-out-surplus-elements($nodes as node()*)
as node()* {
for $node in $nodes return
typeswitch ($node)
case text() return
$node