Facility for open init function

Facility for open init function

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

(:~
    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
    let $hasSource := root($config)//jquery:source
    return (
        ,
        ,
        ,
        <link rel="stylesheet" type="text/css" href="{$cssbase}/smoothness/jquery.ui.all.css"/>,
        if ($hasSource) then jquery:syntax-highlighting-libs($base, $cssbase) else ()
    )
};

declare function jquery:syntax-highlighting-libs($base as xs:string, $cssbase as xs:string) {
    ,
    ,
    ,
    ,
    ,
    ,
    <link type="text/css" rel="stylesheet" href="{$base}/../../styles/syntax/shCore.css" />,
    <link type="text/css" rel="Stylesheet" href="{$base}/../../styles/syntax/shThemeDefault.css" id="theme" />,
    <jquery:script>
        SyntaxHighlighter.config.stripBrs = true;
        SyntaxHighlighter.defaults[ 'auto-links'] = false;
        SyntaxHighlighter.defaults[ 'wrap-lines'] = true;
        SyntaxHighlighter.all();
    </jquery:script>
};

(:~
    Generates a jQuery UI tabset from the given configuration element.

    <jquery:tabset>
        <jquery:tab id="simple" label="Simple Search">
            Tab content 1
        </jquery:tab>
        <jquery:tab id="complex" label="Advanced Search">
            Tab content
        </jquery:tab>
    </jquery:tabset>

:)
declare function jquery:tabset($config as element(jquery:tabset)) as element() {
    let $id := if ($config/@id) then $config/@id/string() else util:uuid()
    let $selected := request:get-parameter($id, ())
    let $selectedId :=
        if ($selected) then $config/jquery:tab[@id = $selected]/@id/string() else ()
    return

            {
                for $tab in $config/jquery:tab
                return
                    if ($tab/@href) then

{$tab/@label/string()}

                    else

{$tab/@label/string()}

            }

            {
                for $tab in $config/jquery:tab[@id]
                return

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

            }
            {
                let $options :=
                    if ($config/@on-select) then
                        concat("var options = { select: ", $config/@on-select, " };")
                    else
                        "var options = null;"
                return
                    <jquery:script>
                        { $options }
                        var tabs = $("#{$id}").tabs(options);
                        {
                            if ($selectedId) then
                                concat("tabs.tabs('select', '", $selectedId, "');")
                            else
                                ()
                        }
                    </jquery:script>
            }

};

(:~
    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
        <jquery:script>
            var options = {{
                deleteTrigger: '{$delete}',
                onReady: { if ($onReady) then $onReady else 'new function() {}' }
            }};
            $('{$formSelector}').repeat('{$trigger}', options);
        </jquery:script>
};

(:~
    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:script>
            $('#{$id}').css('display', 'none');
            $('{$trigger}').click(function(ev) {{
                if ($(this).hasClass('expanded')) {{
                    $('#{$id}').css('display', 'none');
                }} else {{
                    var trigger = this;
                    {
                        if ($beforeCallback) then
                            jquery:script(<s>
                                var r = {$beforeCallback}.call(trigger, '{$id}');
                                if (!r) return false;
                            </s>)
                        else
                            ()
                    }
                    $('#{$id}').css('display', 'block');
                    {
                        if ($href) then
                            jquery:script(<s>$('#{$id}').load('{$href}', null, function() {{
                                {
                                    if ($callback) then
                                        concat($callback, ".call(trigger, '", $id, "');")
                                    else
                                        ()
                                }
                            }});</s>)
                        else
                            ()
                    }
                }}
                $(this).toggleClass('expanded');
                ev.preventDefault();
            }});
        </jquery:script>
    )
};

(:~
    <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/string() else util:uuid()
    let $onReady := $config/@on-ready/string()
    return (

,
        <jquery:script>
            $('#{$id}').pagination('{$config/@href/string()}', {{
                    totalItems: {count($hits)},
                    itemsPerPage: 10,
                    navContainer: '{$config/@navcontainer/string()}'
                    { if ($onReady) then (", readyCallback: ", $onReady) else () }
            }});
        </jquery:script>
    )
};

(:~
    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
                <jquery:script>
                        $('#{$id}').autocomplete({{
                            source: function(request, response) {{
                                var data = {{ term: request.term }};
                                {
                                    for $cb in $auto/@paramsCallback
                                    return
                                        concat($cb, '($("#', $id, '"), data);')
                                }
                                $.ajax({{
                                    url: "{$auto/@url/string()}",
                                    dataType: "json",
                   					data: data,
                                    success: function(data) {{
                                        response(data);
                                    }}
                                }});
                            }},
                            minLength: { $minLength },
                            delay: 700
                        }});
                </jquery:script>
        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>,
        <jquery:script>
        $('#{$id}').button();
        </jquery:script>
    )
};

declare function jquery:accordion($config as element(jquery:ajax-accordion)) as element()+ {
    let $id := if ($config/@id) then $config/@id/string() else concat('N', util:uuid())
    let $panels :=
        for $panel in $config/jquery:panel
        let $panelId := if ($panel/@id) then $panel/@id/string() else concat('N', util:uuid())
        return

{$panel/@title/string()}

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

Loading ...

                }

    return (

        {   $config/@class, $panels/* }

,
        <jquery:script>
            var actions = {{}};
            {
                for $panel at $p in $config/jquery:panel
                return
                    if ($panel/@href) then
                        jquery:script(<s>actions["{$panels[$p]/h3/@id/string()}"] = "{$panel/@href/string()}";</s>)
                    else if ($panel/@on-select) then
                        jquery:script(<s>actions["{$panels[$p]/h3/@id/string()}"] = {$panel/@on-select/string()};</s>)
                    else ()
            }
            $('#{$id}').accordion({{
                autoHeight: { if ($config/@autoHeight eq "true") then "true" else "false" },
                fillSpace: { if ($config/@fillSpace eq "true") then "true" else "false" },
                collapsible: true,
                active: { if ($config/@active eq "true") then "true" else "false" },
                change: function (event, ui) {{
                    var id = ui.newHeader.attr('id');
                    var panel = ui.newHeader.next();
                    var action = actions[id];
                    if (typeof action == "function")
                        action.call();
                    else
                        panel.load(action);
                }}
            }});
        </jquery:script>
    )
};

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() { $(this).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, '($(this)); }'), "")
                else
                    string-join(('"', $button/@label, '": function() {', $button/node(), '}'), "")
            , ", ")
    return

            { $id, for $child in $config/*[not(self::jquery:*)] return jquery:process-templates($child) }
            <jquery:script>
                    $('#{$id/string()}').dialog({{
                        modal: { if ($modal) then 'true' else 'false' },
                        autoOpen: false,
                        {
                            if($config/@onOpen)then(
                                fn:concat("open: function(event, ui){ ",  string($config/@onOpen), "(); },")
                            )else()
                        }
                        buttons: {{ { $buttons } }}
                        {
                            let $attribs :=
                                for $attr in $config/@*
                                return
                                    if ($attr/local-name() = ("height", "minHeight", "maxHeight",
                                        "minWidth", "maxWidth", "width")) then
                                        concat($attr/local-name(), ": ", $attr/string())
                                    else if ($attr/local-name() = ("title", "position",
                                        "dialogClass", "closeText")) then
                                        concat($attr/local-name(), ': "', $attr/string(), '"')
                                    else
                                        ()
                            return
                                if (exists($attribs)) then
                                    concat(", ", string-join($attribs, ", "))
                                else
                                    ()
                        }
                    }});
            </jquery:script>
            {
                if($trigger)then
                (
                    <jquery:script>
                        var trigger = '{$trigger/string()}';
                        if (trigger != '')
                            $(trigger).click(function() {{
                                $('#{$id/string()}').dialog('open');
                                return false;
                            }});
                    </jquery:script>
                )else()
            }

};

declare function jquery:escape-xml($node as node()) as xs:string* {
    typeswitch ($node)
        case element() return
            string-join(
                (
                    "<", node-name($node),
                    for $attr in $node/@* return concat(" ", node-name($attr), '="', $attr/string(), '"'),
                    if ($node/node()) then
                        (">", for $child in $node/node() return jquery:escape-xml($child), "</", node-name($node), ">")
                    else
                        "/>"
                ),
                ""
            )
        default return
            $node
};

declare function jquery:source($node as node()) as node()* {
    let $id := $node/@ref
    let $code := root($node)//*[@id = $id][1]
    return

            { jquery:escape-xml($code) }

};

(:~
    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(jquery:source) return
            jquery:source($node)
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node()
                return
                    jquery:process-templates($child)
            }
        default return
            $node
};

declare function jquery:process($node as node()) as node()* {
     jquery:cleanup-javascript(
        jquery:process-templates($node)
    )
};

declare function jquery:cleanup-javascript($node as node(), $scripts as element()*) {
    typeswitch ($node)
        case element(body) return
            {
                $node/@*,
                for $child in $node/node() return jquery:cleanup-javascript($child, $scripts),

            }
        case element(jquery:script) return
            ()
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node() return jquery:cleanup-javascript($child, $scripts)
            }
        default return
            $node
};

declare function jquery:cleanup-javascript($root as element()) {
    let $scripts := $root//jquery:script
    return
        jquery:cleanup-javascript($root, $scripts)
};

(:~
    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
    let $hasSource := root($config)//jquery:source
    return (
        ,
        ,
        ,
        <link rel="stylesheet" type="text/css" href="{$cssbase}/smoothness/jquery.ui.all.css"/>,
        if ($hasSource) then jquery:syntax-highlighting-libs($base, $cssbase) else ()
    )
};

declare function jquery:syntax-highlighting-libs($base as xs:string, $cssbase as xs:string) {
    ,
    ,
    ,
    ,
    ,
    ,
    <link type="text/css" rel="stylesheet" href="{$base}/../../styles/syntax/shCore.css" />,
    <link type="text/css" rel="Stylesheet" href="{$base}/../../styles/syntax/shThemeDefault.css" id="theme" />,
    <jquery:script>
        SyntaxHighlighter.config.stripBrs = true;
        SyntaxHighlighter.defaults[ 'auto-links'] = false;
        SyntaxHighlighter.defaults[ 'wrap-lines'] = true;
        SyntaxHighlighter.all();
    </jquery:script>
};

(:~
    Generates a jQuery UI tabset from the given configuration element.

    <jquery:tabset>
        <jquery:tab id="simple" label="Simple Search">
            Tab content 1
        </jquery:tab>
        <jquery:tab id="complex" label="Advanced Search">
            Tab content
        </jquery:tab>
    </jquery:tabset>

:)
declare function jquery:tabset($config as element(jquery:tabset)) as element() {
    let $id := if ($config/@id) then $config/@id/string() else util:uuid()
    let $selected := request:get-parameter($id, ())
    let $selectedId :=
        if ($selected) then $config/jquery:tab[@id = $selected]/@id/string() else ()
    return

            {
                for $tab in $config/jquery:tab
                return
                    if ($tab/@href) then

{$tab/@label/string()}

                    else

{$tab/@label/string()}

            }

            {
                for $tab in $config/jquery:tab[@id]
                return

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

            }
            {
                let $options :=
                    if ($config/@on-select) then
                        concat("var options = { select: ", $config/@on-select, " };")
                    else
                        "var options = null;"
                return
                    <jquery:script>
                        { $options }
                        var tabs = $("#{$id}").tabs(options);
                        {
                            if ($selectedId) then
                                concat("tabs.tabs('select', '", $selectedId, "');")
                            else
                                ()
                        }
                    </jquery:script>
            }

};

(:~
    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
        <jquery:script>
            var options = {{
                deleteTrigger: '{$delete}',
                onReady: { if ($onReady) then $onReady else 'new function() {}' }
            }};
            $('{$formSelector}').repeat('{$trigger}', options);
        </jquery:script>
};

(:~
    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:script>
            $('#{$id}').css('display', 'none');
            $('{$trigger}').click(function(ev) {{
                if ($(this).hasClass('expanded')) {{
                    $('#{$id}').css('display', 'none');
                }} else {{
                    var trigger = this;
                    {
                        if ($beforeCallback) then
                            jquery:script(<s>
                                var r = {$beforeCallback}.call(trigger, '{$id}');
                                if (!r) return false;
                            </s>)
                        else
                            ()
                    }
                    $('#{$id}').css('display', 'block');
                    {
                        if ($href) then
                            jquery:script(<s>$('#{$id}').load('{$href}', null, function() {{
                                {
                                    if ($callback) then
                                        concat($callback, ".call(trigger, '", $id, "');")
                                    else
                                        ()
                                }
                            }});</s>)
                        else
                            ()
                    }
                }}
                $(this).toggleClass('expanded');
                ev.preventDefault();
            }});
        </jquery:script>
    )
};

(:~
    <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/string() else util:uuid()
    let $onReady := $config/@on-ready/string()
    return (

,
        <jquery:script>
            $('#{$id}').pagination('{$config/@href/string()}', {{
                    totalItems: {count($hits)},
                    itemsPerPage: 10,
                    navContainer: '{$config/@navcontainer/string()}'
                    { if ($onReady) then (", readyCallback: ", $onReady) else () }
            }});
        </jquery:script>
    )
};

(:~
    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
                <jquery:script>
                        $('#{$id}').autocomplete({{
                            source: function(request, response) {{
                                var data = {{ term: request.term }};
                                {
                                    for $cb in $auto/@paramsCallback
                                    return
                                        concat($cb, '($("#', $id, '"), data);')
                                }
                                $.ajax({{
                                    url: "{$auto/@url/string()}",
                                    dataType: "json",
                   					data: data,
                                    success: function(data) {{
                                        response(data);
                                    }}
                                }});
                            }},
                            minLength: { $minLength },
                            delay: 700
                        }});
                </jquery:script>
        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>,
        <jquery:script>
        $('#{$id}').button();
        </jquery:script>
    )
};

declare function jquery:accordion($config as element(jquery:ajax-accordion)) as element()+ {
    let $id := if ($config/@id) then $config/@id/string() else concat('N', util:uuid())
    let $panels :=
        for $panel in $config/jquery:panel
        let $panelId := if ($panel/@id) then $panel/@id/string() else concat('N', util:uuid())
        return

{$panel/@title/string()}

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

Loading ...

                }

    return (

        {   $config/@class, $panels/* }

,
        <jquery:script>
            var actions = {{}};
            {
                for $panel at $p in $config/jquery:panel
                return
                    if ($panel/@href) then
                        jquery:script(<s>actions["{$panels[$p]/h3/@id/string()}"] = "{$panel/@href/string()}";</s>)
                    else if ($panel/@on-select) then
                        jquery:script(<s>actions["{$panels[$p]/h3/@id/string()}"] = {$panel/@on-select/string()};</s>)
                    else ()
            }
            $('#{$id}').accordion({{
                autoHeight: { if ($config/@autoHeight eq "true") then "true" else "false" },
                fillSpace: { if ($config/@fillSpace eq "true") then "true" else "false" },
                collapsible: true,
                active: { if ($config/@active eq "true") then "true" else "false" },
                change: function (event, ui) {{
                    var id = ui.newHeader.attr('id');
                    var panel = ui.newHeader.next();
                    var action = actions[id];
                    if (typeof action == "function")
                        action.call();
                    else
                        panel.load(action);
                }}
            }});
        </jquery:script>
    )
};

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() { $(this).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, '($(this)); }'), "")
                else
                    string-join(('"', $button/@label, '": function() {', $button/node(), '}'), "")
            , ", ")
    return

            { $id, for $child in $config/*[not(self::jquery:*)] return jquery:process-templates($child) }
            <jquery:script>
                    $('#{$id/string()}').dialog({{
                        modal: { if ($modal) then 'true' else 'false' },
                        autoOpen: false,
                        buttons: {{ { $buttons } }}
                        {
                            let $attribs :=
                                for $attr in $config/@*
                                return
                                    if ($attr/local-name() = ("height", "minHeight", "maxHeight",
                                        "minWidth", "maxWidth", "width")) then
                                        concat($attr/local-name(), ": ", $attr/string())
                                    else if ($attr/local-name() = ("title", "position",
                                        "dialogClass", "closeText")) then
                                        concat($attr/local-name(), ': "', $attr/string(), '"')
                                    else
                                        ()
                            return
                                if (exists($attribs)) then
                                    concat(", ", string-join($attribs, ", "))
                                else
                                    ()
                        }
                    }});
            </jquery:script>
            {
                if($trigger)then
                (
                    <jquery:script>
                        var trigger = '{$trigger/string()}';
                        if (trigger != '')
                            $(trigger).click(function() {{
                                $('#{$id/string()}').dialog('open');
                                return false;
                            }});
                    </jquery:script>
                )else()
            }

};

declare function jquery:escape-xml($node as node()) as xs:string* {
    typeswitch ($node)
        case element() return
            string-join(
                (
                    "<", node-name($node),
                    for $attr in $node/@* return concat(" ", node-name($attr), '="', $attr/string(), '"'),
                    if ($node/node()) then
                        (">", for $child in $node/node() return jquery:escape-xml($child), "</", node-name($node), ">")
                    else
                        "/>"
                ),
                ""
            )
        default return
            $node
};

declare function jquery:source($node as node()) as node()* {
    let $id := $node/@ref
    let $code := root($node)//*[@id = $id][1]
    return

            { jquery:escape-xml($code) }

};

(:~
    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(jquery:source) return
            jquery:source($node)
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node()
                return
                    jquery:process-templates($child)
            }
        default return
            $node
};

declare function jquery:process($node as node()) as node()* {
     jquery:cleanup-javascript(
        jquery:process-templates($node)
    )
};

declare function jquery:cleanup-javascript($node as node(), $scripts as element()*) {
    typeswitch ($node)
        case element(body) return
            {
                $node/@*,
                for $child in $node/node() return jquery:cleanup-javascript($child, $scripts),

            }
        case element(jquery:script) return
            ()
        case element() return
            element { node-name($node) } {
                $node/@*,
                for $child in $node/node() return jquery:cleanup-javascript($child, $scripts)
            }
        default return
            $node
};

declare function jquery:cleanup-javascript($root as element()) {
    let $scripts := $root//jquery:script
    return
        jquery:cleanup-javascript($root, $scripts)
};


Added info about the change.

Added info about the change.

(:
 : Copyright 2006-2011 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
 :)

(:~
 : Zorba FOTS driver CLI
 : @author Ghislain Fourny, Sorin Nasoi
 :)

import module namespace d =
  "http://www.zorba-xquery.com/fots-driver" at "fots-driver.xq";
import module namespace r =
  "http://www.zorba-xquery.com/fots-driver/reporting" at "reporting.xq";

(:~ path to the place where FOTS can be found :)
declare variable $fotsPath as xs:string external := "";

(:~ path to the place where the FOTS Zorba manifest can be found :)
declare variable $fotsZorbaManifestPath as xs:string external :=
  "FOTSZorbaManifest.xml";

(:~ Path to the results from a previous run :)
declare variable $failuresFilePath as xs:string external := "";

(:~ choose the CLI option you want to run :)
declare variable $mode as xs:string external := "";

(:~ name/criteria for the test sets :)
declare variable $testSetPrefixes as xs:string external := "";

(:~ name/criteria for the test cases :)
declare variable $testCasePrefixes as xs:string external := "";

(:~ name for the test set :)
declare variable $testSetName as xs:string external := "";

(:~ name for the test case :)
declare variable $testCaseName as xs:string external := "";

(:~ Enable or disable verbose output. When this is set to true the exact query
    that is run by XQXQ is also written down into a query_*.xq file :)
declare variable $verbose as xs:string external := "false";

(:~ assertion type :)
declare variable $assertType as xs:string external := "";

(:~ dependency: used in 'list-test-cases' and 'run-test-sets' modes:)
declare variable $dependency as xs:string external := "";

(:~ regex for the tests in the 'list-matching-test-cases' option :)
declare variable $pattern as xs:string external := "";

(:~ flags for the tests in the 'list-matching-test-cases' option :)
declare variable $flags as xs:string external := "";

(:~ Path to the ExpectedFailures file :)
declare variable $expectedFailuresPath  as xs:string external :="";

(:~ Tokenize comma or empty string :)
declare %private function local:tokenize(
  $input as xs:string
) as xs:string*
{
  let $tokens := tokenize($input, ",")
  return if (exists($tokens)) then $tokens else ""
};

declare function local:usage() as xs:string
{
  string-join((
    "Zorba FOTS driver usage examples:",
    "If you run Zorba from a checkout of the trunk and the build/bin folder,",
    "- /path/to/cli.xq can be set to ../../test/fots_driver/cli.xq",
    "",
    "Always try to output the result back to an XML file with nice indentation:",
    "./zorba -f -q ../../test/fots_driver/cli.xq -e SET_CLI_OPTIONS_HERE -o output.xml --indent",
    "",
    " This way you will see trace information in the CLI window and detailed",
    " results of the test cases in the 'output.xml'.",
    "",
    " Please note that when Verbose is set to true the query that is executed",
    " is written down into a query_TESTCASENAME.xq file, where TESTCASENAME is",
    " the test case name.",
    "",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e fotsZorbaManifestPath:=/path/to/Zorba_manifest.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets -e testSetPrefixes:=prod,app",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e testSetPrefixes:=prod-Literal",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions -e assertType:=assert-count",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-matching-test-cases -e pattern:=catch",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e expectedFailuresPath:=ExpectedFailures.xml -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e dependency:=higherOrderFunctions_false -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e assertType:=assert-count -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod-Literal -e verbose:=false -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-case -e testSetName:=prod-Literal -e testCaseName:=Literals001 -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-and-report -o report.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=report -e failuresFilePath:=failures.xml -e verbose:=false -o report.xml --indent",
    "zorba -f -q /path/to/cli.xq -e mode:=generate-expected-failures -e failuresFilePath:=failures.xml -o ExpectedFailures.xml --indent",
    ""
    ), "
")
};

(:~ The test cases in this list have bugs assigned and should not be run :)
variable $exceptedTestCases := (
"cbcl-subsequence-011", "cbcl-subsequence-012", "cbcl-subsequence-013",
"cbcl-subsequence-014"                                  (:see bug lp:1069794 :)
, "re00975", "re00976", "re00976a"                      (:see bug lp:1070533 :)
, "fn-unparsed-text-lines-052"                          (:see bug lp:1073175 :)
);

(:~  The test in this list have bugs assigned already and should not be run :)
variable $exceptedTestSets := ();

switch ($mode)
case "list-test-sets"
  return
    string-join(
      (d:list-test-sets(
        trace($fotsPath, "Path to FOTS catalog.xml set to: "),
        local:tokenize(trace($testSetPrefixes, "'testSetPrefixes' set to: ")))),
       "
")
case "list-test-cases"
  return
    string-join(
      (d:list-test-cases( trace($fotsPath,
                                "Path to FOTS catalog.xml set to: "),
                          local:tokenize(trace($testSetPrefixes,
                                          "'testSetPrefixes' set to: ")),
                          "",
                          trace($dependency,
                                "'dependency' set to:"),
                          trace($assertType,
                                "'assertType' set to: "))),
               "
")
case "list-matching-test-cases"
  return string-join((d:list-matching-test-cases(
                            trace($fotsPath,
                                  "Path to FOTS catalog.xml set to: "),
                            trace($pattern,
                                  "pattern set to: "),
                            trace($flags,
                                  "flags were set to: ")), ""),
                     "
")
case "run-test-sets"
  return d:run-fots($fotsPath,
                    $fotsZorbaManifestPath,
                    d:list-test-sets($fotsPath,
                                     local:tokenize(trace($testSetPrefixes,
                                       "'testSetPrefixes' set to: "))),
                    d:list-test-cases($fotsPath,
                                      local:tokenize($testSetPrefixes),
                                      local:tokenize(trace($testCasePrefixes,
                                        "'testCasePrefixes' set to: ")),
                                      trace($dependency,
                                        "'dependency' set to:"),
                                      trace($assertType,
                                         "'assertType' set to: ")),
                    $exceptedTestCases,
                    $exceptedTestSets,
                    xs:boolean($verbose),
                    $expectedFailuresPath)
case "run-test-case"
  return d:run-fots($fotsPath,
                    $fotsZorbaManifestPath,
                    trace($testSetName, "'testSetName' set to: "),
                    trace($testCaseName,"'testCaseName' set to: "),
                    $exceptedTestCases,
                    $exceptedTestSets,
                    xs:boolean($verbose),
                    $expectedFailuresPath)
case "run-and-report"
  return r:run-and-report($fotsPath,
                          $fotsZorbaManifestPath,
                          $exceptedTestCases,
                          $exceptedTestSets,
        (: the reports to W3C are always generated with verbose set to false:)
                          fn:false())
case "report"
  return r:report($fotsPath,
                  $failuresFilePath,
                  $exceptedTestCases,
                  $exceptedTestSets,
                  xs:boolean($verbose))
case "generate-expected-failures"
  return r:generate-expected-failures($failuresFilePath)
default
  return local:usage()

(:
 : Copyright 2006-2011 The FLWOR Foundation.
 :
 : Licensed under the Apache License, Version 2.0 (the "License");
 : you may not use this file except in compliance with the License.
 : You may obtain a copy of the License at
 :
 : http://www.apache.org/licenses/LICENSE-2.0
 :
 : Unless required by applicable law or agreed to in writing, software
 : distributed under the License is distributed on an "AS IS" BASIS,
 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 : See the License for the specific language governing permissions and
 : limitations under the License.
 :)

(:~
 : Zorba FOTS driver CLI
 : @author Ghislain Fourny, Sorin Nasoi
 :)

import module namespace d =
  "http://www.zorba-xquery.com/fots-driver" at "fots-driver.xq";
import module namespace r =
  "http://www.zorba-xquery.com/fots-driver/reporting" at "reporting.xq";

(:~ path to the place where FOTS can be found :)
declare variable $fotsPath as xs:string external := "";

(:~ path to the place where the FOTS Zorba manifest can be found :)
declare variable $fotsZorbaManifestPath as xs:string external :=
  "FOTSZorbaManifest.xml";

(:~ Path to the results from a previous run :)
declare variable $failuresFilePath as xs:string external := "";

(:~ choose the CLI option you want to run :)
declare variable $mode as xs:string external := "";

(:~ name/criteria for the test sets :)
declare variable $testSetPrefixes as xs:string external := "";

(:~ name/criteria for the test cases :)
declare variable $testCasePrefixes as xs:string external := "";

(:~ name for the test set :)
declare variable $testSetName as xs:string external := "";

(:~ name for the test case :)
declare variable $testCaseName as xs:string external := "";

(:~ Enable or disable verbose output :)
declare variable $verbose as xs:string external := "false";

(:~ assertion type :)
declare variable $assertType as xs:string external := "";

(:~ dependency: used in 'list-test-cases' and 'run-test-sets' modes:)
declare variable $dependency as xs:string external := "";

(:~ regex for the tests in the 'list-matching-test-cases' option :)
declare variable $pattern as xs:string external := "";

(:~ flags for the tests in the 'list-matching-test-cases' option :)
declare variable $flags as xs:string external := "";

(:~ Path to the ExpectedFailures file :)
declare variable $expectedFailuresPath  as xs:string external :="";

(:~ Tokenize comma or empty string :)
declare %private function local:tokenize(
  $input as xs:string
) as xs:string*
{
  let $tokens := tokenize($input, ",")
  return if (exists($tokens)) then $tokens else ""
};

declare function local:usage() as xs:string
{
  string-join((
    "Zorba FOTS driver usage examples:",
    "If you run Zorba from a checkout of the trunk and the build/bin folder,",
    "- /path/to/cli.xq can be set to ../../test/fots_driver/cli.xq",
    "",
    "Always try to output the result back to an XML file with nice indentation:",
    "./zorba -f -q ../../test/fots_driver/cli.xq -e SET_CLI_OPTIONS_HERE -o output.xml --indent",
    "",
    " This way you will see trace information in the CLI window and detailed",
    " results of the test cases in the 'output.xml'.",
    "",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e fotsZorbaManifestPath:=/path/to/Zorba_manifest.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-sets -e testSetPrefixes:=prod,app",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e testSetPrefixes:=prod-Literal",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-test-cases -e dependency:=higherOrderFunctions -e assertType:=assert-count",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=list-matching-test-cases -e pattern:=catch",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e expectedFailuresPath:=ExpectedFailures.xml -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e dependency:=higherOrderFunctions_false -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod -e assertType:=assert-count -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-sets -e testSetPrefixes:=prod-Literal -e verbose:=false -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-test-case -e testSetName:=prod-Literal -e testCaseName:=Literals001 -o result.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=run-and-report -o report.xml --indent",
    "zorba -f -q /path/to/cli.xq -e fotsPath:=/path/to/QT3-test-suite/catalog.xml -e mode:=report -e failuresFilePath:=failures.xml -e verbose:=false -o report.xml --indent",
    "zorba -f -q /path/to/cli.xq -e mode:=generate-expected-failures -e failuresFilePath:=failures.xml -o ExpectedFailures.xml --indent",
    ""
    ), "
")
};

(:~ The test cases in this list have bugs assigned and should not be run :)
variable $exceptedTestCases := (
"cbcl-subsequence-011", "cbcl-subsequence-012", "cbcl-subsequence-013",
"cbcl-subsequence-014"                                  (:see bug lp:1069794 :)
, "re00975", "re00976", "re00976a"                      (:see bug lp:1070533 :)
, "fn-unparsed-text-lines-052"                          (:see bug lp:1073175 :)
);

(:~  The test in this list have bugs assigned already and should not be run :)
variable $exceptedTestSets := ();

switch ($mode)
case "list-test-sets"
  return
    string-join(
      (d:list-test-sets(
        trace($fotsPath, "Path to FOTS catalog.xml set to: "),
        local:tokenize(trace($testSetPrefixes, "'testSetPrefixes' set to: ")))),
       "
")
case "list-test-cases"
  return
    string-join(
      (d:list-test-cases( trace($fotsPath,
                                "Path to FOTS catalog.xml set to: "),
                          local:tokenize(trace($testSetPrefixes,
                                          "'testSetPrefixes' set to: ")),
                          "",
                          trace($dependency,
                                "'dependency' set to:"),
                          trace($assertType,
                                "'assertType' set to: "))),
               "
")
case "list-matching-test-cases"
  return string-join((d:list-matching-test-cases(
                            trace($fotsPath,
                                  "Path to FOTS catalog.xml set to: "),
                            trace($pattern,
                                  "pattern set to: "),
                            trace($flags,
                                  "flags were set to: ")), ""),
                     "
")
case "run-test-sets"
  return d:run-fots($fotsPath,
                    $fotsZorbaManifestPath,
                    d:list-test-sets($fotsPath,
                                     local:tokenize(trace($testSetPrefixes,
                                       "'testSetPrefixes' set to: "))),
                    d:list-test-cases($fotsPath,
                                      local:tokenize($testSetPrefixes),
                                      local:tokenize(trace($testCasePrefixes,
                                        "'testCasePrefixes' set to: ")),
                                      trace($dependency,
                                        "'dependency' set to:"),
                                      trace($assertType,
                                         "'assertType' set to: ")),
                    $exceptedTestCases,
                    $exceptedTestSets,
                    xs:boolean($verbose),
                    $expectedFailuresPath)
case "run-test-case"
  return d:run-fots($fotsPath,
                    $fotsZorbaManifestPath,
                    trace($testSetName, "'testSetName' set to: "),
                    trace($testCaseName,"'testCaseName' set to: "),
                    $exceptedTestCases,
                    $exceptedTestSets,
                    xs:boolean($verbose),
                    $expectedFailuresPath)
case "run-and-report"
  return r:run-and-report($fotsPath,
                          $fotsZorbaManifestPath,
                          $exceptedTestCases,
                          $exceptedTestSets,
        (: the reports to W3C are always generated with verbose set to false:)
                          fn:false())
case "report"
  return r:report($fotsPath,
                  $failuresFilePath,
                  $exceptedTestCases,
                  $exceptedTestSets,
                  xs:boolean($verbose))
case "generate-expected-failures"
  return r:generate-expected-failures($failuresFilePath)
default
  return local:usage()


Added further XQuery 3.0 String Conatentaion Expression tests

xquery version "3.0";

declare namespace other = "http://other";

declare variable $local:straight-var1 := 'one';

declare %private variable $local:private-var := 'secret';
declare %public variable $local:public-var := 'not-secret';
declare %other:annotation('param1', "param2") variable $local:some-var := 'anything';

declare variable $local:straight-var2 := 'two';

(: Simple Map Operator example :)
declare function local:word-count($elms as element()*) as xs:integer {
	sum($elms ! count(tokenize(., '\s+')))
};

declare function local:add($a, $b) {
	$a + $b
};

declare function local:dispatch($node as node()) as item()* {
    typeswitch($node)
        case text() return $node
        case comment() return $node
        case element(bill) return local:bill($node)
        case element(btitle) return local:btitle($node)
        case element(section-id) return local:section-id($node)
        case element(bill-text) return local:bill-text($node)
        case element(strike) return local:strike($node)
        default return local:passthru($node)
};

(: `switch` expression example :)
declare function local:noise($animal) {
	let $duck := "Duck",
	$quack := "Quack"
	return
		switch ($animal)
			case "Cow" return "Moo"
			case 'Cat' return 'Meow'
			case $duck return $quack
			default return "What's that odd noise?"
};

(: `group by` expression with binding example :)
declare function local:a-to-z() {
	let $data as element()* := (
		<item>Apples</item>,
		<item>Bananas</item>,
		<item>Apricots</item>,
		<item>Pears</item>,
		<item>Brambles</item>
	) return
		<GroupedItems>{
			for $item in $data
			group by $key := upper-case(substring($item, 1, 1))
			order by $key
			return
				<Group key="{$key}">{$item}</Group>
		}</GroupedItems>
};

(: `group by` expression example :)
declare function local:plays-by-character() {
	let $plays := (
		document {
			<play>
				<title>Hamlet</title>
				<characters>
					<character>Hamlet</character>
					<character>Claudius</character>
					<character>Polonius</character>
					<character>Rosencrantz</character>
					<character>Guildenstern</character>
					<character>Francisco</character>
					<character>Reynaldo</character>
				</characters>
			</play>
		},
		document {
			<play>
				<title>Rosenkrantz and Guildenstern are Dead</title>
				<characters>
					<character>Alfred</character>
					<character>Rosencrantz</character>
					<character>Guildenstern</character>
					<character>Hamlet</character>
					<character>Claudius</character>
				</characters>
			</play>
		}
	) return

		for $play in $plays/play
		let $title := $play/title
		for $character in $play/characters/character
		group by $character
		return
			<character name="{$character}">
			{
				$title ! <play>{ . }</play>
     		}
			</character>
};

declare
	%other:a
	%private
	%other:b('1')
	%other:c("1", "2", "3", "4")
function local:very-annotated() {
	let $thing := "thing"
	return
		$thing
};

declare %public function local:slightly-annotated() {
	let $nothing := ()
	return
		$nothing
};

declare function local:ordered() {
	for $hit in doc("/db/doc-with-indexes.xml")//tei:p[other:query(., $search-expression)]
	let $score as xs:float := other:score($hit)
	order by $score descending
	return (

Score: {$score}:
,
		other:summarize($hit, <config width="40"/>)
	)
};

declare function local:concat-expr($postfix) {

	let $concatenated := other:uri() || "/" || $postfix
	return
		$concatenated
};

declare function local:human-units($bytes) {
	let $unit := if($bytes > math:pow(1024, 3)) then
		(math:pow(1024, 3), "GB")
	else if($bytes > math:pow(1024, 2)) then
		(math:pow(1024, 2), "MB")
	else
		(1024, "KB")
	return
		format-number($bytes div $unit[1], ".00") || " " || $unit[2]
};

declare function local:merge-simple($a as xs:string+, $b as xs:string+) as xs:string+ {
	($a, $b)
};

(: higher order function example 1 :)
declare function local:apply($func, $value) {
	$func($value)
};

(: higher order function example 2 :)
declare function local:apply-all($func, $list) {
	$list ! $func(.)
};

(: higher order function example 3 :)
declare function local:apply-all-long($func as function(xs:string) as xs:string, $list) {
	$list ! $func(.)
};

(: higher order function example 4 :)
declare function local:merge($func as function(xs:string+, xs:string+) as xs:string+, $a as xs:string+, $b as xs:string+) as xs:string+ {
	$func($a, $b)
};

let $to-upper := upper-case#1
let $to-upper-long as function(xs:string) as xs:string := upper-case#1
return
    <case>
    {
        local:apply-all($to-upper, ("Hello", "world!")) ! <upper>{.}</upper>,
        local:apply-all-long(lower-case#1, ("Hello", "world!")) ! <lower>{.}</lower>
    }
    </case>

xquery version "3.0";

declare namespace other = "http://other";

declare variable $local:straight-var1 := 'one';

declare %private variable $local:private-var := 'secret';
declare %public variable $local:public-var := 'not-secret';
declare %other:annotation('param1', "param2") variable $local:some-var := 'anything';

declare variable $local:straight-var2 := 'two';

(: Simple Map Operator example :)
declare function local:word-count($elms as element()*) as xs:integer {
	sum($elms ! count(tokenize(., '\s+')))
};

declare function local:add($a, $b) {
	$a + $b
};

declare function local:dispatch($node as node()) as item()* {
    typeswitch($node)
        case text() return $node
        case comment() return $node
        case element(bill) return local:bill($node)
        case element(btitle) return local:btitle($node)
        case element(section-id) return local:section-id($node)
        case element(bill-text) return local:bill-text($node)
        case element(strike) return local:strike($node)
        default return local:passthru($node)
};

(: `switch` expression example :)
declare function local:noise($animal) {
	let $duck := "Duck",
	$quack := "Quack"
	return
		switch ($animal)
			case "Cow" return "Moo"
			case 'Cat' return 'Meow'
			case $duck return $quack
			default return "What's that odd noise?"
};

(: `group by` expression with binding example :)
declare function local:a-to-z() {
	let $data as element()* := (
		<item>Apples</item>,
		<item>Bananas</item>,
		<item>Apricots</item>,
		<item>Pears</item>,
		<item>Brambles</item>
	) return
		<GroupedItems>{
			for $item in $data
			group by $key := upper-case(substring($item, 1, 1))
			order by $key
			return
				<Group key="{$key}">{$item}</Group>
		}</GroupedItems>
};

(: `group by` expression example :)
declare function local:plays-by-character() {
	let $plays := (
		document {
			<play>
				<title>Hamlet</title>
				<characters>
					<character>Hamlet</character>
					<character>Claudius</character>
					<character>Polonius</character>
					<character>Rosencrantz</character>
					<character>Guildenstern</character>
					<character>Francisco</character>
					<character>Reynaldo</character>
				</characters>
			</play>
		},
		document {
			<play>
				<title>Rosenkrantz and Guildenstern are Dead</title>
				<characters>
					<character>Alfred</character>
					<character>Rosencrantz</character>
					<character>Guildenstern</character>
					<character>Hamlet</character>
					<character>Claudius</character>
				</characters>
			</play>
		}
	) return

		for $play in $plays/play
		let $title := $play/title
		for $character in $play/characters/character
		group by $character
		return
			<character name="{$character}">
			{
				$title ! <play>{ . }</play>
     		}
			</character>
};

declare
	%other:a
	%private
	%other:b('1')
	%other:c("1", "2", "3", "4")
function local:very-annotated() {
	let $thing := "thing"
	return
		$thing
};

declare %public function local:slightly-annotated() {
	let $nothing := ()
	return
		$nothing
};

declare function local:ordered() {
	for $hit in doc("/db/doc-with-indexes.xml")//tei:p[other:query(., $search-expression)]
	let $score as xs:float := other:score($hit)
	order by $score descending
	return (

Score: {$score}:
,
		other:summarize($hit, <config width="40"/>)
	)
};

declare function local:merge-simple($a as xs:string+, $b as xs:string+) as xs:string+ {
	($a, $b)
};

(: higher order function example 1 :)
declare function local:apply($func, $value) {
	$func($value)
};

(: higher order function example 2 :)
declare function local:apply-all($func, $list) {
	$list ! $func(.)
};

(: higher order function example 3 :)
declare function local:apply-all-long($func as function(xs:string) as xs:string, $list) {
	$list ! $func(.)
};

(: higher order function example 4 :)
declare function local:merge($func as function(xs:string+, xs:string+) as xs:string+, $a as xs:string+, $b as xs:string+) as xs:string+ {
	$func($a, $b)
};

let $to-upper := upper-case#1
let $to-upper-long as function(xs:string) as xs:string := upper-case#1
return
    <case>
    {
        local:apply-all($to-upper, ("Hello", "world!")) ! <upper>{.}</upper>,
        local:apply-all-long(lower-case#1, ("Hello", "world!")) ! <lower>{.}</lower>
    }
    </case>


index declarations in data modules only

index declarations in data modules only

import datamodule namespace idx = "http://www.w3.org/TestModules/idx" at
                                  "data_module2.xqlib";

declare namespace zorba = "http://www.zorba-xquery.com/zorba/op-extensions";

zorba:create-index(xs:QName("idx:foo"));

(
for $x in idx:probe(xs:QName("idx:foo"), ())
return <person id = "{$x/@id}">{$x//city}</person>,

"

",

for $x in idx:probe2(xs:QName("idx:foo"), "Foo")
return <person id = "{$x/@id}">{$x//city}</person>
);


Continued fix for http://sourceforge.net/tracker/index.php?func=detail&aid=1535514&group_id=17691&atid=317691 ([ 1535514 ] decoding resources names in webadmin interface)
Removal of collection now ok......

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

(:
    Module: display and browse collections.
:)
module namespace browse="http://exist-db.org/xquery/admin-interface/browse";

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

import module namespace date="http://exist-db.org/xquery/admin-interface/date" at "dates.xqm";

(:
    Main function: outputs the page.
:)
declare function browse:main($user as xs:string, $passwd as xs:string) as element() {
    let $colName := request:get-parameter("collection", "/db"),
        $collection := xdb:collection($colName, $user, $passwd)
    return

            { browse:process-action($collection) }

Browsing Collection: {xdb:decode-uri(xs:anyURI($colName))}

            <form method="POST" enctype="multipart/form-data">
                {
                    browse:display-collection($collection)
                }

<input type="submit" name="action" value="Remove Selected"/>

                        <input type="submit" name="action" value="Create Collection"/>
                        New collection:

                        <input type="text" name="create" size="40"/>

                        <input type="submit" name="action" value="Upload"/>
                        <input type="file" size="30" name="upload"/>
                        Store as:

                        <input type="text" name="name" size="20"/>

                <input type="hidden" name="collection" value="{$colName}"/>
                <input type="hidden" name="panel" value="browse"/>
            </form>

};

(:
    Process an action.
:)
declare function browse:process-action($collection as object) as element()* {
    let $action := request:get-parameter("action", ())
    return
        util:catch("java.lang.Exception",
            if($action eq "Remove Selected") then
                browse:remove()
            else if($action eq "Create Collection") then
                browse:create-collection($collection)
            else if($action eq "Store") then
                browse:store($collection)
            else if($action eq "Upload") then
                browse:upload($collection)
            else
                (),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

(:
    Store uploaded content.
:)
declare function browse:upload($collection as object) as element() {
    let $name := request:get-parameter("name", ()),
        $docName :=
            if($name) then $name
            else request:get-uploaded-file-name("upload"),
        $file := request:get-uploaded-file("upload")
    return

Actions:

Storing uploaded content to: {$docName}

                {
                    xdb:store($collection, $docName, $file)
                }

};

(:
    Store files from an URI.

    Allowing this opens a security whole as a user can
    upload arbitrary files on the server if the process is running
    as root.
:)
declare function browse:store($collection as object) as element() {
    let $uri := request:get-parameter("uri", ()),
        $path := if(starts-with($uri, "file:")) then $uri
            else concat("file:", $uri),
        $docName := request:get-parameter("name", ())
    return

Actions:

Storing resources from URI: {$path}

                {
                    xdb:store($collection, $docName, xs:anyURI($path))
                }

};

(:
    Remove a set of resources.
:)
declare function browse:remove() as element() {
    let $resources := request:get-parameter("resource", ())
    return

Actions:

                {
                    for $resource in $resources
                    return
                        browse:remove-resource($resource)
                }

};

(:
    Remove a resource.
:)
declare function browse:remove-resource($resource as xs:string) as element()* {
    let $isBinary := util:binary-doc-available($resource)
    let $doc := if ($isBinary) then $resource else doc($resource)
    return
        if($doc) then (

Removing document: {$resource} ...
,
            xdb:remove(util:collection-name($doc), util:document-name($doc))
        ) else (

Removing collection: {xdb:decode-uri(xs:anyURI($resource))} ...
,
            xdb:remove($resource)
        )
};

(:
    Create a collection.
:)
declare function browse:create-collection($parent as object) as element() {
    let $newcol := request:get-parameter("create", ())
    return

Actions:

            {
                if($newcol) then
                    let $col := xdb:create-collection($parent, $newcol)
                    return

Created collection: {util:collection-name($col)}.

                else

No name specified for new collection!

            }

};

(:
    Display the contents of a collection in a table view.
:)
declare function browse:display-collection($collection as object)
as element() {
    let $colName := util:collection-name($collection)
    return

                Name
                Permissions
                Owner
                Group
                Created
                Modified
                Size (KB)

                Up

            {
                browse:display-child-collections($collection),
                browse:display-child-resources($collection)
            }

};

declare function browse: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),
        $created := xdb:created($path)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$path}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($path))}
            {xdb:get-owner($path)}
            {xdb:get-group($path)}
            {date:format-dateTime($created)}

};

declare function browse:display-child-resources($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-resources($collection)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$parent}/{$child}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($collection, $child))}
            {xdb:get-owner($collection, $child)}
            {xdb:get-group($collection, $child)}
            {date:format-dateTime(xdb:created($collection, $child))}
            {date:format-dateTime(xdb:last-modified($collection, $child))}
            {fn:ceiling(xdb:size($collection, $child) div 1024)}

};

(:
    Get the name of the parent collection from a specified collection path.
:)
declare function browse:get-parent-collection($path as xs:string) as xs:string {
    if($path eq "/db") then
        $path
    else
        replace($path, "/[^/]*$", "")
};

(:
    Module: display and browse collections.
:)
module namespace browse="http://exist-db.org/xquery/admin-interface/browse";

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

import module namespace date="http://exist-db.org/xquery/admin-interface/date" at "dates.xqm";

(:
    Main function: outputs the page.
:)
declare function browse:main($user as xs:string, $passwd as xs:string) as element() {
    let $colName := request:get-parameter("collection", "/db"),
        $collection := xdb:collection($colName, $user, $passwd)
    return

            { browse:process-action($collection) }

Browsing Collection: {xdb:decode-uri(xs:anyURI($colName))}

            <form method="POST" enctype="multipart/form-data">
                {
                    browse:display-collection($collection)
                }

<input type="submit" name="action" value="Remove Selected"/>

                        <input type="submit" name="action" value="Create Collection"/>
                        New collection:

                        <input type="text" name="create" size="40"/>

                        <input type="submit" name="action" value="Upload"/>
                        <input type="file" size="30" name="upload"/>
                        Store as:

                        <input type="text" name="name" size="20"/>

                <input type="hidden" name="collection" value="{$colName}"/>
                <input type="hidden" name="panel" value="browse"/>
            </form>

};

(:
    Process an action.
:)
declare function browse:process-action($collection as object) as element()* {
    let $action := request:get-parameter("action", ())
    return
        util:catch("java.lang.Exception",
            if($action eq "Remove Selected") then
                browse:remove()
            else if($action eq "Create Collection") then
                browse:create-collection($collection)
            else if($action eq "Store") then
                browse:store($collection)
            else if($action eq "Upload") then
                browse:upload($collection)
            else
                (),

                An error occurred while processing the action:

                {$util:exception-message}

        )
};

(:
    Store uploaded content.
:)
declare function browse:upload($collection as object) as element() {
    let $name := request:get-parameter("name", ()),
        $docName :=
            if($name) then $name
            else request:get-uploaded-file-name("upload"),
        $file := request:get-uploaded-file("upload")
    return

Actions:

Storing uploaded content to: {$docName}

                {
                    xdb:store($collection, $docName, $file)
                }

};

(:
    Store files from an URI.

    Allowing this opens a security whole as a user can
    upload arbitrary files on the server if the process is running
    as root.
:)
declare function browse:store($collection as object) as element() {
    let $uri := request:get-parameter("uri", ()),
        $path := if(starts-with($uri, "file:")) then $uri
            else concat("file:", $uri),
        $docName := request:get-parameter("name", ())
    return

Actions:

Storing resources from URI: {$path}

                {
                    xdb:store($collection, $docName, xs:anyURI($path))
                }

};

(:
    Remove a set of resources.
:)
declare function browse:remove() as element() {
    let $resources := request:get-parameter("resource", ())
    return

Actions:

                {
                    for $resource in $resources
                    return
                        browse:remove-resource($resource)
                }

};

(:
    Remove a resource.
:)
declare function browse:remove-resource($resource as xs:string) as element()* {
    let $isBinary := util:binary-doc-available($resource)
    let $doc := if ($isBinary) then $resource else doc($resource)
    return
        if($doc) then (

Removing document: {$resource} ...
,
            xdb:remove(util:collection-name($doc), util:document-name($doc))
        ) else (

Removing collection: {$resource} ...
,
            xdb:remove($resource)
        )
};

(:
    Create a collection.
:)
declare function browse:create-collection($parent as object) as element() {
    let $newcol := request:get-parameter("create", ())
    return

Actions:

            {
                if($newcol) then
                    let $col := xdb:create-collection($parent, $newcol)
                    return

Created collection: {util:collection-name($col)}.

                else

No name specified for new collection!

            }

};

(:
    Display the contents of a collection in a table view.
:)
declare function browse:display-collection($collection as object)
as element() {
    let $colName := util:collection-name($collection)
    return

                Name
                Permissions
                Owner
                Group
                Created
                Modified
                Size (KB)

                Up

            {
                browse:display-child-collections($collection),
                browse:display-child-resources($collection)
            }

};

declare function browse: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),
        $created := xdb:created($path)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$path}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($path))}
            {xdb:get-owner($path)}
            {xdb:get-group($path)}
            {date:format-dateTime($created)}

};

declare function browse:display-child-resources($collection as object)
as element()* {
    let $parent := util:collection-name($collection)
    for $child in xdb:get-child-resources($collection)
    order by $child
    return

            <input type="checkbox" name="resource" value="{$parent}/{$child}"/>
            {xdb:decode-uri(xs:anyURI($child))}
            {xdb:permissions-to-string(xdb:get-permissions($collection, $child))}
            {xdb:get-owner($collection, $child)}
            {xdb:get-group($collection, $child)}
            {date:format-dateTime(xdb:created($collection, $child))}
            {date:format-dateTime(xdb:last-modified($collection, $child))}
            {fn:ceiling(xdb:size($collection, $child) div 1024)}

};

(:
    Get the name of the parent collection from a specified collection path.
:)
declare function browse:get-parent-collection($path as xs:string) as xs:string {
    if($path eq "/db") then
        $path
    else
        replace($path, "/[^/]*$", "")
};


[xproc.escapemarkup] implemented escape markup step

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