Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

ExtendScript Recursion

Community Expert ,
Aug 08, 2025 Aug 08, 2025

Hi All, I have a script that builds and XML object of a FrameMaker document's structure. I use this map in subsequent functions that need to query the document's element/attribute structure using ExtendScript's xpath function. This is faster than traversing the document's structure multiple times.

 

Now the client has a large document--almost 700 pages--that has tons of tables. The script uses recursion to traverse and build the XML object map and I think that is where it is failing. I am not sure there is a good solution, but I wanted to put it out there in case you wireheads have any ideas. Thanks.

 

#target framemaker

var GS_XMNT = GS_XMNT || {};
GS_XMNT.version = "Version 0.1b November 18, 2024";
GS_XMNT.file = $.fileName;

GS_XMNT.main = function (doc, element) {
    
    var xmlMap;
    
    if (doc.ObjectValid () === 1) {
        if (element.ObjectValid () === 1) {
            xmlMap = GS_XMNT.getXmlMap (doc, element);
			alert (xmlMap.hasComplexContent ());
            return (xmlMap);
        }
    }
};

GS_XMNT.getXmlMap = function (doc, element) {
    
    var xmlMap;

    if (element.ObjectValid () === 1) {
        textList = element.GetText (Constants.FTI_ElementBegin);
        if (textList.length > 1) {
            xmlMap = GS_XMNT.makeNode (element, "");
            GS_XMNT.makeXmlMap (element, 0, xmlMap, doc, {});
        }
        else {
            xmlMap = GS_XMNT.makeNode (element, GS_XMNT.getText (element, doc));
        }
    }

    return xmlMap;
};

GS_XMNT.getElementText = function (element, doc) {
    
    var text = "";
    
    if (!element.FirstChildElement.ObjectValid ()) {
        text = GS_XMNT.getText (element, doc);
    }

    return text;
};
        
GS_XMNT.getDocById = function (docId) {
    
    var doc = app.FirstOpenDoc;
    while (doc.ObjectValid ()) {
        if (doc.id === docId) {
            return doc;
        }
        doc = doc.NextOpenDocInSession;
    }
};

GS_XMNT.makeXmlMap = function (element, level, xmlMap, doc, currNode) {
    
    var node, text, element2;
    
    if (!element.ElementDef.ElementDefIsText ()) {
        if (level > 0) {
			text = GS_XMNT.getElementText (element, doc);
            node = GS_XMNT.makeNode (element, text);
            node = GS_XMNT.addNode (node, xmlMap, level);
            currNode.node = node;
        }
    }
    else {
		currNode.node = node;
/*		text = GS_XMNT.getElementText (element, doc);
        try {
    		node = new XML (text);
    		node = GS_XMNT.addNode (node, xmlMap, level);
        }
        catch (e) {
            alert (e);
        } */
    }

    element2 = element.FirstChildElement;
    while(element2.ObjectValid () === 1) {
        GS_XMNT.makeXmlMap (element2, level + 1, xmlMap, doc, currNode);
        element2 = element2.NextSiblingElement;
    }
};

GS_XMNT.addNode = function (node, xmlMap, level) {
    
    var i, parent;

    parent = xmlMap;
    for (i = 1; i < level; i += 1) {
        parent = parent.child (parent.children ().length () - 1);
    }
    parent.appendChild (node);
    
    return node;
};

GS_XMNT.makeNode = function (element) {
    
    var node, attrList, count, i, attrValue;

    node = <{element.ElementDef.Name} unique={element.Unique}/>;
    attrList = element.Attributes;
	count = attrList.length;
    for (i = 0; i < count; i += 1) {
        attrValue = attrList[i].values[0] ? attrList[i].values[0] : "";
        if (attrValue !== "") {
            node.@[attrList[i].name] = attrValue;
        }
    }

    return node;
};

GS_XMNT.getText = function (textObj, doc) {
                
    // Gets the text from the text object.

    var text = "", textItems, count, i;
    return text;
                
    // Get a list of the strings in the text object or text range.
    if (textObj.constructor.name !== "TextRange") {
        textItems = textObj.GetText (Constants.FTI_String);
    } 
	else {
        textItems = doc.GetTextForRange (textObj, Constants.FTI_String);
    }
                
    // Concatenate the strings.
	count = textItems.len;
    for (var i = 0; i < count; i += 1) {
        text += (textItems[i].sdata);
    }

    return GS_XMNT.cleanText (text); // Return the text
};

GS_XMNT.cleanText = function (text) {
    
    var regex;
    
    // Non-standard spaces to regular spaces.
    regex = /[\x08-\x14\x20]+/g;
    if (regex.test (text) === true) {
        text = text.replace (regex, " ");
    }
/*    // Delete leading and trailing spaces.
    regex = /(?:^[\x08-\x14\x20]+|[\x08-\x14\x20:]+$)/g;
    if (regex.test (text) === true) {
        text = text.replace (regex, "");
    } */

    return text;    
};

GS_XMNT.main (app.ActiveDoc, app.ActiveDoc.MainFlowInDoc.HighestLevelElement);
149
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Aug 08, 2025 Aug 08, 2025
LATEST

Hi Rick, 

I don't have much time right now, so I can't take a closer look at the script.
But maybe my experience from last year will help you.
Passing larger XML to a function always caused a crash for me. I then changed it and accessed the global XML object DIRECTLY in the function. That worked. Give it a try.

Have a nice weekend.

Klaus

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines