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.

AttributeTypeRole
g:idAny 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:nestingPositive 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);1

    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]] {2
    set-variable("listItem", preceding-sibling::li[last()]);

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

group();

1

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.

2

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.