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

Add an Element to an ElementRange

Community Expert ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

Here is a screenshot to illustrate my question:

AddToElementRange.png

I have an ElementRange as illustrated by the selection in the screenshot. I need to add the two follow li elements to the ElementRange because they have the same status attribute value ("new"). Give an ElementRange, how do I add the next sibling element to the ElementRange? I am content with adding them one at a time, if necessary. I have some ideas and will post some code, but I am trying to get a head-start by asking here. Thank you very much.

TOPICS
Scripting

Views

542

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 ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

Here is what I have working:

function addNextSiblingToElementRange (element, doc) {

   

    var nextSibling = element.NextSiblingElement.NextSiblingElement;

    var elementRange = new ElementRange;

   

    if (nextSibling.NextSiblingElement.ObjectValid ()) {

        elementRange.beg.parent = element.ParentElement;

        elementRange.beg.child = element;

        elementRange.end.parent = element.ParentElement;

        elementRange.end.child = nextSibling.NextSiblingElement;

    }

    else {

        elementRange.beg.parent = element.ParentElement;

        elementRange.beg.child = element;

        elementRange.beg.offset = 0;

        elementRange.end.parent = element.ParentElement;

        elementRange.end.offset = 0;

    }

    return elementRange;

}

Thanks to Russ for the original code. What I want to do now is to find the last consecutive sibling element that has the status="new" value. Given the selected element, I can use this xpath statement to get the next one:

following-sibling::*[1][@status='new']

I really want to get the last one, but I am not sure if there is a single xpath statement that I can use. At this point I can use a loop, which I am developing now.

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 ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

Here is my function to get the last consecutive sibling element that has the xpath statement.

var doc = app.ActiveDoc;

// The first selected element.

var element = doc.ElementSelection.beg.child;

alert (element.id);

// Get the last consecutive element of the selected element that matches the xpath statement.

element = getLastConsecutiveSibling (element, "following-sibling::*[1][@status='new']", doc);

alert (element.id);

function getLastConsecutiveSibling (element, xpath, doc) {

  

    var elements;

  

    do {

        elements = getElements (xpath, element, doc);

        if (elements.length) {

            element = elements[0];

        }

    }

    while (elements.length);

  

    return element;      

}

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 ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

Here is the required getElements function that uses FrameSLT:

function getElements (xpath, element, doc) {

   

    var elements = [], seq, id, contextId;

   

    contextId = element === 0 ? 0 : element.id;

   

    CallClient ("FrameSLT" , "SetAppParm---EC_ReturnIDType---UID");

    seq = CallClient ("FrameSLT", "ParseXPath---" + xpath + "---False");

    if (seq > 100) {

        id = CallClient ("FrameSLT" , "FindNextNode---" + seq + "---" + doc.id + "---" + contextId + "---Main");

        while (id > 100) {

            elem = doc.GetUniqueObject (Constants.FO_Element, id);

            if (elem.ObjectValid () === 1) {

                elements.push (elem);

            }

            id = CallClient ("FrameSLT" , "FindNextNode---" + seq + "---" + doc.id + "---" + contextId + "---Main");

        }

    }

   

    return elements;   

}

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
Advocate ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

Hi Rick,

It will be much faster to just walk the tree and check the attribute values until you find one that does not have the required value. Running an Xpath everytime. Even a single CallClient will take more tine, and Russ' code probably does the same.

If your current last element is oLastElement, this is what you would need to run:

var oNext = oLastElement.NextSiblingElement;

if( GetAttribute( oNext, "status" ) == "new" )

{

     var bFound = false;

     while( oNext.NextSiblingElement.ObjectValid( ) && !bFound )

     {

          if( GetAttribute( oNext.NextSiblingElement, "status" ) == 'new' )

               oNext = oNext.NextSiblingElement;

          else

               bFound = true;

     }

}

now your oNext contains the last element with the status 'new'.

function GetAttribute ( myElem, myAttrName )

{

     var attrs = myElem.Attributes;

     var retval = "";

     var i = 0;

     var bFound = false;

    while ( i < attrs.length && !bFound )

    {

          if ( attrs.name == myAttrName )

          {

                 bFound = true;

                 if ( attrs.values.length > 0 )

                 {

                      retval = attrs.values[0];

                 }

         }

         i++;

  }

  return retval;

}

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 ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

Hi Jang, All good points, thank you. I want to use xpath here, because there will be some variety in the attribute/value combinations that I will need to find. So I am opting to use FrameSLT and xpath because this will give me some flexibility in isolating exactly what I need, even at the cost of some efficiency. -Rick

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 ,
Aug 31, 2016 Aug 31, 2016

Copy link to clipboard

Copied

LATEST

Hi Rick,

Fun challenge. Thanks for giving me the chance to stew over what FrameSLT can do. And, my apologies for the delay in response.

I think I figured it out, but you will need to be the judge. Given some context element with @status="new", I believe this will get the last consecutive sibling with the same status:

.[following-sibling::*[1 and @status="new"]]/following-sibling::*[@status="new" and not(following-sibling::*[1 and @status="new"])]

...or it will get nothing if there are no qualifying siblings. Literally, this expression says "first check to see if there is at least one adjacent @status="new" sibling -and then- "match me if I'm a sibling with @status="new" -and- I myself do not have the same sibling immediately following (position() = 1).

Note that this expression could match all "last elements" on a branch if there are multiple independent groups. So you should just call it once for each original context element, then don't forget to call ResetSequence to initialize the navigator for the expression. Or, ParseXPath will also reset the sequence with no additional expense. If the expression is already parsed, a ParseXPath call basically just invokes a ResetSequence and returns you the same sequence number.

Hope this helps.

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 ,
Aug 26, 2016 Aug 26, 2016

Copy link to clipboard

Copied

OK, let me restate my task. Given any element with a particular status attribute value, I want to make an element range containing all of the following consecutive elements with the same status attribute value. First, to isolate the elements that are the first element in a series of sibling elements (or the first with no ancestors), I am using this xpath statement with FrameSLT:

//*[@status="new"][not(ancestor::*[@status="new"])][not(preceding-sibling::*[1][@status="new"])]

This makes sure that I don't process any elements that have ancestors with the same value, or elements that have immediate preceding sibling elements with the same value. For each of these elements, I will get the appropriate range by using the code below. NOTE: For the code below, I am testing with the "first" element being the currently selected element in the document. In the finished script, I will loop through the elements returned by the xpath statement above.

#target framemaker

var doc = app.ActiveDoc;

// Start with the first selected element.

var element = doc.ElementSelection.beg.child;

// Get the last consecutive following sibling element that matches the xpath statement.

var lastElement = getLastConsecutiveSibling (element, "following-sibling::*[1][@status='new']", doc);

// Now that we have the last element (which may actually be the same as the first), get the element range.

var elementRange = getConsecutiveElementRange (element, lastElement, doc);

// Select the element range.

doc.ElementSelection = elementRange;

function getLastConsecutiveSibling (element, xpath, doc) {

   

    var elements;

   

    do {

        elements = getElements (xpath, element, doc);

        if (elements.length) {

            element = elements[0];

        }

    }

    while (elements.length);

   

    return element;       

}

function getConsecutiveElementRange (firstElement, lastElement, doc) {

   

    var nextSibling = lastElement.NextSiblingElement;

    var elementRange = new ElementRange;

   

    if (lastElement.NextSiblingElement.ObjectValid ()) {

        elementRange.beg.parent = element.ParentElement;

        elementRange.beg.child = element;

        elementRange.end.parent = element.ParentElement;

        elementRange.end.child = nextSibling;

    }

    else {

        elementRange.beg.parent = element.ParentElement;

        elementRange.beg.child = element;

        elementRange.beg.offset = 0;

        elementRange.end.parent = element.ParentElement;

        elementRange.end.offset = 0;

    }

    return elementRange;

   

}

function getElements (xpath, element, doc) {

   

    var elements = [], seq, id, contextId;

   

    contextId = element === 0 ? 0 : element.id;

   

    CallClient ("FrameSLT" , "SetAppParm---EC_ReturnIDType---UID");

    seq = CallClient ("FrameSLT", "ParseXPath---" + xpath + "---False");

    if (seq > 100) {

        id = CallClient ("FrameSLT" , "FindNextNode---" + seq + "---" + doc.id + "---" + contextId + "---Main");

        while (id > 100) {

            elem = doc.GetUniqueObject (Constants.FO_Element, id);

            if (elem.ObjectValid () === 1) {

                elements.push (elem);

            }

            id = CallClient ("FrameSLT" , "FindNextNode---" + seq + "---" + doc.id + "---" + contextId + "---Main");

        }

    }

   

    return elements;   

}

My only wish list item is to find a single xpath statement that would replace the getLastConsecutiveSibling function, but that is not a big deal. I am fascinated by the xpath language and always wonder if certain things can be done with it.

Any feedback or suggestions are appreciated.

-Rick

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