7. group
| Chapter 2. Predefined commands |

## 7. group

    group()

Command `group()` groups under a common parent element all the sibling elements having the same group mark (which is attribute `g:id`).

Text, comment and processing-instruction nodes found between elements having the same group mark are automatically added to the common parent element.

Using command `group()` is a two step process:

1. Add the following attributes to the elements you want to group: `g:id`, `g:container`, `g:nesting`, where "`g`" is the prefix of namespace "`urn:x-mlmind:namespace:group`".

2. Invoke command `group()`.

Command `group()` traverses the document modified by the script. It processes separately “sections” containing marked elements. What we call a “section” here is simply any element directly containing at least one child element having a `g:container` attribute.

When done, command `group()` automatically removes all `g:id`, `g:container`, `g:nesting` attributes from the document.

Attribute | Type | Role
--- | --- | ---
`g:id` | Any non empty string. | Elements having the same `g:id` belong to the same group, hence will be given a common container parent. This attribute is required for `group()` to work.
`g:container` | String having the following format:
    container -> element_qname
                 [ attribute ]*

    attribute -> attr_qname '='
                 attr_value

    attr_value -> '"' string '"'
                  | "'" string "'"
`string` may contain variable "`{$nesting}`" which is substituted with the actual nesting level of the group to be created. This actual nesting level is an integer starting at 1. | Specifies the container parent. Examples:
    ul

    ol type='A' compact='compact'

    div class="role-section{$nesting}"
This attribute is required for `group()` to work.In principle, suffice to set `g:container` on the first member of a group. However it's often simpler, and it does not harm, to set `g:container` on all the members of a group.
`g:nesting` | Positive number. Default value: 0. | This number allows to specify how groups nest in each other. Example:
    <li g:id="a" g:container="ul" g:nesting="1">
      ...
    </li>
    <li g:id="b" g:container="ul" g:nesting="3.14">
      ...
    </li>
First `li` will be made a child of an `ul` created by `group()`. Second `li`, because 3.14 is greater than 1, will be made a child of a second `ul`. This second `ul` will be nested into the first `ul`. Note that the exact values of `g:nesting` do not matter as these numbers are simply compared to each others. When set on an element having no `g:id`, this attribute allows to end groups. Because the default value of `g:nesting` is 0, any element having no `g:nesting` and no `g:id` attributes automatically ends all groups.

Example 2.1. Basic use of command `group()`

In `samples/group1.xhtml`, convert sibling `p` elements starting with "`1)`", "`2)`", "`3)`", etc, to proper lists.

    <p>1) Item #1.</p>
    <p>2) Item #2.</p>
    <p>3) Item #3.</p>

You can run the following script, `samples/group1.xed`, using `samples/make_samples.bat` (Windows) or `samples/make_samples` (Linux, Mac) . The resulting file is `samples/out1.xhtml`.

    namespace "http://www.w3.org/1999/xhtml";
    namespace g="urn:x-mlmind:namespace:group";

    for-each //p[matches(., "^\d+\)\s*")] {
        delete-text("^\d+\)\s*");

        set-element-name("li");

        set-attribute("g:id", "numbered");
        set-attribute("g:container", "ol style='list-style-type: upper-roman;");
    }

    group();

Example 2.2. Creating nested groups

In `samples/group2.xhtml`, convert sibling `p` elements starting with "`*`", "`**`", "`***`", etc, to proper lists. These lists may be nested. For example, a `p` element starting with "`**`" is to be contained in a list nested in the list containing `p` elements starting with "`*`".

    <p>* First item.</p>
    <p>* Second item.</p>

    <p>** Nested first item.</p>

    <p>*** Nested, nested first item.</p>
    <p>*** Nested, nested second item.</p>

    <p>** Nested second item.</p>
    ...

You can run the following script, `samples/group2.xed`, using `samples/make_samples.bat` (Windows) or `samples/make_samples` (Linux, Mac) . The resulting file is `samples/out2.xhtml`.

    namespace "http://www.w3.org/1999/xhtml";
    namespace g="urn:x-mlmind:namespace:group";

    for-each //p[matches(., "^\*+\s+")] {
        set-variable("label", substring-before(., " "));
        set-variable("bullets", count(tokenize($label, "\*")) - 1);
        message("label=&quot;", $label, "&quot;, bullets=", $bullets);

        delete-text("^\*+\s+");

        set-element-name("li");

        set-attribute("g:id", concat("bulleted", $bullets));
        set-attribute("g:container", "ul");
        set-attribute("g:nesting", $bullets);
    }

    group();

Example 2.3. Group members having different element types

Variant of Example 2.1, “Basic use of command `group()`”. In `samples/group3.xhtml`, we want “continuation paragraphs” to be part of the current list item rather than end the current list group:

    <p>1) Item #1.</p>
    <p>Continuation paragraph.</p>

    <p>2) Item #2.</p>
    <p>Continuation paragraph.</p>

    <p>3) Item #3.</p>

    <p>Not a continuation paragraph.</p>

You can run the following script, `samples/group3.xed`, using `samples/make_samples.bat` (Windows) or `samples/make_samples` (Linux, Mac) . The resulting file is `samples/out3.xhtml`.

    namespace "http://www.w3.org/1999/xhtml";
    namespace g="urn:x-mlmind:namespace:group";

    for-each //p[matches(., "^\d+\)\s*")] {
        delete-text("^\d+\)\s*");

        set-variable("listItem", <li/>);
        wrap-element($listItem);

        set-attribute("g:id", "numbered", $listItem);
        set-attribute("g:container", "ol", $listItem);
    }

    for-each //p[preceding-sibling::li[position()=last() and @g:id] and
                 following-sibling::li[position()=1 and @g:id]] {
        set-variable("listItem", preceding-sibling::li[last()]);

        set-attribute("g:id", $listItem/@g:id);
    }

    group();

| We could have used:
    set-element-name("li");
just like in `samples/group1.xed`. The above variant wraps the `p` into an `li` rather than changing the `p` to an `li`.
| Detect “continuation paragraphs” and give them the same `g:id` as the current list item. That makes these paragraphs members of the current list group. Normally `out3.xhtml` should contain:
    <ol>
      <li><p>Item #1.</p></li>
      <p>Continuation paragraph.</p>
      <li><p>Item #2.</p></li>
      <p>Continuation paragraph.</p>
      <li><p>Item #3.</p></li>
    </ol>
which is invalid. However `out3.xhtml` actually contains:
    <ol>
      <li><p>Item #1.</p><p>Continuation paragraph.</p></li>
      <li><p>Item #2.</p><p>Continuation paragraph.</p></li>
      <li><p>Item #3.</p></li>
    </ol>
This works because command `group()` knows that a `p` element cannot be a child of an `ol` element[2]. When this occurs, `group()` will attempt to add the “alien group member” at the end of the preceding group member rather than adding it at the end of the group container.

[2] Command `group()` uses the schema of the document edited by the script to determine that.

6. error |  | 8. insert-after