Pages: 1
Author Topic: Sort topicrefs alphabetically by topic title  (Read 2681 times)

Posts: 1

« on: November 01, 2013, 09:04:22 AM »


Is there a way to sort topics alphabetically by title? We'd like to create a map that has all of our procedures in alphabetical order by title. Is there a macro or script to do this? We could do it manually, but it seems like it could be done programmatically. Any recommendations? Has anyone else done something like this?

Derek Read
Program Manager (XMetaL)

Posts: 2621

« Reply #1 on: November 01, 2013, 11:08:58 AM »

There is no built-in functionality that will do this for you for a DITA map. In XMetaL Author 8.0 (Enterprise and Essential) we've added sorting for HTML and CALS tables as well as elements that have been customized for a particular DTD to be treated as a list, but that won't help here.

You are correct that this should be possible to do programmatically. I can add it to my to-do list as it sounds like an interesting challenge, but maybe someone else would want to give it a try as I really don't know if I have time for this at the moment.

If the portion of your map that you wish to sort is anything more than just a series of <topicref> elements, without nesting, that would probably make things a lot harder. If it is then I think we'd want to see a sample and a description of what you expect the behaviour to be (especially for the nested parts).
« Last Edit: November 01, 2013, 11:14:40 AM by Derek Read » Logged
Derek Read
Program Manager (XMetaL)

Posts: 2621

« Reply #2 on: November 14, 2013, 08:07:29 PM »

I've come up with the following script (based on something I originally created for sorting tables that is now obsolete).

It has not been coded to be very robust (at least I did not think about it much while coding) but it handles the two basic test cases I've tried:
  a) Sort a selection of <topicref> elements that are all siblings.
  b) Sort a selection of sibling <topicref> elements with child <topicref> elements. I believe it sorts as one would expect in this case: elements at the same "nesting level" are sorted against each other and not against their children (or parents).

You need to create a selection containing <topicref> elements before you run the script.
Sorting is performed on the value of the @navtitle for each <topicref> so that attribute must be set. You may wish to try running "Refresh References (F11)" to have the @navtitle set if you see some elements being sorted out of order.

There are bound to be some cases where it might fail for more complex structures than the two listed above.

Use this at your own risk and only with test content until you are satisfied it works for you.

I have only done absolute minimum testing on this and there are probably lots of potential use cases I have not thought of, which is why I'm providing the plain script code and not an MCR file. You will need to know enough about implementing an XMetaL macro to use this (a Startup macro would be good enough for most cases).

This example could easily be altered to enable sorting for other types of elements than requested by the OP (Susan_C_1). It could also be made more foolproof or more automated depending on your exact needs. I leave it up to you to decide the macro name, whether it gets a shortcut key, whether you wish to allow users to run it from the context menu, etc.

I've also left in a bunch of Alerts to show the state of the unsorted vs sorted XML so you'd want to comment those out for a distributed version.

//XMetaL Script Language JScript:
function derekread_DemoExtension_sortTopicrefs() {
var rng = ActiveDocument.Range;
if(!rng.IsInsertionPoint) {
//wrap string with a root element (need a single root node) for MSXML
var selectionStr = "<derektemp>" + rng.TextWithRM + "</derektemp>";

//Build XSLT --------------------------------------------------------- START
var xsltStr = '<?xml version="1.0" encoding="utf-8"?>\n';
xsltStr += '<xsl:stylesheet version="1.0" xmlns:xsl="">\n';
xsltStr += '<xsl:output method="xml" omit-xml-declaration="yes" indent="no"/>\n';

xsltStr += '<xsl:template match="@* | node()">\n';
xsltStr += ' <xsl:copy>\n';
xsltStr += ' <xsl:apply-templates select="@* | node()"/>\n';
xsltStr += ' </xsl:copy>\n';
xsltStr += '</xsl:template>\n';

xsltStr += '<xsl:template match="derektemp">\n';
xsltStr += ' <xsl:copy>\n';
xsltStr += ' <xsl:apply-templates select="topicref">\n';
xsltStr += ' <xsl:sort select="@navtitle"/>\n';
xsltStr += ' </xsl:apply-templates>\n';
xsltStr += ' </xsl:copy>\n';
xsltStr += '</xsl:template>\n';

xsltStr += '</xsl:stylesheet>\n';
//Build XSLT ----------------------------------------------------------- END

// Load the XML string into MSXML
try {
xmlDoc = new ActiveXObject("Msxml2.DOMDocument.4.0");
xmlDoc.async = false;

// Load XSL file into MSXML
xslDoc = new ActiveXObject("Msxml2.FreeThreadedDOMDocument.4.0");
xslDoc.async = false;
if (xslDoc.parseError.errorCode != 0) {
var xsltErr = xslDoc.parseError;
  Application.Alert("XSLT load error during table sort: " + xsltErr.reason);
else {
// Set up for the XSLTransform
xslTpl = new ActiveXObject("Msxml2.XSLTemplate.4.0");
xslTpl.stylesheet = xslDoc;
xslProc = xslTpl.createProcessor();
xslProc.input = xmlDoc;

//Run the transform

//Get the output after the transform
var sortedStr = xslProc.output;
//remove "derektemp" root node wrapper element
sortedStr = sortedStr.substring(11,sortedStr.length - 12);
xmlDoc = null;
catch(e) {
var msg = "An error with this script has occurred. It might ";
msg += "have something to do with MSXML or the MSXML version but it ";
msg += "could also easily have something to do with the selection. ";
msg += "No checking is done in that regard.\n\nScript Error: " + e.description;
Pages: 1
Jump to: