Create a numbering for nodes
This guide describes how to create numbering for nodes. It also has a few example cases in which using node numbering can be useful. This guide focuses on using the add
Basic steps
These are the basic steps for adding numbering for nodes.
Step 1: Create an XQuery module to count the nodes
Create an XQuery module in the src
directory of a package. The module will be registered automatically when placed in an src
directory.
The XQuery module is used to declare a function that counts the nodes that we want to number. This function contains the logic used for counting the nodes. The arguments and the return value of this function are explained in the add
documentation.
myNodeNumberingCallback.xqm
XQuery
xquery version "3.0";
(: The namespace URI that is used in other places of the editor :)
module namespace app="http://www.fontoxml.com/app";
declare function app:myNodeNumberingCallback(
$previousAccumulator as item()*,
$relType as xs:string,
$node as node(),
$isUnloaded as xs:boolean
) as item()* {
(: Compute a new accumulator value for $node :)
};
Step 2: Add the reducer
Add the reducer in a configure
file. More details about this function are available in the add
documentation.
configureSxModule.js
JavaScript
import addReducer from 'fontoxml-indices/src/addReducer.js';
export default function configureSxModule(sxModule) {
addReducer(
'http://www.fontoxml.com/app',
'myReducerForNodeNumbering',
// In this case we decided to count the "mynodename" elements. We could also have counted
// multiple elements like this: 'self::mynodename or self::othernodename'. This will result
// in the same numbering but pass both the mynodename and othernodename elements as $node.
'self::mynodename',
'http://www.fontoxml.com/app',
'myNodeNumberingCallback'
);
// my other codes...
}
Step 3: Use the reducer in a title query
The added reducer function can be used in the same way registered custom XPath functions are used. Pass the current hierarchy node id and the node to the reducer function. It will return a number or a sequence of numbers. The result can be used in a title or label.
configureSxModule.js
JavaScript
import addReducer from 'fontoxml-indices/src/addReducer.js';
import configureAsFrame from 'fontoxml-families/src/configureAsFrame.js';
import createLabelQueryWidget from 'fontoxml-families/src/createLabelQueryWidget.js';
export default function configureSxModule(sxModule) {
addReducer(
'http://www.fontoxml.com/app',
'myReducerForNodeNumbering',
// In this case we decided to count the "mynodename" elements. We could also have counted
// multiple elements like this: 'self::mynodename or self::othernodename'. This will result
// in the same numbering but pass both the mynodename and othernodename elements as $node.
'self::mynodename',
'http://www.fontoxml.com/app',
'myNodeNumberingCallback'
);
configureAsFrame(sxModule, 'self::mynodename', t('My Title'), {
// Here we can use the result of the reducer function.
// If the result is "1" and the title is "My Title" for a node, the titleQuery will returns "1 - My Title".
blockHeaderLeft: [
createLabelQueryWidget(
`import module namespace app = "http://www.fontoxml.com/app";
app:myReducerForNodeNumbering(fonto:current-hierarchy-node-id(), .)`
)
]
});
// my other codes...
}
Example: Flat numbering
One common use for reducers is the numbering of images, tables, and other objects. This example will show how to show a figure element's number in a widget. The number shown will be the number of the previous figure element + 1. This even works across different documents.

X Query module
In this example we use $rel
to determine whether the current item is the first item. If it is the first item, $previous
contains an empty sequence. If it is not the first item, we can use the previous value stored in $previous
, add 1 to it, and return the result.
figNumberingCallback.xqm
XQuery
xquery version "3.0";
module namespace app = "http://www.fontoxml.com/app";
declare %public function app:figNumberingCallback(
$previousAccumulator as item()*,
$relType as xs:string,
$node as node(),
$isUnloaded as xs:boolean
) as item()* {
(: Compute a new accumulator value for $node :)
if ($relType eq "first") then
(: First figure :)
1
else
(: Increment the counter :)
$previousAccumulator + 1
};
add Reducer
The reducer is added for fig
nodes and uses the callback defined in the XQuery module.
configureSxModule.js
JavaScript
import addReducer from 'fontoxml-indices/src/addReducer.js';
import configureAsFrame from 'fontoxml-families/src/configureAsFrame.js';
import createLabelQueryWidget from 'fontoxml-families/src/createLabelQueryWidget.js';
export default function configureSxModule(sxModule) {
addReducer(
'http://www.fontoxml.com/app',
'numberFigs',
'self::fig',
'http://www.fontoxml.com/app',
'figNumberingCallback'
);
// figure
configureAsFrame(sxModule, 'self::fig', 'figure', {
blockHeaderLeft: [
createLabelQueryWidget(
`import module namespace app = "http://www.fontoxml.com/app";
app:numberFigs(fonto:current-hierarchy-node-id(), .)`
)
]
// other options...
});
}
Example: Hierarchical numbering
Another common use for reducers is numbering documents in the outline view. This numbering needs to be updated automatically when the order of documents is changed.


This example will run the callback function defined in the XQuery module for each topicref
node. It will produce a hierarchical numbering for the documents, depending on the nesting of the topicref
node. The first document will get number 1, its first child will get number 1.1, its sibling will get number 1.2, and so on.
X Query module
This example uses the $rel
to determine whether the current topicref
node is a child of the previous topicref
node. If it is, it will add a new "start" value to the return sequence. We can do this since nested sequences will always be flattened. Refer to our XPath and XQuery documentation for more information on how sequences work and behave.
topicNumberingCallback.xqm
XQuery
xquery version "3.0";
module namespace app = "http://www.fontoxml.com/app";
declare %public function app:topicNumberingCallback(
$previousAccumulator as item()*,
$relType as xs:string,
$node as node(),
$isUnloaded as xs:boolean
) as item()* {
(: Compute a new accumulator value for $node :)
if ($relType eq "first") then
(: First item in the hierarchy :)
1
else
if ($relType eq "parent") then
(: No preceding siblings under this parent, append our numbering sub-level :)
($previousAccumulator, 1)
else
(: Increment the counter for our preceding sibling :)
$previousAccumulator!(if (position() = last()) then . + 1 else .)
};
add Reducer
If you want to count documents (e.g. "topic" in DITA), we strongly recommend you to use document references (e.g. "topicref" in DITA) as selector
. Allowing you to count documents without having to loading them.
configureSxModule.js
JavaScript
import addReducer from 'fontoxml-indices/src/addReducer.js';
import configureAsStructureViewItem from 'fontoxml-families/src/configureAsStructureViewItem.js';
export default function configureSxModule(sxModule) {
addReducer(
'http://www.fontoxml.com/app',
'numberTopics',
// In this case we decided to count the topicref elements. We could also have used
// self::topic. This will result in the same numbering but pass the topic element as $node.
'self::topicref',
'http://www.fontoxml.com/app',
'topicNumberingCallback'
);
// topic svk
configureAsStructureViewItem(sxModule, 'self::topic', {
icon: 'file-text-o',
titleQuery:
`import module namespace app = "http://www.fontoxml.com/app";
string-join(app:numberTopics(fonto:current-hierarchy-node-id(), .), ".") || " - " || ./title`
});
}
Example: Numbering Elements In Case Of Unloaded Documents
Some documents may not yet be loaded when J
To make the numbering of these elements possible, the CMS can fill in the gaps by keeping track of the number of those elements in a document. The CMS can then save these numbers in an attribute on, for example, DITA topicref
element.
X Query module
The callback function uses the $is
argument to determine which source to use for the number of elements. This source can be either an attribute set on the topicref
element when the document is not loaded or the actual number of fig
elements in a document.
figNumberingCallback.xqm
XQuery
xquery version "3.0";
module namespace app = "http://www.fontoxml.com/app";
declare %public function app:figNumberingCallback(
$previousAccumulator as item()*,
$relType as xs:string,
$node as node(),
$isUnloaded as xs:boolean
) as item()* {
let $previous := if ($relType eq "first") then 0 else $previousAccumulator
return
(: if $isUnloaded is true, the document is unloaded and we just have the topicref as $node :)
if ($isUnloaded) then
if ($node[@unloaded-count]) then
$previous + number($node/@unloaded-count)
else
$previous
else
$previous + 1
};
add Reducer
configureSxModule.js
JavaScript
import addReducer from 'fontoxml-indices/src/addReducer.js';
import configureAsFrame from 'fontoxml-families/src/configureAsFrame.js';
import createLabelQueryWidget from 'fontoxml-families/src/createLabelQueryWidget.js';
export default function configureSxModule(sxModule) {
addReducer(
'http://www.fontoxml.com/app',
'numberFigs',
'self::fig',
'http://www.fontoxml.com/app',
'figNumberingCallback'
);
// figure
configureAsFrame(sxModule, 'self::fig', 'figure', {
blockHeaderLeft: [
createLabelQueryWidget(
`import module namespace app = "http://www.fontoxml.com/app";
app:numberFigs(fonto:current-hierarchy-node-id(), .)`
)
],
// other options...
});
}