[ignore] bibliographic demo: fixed function call

svn path=/trunk/eXist/; revision=12066

(:~
    jQuery module: a set of functions to extend HTML forms with AJAX
    functionality, based on the jQuery javascript library.

    Use: each function takes an XML configuration element as parameter.
    You can either call functions directly from another XQuery module
    or pass a template page to the jquery:process-templates function.
    jquery:process-templates will parse the template page and expand all
    jquery:* elements it knows.

:)
module namespace jquery="http://exist-db.org/xquery/jquery";

import module namespace util="http://exist-db.org/xquery/util";
import module namespace request="http://exist-db.org/xquery/request";
import module namespace session="http://exist-db.org/xquery/session";

declare function jquery:script($node as element()?) as xs:string? {
    string-join($node/node(), "")
};

(:~
    Outputs the HTML script and link tags which are required by jQuery.
    You need to call this function once in your HTML head.

<![CDATA[<jquery:header base="scripts/jquery" cssbase="scripts/jquery/css"/>]]>

:)
declare function jquery:header($config as element(jquery:header)) as element()* {
    let $base := $config/@base/string()
    let $cssbase := if ($config/@cssbase) then $config/@cssbase/string() else $base
    return (

            }

};

(:~
    Creates a repeatable set of form fields. Clicking the trigger will
    clone a given part of the form. The new element is inserted immediately
    after the cloned element.

    The element to be cloned should have a class 'repeat'. It can be an arbitrary
    HTML node. Form fields within the repeated element should have a name attribute
    ending with a number (usually 1), e.g. "input1", "phone1". After inserting a new
    clone, the script will change the names for all form elements in the inserted
    element. So "phone1" will become "phone2" and so on.

    The function expects a configuration element jquery:form-repeat as parameter.
    Two attributes should be specified:

form: a jQuery selector pointing to the HTML form element

trigger: a jQuery selector pointing to the HTML element on which
       the user should click to insert a new row into the form

    Example:

<jquery:form-repeat trigger="#add-field" form="#search-form"/>

    @param $config the jquery:form-repeat configuration element
:)
declare function jquery:form-repeat($config as element(jquery:form-repeat)) as element() {
    let $formSelector := $config/@form/string()
    let $trigger := $config/@trigger/string()
    let $delete := $config/@delete/string()
    let $onReady := $config/@on-ready/string()
    return

};

(:~
    Toggles an HTML block upon the click on a link or button.
    The contents of the HTML block are retrieved via AJAX from
    the URL given in attribute "href".

<![CDATA[
        <jquery:toggle href="filters.xql?type=date"
            trigger="#filters li:nth-child(3) .expand"/>
    ]]>

href: the URL to load the HTML content block from

trigger: jQuery selector pointing to the trigger element
            on which the user should click

callback: optional callback javascript function which will
            be called once the contents of the toggle block are loaded.
            The function receives the trigger element as first parameter,
            the id of the toggle block as second.

:)
declare function jquery:toggle($config as element(jquery:toggle)) as element()+ {
    let $id0 := $config/@id/string()
    let $id := if ($id0) then $id0 else util:uuid()
    let $trigger := $config/@trigger/string()
    let $href := $config/@href/string()
    let $callback := $config/@callback/string()
    let $beforeCallback := $config/@beforeCallback/string()
    return (
        if (empty($id0)) then

        else (),

    )
};

(:~
    <jquery:paginate id="results" attribute="results"
        href="retrieve" navcontainer="#results-head .navbar"/>
:)
declare function jquery:paginate($config as element(jquery:paginate)) as element()+ {
    let $hits := session:get-attribute($config/@attribute)
    let $id := if ($config/@id) then $config/@id else 'jquery-paginate'
    let $onReady := $config/@on-ready/string()
    return (

,

    )
};

(:~
    Helper function for jquery:input-field()
:)
declare function jquery:extra-params($id as xs:string, $auto as element(jquery:autocomplete)) {
    if ($auto/jquery:param) then
        string-join(
            for $param in $auto/jquery:param
            return
                concat($param/@name, ': function () { ',
                    " return ", $param/@function, '.call($("#', $id, '"));}'),
            ', '
        )
    else
        ()
};

(:~
    Create an HTML input element. Copies all attributes of the jquery:input
    element. If no value attribute is present, the function will look up the
    parameter in the HTTP request, and if the parameter is set, add it as value
    attribute.

<![CDATA[<jquery:input name="input1"/>]]>

:)
declare function jquery:input-field($node as element(jquery:input)) as element()+ {
    let $name := $node/@name/string()
    let $id := if ($node/@id) then $node/@id/string() else util:uuid()
    return (
        if ($node/jquery:autocomplete) then
            let $auto := $node/jquery:autocomplete
            let $width := if ($auto/@width) then $auto/@width/string() else 200
            let $multiple := if ($auto/@multiple) then $auto/@multiple/string() else "false"
            let $matchContains := if ($auto/@matchContains) then $auto/@matchContains/string() else "false"
            let $minLength := if ($auto/@minLength) then $auto/@minLength/string() else "3"
            return

        else
            (),
        <input>
        { $node/@* }
        { if (empty($node/@id)) then attribute id { $id } else () }
        {
            if (empty($node/@value)) then
                attribute value { request:get-parameter($name, "") }
            else
                ()
        }
        </input>
    )
};

(:~
    { jquery:extra-params($id, $auto) }
                        width: { $width },
                        minChars: 3
    Create an HTML select element. Copies all attributes of the jquery:select
    element. jquery:select should contain zero or more jquery:option elements
    which are transformed into HTML option elements. Each option must have a
    value attribute and may have an optional text content. Both are copied to
    the generated HTML option.

    For each option, the HTTP request is checked for a corresponding parameter
    value. If present, the option will be selected.

<![CDATA[
        <jquery:select name="sort">
            <jquery:option value="Author"/>
            <jquery:option value="Title"/>
            <jquery:option value="Date Issued"/>
        </jquery:select>
    ]]>

:)
declare function jquery:select-field($node as element(jquery:select)) as element() {
    let $name := $node/@name/string()
    let $value := request:get-parameter($name, "")
    return
        <select>
        { $node/@* }
        {
            for $option in $node/jquery:option
            return
                <option>
                { $option/@value }
                { if ($option/@value eq $value) then attribute selected { "selected" } else () }
                { if ($option/text()) then $option/text() else $option/@value/string() }
                </option>
        }
        </select>
};

declare function jquery:button($node as element(jquery:button)) as element()+ {
    let $id := if ($node/@id) then $node/@id/string() else util:uuid()
    return (
        <button id="{$id}">{$node/@*[not(local-name(.) = 'id')], $node/node()}</button>,

    )
};

declare function jquery:accordion($config as element(jquery:ajax-accordion)) as element()+ {
    let $id := if ($config/@id) then $config/@id else 'jquery-accordion'
    return (

        {
            for $panel in $config/jquery:panel
            return (

{$panel/@id}{$panel/@title/string()}
,

                {
                    for $child in $panel/node() return
                        jquery:process-templates($child)
                }

            )
        }

,

    )
};

declare function jquery:dialog($config as element(jquery:dialog)) as node()* {
    let $trigger := $config/@trigger
    let $id := $config/@id
    let $modal := $config/@modal = 'true'
    let $buttons :=
        string-join(
            for $button in $config/jquery:button
            return
                if ($button/@id eq 'cancel') then
                    string-join(('"', $button/@label, '": function() { $("#', $id/string(), '").dialog("close"); }'), "")
                else if ($button/@id eq 'submit') then
                    string-join(('"', $button/@label, '": function() { $("#', $id/string(), ' form").submit(); }'), "")
                else if ($button/@function) then
                    string-join(('"', $button/@label, '": function () { ', $button/@function, '($("#', $id/string(), '")); }'), "")
                else
                    string-join(('"', $button/@label, '": function() {', $button/node(), '}'), "")
            , ", ")
    return

            { $id, for $child in $config/node()[not(self::jquery:*)] return jquery:process-templates($child) }

};

(:~
    Main function to process an HTML template. All jquery:* elements
    in the template are expanded, if known.
:)
declare function jquery:process-templates($node as node()) as node()* {
    typeswitch ($node)
        case element(jquery:form-repeat) return
            jquery:form-repeat($node)
        case element(jquery:tabset) return
            jquery:tabset($node)
        case element(jquery:toggle) return
            jquery:toggle($node)
        case element(jquery:paginate) return
            jquery:paginate($node)
        case element(jquery:input) return
            jquery:input-field($node)
        case element(jquery:select) return
            jquery:select-field($node)
        case element(jquery:button) return
            jquery:button($node)
        case element(jquery:ajax-accordion) return
            jquery:accordion($node)
        case element(jquery:dialog) return
            jquery:dialog($node)
        case element(jquery:header) return
            jquery:header($node)
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node()
                return
                    jquery:process-templates($child)
            }
        default return
            $node
};

(:~
    jQuery module: a set of functions to extend HTML forms with AJAX
    functionality, based on the jQuery javascript library.

    Use: each function takes an XML configuration element as parameter.
    You can either call functions directly from another XQuery module
    or pass a template page to the jquery:process-templates function.
    jquery:process-templates will parse the template page and expand all
    jquery:* elements it knows.

:)
module namespace jquery="http://exist-db.org/xquery/jquery";

import module namespace util="http://exist-db.org/xquery/util";
import module namespace request="http://exist-db.org/xquery/request";
import module namespace session="http://exist-db.org/xquery/session";

declare function jquery:script($node as element()?) as xs:string? {
    string-join($node/node(), "")
};

(:~
    Outputs the HTML script and link tags which are required by jQuery.
    You need to call this function once in your HTML head.

<![CDATA[<jquery:header base="scripts/jquery" cssbase="scripts/jquery/css"/>]]>

:)
declare function jquery:header($config as element(jquery:header)) as element()* {
    let $base := $config/@base/string()
    let $cssbase := if ($config/@cssbase) then $config/@cssbase/string() else $base
    return (

            }

};

(:~
    Creates a repeatable set of form fields. Clicking the trigger will
    clone a given part of the form. The new element is inserted immediately
    after the cloned element.

    The element to be cloned should have a class 'repeat'. It can be an arbitrary
    HTML node. Form fields within the repeated element should have a name attribute
    ending with a number (usually 1), e.g. "input1", "phone1". After inserting a new
    clone, the script will change the names for all form elements in the inserted
    element. So "phone1" will become "phone2" and so on.

    The function expects a configuration element jquery:form-repeat as parameter.
    Two attributes should be specified:

form: a jQuery selector pointing to the HTML form element

trigger: a jQuery selector pointing to the HTML element on which
       the user should click to insert a new row into the form

    Example:

<jquery:form-repeat trigger="#add-field" form="#search-form"/>

    @param $config the jquery:form-repeat configuration element
:)
declare function jquery:form-repeat($config as element(jquery:form-repeat)) as element() {
    let $formSelector := $config/@form/string()
    let $trigger := $config/@trigger/string()
    let $delete := $config/@delete/string()
    let $onReady := $config/@on-ready/string()
    return

};

(:~
    Toggles an HTML block upon the click on a link or button.
    The contents of the HTML block are retrieved via AJAX from
    the URL given in attribute "href".

<![CDATA[
        <jquery:toggle href="filters.xql?type=date"
            trigger="#filters li:nth-child(3) .expand"/>
    ]]>

href: the URL to load the HTML content block from

trigger: jQuery selector pointing to the trigger element
            on which the user should click

callback: optional callback javascript function which will
            be called once the contents of the toggle block are loaded.
            The function receives the trigger element as first parameter,
            the id of the toggle block as second.

:)
declare function jquery:toggle($config as element(jquery:toggle)) as element()+ {
    let $id0 := $config/@id/string()
    let $id := if ($id0) then $id0 else util:uuid()
    let $trigger := $config/@trigger/string()
    let $href := $config/@href/string()
    let $callback := $config/@callback/string()
    let $beforeCallback := $config/@beforeCallback/string()
    return (
        if (empty($id0)) then

        else (),

    )
};

(:~
    <jquery:paginate id="results" attribute="results"
        href="retrieve" navcontainer="#results-head .navbar"/>
:)
declare function jquery:paginate($config as element(jquery:paginate)) as element()+ {
    let $hits := session:get-attribute($config/@attribute)
    let $id := if ($config/@id) then $config/@id else 'jquery-paginate'
    let $onReady := $config/@on-ready/string()
    return (

,

    )
};

(:~
    Helper function for jquery:input-field()
:)
declare function jquery:extra-params($id as xs:string, $auto as element(jquery:autocomplete)) {
    if ($auto/jquery:param) then
        string-join(
            for $param in $auto/jquery:param
            return
                concat($param/@name, ': function () { ',
                    " return ", $param/@function, '.call($("#', $id, '"));}'),
            ', '
        )
    else
        ()
};

(:~
    Create an HTML input element. Copies all attributes of the jquery:input
    element. If no value attribute is present, the function will look up the
    parameter in the HTTP request, and if the parameter is set, add it as value
    attribute.

<![CDATA[<jquery:input name="input1"/>]]>

:)
declare function jquery:input-field($node as element(jquery:input)) as element()+ {
    let $name := $node/@name/string()
    let $id := if ($node/@id) then $node/@id/string() else util:uuid()
    return (
        if ($node/jquery:autocomplete) then
            let $auto := $node/jquery:autocomplete
            let $width := if ($auto/@width) then $auto/@width/string() else 200
            let $multiple := if ($auto/@multiple) then $auto/@multiple/string() else "false"
            let $matchContains := if ($auto/@matchContains) then $auto/@matchContains/string() else "false"
            let $minLength := if ($auto/@minLength) then $auto/@minLength/string() else "3"
            return

        else
            (),
        <input>
        { $node/@* }
        { if (empty($node/@id)) then attribute id { $id } else () }
        {
            if (empty($node/@value)) then
                attribute value { request:get-parameter($name, "") }
            else
                ()
        }
        </input>
    )
};

(:~
    { jquery:extra-params($id, $auto) }
                        width: { $width },
                        minChars: 3
    Create an HTML select element. Copies all attributes of the jquery:select
    element. jquery:select should contain zero or more jquery:option elements
    which are transformed into HTML option elements. Each option must have a
    value attribute and may have an optional text content. Both are copied to
    the generated HTML option.

    For each option, the HTTP request is checked for a corresponding parameter
    value. If present, the option will be selected.

<![CDATA[
        <jquery:select name="sort">
            <jquery:option value="Author"/>
            <jquery:option value="Title"/>
            <jquery:option value="Date Issued"/>
        </jquery:select>
    ]]>

:)
declare function jquery:select-field($node as element(jquery:select)) as element() {
    let $name := $node/@name/string()
    let $value := request:get-parameter($name, "")
    return
        <select>
        { $node/@* }
        {
            for $option in $node/jquery:option
            return
                <option>
                { $option/@value }
                { if ($option/@value eq $value) then attribute selected { "selected" } else () }
                { if ($option/text()) then $option/text() else $option/@value/string() }
                </option>
        }
        </select>
};

declare function jquery:button($node as element(jquery:button)) as element()+ {
    let $id := if ($node/@id) then $node/@id/string() else util:uuid()
    return (
        <button id="{$id}">{$node/@*[not(local-name(.) = 'id')], $node/node()}</button>,

    )
};

declare function jquery:accordion($config as element(jquery:ajax-accordion)) as element()+ {
    let $id := if ($config/@id) then $config/@id else 'jquery-accordion'
    return (

        {
            for $panel in $config/jquery:panel
            return (

{$panel/@id}{$panel/@title/string()}
,

                {
                    for $child in $panel/node() return
                        jquery:process-templates($child)
                }

            )
        }

,

    )
};

declare function jquery:dialog($config as element(jquery:dialog)) as node()* {
    let $trigger := $config/@trigger
    let $id := $config/@id
    let $modal := $config/@modal = 'true'
    let $buttons :=
        string-join(
            for $button in $config/jquery:button
            return
                if ($button/@id eq 'cancel') then
                    string-join(('"', $button/@label, '": function() { $("#', $id/string(), '").dialog("close"); }'), "")
                else if ($button/@id eq 'submit') then
                    string-join(('"', $button/@label, '": function() { $("#', $id/string(), ' form").submit(); }'), "")
                else if ($button/@function) then
                    string-join(('"', $button/@label, '": ', $button/@function), "")
                else
                    string-join(('"', $button/@label, '": function() {', $button/node(), '}'), "")
            , ", ")
    return

            { $id, for $child in $config/node()[not(self::jquery:*)] return jquery:process-templates($child) }

};

(:~
    Main function to process an HTML template. All jquery:* elements
    in the template are expanded, if known.
:)
declare function jquery:process-templates($node as node()) as node()* {
    typeswitch ($node)
        case element(jquery:form-repeat) return
            jquery:form-repeat($node)
        case element(jquery:tabset) return
            jquery:tabset($node)
        case element(jquery:toggle) return
            jquery:toggle($node)
        case element(jquery:paginate) return
            jquery:paginate($node)
        case element(jquery:input) return
            jquery:input-field($node)
        case element(jquery:select) return
            jquery:select-field($node)
        case element(jquery:button) return
            jquery:button($node)
        case element(jquery:ajax-accordion) return
            jquery:accordion($node)
        case element(jquery:dialog) return
            jquery:dialog($node)
        case element(jquery:header) return
            jquery:header($node)
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node()
                return
                    jquery:process-templates($child)
            }
        default return
            $node
};


Update function signatures for lucene module

xquery version "3.0";
(:~
 : eXist-db Lucene module functions
 :
 : @see http://exist-db.org/exist/apps/fundocs/view.html?uri=http://exist-db.org/xquery/lucene&location=java:org.exist.xquery.modules.lucene.LuceneModule&details=true
 :)
module namespace ft = "http://exist-db.org/xquery/lucene";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare %a:since("exist", "4.4") function ft:close() as empty-sequence() external;
declare %a:since("exist", "5.1") function ft:facets($nodes as node()*, $dimension as xs:string) as map(*) external;
declare %a:since("exist", "5.1") function ft:facets($nodes as node()*, $dimension as xs:string, $count as xs:integer?) as map(*) external;
declare %a:since("exist", "5.1") function ft:facets($nodes as node()*, $dimension as xs:string, $count as xs:integer?, $paths as xs:string+) as map(*) external;
declare %a:since("exist", "5.1") function ft:field($node as node(), $field as xs:string) as xs:string* external;
declare %a:since("exist", "5.1") function ft:field($node as node(), $field as xs:string, $type as xs:string) as item()* external;
declare %a:since("exist", "4.4") function ft:get-field($path as xs:string*, $field as xs:string) as xs:string* external;
declare %a:since("exist", "4.4") function ft:has-index($path as xs:string) as xs:boolean* external;
declare %a:since("exist", "5.3") function ft:highlight-field-matches($node as node(), $field as xs:string) as element()? external;
declare %a:since("exist", "4.4") function ft:index($documentPath as xs:string, $solrExression as node()) as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:index($documentPath as xs:string, $solrExression as node(), $close as xs:boolean) as empty-sequence() external;
declare %a:since("exist", "5.1") function ft:index-keys-for-field($field as xs:string, $start-value as xs:string?, $function-reference as function(*), $max-number-returned as xs:int?) as item()* external;
declare %a:since("exist", "4.4") function ft:optimize() as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:query($nodes as node()*, $query as item()) as node()* external;
declare %a:since("exist", "4.4") function ft:query($nodes as node()*, $query as item(), $options as node()?) as node()* external;
declare %a:since("exist", "4.4") function ft:query-field($field as xs:string*, $query as item()) as node()* external;
declare %a:since("exist", "4.4") function ft:query-field($field as xs:string*, $query as item(), $options as node()?) as node()* external;
declare %a:since("exist", "4.4") function ft:remove-index($documentPath as xs:string) as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:score($node as node()) as xs:float* external;
declare %a:since("exist", "4.4") function ft:search($path as xs:string*, $query as xs:string, $fields as xs:string*) as node() external;
declare %a:since("exist", "4.4") function ft:search($path as xs:string*, $query as xs:string) as node() external;
declare %a:since("exist", "4.4") function ft:search($query as xs:string) as node() external;

xquery version "3.0";
(:~
 : eXist-db Lucene module functions
 :
 : @see http://exist-db.org/exist/apps/fundocs/view.html?uri=http://exist-db.org/xquery/lucene&location=java:org.exist.xquery.modules.lucene.LuceneModule&details=true
 :)
module namespace ft = "http://exist-db.org/xquery/lucene";

declare namespace a = "http://reecedunn.co.uk/xquery/annotations";
declare namespace o = "http://reecedunn.co.uk/xquery/options";

declare %a:since("exist", "4.4") function ft:close() as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:get-field($path as xs:string*, $field as xs:string) as xs:string* external;
declare %a:since("exist", "4.4") function ft:has-index($path as xs:string) as xs:boolean* external;
declare %a:since("exist", "4.4") function ft:index($documentPath as xs:string, $solrExression as node()) as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:index($documentPath as xs:string, $solrExression as node(), $close as xs:boolean) as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:optimize() as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:query($nodes as node()*, $query as item()) as node()* external;
declare %a:since("exist", "4.4") function ft:query($nodes as node()*, $query as item(), $options as node()?) as node()* external;
declare %a:since("exist", "4.4") function ft:query-field($field as xs:string*, $query as item()) as node()* external;
declare %a:since("exist", "4.4") function ft:query-field($field as xs:string*, $query as item(), $options as node()?) as node()* external;
declare %a:since("exist", "4.4") function ft:remove-index($documentPath as xs:string) as empty-sequence() external;
declare %a:since("exist", "4.4") function ft:score($node as node()) as xs:float* external;
declare %a:since("exist", "4.4") function ft:search($path as xs:string*, $query as xs:string, $fields as xs:string*) as node() external;
declare %a:since("exist", "4.4") function ft:search($path as xs:string*, $query as xs:string) as node() external;
declare %a:since("exist", "4.4") function ft:search($query as xs:string) as node() external;


vezzini test code

vezzini test code

xquery version "1.0";

declare namespace c4="http://falutin.net/connect4";

import module namespace util="http://falutin.net/connect4/util" at "util.xqy";
import module namespace layout="http://falutin.net/connect4/layout" at "layout.xqy";
import module namespace c4a="http://falutin.net/connect4/analysis" at "analysis.xqy";

declare variable $lux:http as document-node() external;

declare function c4:test ()
{
  let $game-id := util:param ($lux:http, 'game')
  let $game := collection()/game[@id=$game-id]
  let $inigo-grid := c4a:inigo-grid($game, $game//player[2])
  return layout:outer('/connect4/test.xqy',
    c4a:draw-vezzini ($game, $game//player[2])
    <div>{
      for $i in (1 to 7) return
        c4a:draw-vezzini-grid ($game)
    }</div>
(:
    <textarea rows="24" cols="80">{
    $inigo-grid
                                  } </textarea>
:)
                )
};

c4:test()

xquery version "1.0";

declare namespace c4="http://falutin.net/connect4";

import module namespace util="http://falutin.net/connect4/util" at "util.xqy";
import module namespace layout="http://falutin.net/connect4/layout" at "layout.xqy";
import module namespace c4a="http://falutin.net/connect4/analysis" at "analysis.xqy";

declare variable $lux:http as document-node() external;

declare function c4:test ()
{
  let $game-id := util:param ($lux:http, 'game')
  let $game := collection()/game[@id=$game-id]
  let $inigo-grid := c4a:inigo-grid($game, $game//player[2])
  return layout:outer('/connect4/test.xqy',
    c4a:draw-vezzini ($game, $game//player[2])
(:
    <div>{
      for $i in (1 to 7) return
        c4a:draw-vezzini-grid ($game)
    }</div>

    <textarea rows="24" cols="80">{
    $inigo-grid
                                  } </textarea>
:)
                )
};

c4:test()


[ignore] Minor syntax reformatting

[ignore] Minor syntax reformatting

(:~
    KWIC module: formats query results to display keywords in context (KWIC). A configurable
    amount of text is displayed to the left and right of a matching keyword (or phrase).

    The module works with all indexes that support match highlighting (matches are tagged
    with an <exist:match> element or exist:matches attribute). This includes the old
    full text index, the new Lucene-based full text index, as well as the NGram index.

    The kwic:summarize() function represents the main entry point into the module.
    To have more control over the text extraction context, you can also call
    kwic:get-summary() instead. For example, the following snippet will only print the
    first match within a given set of context nodes ($ancestor):

    let $matches := kwic:get-matches($hit)

    for $ancestor in $matches/ancestor::para | $matches/ancestor::title | $matches/ancestor::td

    return

        kwic:get-summary($ancestor, ($ancestor//exist:match, $ancestor//*[@exist:matches])[1], $config)

    The configuration element used by some functions has the following structure:

    <config
        width="character width"
        table="yes|no"
        link="URL to which the match is linked"
        whitespace-text-nodes="collapse|drop|leave"/>
:)
xquery version "3.0";

module namespace kwic = "http://exist-db.org/xquery/kwic";

import module namespace util = "http://exist-db.org/xquery/util";

declare namespace exist = "http://exist.sourceforge.net/NS/exist";

declare variable $kwic:MODE_BEFORE := "before";
declare variable $kwic:MODE_AFTER := "after";

declare %private variable $kwic:CHARS_SUMMARY := 120;
declare %private variable $kwic:CHARS_KWIC := 40;

(:~
	Like fn:substring, but takes a node argument. If the node is an element,
	a new element is created with the same node-name as the old one and the
	shortened text content.
:)
declare
    %private
function kwic:substring($node as item(), $start as xs:integer, $count as xs:integer) as node() {
	let $str := substring($node, $start, $count)
	return
		if ($node instance of element()) then
			element { node-name($node) } { $str }
		else
			text { $str }
};

declare
    %private
function kwic:display-text($text as text()?) as node()? {
    if ($text/parent::exist:match) then
    	{$text}
    else
        $text
};

declare
    %private
function kwic:callback($callback as (function(text(), xs:string) as text()?)?, $node as text(), $mode as xs:string) as text()? {
    if (exists($callback)) then
        $callback($node, $mode)
    else
        $node
};

(:~
	Generate the left-hand context of the match. Returns a sequence of nodes
	and strings, whose total string length is less than or equal to $max characters.

	Note: this function calls itself recursively until $nodes is empty or
	the returned sequence has the desired total string length.
:)
declare
    %private
function kwic:truncate-previous($root as node(), $node as node()?, $truncated as item()*,
	   $max as xs:integer, $chars as xs:integer, $callback as (function(text(), xs:string) as text()?)?) {
	if ($node) then
		let $next := $node/preceding::text()[1] intersect $root//text()
		return
			if (empty($next)) then
				$truncated
			else if ($chars + string-length($next) gt $max) then
			    let $str := kwic:callback($callback, $next, $kwic:MODE_BEFORE)
			    return
    			    if (exists($str)) then
        				let $remaining := $max - $chars
        				return (
        				    text { "..." },
        				    kwic:substring($str, string-length($str) - $remaining + 1, $remaining),
        				    $truncated
        			    )
        			else
        			    kwic:truncate-previous($root, $next, $truncated, $max, $chars, $callback)
			else
				kwic:truncate-previous($root, $next,
				    (kwic:callback($callback, $next, $kwic:MODE_BEFORE), $truncated),
					$max, $chars + string-length($next), $callback)
	else
		$truncated
};

(:~
	Generate the right-hand context of the match. Returns a sequence of nodes
	and strings, whose total string length is less than or equal to $max characters.

	Note: this function calls itself recursively until $nodes is empty or
	the returned sequence has the desired total string length.
:)
declare
    %private
function kwic:truncate-following($root as node(), $node as node()?, $truncated as item()*,
	$max as xs:integer, $chars as xs:integer, $callback as (function(text(), xs:string) as text()?)?) {
	if ($node) then
		let $next := $node/following::text()[1] intersect $root//text()
		return
			if (empty($next)) then
				$truncated
			else if ($chars + string-length($next) gt $max) then
			    let $str := kwic:callback($callback, $next, $kwic:MODE_AFTER)
			    return
    			    if (exists($str)) then
        				let $remaining := $max - $chars
        				return (
        				    $truncated, kwic:substring($next, 1, $remaining), text { "..." }
        			    )
        			else
        			    kwic:truncate-following($root, $next, $truncated, $max, $chars, $callback)
			else
				kwic:truncate-following($root, $next,
				    ($truncated, kwic:callback($callback, $next, $kwic:MODE_AFTER)),
					$max, $chars + string-length($next), $callback)
	else
		$truncated
};

(:~
	Computes the total string length of the nodes in the argument sequence
:)
declare
    %private
function kwic:string-length($nodes as item()*) as xs:integer {
	if (exists($nodes)) then
		sum($nodes ! string-length(.))
	else
		0
};

declare
    %private
function kwic:collapse-whitespace-fn($callback as (function(text(), xs:string) as text()?)?) as (function(text(), xs:string) as text()) {
    function($text as text(), $mode as xs:string) as text()? {
        let $text :=
            if (matches($text, "\s+")) then
                text { " " }
            else
                $text
        return
            if (exists($callback)) then
                $callback($text, $mode)
            else
                $text
    }
};

declare
    %private
function kwic:drop-whitespace-fn($callback as (function(text(), xs:string) as text()?)?) as (function(text(), xs:string) as text()?) {
    function($text as text(), $mode as xs:string) as text()? {
        let $text :=
            if (matches($text, "\s+")) then
                ()
            else
                $text
        return
            if (exists($text) and exists($callback)) then
                $callback($text, $mode)
            else
                $text
    }
};

declare function kwic:get-summary($root as node(), $node as element(),
	$config as element(config)?) as element() {
	kwic:get-summary($root, $node, $config, ())
};

(:~
	Print a summary of the match in $node. Output a predefined amount of text to
	the left and the right of the match.

	@param $root root element which should be used as context for the match. It defines the
	    boundaries for the text extraction. Text will be taken from this context.
	@param $node the exist:match element or the element with @exist:macthes attribute to process.
	@param $config configuration element which determines the behaviour of the function
	@param $callback (optional) reference to a callback function which will be called
	once for every text node before it is appended to the displayed text. The function
	should accept 2 parameters: 1) a single text node, 2) a string indicating the
	current direction in which text is appended, i.e. $kwic:MODE_BEFORE or $kwic:MODE_AFTER.
	The function may return the empty sequence if the current node should be ignore
	(e.g. if it belongs to a "footnote" which should not be displayed). Otherwise it should
	return a single string.
:)
declare function kwic:get-summary($root as node(), $node as element(),
	$config as element(config)?,
    $callback as (function(text(), xs:string) as text()?)?
) as element() {
	let $chars := xs:integer(($config/@width, $kwic:CHARS_KWIC)[1])
	let $table := $config/@table = ('yes', 'true')
	let $whitespace-text-nodes := string($config/@whitespace-text-nodes)

	let $callback :=
	   if ($whitespace-text-nodes = "collapse") then
	       kwic:collapse-whitespace-fn($callback)
	   else if ($whitespace-text-nodes = "drop") then
	       kwic:drop-whitespace-fn($callback)
	   else
	       $callback

	let $prevTrunc := kwic:truncate-previous($root, $node, (), $chars, 0, $callback)
	let $remain :=
		if (not($table)) then
			$chars * 2 - kwic:string-length($prevTrunc)
		else
			$chars
	let $followingTrunc := kwic:truncate-following($root, $node, (), $remain, 0, $callback)
	return
		if (not($table)) then

				{$prevTrunc}
				{
					if ($config/@link) then
						{ $node/text() }
					else
						{ $node/text() }
				}
				{$followingTrunc}

		else

				{$prevTrunc}

				{
					if ($config/@link) then
						{$node/text()}
					else
						$node/text()
				}

				{$followingTrunc}

};

(:~
    Expand the element in $hit. Creates an in-memory copy of the element and marks
    all element matches with an exist:match tag and attribute matches with an
    exist:matches tag, which will be used by all other functions in
    this module. You need to call kwic:expand before kwic:get-summary.
    kwic:summarize will call it automatically.
:)
declare function kwic:expand($hit as element()) as element() {
    util:expand($hit, "expand-xincludes=no highlight-matches=both")
};

(:~
    Return all matches within the specified element, $hit. Matches are returned as
    exist:match elements or elements with exist:matches attribute. The returned nodes
    are part of a new document whose root element is a copy of the specified $hit element.

    @param $hit an arbitrary XML element which has been selected by one of the full text
		operations or an ngram search.
:)
declare function kwic:get-matches($hit as element()) as element()* {
    let $expanded := kwic:expand($hit)
	return ($expanded//exist:match, $expanded//*[@exist:matches])
};

declare function kwic:summarize($hit as element(), $config as element(config)?) as element()* {
    kwic:summarize($hit, $config, ())
};

(:~
	Main function of the KWIC module: takes the passed element and returns an
	XHTML fragment containing a chunk of text before and after the first full text
	match in the node.

	The optional config parameter is used to configure the behaviour of the function:

	<config width="character width" table="yes|no" link="URL to which the match is linked"/>

	By default, kwic:summarize returns an XHTML fragment with the following structure:

	<p xmlns="http://www.w3.org/1999/xhtml">
		<span class="previous">Text before match</span>
		<a href="passed URL if any" class="hi">The highlighted term</a>
		<span class="following">Text after match</span>
	</p>

	If table=yes is passed with the config element, a tr table row will be returned instead
	of a span (using the same class names).

	@param $hit an arbitrary XML element which has been selected by one of the full text
		operations or an ngram search.
	@param $config configuration element to configure the behaviour of the function
:)
declare function kwic:summarize($hit as element(), $config as element(config)?,
    $callback as (function(text(), xs:string) as text()?)?) as element()* {
    let $expanded := util:expand($hit, "expand-xincludes=no highlight-matches=both")
	for $match in ($expanded//exist:match, $expanded//*[@exist:matches])
	return
		kwic:get-summary($expanded, $match, $config, $callback)
};

(:
declare function kwic:summarize-all($hits as element()*, $config as element(config),
    $wrap as function(node(), function(node()) as element()) as element()) {
    let $hits := util:expand($hits, "expand-xincludes=no")
    for $hit in $hits
    return
        $wrap($hit, function($node) {
            for $match in $node//exist:match
            return
        		kwic:get-summary($node, $match, $config, ())
        })
};
:)

(:~
    KWIC module: formats query results to display keywords in context (KWIC). A configurable
    amount of text is displayed to the left and right of a matching keyword (or phrase).

    The module works with all indexes that support match highlighting (matches are tagged
    with an <exist:match> element or exist:matches attribute). This includes the old
    full text index, the new Lucene-based full text index, as well as the NGram index.

    The kwic:summarize() function represents the main entry point into the module.
    To have more control over the text extraction context, you can also call
    kwic:get-summary() instead. For example, the following snippet will only print the
    first match within a given set of context nodes ($ancestor):

    let $matches := kwic:get-matches($hit)

    for $ancestor in $matches/ancestor::para | $matches/ancestor::title | $matches/ancestor::td

    return

        kwic:get-summary($ancestor, ($ancestor//exist:match, $ancestor//*[@exist:matches])[1], $config)

    The configuration element used by some functions has the following structure:

    <config
        width="character width"
        table="yes|no"
        link="URL to which the match is linked"
        whitespace-text-nodes="collapse|drop|leave"/>
:)
xquery version "3.0";

module namespace kwic = "http://exist-db.org/xquery/kwic";

import module namespace util = "http://exist-db.org/xquery/util";

declare namespace exist = "http://exist.sourceforge.net/NS/exist";

declare variable $kwic:MODE_BEFORE := "before";
declare variable $kwic:MODE_AFTER := "after";

declare %private variable $kwic:CHARS_SUMMARY := 120;
declare %private variable $kwic:CHARS_KWIC := 40;

(:~
	Like fn:substring, but takes a node argument. If the node is an element,
	a new element is created with the same node-name as the old one and the
	shortened text content.
:)
declare
    %private
function kwic:substring($node as item(), $start as xs:integer, $count as xs:integer) as item()? {
	let $str := substring($node, $start, $count)
	return
		if ($node instance of element()) then
			element { node-name($node) } { $str }
		else
			text { $str }
};

declare
    %private
function kwic:display-text($text as text()?) as node()? {
    if ($text/parent::exist:match) then
    	{$text}
    else
        $text
};

declare
    %private
function kwic:callback($callback as (function(text(), xs:string) as text()?)?, $node as text(), $mode as xs:string) as text()? {
    if (exists($callback)) then
        $callback($node, $mode)
    else
        $node
};

(:~
	Generate the left-hand context of the match. Returns a sequence of nodes
	and strings, whose total string length is less than or equal to $max characters.

	Note: this function calls itself recursively until $nodes is empty or
	the returned sequence has the desired total string length.
:)
declare
    %private
function kwic:truncate-previous($root as node(), $node as node()?, $truncated as item()*,
	   $max as xs:integer, $chars as xs:integer, $callback as (function(text(), xs:string) as text()?)?) {
	if ($node) then
		let $next := $node/preceding::text()[1] intersect $root//text()
		return
			if (empty($next)) then
				$truncated
			else if ($chars + string-length($next) gt $max) then
			    let $str := kwic:callback($callback, $next, $kwic:MODE_BEFORE)
			    return
    			    if (exists($str)) then
        				let $remaining := $max - $chars
        				return (
        				    text { "..." },
        				    kwic:substring($str, string-length($str) - $remaining + 1, $remaining),
        				    $truncated
        			    )
        			else
        			    kwic:truncate-previous($root, $next, $truncated, $max, $chars, $callback)
			else
				kwic:truncate-previous($root, $next,
				    (kwic:callback($callback, $next, $kwic:MODE_BEFORE), $truncated),
					$max, $chars + string-length($next), $callback)
	else
		$truncated
};

(:~
	Generate the right-hand context of the match. Returns a sequence of nodes
	and strings, whose total string length is less than or equal to $max characters.

	Note: this function calls itself recursively until $nodes is empty or
	the returned sequence has the desired total string length.
:)
declare
    %private
function kwic:truncate-following($root as node(), $node as node()?, $truncated as item()*,
	$max as xs:integer, $chars as xs:integer, $callback as (function(text(), xs:string) as text()?)?) {
	if ($node) then
		let $next := $node/following::text()[1] intersect $root//text()
		return
			if (empty($next)) then
				$truncated
			else if ($chars + string-length($next) gt $max) then
			    let $str := kwic:callback($callback, $next, $kwic:MODE_AFTER)
			    return
    			    if (exists($str)) then
        				let $remaining := $max - $chars
        				return (
        				    $truncated, kwic:substring($next, 1, $remaining), text { "..." }
        			    )
        			else
        			    kwic:truncate-following($root, $next, $truncated, $max, $chars, $callback)
			else
				kwic:truncate-following($root, $next,
				    ($truncated, kwic:callback($callback, $next, $kwic:MODE_AFTER)),
					$max, $chars + string-length($next), $callback)
	else
		$truncated
};

(:~
	Computes the total string length of the nodes in the argument sequence
:)
declare
    %private
function kwic:string-length($nodes as item()*) as xs:integer {
	if (exists($nodes)) then
		sum($nodes ! string-length(.))
	else
		0
};

declare
    %private
function kwic:collapse-whitespace-fn($callback as (function(text(), xs:string) as text()?)?) as (function(text(), xs:string) as text()) {
    function($text as text(), $mode as xs:string) as text()? {
        let $text :=
            if(matches($text, "\s+")) then
                text { " " }
            else
                $text
        return
            if(exists($callback))then
                $callback($text, $mode)
            else
                $text
    }
};

declare
    %private
function kwic:drop-whitespace-fn($callback as (function(text(), xs:string) as text()?)?) as (function(text(), xs:string) as text()?) {
    function($text as text(), $mode as xs:string) as text()? {
        let $text :=
            if(matches($text, "\s+")) then
                ()
            else
                $text
        return
            if(exists($text) and exists($callback))then
                $callback($text, $mode)
            else
                $text
    }
};

declare function kwic:get-summary($root as node(), $node as element(),
	$config as element(config)?) as element() {
	kwic:get-summary($root, $node, $config, ())
};

(:~
	Print a summary of the match in $node. Output a predefined amount of text to
	the left and the right of the match.

	@param $root root element which should be used as context for the match. It defines the
	    boundaries for the text extraction. Text will be taken from this context.
	@param $node the exist:match element or the element with @exist:macthes attribute to process.
	@param $config configuration element which determines the behaviour of the function
	@param $callback (optional) reference to a callback function which will be called
	once for every text node before it is appended to the displayed text. The function
	should accept 2 parameters: 1) a single text node, 2) a string indicating the
	current direction in which text is appended, i.e. $kwic:MODE_BEFORE or $kwic:MODE_AFTER.
	The function may return the empty sequence if the current node should be ignore
	(e.g. if it belongs to a "footnote" which should not be displayed). Otherwise it should
	return a single string.
:)
declare function kwic:get-summary($root as node(), $node as element(),
	$config as element(config)?,
    $callback as (function(text(), xs:string) as text()?)?
) as element() {
	let $chars := xs:integer(($config/@width, $kwic:CHARS_KWIC)[1])
	let $table := $config/@table = ('yes', 'true')
	let $whitespace-text-nodes := string($config/@whitespace-text-nodes)

	let $callback :=
	   if($whitespace-text-nodes = "collapse") then
	       kwic:collapse-whitespace-fn($callback)
	   else if($whitespace-text-nodes = "drop") then
	       kwic:drop-whitespace-fn($callback)
	   else
	       $callback

	let $prevTrunc := kwic:truncate-previous($root, $node, (), $chars, 0, $callback)
	let $remain :=
		if (not($table)) then
			$chars * 2 - kwic:string-length($prevTrunc)
		else
			$chars
	let $followingTrunc := kwic:truncate-following($root, $node, (), $remain, 0, $callback)
	return
		if (not($table)) then

				{$prevTrunc}
				{
					if ($config/@link) then
						{ $node/text() }
					else
						{ $node/text() }
				}
				{$followingTrunc}

		else

				{$prevTrunc}

				{
					if ($config/@link) then
						{$node/text()}
					else
						$node/text()
				}

				{$followingTrunc}

};

(:~
    Expand the element in $hit. Creates an in-memory copy of the element and marks
    all element matches with an exist:match tag and attribute matches with an
    exist:matches tag, which will be used by all other functions in
    this module. You need to call kwic:expand before kwic:get-summary.
    kwic:summarize will call it automatically.
:)
declare function kwic:expand($hit as element()) as element() {
    util:expand($hit, "expand-xincludes=no highlight-matches=both")
};

(:~
    Return all matches within the specified element, $hit. Matches are returned as
    exist:match elements or elements with exist:matches attribute. The returned nodes
    are part of a new document whose root element is a copy of the specified $hit element.

    @param $hit an arbitrary XML element which has been selected by one of the full text
		operations or an ngram search.
:)
declare function kwic:get-matches($hit as element()) as element()* {
    let $expanded := kwic:expand($hit)
	return ($expanded//exist:match, $expanded//*[@exist:matches])
};

declare function kwic:summarize($hit as element(), $config as element(config)?) as element()* {
    kwic:summarize($hit, $config, ())
};

(:~
	Main function of the KWIC module: takes the passed element and returns an
	XHTML fragment containing a chunk of text before and after the first full text
	match in the node.

	The optional config parameter is used to configure the behaviour of the function:

	<config width="character width" table="yes|no" link="URL to which the match is linked"/>

	By default, kwic:summarize returns an XHTML fragment with the following structure:

	<p xmlns="http://www.w3.org/1999/xhtml">
		<span class="previous">Text before match</span>
		<a href="passed URL if any" class="hi">The highlighted term</a>
		<span class="following">Text after match</span>
	</p>

	If table=yes is passed with the config element, a tr table row will be returned instead
	of a span (using the same class names).

	@param $hit an arbitrary XML element which has been selected by one of the full text
		operations or an ngram search.
	@param $config configuration element to configure the behaviour of the function
:)
declare function kwic:summarize($hit as element(), $config as element(config)?,
    $callback as (function(text(), xs:string) as text()?)?) as element()* {
    let $expanded := util:expand($hit, "expand-xincludes=no highlight-matches=both")
	for $match in ($expanded//exist:match, $expanded//*[@exist:matches])
	return
		kwic:get-summary($expanded, $match, $config, $callback)
};

(:
declare function kwic:summarize-all($hits as element()*, $config as element(config),
    $wrap as function(node(), function(node()) as element()) as element()) {
    let $hits := util:expand($hits, "expand-xincludes=no")
    for $hit in $hits
    return
        $wrap($hit, function($node) {
            for $match in $node//exist:match
            return
        		kwic:get-summary($node, $match, $config, ())
        })
};
:)


adjust tests to include @joewiz's remarks

xquery version "3.0";

module namespace flwor="http://exist-db.org/xquery/test/flwor";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $flwor:COLLECTION_CONFIG :=
    <collection xmlns="http://exist-db.org/collection-config/1.0">
        <index xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <fulltext default="none" attributes="false"/>
            <range>
                <create qname="place">
                    <field name="place-id" type="xs:string" match="@xml:id"/>
                    <field name="place-name" type="xs:string" match="placeName"/>
                </create>
            </range>
        </index>
    </collection>;

declare variable $flwor:DATA :=
    <listPlace>
         <place xml:id="warsaw">
             <placeName>Warsaw</placeName>
         </place>
         <place xml:id="berlin">
             <placeName>Berlin</placeName>
         </place>
    </listPlace>;

declare variable $flwor:COLLECTION_NAME := "flwortest";
declare variable $flwor:COLLECTION := "/db/" || $flwor:COLLECTION_NAME;

declare
    %test:setUp
function flwor:setup() {
    xmldb:create-collection("/db/system/config/db", $flwor:COLLECTION_NAME),
    xmldb:store("/db/system/config/db/" || $flwor:COLLECTION_NAME, "collection.xconf", $flwor:COLLECTION_CONFIG),
    xmldb:create-collection("/db", $flwor:COLLECTION_NAME),
    xmldb:store($flwor:COLLECTION, "test.xml", $flwor:DATA)
};

declare
    %test:tearDown
function flwor:cleanup() {
    xmldb:remove($flwor:COLLECTION),
    xmldb:remove("/db/system/config/db/" || $flwor:COLLECTION_NAME)
};

declare function flwor:test($name) {
    collection($flwor:COLLECTION_NAME)//place[placeName = $name]/string()
};

declare
    %test:assertEquals("Berlin", "Berlin")
function flwor:order-by-with-range() {
    for $i in 1 to 2
    order by $i
    return
        flwor:test("Berlin")
};

(: https://github.com/eXist-db/exist/issues/739#issuecomment-130997865 :)
declare
    %test:assertEquals(1, 2, 1, 2, 3, 1, 2, 3, 4)
function flwor:orderby-for-multi() {
    for $x in (3, 2, 4)
    order by $x
    for $y in (1 to $x)
    return $y
};

declare
    %test:assertEquals("a1", "a2", "b1", "b2")
function flwor:orderby-multi() {
  for $a in ("b", "a")
  order by $a
  for $b in (2, 1)
  order by $b
  return
    $a || $b
};

declare
    %test:assertEquals("a1", "a2", "b1", "b2")
function flwor:orderby-where-multi() {
    for $a in ("b", "a", "c")
    order by $a
    where $a = ("a", "b")
    for $b in (2, 1, 5, 6, 9)
    where $b < 4
    order by $b
    return
        $a || $b
};

declare
    %test:assertEquals(2, 4)
function flwor:where-multi() {
    for $a in 1 to 10
    where $a mod 2 = 0
    where $a < 5
    return
        $a
};

declare
    %test:assertEquals(1, 1)
function flwor:where-multi-groupby() {
    let $xml :=
        <t>
            <n class="a">1</n>
            <n class="b">2</n>
            <n class="a">1</n>
            <n class="a">3</n>
        </t>
    for $a in $xml/n
    where $a != 3
    group by $c := $a/@class
    where count($a) > 1
    return
        $a/string()
};

declare
    %test:args(0)
    %test:assertEquals("[]")
    %test:args(1)
    %test:assertEquals("[1]")
    %test:args(6)
    %test:assertEquals("[1]", "[2]", "[3]", "[4]", "[6]")
function flwor:allowing-empty($n as xs:integer) {
    for $x allowing empty in 1 to $n
    where not($x = 5)
    return concat("[", $x, "]")
};

xquery version "3.0";

module namespace flwor="http://exist-db.org/xquery/test/flwor";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $flwor:COLLECTION_CONFIG :=
    <collection xmlns="http://exist-db.org/collection-config/1.0">
        <index xmlns:xs="http://www.w3.org/2001/XMLSchema">
            <fulltext default="none" attributes="false"/>
            <range>
                <create qname="place">
                    <field name="place-id" type="xs:string" match="@xml:id"/>
                    <field name="place-name" type="xs:string" match="placeName"/>
                </create>
            </range>
        </index>
    </collection>;

declare variable $flwor:DATA :=
    <listPlace>
         <place xml:id="warsaw">
             <placeName>Warsaw</placeName>
         </place>
         <place xml:id="berlin">
             <placeName>Berlin</placeName>
         </place>
    </listPlace>;

declare variable $flwor:COLLECTION_NAME := "flwortest";
declare variable $flwor:COLLECTION := "/db/" || $flwor:COLLECTION_NAME;

declare
    %test:setUp
function flwor:setup() {
    xmldb:create-collection("/db/system/config/db", $flwor:COLLECTION_NAME),
    xmldb:store("/db/system/config/db/" || $flwor:COLLECTION_NAME, "collection.xconf", $flwor:COLLECTION_CONFIG),
    xmldb:create-collection("/db", $flwor:COLLECTION_NAME),
    xmldb:store($flwor:COLLECTION, "test.xml", $flwor:DATA)
};

declare
    %test:tearDown
function flwor:cleanup() {
    xmldb:remove($flwor:COLLECTION),
    xmldb:remove("/db/system/config/db/" || $flwor:COLLECTION_NAME)
};

declare function flwor:test($a) {
    collection($flwor:COLLECTION_NAME)//place[placeName = "berlin"]/string()
};

declare
    %test:assertEquals("Berlin")
function flwor:order-by-with-range() {
    for $i in 1 to 2
    order by $i
    return
        flwor:test("Berlin")
};

(: https://github.com/eXist-db/exist/issues/739#issuecomment-130997865 :)
declare
    %test:assertEquals(1, 2, 1, 2, 3, 1, 2, 3, 4)
function flwor:orderby-for-multi() {
    for $x in (3, 2, 4)
    order by $x
    for $y in (1 to $x)
    return $y
};

declare
    %test:assertEquals("a1", "a2", "b1", "b2")
function flwor:orderby-multi() {
  for $a in ("b", "a")
  order by $a
  for $b in (2, 1)
  order by $b
  return
    $a || $b
};

declare
    %test:assertEquals("a1", "a2", "b1", "b2")
function flwor:orderby-where-multi() {
    for $a in ("b", "a", "c")
    order by $a
    where $a = ("a", "b")
    for $b in (2, 1, 5, 6, 9)
    where $b < 4
    order by $b
    return
        $a || $b
};

declare
    %test:assertEquals(2, 4)
function flwor:where-multi() {
    for $a in 1 to 10
    where $a mod 2 = 0
    where $a < 5
    return
        $a
};

declare
    %test:assertEquals(1, 1)
function flwor:where-multi-groupby() {
    let $xml :=
        <t>
            <n class="a">1</n>
            <n class="b">2</n>
            <n class="a">1</n>
            <n class="a">3</n>
        </t>
    for $a in $xml/n
    where $a != 3
    group by $c := $a/@class
    where count($a) > 1
    return
        $a/string()
};

declare
    %test:args(0)
    %test:assertEquals("[]")
    %test:args(1)
    %test:assertEquals("[1]")
    %test:args(6)
    %test:assertEquals("[1]", "[2]", "[3]", "[4]", "[6]")
function flwor:allowing-empty($n as xs:integer) {
    for $x allowing empty in 1 to $n
    where not($x = 5)
    return concat("[", $x, "]")
};


Use 'xsd' in the since annotations, to match the conformance check name.

xquery version "3.1";

(:~
 : XPath and XQuery Functions and Operators: Constructor Functions
 :
 : @see https://www.w3.org/TR/2007/REC-xpath-functions-20070123/
 : @see https://www.w3.org/TR/2010/REC-xpath-functions-20101214/
 : @see https://www.w3.org/TR/2014/REC-xpath-functions-30-20140408/
 : @see https://www.w3.org/TR/2016/CR-xpath-functions-31-20170321/
 :)
module  namespace xs = "http://www.w3.org/2001/XMLSchema";
declare namespace a  = "http://reecedunn.co.uk/xquery/annotations";

declare %a:since("xsd", "1.0") function xs:ENTITY($arg as xs:anyAtomicType?) as xs:ENTITY? external;
declare %a:since("xsd", "1.0") function xs:ID($arg as xs:anyAtomicType?) as xs:ID? external;
declare %a:since("xsd", "1.0") function xs:IDREF($arg as xs:anyAtomicType?) as xs:IDREF? external;
declare %a:since("xsd", "1.0") function xs:NCName($arg as xs:anyAtomicType?) as xs:NCName? external;
declare %a:since("xsd", "1.0") function xs:NMTOKEN($arg as xs:anyAtomicType?) as xs:NMTOKEN? external;
declare %a:since("xsd", "1.0") function xs:Name($arg as xs:anyAtomicType?) as xs:Name? external;
(: NOTE: $arg is required in XQuery 1.0 :)
declare %a:since("xsd", "1.0") function xs:QName($arg as xs:anyAtomicType?) as xs:QName? external;

declare %a:since("xsd", "1.0") function xs:anyURI($arg as xs:anyAtomicType?) as xs:anyURI? external;
declare %a:since("xsd", "1.0") function xs:base64Binary($arg as xs:anyAtomicType?) as xs:base64Binary? external;
declare %a:since("xsd", "1.0") function xs:boolean($arg as xs:anyAtomicType?) as xs:boolean? external;
declare %a:since("xsd", "1.0") function xs:byte($arg as xs:anyAtomicType?) as xs:byte? external;
declare %a:since("xsd", "1.0") function xs:date($arg as xs:anyAtomicType?) as xs:date? external;
declare %a:since("xsd", "1.0") function xs:dateTime($arg as xs:anyAtomicType?) as xs:dateTime? external;
declare %a:since("xsd", "1.0") function xs:dayTimeDuration($arg as xs:anyAtomicType?) as xs:dayTimeDuration? external;
declare %a:since("xsd", "1.0") function xs:decimal($arg as xs:anyAtomicType?) as xs:decimal? external;
declare %a:since("xsd", "1.0") function xs:double($arg as xs:anyAtomicType?) as xs:double? external;
declare %a:since("xsd", "1.0") function xs:duration($arg as xs:anyAtomicType?) as xs:duration? external;
declare %a:since("xsd", "1.0") function xs:float($arg as xs:anyAtomicType?) as xs:float? external;
declare %a:since("xsd", "1.0") function xs:gDay($arg as xs:anyAtomicType?) as xs:gDay? external;
declare %a:since("xsd", "1.0") function xs:gMonth($arg as xs:anyAtomicType?) as xs:gMonth? external;
declare %a:since("xsd", "1.0") function xs:gMonthDay($arg as xs:anyAtomicType?) as xs:gMonthDay? external;
declare %a:since("xsd", "1.0") function xs:gYear($arg as xs:anyAtomicType?) as xs:gYear? external;
declare %a:since("xsd", "1.0") function xs:gYearMonth($arg as xs:anyAtomicType?) as xs:gYearMonth? external;
declare %a:since("xsd", "1.0") function xs:hexBinary($arg as xs:anyAtomicType?) as xs:hexBinary? external;
declare %a:since("xsd", "1.0") function xs:int($arg as xs:anyAtomicType?) as xs:int? external;
declare %a:since("xsd", "1.0") function xs:integer($arg as xs:anyAtomicType?) as xs:integer? external;
declare %a:since("xsd", "1.0") function xs:language($arg as xs:anyAtomicType?) as xs:language? external;
declare %a:since("xsd", "1.0") function xs:long($arg as xs:anyAtomicType?) as xs:long? external;
declare %a:since("xsd", "1.0") function xs:negativeInteger($arg as xs:anyAtomicType?) as xs:negativeInteger? external;
declare %a:since("xsd", "1.0") function xs:nonNegativeInteger($arg as xs:anyAtomicType?) as xs:nonNegativeInteger? external;
declare %a:since("xsd", "1.0") function xs:nonPositiveInteger($arg as xs:anyAtomicType?) as xs:nonPositiveInteger? external;
declare %a:since("xsd", "1.0") function xs:normalizedString($arg as xs:anyAtomicType?) as xs:normalizedString? external;
declare %a:since("xsd", "1.0") function xs:positiveInteger($arg as xs:anyAtomicType?) as xs:positiveInteger? external;
declare %a:since("xsd", "1.0") function xs:short($arg as xs:anyAtomicType?) as xs:short? external;
declare %a:since("xsd", "1.0") function xs:string($arg as xs:anyAtomicType?) as xs:string? external;
declare %a:since("xsd", "1.0") function xs:time($arg as xs:anyAtomicType?) as xs:time? external;
declare %a:since("xsd", "1.0") function xs:token($arg as xs:anyAtomicType?) as xs:token? external;
declare %a:since("xsd", "1.0") function xs:unsignedByte($arg as xs:anyAtomicType?) as xs:unsignedByte? external;
declare %a:since("xsd", "1.0") function xs:unsignedInt($arg as xs:anyAtomicType?) as xs:unsignedInt? external;
declare %a:since("xsd", "1.0") function xs:unsignedLong($arg as xs:anyAtomicType?) as xs:unsignedLong? external;
declare %a:since("xsd", "1.0") function xs:unsignedShort($arg as xs:anyAtomicType?) as xs:unsignedShort? external;
declare %a:since("xsd", "1.0") function xs:untypedAtomic($arg as xs:anyAtomicType?) as xs:untypedAtomic? external;
declare %a:since("xsd", "1.0") function xs:yearMonthDuration($arg as xs:anyAtomicType?) as xs:yearMonthDuration? external;
declare %a:since("xsd", "1.1") function xs:dateTimeStamp($arg as xs:anyAtomicType?) as xs:dateTimeStamp? external;

xquery version "3.1";

(:~
 : XPath and XQuery Functions and Operators: Constructor Functions
 :
 : @see https://www.w3.org/TR/2007/REC-xpath-functions-20070123/
 : @see https://www.w3.org/TR/2010/REC-xpath-functions-20101214/
 : @see https://www.w3.org/TR/2014/REC-xpath-functions-30-20140408/
 : @see https://www.w3.org/TR/2016/CR-xpath-functions-31-20170321/
 :)
module  namespace xs = "http://www.w3.org/2001/XMLSchema";
declare namespace a  = "http://reecedunn.co.uk/xquery/annotations";

(: XSD 1.0 => XQuery 1.0
 : XSD 1.1 => XQuery 3.0+
 :)

declare %a:since("XSD", "1.0") function xs:ENTITY($arg as xs:anyAtomicType?) as xs:ENTITY? external;
declare %a:since("XSD", "1.0") function xs:ID($arg as xs:anyAtomicType?) as xs:ID? external;
declare %a:since("XSD", "1.0") function xs:IDREF($arg as xs:anyAtomicType?) as xs:IDREF? external;
declare %a:since("XSD", "1.0") function xs:NCName($arg as xs:anyAtomicType?) as xs:NCName? external;
declare %a:since("XSD", "1.0") function xs:NMTOKEN($arg as xs:anyAtomicType?) as xs:NMTOKEN? external;
declare %a:since("XSD", "1.0") function xs:Name($arg as xs:anyAtomicType?) as xs:Name? external;
(: NOTE: $arg is required in XQuery 1.0 :)
declare %a:since("XSD", "1.0") function xs:QName($arg as xs:anyAtomicType?) as xs:QName? external;

declare %a:since("XSD", "1.0") function xs:anyURI($arg as xs:anyAtomicType?) as xs:anyURI? external;
declare %a:since("XSD", "1.0") function xs:base64Binary($arg as xs:anyAtomicType?) as xs:base64Binary? external;
declare %a:since("XSD", "1.0") function xs:boolean($arg as xs:anyAtomicType?) as xs:boolean? external;
declare %a:since("XSD", "1.0") function xs:byte($arg as xs:anyAtomicType?) as xs:byte? external;
declare %a:since("XSD", "1.0") function xs:date($arg as xs:anyAtomicType?) as xs:date? external;
declare %a:since("XSD", "1.0") function xs:dateTime($arg as xs:anyAtomicType?) as xs:dateTime? external;
declare %a:since("XSD", "1.0") function xs:dayTimeDuration($arg as xs:anyAtomicType?) as xs:dayTimeDuration? external;
declare %a:since("XSD", "1.0") function xs:decimal($arg as xs:anyAtomicType?) as xs:decimal? external;
declare %a:since("XSD", "1.0") function xs:double($arg as xs:anyAtomicType?) as xs:double? external;
declare %a:since("XSD", "1.0") function xs:duration($arg as xs:anyAtomicType?) as xs:duration? external;
declare %a:since("XSD", "1.0") function xs:float($arg as xs:anyAtomicType?) as xs:float? external;
declare %a:since("XSD", "1.0") function xs:gDay($arg as xs:anyAtomicType?) as xs:gDay? external;
declare %a:since("XSD", "1.0") function xs:gMonth($arg as xs:anyAtomicType?) as xs:gMonth? external;
declare %a:since("XSD", "1.0") function xs:gMonthDay($arg as xs:anyAtomicType?) as xs:gMonthDay? external;
declare %a:since("XSD", "1.0") function xs:gYear($arg as xs:anyAtomicType?) as xs:gYear? external;
declare %a:since("XSD", "1.0") function xs:gYearMonth($arg as xs:anyAtomicType?) as xs:gYearMonth? external;
declare %a:since("XSD", "1.0") function xs:hexBinary($arg as xs:anyAtomicType?) as xs:hexBinary? external;
declare %a:since("XSD", "1.0") function xs:int($arg as xs:anyAtomicType?) as xs:int? external;
declare %a:since("XSD", "1.0") function xs:integer($arg as xs:anyAtomicType?) as xs:integer? external;
declare %a:since("XSD", "1.0") function xs:language($arg as xs:anyAtomicType?) as xs:language? external;
declare %a:since("XSD", "1.0") function xs:long($arg as xs:anyAtomicType?) as xs:long? external;
declare %a:since("XSD", "1.0") function xs:negativeInteger($arg as xs:anyAtomicType?) as xs:negativeInteger? external;
declare %a:since("XSD", "1.0") function xs:nonNegativeInteger($arg as xs:anyAtomicType?) as xs:nonNegativeInteger? external;
declare %a:since("XSD", "1.0") function xs:nonPositiveInteger($arg as xs:anyAtomicType?) as xs:nonPositiveInteger? external;
declare %a:since("XSD", "1.0") function xs:normalizedString($arg as xs:anyAtomicType?) as xs:normalizedString? external;
declare %a:since("XSD", "1.0") function xs:positiveInteger($arg as xs:anyAtomicType?) as xs:positiveInteger? external;
declare %a:since("XSD", "1.0") function xs:short($arg as xs:anyAtomicType?) as xs:short? external;
declare %a:since("XSD", "1.0") function xs:string($arg as xs:anyAtomicType?) as xs:string? external;
declare %a:since("XSD", "1.0") function xs:time($arg as xs:anyAtomicType?) as xs:time? external;
declare %a:since("XSD", "1.0") function xs:token($arg as xs:anyAtomicType?) as xs:token? external;
declare %a:since("XSD", "1.0") function xs:unsignedByte($arg as xs:anyAtomicType?) as xs:unsignedByte? external;
declare %a:since("XSD", "1.0") function xs:unsignedInt($arg as xs:anyAtomicType?) as xs:unsignedInt? external;
declare %a:since("XSD", "1.0") function xs:unsignedLong($arg as xs:anyAtomicType?) as xs:unsignedLong? external;
declare %a:since("XSD", "1.0") function xs:unsignedShort($arg as xs:anyAtomicType?) as xs:unsignedShort? external;
declare %a:since("XSD", "1.0") function xs:untypedAtomic($arg as xs:anyAtomicType?) as xs:untypedAtomic? external;
declare %a:since("XSD", "1.0") function xs:yearMonthDuration($arg as xs:anyAtomicType?) as xs:yearMonthDuration? external;
declare %a:since("XSD", "1.1") function xs:dateTimeStamp($arg as xs:anyAtomicType?) as xs:dateTimeStamp? external;


Update fn.xql

Update fn.xql

(:
 : eXist-db Open Source Native XML Database
 : Copyright (C) 2001 The eXist-db Authors
 :
 : info@exist-db.org
 : http://www.exist-db.org
 :
 : This library is free software; you can redistribute it and/or
 : modify it under the terms of the GNU Lesser General Public
 : License as published by the Free Software Foundation; either
 : version 2.1 of the License, or (at your option) any later version.
 :
 : This library is distributed in the hope that it will be useful,
 : but WITHOUT ANY WARRANTY; without even the implied warranty of
 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 : Lesser General Public License for more details.
 :
 : You should have received a copy of the GNU Lesser General Public
 : License along with this library; if not, write to the Free Software
 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 :)
xquery version "3.1";

(:~
 : Test various F+O functions
 :)
module namespace fnt="http://exist-db.org/xquery/test/fn";

import module namespace test="http://exist-db.org/xquery/xqsuite" at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";

declare variable $fnt:composed := 'Åström';
declare variable $fnt:decomposed := 'Åström';

declare
    %test:setUp
function fnt:store() {
    let $col := xmldb:create-collection("/db", "fn-test")
    return
        (
            xmldb:store($col, "test.xml", <books><book/></books>),
            xmldb:store($col, "test.bin", "some binary", "application/octet-stream")
        )
};

declare
    %test:tearDown
function fnt:cleanup() {
    xmldb:remove("/db/fn-test")
};

declare
    %test:args("NFC")
    %test:assertEquals(6, 6)
    %test:args("NFD")
    %test:assertEquals(8, 8)
    %test:args("NFKD")
    %test:assertEquals(8, 8)
    %test:args("NFKC")
    %test:assertEquals(6, 6)
    %test:args("XYZ")
    %test:assertError
function fnt:normalize-unicode($normalization-form as xs:string) {
    count(string-to-codepoints(normalize-unicode($fnt:composed, $normalization-form))),
    count(string-to-codepoints(normalize-unicode($fnt:decomposed, $normalization-form)))
};

declare
    %test:args("test.xml")
    %test:assertEquals("true")
    %test:args("test.bin")
    %test:assertEquals("false")
    %test:args("no-such-file.xml")
    %test:assertEquals("false")
    %test:args("no-such-file.bin")
    %test:assertEquals("false")
function fnt:doc-available($filename as xs:string) {
    fn:doc-available("/db/fn-test/" || $filename)
};

(:
  Intentionally marked as pending as it has
  an external dependency
:)
declare
    %test:pending
    %test:args("https://www.kingjamesbibleonline.org/Genesis-Chapter-1_Original-1611-KJV/")
    %test:assertEquals("false")
function fnt:doc-available-remote($uri as xs:string) {
    fn:doc-available($uri)
};

declare
    %test:args("\adamretter")
    %test:assertError("FODC0005")
function fnt:doc-available-invalid-uri($uri as xs:string) {
    fn:doc-available($uri)
};

declare
    %test:args("test.xml")
    %test:assertEquals("true")
function fnt:doc-returns-document-node($filename as xs:string) {
    fn:doc("/db/fn-test/" || $filename) instance of document-node()
};

declare
    %test:args("test.xml")
    %test:assertEmpty
function fnt:doc-does-not-return-element-node($filename as xs:string) {
    fn:doc("/db/fn-test/" || $filename)/book
};

declare
    %test:assertEquals(0, 0)
function fnt:tokenize-empty() {
     count(() => tokenize("\s")),
     count(tokenize((), "\s"))
};

declare
    %test:args("hello adam")
    %test:assertEquals("hello", "adam")
    %test:args("hello   adam")
    %test:assertEquals("hello", "adam")
function fnt:tokenize-onearg($str as xs:string) {
     tokenize($str)
};

declare
    %test:assertError("err:XPDY0002")
function fnt:document-uri0_noarg() {
     document-uri()
};

declare
    %test:assertError("err:XPDY0002")
function fnt:document-uri0() {
     document-uri(.)
};

declare
    %test:assertEquals("/db/fn-test/test.xml")
function fnt:document-uri0_context() {
     root(collection('/db/fn-test')//book)/document-uri()
};

declare
    %test:assertError("err:XPDY0002")
function fnt:document-uri0_context_empty() {
     root(collection('/db/fn-test')//bookies)/document-uri()
};

declare
    %test:assertEquals("/db/fn-test/test.xml")
function fnt:document-uri1() {
     document-uri(root(collection('/db/fn-test')//book))
};

declare
    %test:assertEmpty
function fnt:document-uri1_empty() {
     document-uri(root(collection('/db/fn-test')//bookies))
};

declare
    %test:assertEquals("TRUE")
function fnt:fn-document-uri-32() {
     let $uri := "/db/fn-test/test.xml"
     return
     fn:upper-case(fn:string(fn:contains((fn:doc($uri))/document-uri(),$uri)))
};


[ignore] Apply cosmetic changes suggested by @duncdrum

(:~
 : @see https://github.com/eXist-db/exist/issues/1550
 :)
xquery version "3.1";

module namespace ct="http://exist-db.org/xquery/closures/test";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $ct:test := "123";

declare variable $ct:model := function($param) {
    let $func := function() {
        $param
    }
    return
        map {
        'test1': $func,
        'test2': $func(),
        'test3': function() {
            $param
        }
        }
};

declare %test:assertEquals('Hello World') function ct:test1-function-reference() as xs:string {
    try {
        $ct:model('Hello World')?test1()
    }
    catch * {$err:code || ' ' || $err:description}
};

declare %test:assertEquals('Hello World') function ct:test2-function-call() as xs:string {
    try {
        $ct:model('Hello World')?test2
    }
    catch * {$err:code || ' ' || $err:description}
};

declare %test:assertEquals('Hello World') function ct:test3-inline-function() as xs:string {
    try {
        $ct:model('Hello World')?test3()
    }
    catch * {$err:code || ' ' || $err:description}
};

(:~
 : See https://github.com/eXist-db/exist/issues/1550
 :)
xquery version "3.1";

module namespace ct="http://exist-db.org/xquery/closures/test";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $ct:test := "123";

declare variable $ct:model := function($param) {
    let $func := function() {
        $param
    }
    return
        map {
        'test1' : $func,
        'test2' : $func(),
        'test3' : function() {
            $param
        }
        }
};

declare %test:assertEquals('Hello World') function ct:test1-function-reference() as xs:string {
    try {
        $ct:model('Hello World')?test1()
    }
    catch * {$err:code || ' ' || $err:description}
};

declare %test:assertEquals('Hello World') function ct:test2-function-call() as xs:string {
    try {
        $ct:model('Hello World')?test2
    }
    catch * {$err:code || ' ' || $err:description}
};

declare %test:assertEquals('Hello World') function ct:test3-inline-function() as xs:string {
    try {
        $ct:model('Hello World')?test3()
    }
    catch * {$err:code || ' ' || $err:description}
};


Updated - cant figure out how to test this - can someone please test please please :-p

svn path=/trunk/eXist/; revision=6741

xquery version "1.0";
(: $Id$ :)

declare namespace sandbox="http://exist-db.org/xquery/sandbox";

import module namespace xdb="http://exist-db.org/xquery/xmldb";
import module namespace util="http://exist-db.org/xquery/util";

declare option exist:serialize "media-type=text/xml";

declare function sandbox:display-child-collections($collection as xs:string) as element()*
{
    for $child in xdb:get-child-collections($collection)
    let $path := concat($collection, '/', $child)
    order by $child
    return (
        <option value="{$path}">{$path}</option>,
        sandbox:display-child-collections($path)
    )
};

<select id="collection">
{ sandbox:display-child-collections("/db") }
</select>

xquery version "1.0";
(: $Id$ :)

declare namespace sandbox="http://exist-db.org/xquery/sandbox";

import module namespace xdb="http://exist-db.org/xquery/xmldb";
import module namespace util="http://exist-db.org/xquery/util";

declare option exist:serialize "media-type=text/xml";

declare function sandbox:display-child-collections($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-collections($collection)
    let $path := concat($parent, '/', $child)
    order by $child
    return (
        <option value="{$path}">{$path}</option>,
        sandbox:display-child-collections(xdb:collection($path, "guest", "guest"))
    )
};

<select id="collection">
{ sandbox:display-child-collections(xdb:collection("/db", "guest", "guest")) }
</select>


[ignore] Typo in assertEquals

[ignore] Typo in assertEquals

xquery version "3.1";

module namespace devar="http://exist-db.org/xquery/test/declared-variables";
declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $devar:static := 'I am static.';
declare variable $devar:dynamic := replace($devar:static, 'static', 'dynamic');
declare variable $devar:function := (function ($a) {
    replace($a, 'static', 'the return of a function expression')
})($devar:static);

declare variable $devar:arrow := $devar:static
=> replace('static', 'the return of an arrow expression')
;

declare
%test:assertEquals('I am static.')
function devar:read-static () {
    $devar:static
};

declare
%test:assertEquals('I am dynamic.')
function devar:read-dynamic () {
    $devar:dynamic
};

declare
%test:assertEquals('I am the return of a function expression.')
function devar:read-function () {
    $devar:function
};

declare
%test:assertEquals('I am the return of an arrow expression.')
function devar:read-arrow () {
    $devar:arrow
};

xquery version "3.1";

module namespace devar="http://exist-db.org/xquery/test/declared-variables";
declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $devar:static := 'I am static.';
declare variable $devar:dynamic := replace($devar:static, 'static', 'dynamic');
declare variable $devar:function := (function ($a) {
    replace($a, 'static', 'the return of a function expression')
})($devar:static);

declare variable $devar:arrow := $devar:static
=> replace('static', 'the return of an arrow expression')
;

declare
%test:assertEquals('I am static.')
function devar:read-static () {
    $devar:static
};

declare
%test:assertEquals('I am dynamic.')
function devar:read-dynamic () {
    $devar:dynamic
};

declare
%test:assertEquals('I am the return of a function expression.')
function devar:read-function () {
    $devar:function
};

declare
%test:assertEquals('I am the return of a arrow expression.')
function devar:read-arrow () {
    $devar:arrow
};


fix typo

fix typo

(:~
: This is the Care Services Discovery stored query registry
: @version 1.0
: @see https://github.com/openhie/openinfoman
:
:)
module namespace csd_mcs = "https://github.com/openhie/openinfoman/csd_mcs";
import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace csd_lsc = "https://github.com/openhie/openinfoman/csd_lsc" ;
import module namespace csd_psd = "https://github.com/openhie/openinfoman/csd_psd" ;
import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm" ;

declare namespace csd = "urn:ihe:iti:csd:2013";
declare default element  namespace   "urn:ihe:iti:csd:2013";

declare variable $csd_mcs:merged_services_doc := 'merged_services.xml';

declare function csd_mcs:store_exists() {
  db:is-xml($csd_webconf:db, $csd_mcs:merged_services_doc)
};

declare updating function csd_mcs:init_store() {
  db:add($csd_webconf:db, csd_dm:blank_directory(), $csd_mcs:merged_services_doc)
};

(:
: Merges together all documents registered with the document manager
:)
declare updating function csd_mcs:merge($dest,$sources) {

  (: Merges together all cached documents only
  for $name in csd_psd:registered_directories($db)
  return csd_lsc:refresh_doc(db:open($db,$csd_mcs:merged_services_doc),csd_lsc:get_cache($db,$name))
  :)
  let $dest_doc := csd_dm:open_document($dest)
  return
    if (exists($dest_doc))
    then
      for $name in $sources
      return if ($name != $dest) then
	csd_lsc:refresh_doc($dest_doc, csd_dm:open_document($name))
      else ()
    else ()

};

declare function csd_mcs:get() {
  db:open($csd_webconf:db,$csd_mcs:merged_services_doc)
};

declare updating function csd_mcs:empty() {
  db:replace($csd_webconf:db, $csd_mcs:merged_services_doc, csd_dm:blank_directory())
};

declare function csd_mcs:get_merge_doc_name() {
  let $name := 'merged_remote_services'
  return $name
};

(:~
: This is the Care Services Discovery stored query registry
: @version 1.0
: @see https://github.com/openhie/openinfoman
:
:)
module namespace csd_mcs = "https://github.com/openhie/openinfoman/csd_mcs";
import module namespace csd_webconf =  "https://github.com/openhie/openinfoman/csd_webconf";
import module namespace csd_lsc = "https://github.com/openhie/openinfoman/csd_lsc" ;
import module namespace csd_psd = "https://github.com/openhie/openinfoman/csd_psd" ;
import module namespace csd_dm = "https://github.com/openhie/openinfoman/csd_dm" ;

declare namespace csd = "urn:ihe:iti:csd:2013";
declare default element  namespace   "urn:ihe:iti:csd:2013";

declare variable $csd_mcs:merged_services_doc := 'merged_services.xml';

declare function csd_mcs:store_exists() {
  db:is-xml($csd_webconf:db, $csd_mcs:merged_services_doc)
};

declare updating function csd_mcs:init_store() {
  db:add($csd_webconf:db, csd_dm:blank_directory(), $csd_mcs:merged_services_doc)
};

(:
: Merges together all documents registered with the document manager
:)
declare updating function csd_mcs:merge($dest,$sources) {

  (: Merges together all cached documents only
  for $name in csd_psd:registered_directories($db)
  return csd_lsc:refresh_doc(db:open($db,$csd_mcs:merged_services_doc),csd_lsc:get_cache($db,$name))
  :)
  let $dest_doc := csd_dm:open_document($dest)
  return
    if (exists($dest_doc))
    then
      for $name in $sources
      return if ($name != $dest) then
	csd_lsc:refresh_doc($dest_doc, csd_dm:open_document($name))
      else ()
    else ()

};

declare function csd_mcs:get() {
  db:open($csd_webconf:db,$csd_mcs:merged_services_doc)
};

declare updating function csd_mcs:empty() {
  db:replace($csd_webcond:db, $csd_mcs:merged_services_doc, csd_dm:blank_directory())
};

declare function csd_mcs:get_merge_doc_name() {
  let $name := 'merged_remote_services'
  return $name
};