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

Text nodes in a structured FrameMaker file

Community Expert ,
Apr 12, 2015 Apr 12, 2015

Copy link to clipboard

Copied

Hello Scripters,

I have a recursive function that touches every element in a structured FrameMaker file. I am trying to find out how to get text nodes as I process the elements.

#target framemaker

var doc = app.ActiveDoc;

var element = doc.MainFlowInDoc.HighestLevelElement;

processElements (element);

function processElements (element) {

   

    if (element.ElementDef.ObjectValid()) {

        $.writeln (element.ElementDef.Name);

    }

    else {

        // I thought I could get text nodes here, but it never gets here.

    }

    var element2 = element.FirstChildElement;

    while(element2.ObjectValid()) {

        processElements (element2);

        element2 = element2.NextSiblingElement;

    }

}

I expected that I would get the text nodes on line 14, but it the else statement never gets there. In FrameScript, if an element has no ElementDef, then it is a text node. I am not sure about the FDK. Any ideas how I can pick up the text nodes in a function like this? Thanks.

-Rick

TOPICS
Scripting

Views

624

Translate

Translate

Report

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
Explorer ,
Apr 13, 2015 Apr 13, 2015

Copy link to clipboard

Copied

Hi Rick,

Here is how I test for a text node. I don't remember how I came to this methodology, but it seems to be reliable.

Russ

function elem_IsTextNode(elem)

{

    if(elem.ElementDef.Name == null || elem.ElementDef.Name == "") return true;

    else return false;

}

Votes

Translate

Translate

Report

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
Community Expert ,
Apr 13, 2015 Apr 13, 2015

Copy link to clipboard

Copied

Hi Russ, Thanks for the reply. I am not sure why, but my function is still not hitting any text nodes. -Rick

#target framemaker

var doc = app.ActiveDoc;

var element = doc.MainFlowInDoc.HighestLevelElement;

processElements (element);

function processElements (element) {

    

    if (element.ElementDef.Name != null && element.ElementDef.Name != "") {

        $.writeln (element.ElementDef.Name);

    }

    else {

        // I thought I could get text nodes here, but it never gets here.

        $.writeln ("************");

    }

    var element2 = element.FirstChildElement;

    while(element2.ObjectValid()) {

        processElements (element2);

        element2 = element2.NextSiblingElement;

    }

}

Votes

Translate

Translate

Report

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
Mentor ,
Apr 13, 2015 Apr 13, 2015

Copy link to clipboard

Copied

Hi Rick,

Well, I didn't look much past the test for ElementDef. But with a closer analysis, I see what may be a problem, depending on the document setup... You are starting at the root element, then stepping down through all siblings on the root element branch. It would be very unusual for there to be any text nodes on a root element branch, although I guess not impossible. If you are trying to walk the entire tree out to the ends of branches, maybe try:

element2 = element2.NextElementDFS;

...instead. That will do a full tree walk and I think it will stop at text nodes.

Russ

Votes

Translate

Translate

Report

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
Community Expert ,
Apr 13, 2015 Apr 13, 2015

Copy link to clipboard

Copied

Yes, I want to start at the root element and traverse the entire tree, including text nodes. I tried the following code, but it never writes null or a blank string to the JavaScript console, like I would expect for text nodes. It looks like the ExtendScript API skips text nodes unless I am missing something.

#target framemaker

var doc = app.ActiveDoc; 

var element = doc.MainFlowInDoc.HighestLevelElement; 

while (element.ObjectValid ()) {

    $.writeln (element.ElementDef.Name);

    element = element.NextElementDFS;

}

Votes

Translate

Translate

Report

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
Mentor ,
Apr 13, 2015 Apr 13, 2015

Copy link to clipboard

Copied

Well, I guess I was wrong. NextElementDFS does not stop at text nodes. That is unfortunate.

I use my own custom DFS walker for moving through a tree. I've always done it this way, both with the FDK and ExtendScript. Below is a "class" that I use for this. It is a bit complicated to digest and I don't know if it will be helpful to you. Note that you'll see in there:

- calls to an ov() function, which is just a little shortcut I use to test object validity.

function ov(obj)

{

    if(obj == null) return false;

    else return obj.ObjectValid();

}

- a call to an ElementDefIsText() method for an ElementDef object, to see if the element is a text node. I had forgotten about that handy little thing.

You would create an "object" something like this:

var stepper = new ElementStepper(doc.MainFlowInDoc.HighestLevelElement, false, true);

...then you can get the next step with:

elem = stepper.step();

The first call will return the original start element, then it starts walking with step(). I think it can return null, so a loop might look something like:

while(stepper.step() != null && stepper.step().ObjectValid())

{

    elem = stepper.getCurrentStep();

    if(elem.ElementDef.ElementDefIsText()) alert("text node!");

}

I'm out of time here and just guessing at a few things. I don't know if this will help you or not. It's just the way I've always done it, without any problems ever.

Russ

// Ye olde element stepper "class".

// First step always returns the startElem

// Return backwards steps will not return the startElem the last time.

function ElementStepper(startElem, returnBackwardSteps, returnTextNodes)

{

   

    this.startElem = null;

    this.returnTextNodes = returnTextNodes;

    this.currentElem = null;

    this.lastStepDir = "step_child";

    this.isFirstStep = true;

   

    if(startElem != null && !startElem.ObjectValid())

    {

        this.objectIsValid = false;

    }

     

    else

    {

        this.startElem = startElem;

        this.currentElem = startElem;

        this.objectIsValid = true;

    }

    this.objectValid = function()

    {

       return this.objectIsValid;

    }

    this.step = function()

    {

        if(!this.objectValid()) return null;

        if(!ov(this.currentElem)) return null;

       

        var tempElem = null;

        var keepGoing = true;

       

        while(keepGoing && ov(this.currentElem))

        {

            tempElem = null;

            keepGoing = false;

           

            //On the first step, we always return the starting element,

            //set as the current element

            if(this.isFirstStep)

            {

                this.isFirstStep = false;

                return this.currentElem;

            }

       

            //we'll handle the case of a starting element with no children

            //here. If it is not the first step and the starting element has no

            //children, we're done.

            if(!this.firstStep && !ov(this.startElem.FirstChildElement))

            {

                this.currentElem = null;

                return null;

            }

       

            //now, actual stepping.

           

            //If we did not step to a parent last time,

            //we always test for a child

            if(this.lastStepDir != "step_parent")

            {

                tempElem = this.currentElem.FirstChildElement;

                this.lastStepDir = "step_child";

            }

       

            //if no joy, try a sibling

            if(!ov(tempElem))

            {

                tempElem = this.currentElem.NextSiblingElement;

                this.lastStepDir = "step_sibling";

            }

            //if still no joy, try a parent

            if(!ov(tempElem))

            {

                tempElem = this.currentElem.ParentElement;

                this.lastStepDir = "step_parent";

            }

       

            //now, let''s see what we got

           

            //if we got nothing, we must have stepped off the root.

            //Nothing to do then.

            if(!ov(tempElem)) this.currentElem = null;

           

            //if we've reached our starting element, we are done.

            else if(tempElem.id == this.startElem.id) this.currentElem = null;

           

            //otherwise, time to make some decisions

            else

            {

                //if we stepped to the parent and we don't allow that, just keep going.

                if(!returnBackwardSteps && this.lastStepDir == "step_parent")

                    keepGoing = true;

                   

                //if we stepped to a text node and don't allow that, just keep going.

                if(!returnTextNodes && tempElem.ElementDef.ElementDefIsText())

                    keepGoing = true;

                   

                //otherwise, we are someplace to stop. Just set the current element and let the loop end.

                this.currentElem = tempElem;

            }

        } //end main while

       

        return this.currentElem;

    }

    this.reset = function()

    {

       if(this.objectIsValid)

       {

           this.currentElem = this.startElem;

           this.firstStep = true;

           this.lastStepDir = "step_child";

       }

    }

    this.getCurrentStep = function()

    {

        return this.currentElem;

    }

}

Votes

Translate

Translate

Report

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
Community Expert ,
Apr 13, 2015 Apr 13, 2015

Copy link to clipboard

Copied

LATEST

Hi Russ, Thanks for your generous help. It appears that the key method is ElementDefIsText. I still find recursion to be easier, so here is how I reworked my original function. Thanks for getting my brain cells working. -Rick

#target framemaker 

 

var doc = app.ActiveDoc; 

var element = doc.MainFlowInDoc.HighestLevelElement; 

 

processElements (element); 

 

function processElements (element, doc) { 

   

    var text;

   

    if (!element.ElementDef.ElementDefIsText ()) { 

        $.writeln ("<" + element.ElementDef.Name + ">"); 

        // If we are at the end of a branch, get the text.

        if (!element.FirstChildElement.ObjectValid ()) {

            text = getText (element, doc);

            if (text !== "") {

                $.writeln (getText (element, doc));

            }

        }

    } 

    else { 

        // Text node in mixed content. 

        $.writeln (getText (element, doc));

    } 

 

    var element2 = element.FirstChildElement; 

    while(element2.ObjectValid()) { 

        processElements (element2, doc); 

        element2 = element2.NextSiblingElement; 

    } 

function getText (textObj, doc) {

    // Gets the text from the text object.

    var text = "";

    // Get a list of the strings in the text object or text range.

    if (textObj.constructor.name !== "TextRange") {

        var textItems = textObj.GetText(Constants.FTI_String);

    } else {

         var textItems = doc.GetTextForRange(textObj, Constants.FTI_String);

    }

    // Concatenate the strings.

    for (var i = 0; i < textItems.len; i += 1) {

        text += (textItems.sdata);

    }

    return text; // Return the text

}

Votes

Translate

Translate

Report

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