Highlighted

Trying to understand element selection limitations

Enthusiast ,
Feb 20, 2017

Copy link to clipboard

Copied

I have created an ExtendScript which is used to install structured application definitions the first time that a script is run. Here's what it does:

  1. Opens a copy of a structapps.fm file that contains the required XML application definitions.
  2. Creates a backup of the local structapps file.
  3. Opens the local structapps file.
  4. Delete any XML applications with names that match the new XML apps.
  5. Copies the new XML apps into the local structapps file.
  6. Reads the structapps file using fmDispatcher, saves and closes the files.

This all works very well up to a point. Where I have a problem is with the insertion point for the copied XML applications. I want to insert them directly after the <Version> element.

I can do that when there are other following <XMLApplication> elements. However if there are no following elements it inserts the copied elements before the <Version> element which is invalid. Here's the code:

function copyXApps(localStrappDoc, sourceStrappDoc)

{

    var copyApps, ser = new ElementRange(), ter = new ElementRange(), copyApps, target,

    sourceElem = sourceStrappDoc.MainFlowInDoc.HighestLevelElement.FirstChildElement.NextSiblingElement;

    target = localStrappDoc.MainFlowInDoc.HighestLevelElement.FirstChildElement.NextSiblingElement;

   

    if(sourceElem === null || sourceElem.ObjectValid() === false) return;

   

        ser.beg.parent = ser.end.parent = sourceElem.ParentElement;

        ser.beg.child = sourceElem;

        ser.end.child = sourceElem.ParentElement.LastChildElement;

       

        ser.end.parent = sourceElem.ParentElement;

        ser.beg.offset = 0;

        ser.end.offset = 0;

       

    //Set the document element selection.

    sourceStrappDoc.ElementSelection = ser;

    copyApps = sourceStrappDoc.Copy (0);

   

    if (copyApps === Constants.FE_Success){

        ter.beg.parent = ter.end.parent = target.ParentElement;

        ter.beg.child = ter.end.child = target;

        ter.beg.offset = ter.end.offset = 0;

       

        //Set insertion point for copied Xapps, then paste:

        localStrappDoc.ElementSelection = ter;

        pasteApps = localStrappDoc.Paste (0);

        return pasteApps;

       }

    }

There's also a problem with the selection code which selects all apps except the last one despite using LastChildElement. I seem to be misunderstanding something here.

Thanks for any help.

Ian

Adobe Community Professional
Correct answer by frameexpert | Adobe Community Professional

Hi Ian,

I adapted the functions below from FDK functions that I got from Russ Ward years ago. You call the getElementRangeConstants function to set up some constants for the getElementRange function to use. You pass it an element (in your case, the Version element) and the desired location (in your case, AFTER_ELEMENT) and it rerturns an ElementRange. I have used this function and a FrameScript variation for years without any problems. Please let me know if you have any questions or comments. Thanks.

-Rick

function getElementRange (element, location) {

   

    var elementRange = new ElementRange;

   

    switch (location) {

       

        case BEFORE_ELEMENT :

            elementRange.beg.parent = element.ParentElement;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element;

            elementRange.end.parent = element.ParentElement;

            elementRange.end.child = element;

            elementRange.end.offset = 0;

            break;

       

        case AFTER_ELEMENT :

            elementRange.beg.parent = element.ParentElement;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element.NextSiblingElement;

            elementRange.end.parent = element.ParentElement;

            elementRange.end.child = element.NextSiblingElement;

            elementRange.end.offset = 0;

            break;

       

        case BEGINNING_OF_BRANCH :

            elementRange.beg.parent = element;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element.FirstChildElement;

            elementRange.end.parent = element;

            elementRange.end.child = element.FirstChildElement;

            elementRange.end.offset = 0;

            break;

       

        case END_OF_BRANCH :

            elementRange.beg.parent = element;

            elementRange.beg.offset = 0;

            elementRange.end.parent = element;

            elementRange.end.offset = 0;

            break;

       

        case SELECT_ELEMENT :

            if (element.NextSiblingElement.ObjectValid()) {

                elementRange.beg.parent = element.ParentElement;

                elementRange.beg.child = element;

                elementRange.end.parent = element.ParentElement;

                elementRange.end.child = element.NextSiblingElement;

            }

            else {

                elementRange.beg.parent = element.ParentElement;

                elementRange.beg.child = element;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element.ParentElement;

                elementRange.end.offset = 0;

            }

            break;

           

        case SELECT_CONTENTS :

            if (element.FirstChildElement.ObjectValid()) {

                elementRange.beg.parent = element;

                elementRange.beg.child = element.FirstChildElement;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element;

                elementRange.end.offset = 0;

            }

            else {

                elementRange.beg.parent = element;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element;

                elementRange.end.offset = Constants.FV_OBJ_END_OFFSET;

            }

            break;

    }

    return elementRange;

}

function getElementRangeConstants () {

   

    BEFORE_ELEMENT = 1;

    AFTER_ELEMENT = 2;

    BEGINNING_OF_BRANCH = 3;

    END_OF_BRANCH = 4;

    SELECT_ELEMENT = 5;

    SELECT_CONTENTS = 6;

}

TOPICS
Scripting

Views

627

Likes

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

Trying to understand element selection limitations

Enthusiast ,
Feb 20, 2017

Copy link to clipboard

Copied

I have created an ExtendScript which is used to install structured application definitions the first time that a script is run. Here's what it does:

  1. Opens a copy of a structapps.fm file that contains the required XML application definitions.
  2. Creates a backup of the local structapps file.
  3. Opens the local structapps file.
  4. Delete any XML applications with names that match the new XML apps.
  5. Copies the new XML apps into the local structapps file.
  6. Reads the structapps file using fmDispatcher, saves and closes the files.

This all works very well up to a point. Where I have a problem is with the insertion point for the copied XML applications. I want to insert them directly after the <Version> element.

I can do that when there are other following <XMLApplication> elements. However if there are no following elements it inserts the copied elements before the <Version> element which is invalid. Here's the code:

function copyXApps(localStrappDoc, sourceStrappDoc)

{

    var copyApps, ser = new ElementRange(), ter = new ElementRange(), copyApps, target,

    sourceElem = sourceStrappDoc.MainFlowInDoc.HighestLevelElement.FirstChildElement.NextSiblingElement;

    target = localStrappDoc.MainFlowInDoc.HighestLevelElement.FirstChildElement.NextSiblingElement;

   

    if(sourceElem === null || sourceElem.ObjectValid() === false) return;

   

        ser.beg.parent = ser.end.parent = sourceElem.ParentElement;

        ser.beg.child = sourceElem;

        ser.end.child = sourceElem.ParentElement.LastChildElement;

       

        ser.end.parent = sourceElem.ParentElement;

        ser.beg.offset = 0;

        ser.end.offset = 0;

       

    //Set the document element selection.

    sourceStrappDoc.ElementSelection = ser;

    copyApps = sourceStrappDoc.Copy (0);

   

    if (copyApps === Constants.FE_Success){

        ter.beg.parent = ter.end.parent = target.ParentElement;

        ter.beg.child = ter.end.child = target;

        ter.beg.offset = ter.end.offset = 0;

       

        //Set insertion point for copied Xapps, then paste:

        localStrappDoc.ElementSelection = ter;

        pasteApps = localStrappDoc.Paste (0);

        return pasteApps;

       }

    }

There's also a problem with the selection code which selects all apps except the last one despite using LastChildElement. I seem to be misunderstanding something here.

Thanks for any help.

Ian

Adobe Community Professional
Correct answer by frameexpert | Adobe Community Professional

Hi Ian,

I adapted the functions below from FDK functions that I got from Russ Ward years ago. You call the getElementRangeConstants function to set up some constants for the getElementRange function to use. You pass it an element (in your case, the Version element) and the desired location (in your case, AFTER_ELEMENT) and it rerturns an ElementRange. I have used this function and a FrameScript variation for years without any problems. Please let me know if you have any questions or comments. Thanks.

-Rick

function getElementRange (element, location) {

   

    var elementRange = new ElementRange;

   

    switch (location) {

       

        case BEFORE_ELEMENT :

            elementRange.beg.parent = element.ParentElement;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element;

            elementRange.end.parent = element.ParentElement;

            elementRange.end.child = element;

            elementRange.end.offset = 0;

            break;

       

        case AFTER_ELEMENT :

            elementRange.beg.parent = element.ParentElement;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element.NextSiblingElement;

            elementRange.end.parent = element.ParentElement;

            elementRange.end.child = element.NextSiblingElement;

            elementRange.end.offset = 0;

            break;

       

        case BEGINNING_OF_BRANCH :

            elementRange.beg.parent = element;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element.FirstChildElement;

            elementRange.end.parent = element;

            elementRange.end.child = element.FirstChildElement;

            elementRange.end.offset = 0;

            break;

       

        case END_OF_BRANCH :

            elementRange.beg.parent = element;

            elementRange.beg.offset = 0;

            elementRange.end.parent = element;

            elementRange.end.offset = 0;

            break;

       

        case SELECT_ELEMENT :

            if (element.NextSiblingElement.ObjectValid()) {

                elementRange.beg.parent = element.ParentElement;

                elementRange.beg.child = element;

                elementRange.end.parent = element.ParentElement;

                elementRange.end.child = element.NextSiblingElement;

            }

            else {

                elementRange.beg.parent = element.ParentElement;

                elementRange.beg.child = element;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element.ParentElement;

                elementRange.end.offset = 0;

            }

            break;

           

        case SELECT_CONTENTS :

            if (element.FirstChildElement.ObjectValid()) {

                elementRange.beg.parent = element;

                elementRange.beg.child = element.FirstChildElement;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element;

                elementRange.end.offset = 0;

            }

            else {

                elementRange.beg.parent = element;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element;

                elementRange.end.offset = Constants.FV_OBJ_END_OFFSET;

            }

            break;

    }

    return elementRange;

}

function getElementRangeConstants () {

   

    BEFORE_ELEMENT = 1;

    AFTER_ELEMENT = 2;

    BEGINNING_OF_BRANCH = 3;

    END_OF_BRANCH = 4;

    SELECT_ELEMENT = 5;

    SELECT_CONTENTS = 6;

}

TOPICS
Scripting

Views

628

Likes

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
Feb 20, 2017 0
Adobe Community Professional ,
Feb 20, 2017

Copy link to clipboard

Copied

Hi Ian,

I adapted the functions below from FDK functions that I got from Russ Ward years ago. You call the getElementRangeConstants function to set up some constants for the getElementRange function to use. You pass it an element (in your case, the Version element) and the desired location (in your case, AFTER_ELEMENT) and it rerturns an ElementRange. I have used this function and a FrameScript variation for years without any problems. Please let me know if you have any questions or comments. Thanks.

-Rick

function getElementRange (element, location) {

   

    var elementRange = new ElementRange;

   

    switch (location) {

       

        case BEFORE_ELEMENT :

            elementRange.beg.parent = element.ParentElement;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element;

            elementRange.end.parent = element.ParentElement;

            elementRange.end.child = element;

            elementRange.end.offset = 0;

            break;

       

        case AFTER_ELEMENT :

            elementRange.beg.parent = element.ParentElement;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element.NextSiblingElement;

            elementRange.end.parent = element.ParentElement;

            elementRange.end.child = element.NextSiblingElement;

            elementRange.end.offset = 0;

            break;

       

        case BEGINNING_OF_BRANCH :

            elementRange.beg.parent = element;

            elementRange.beg.offset = 0;

            elementRange.beg.child = element.FirstChildElement;

            elementRange.end.parent = element;

            elementRange.end.child = element.FirstChildElement;

            elementRange.end.offset = 0;

            break;

       

        case END_OF_BRANCH :

            elementRange.beg.parent = element;

            elementRange.beg.offset = 0;

            elementRange.end.parent = element;

            elementRange.end.offset = 0;

            break;

       

        case SELECT_ELEMENT :

            if (element.NextSiblingElement.ObjectValid()) {

                elementRange.beg.parent = element.ParentElement;

                elementRange.beg.child = element;

                elementRange.end.parent = element.ParentElement;

                elementRange.end.child = element.NextSiblingElement;

            }

            else {

                elementRange.beg.parent = element.ParentElement;

                elementRange.beg.child = element;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element.ParentElement;

                elementRange.end.offset = 0;

            }

            break;

           

        case SELECT_CONTENTS :

            if (element.FirstChildElement.ObjectValid()) {

                elementRange.beg.parent = element;

                elementRange.beg.child = element.FirstChildElement;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element;

                elementRange.end.offset = 0;

            }

            else {

                elementRange.beg.parent = element;

                elementRange.beg.offset = 0;

                elementRange.end.parent = element;

                elementRange.end.offset = Constants.FV_OBJ_END_OFFSET;

            }

            break;

    }

    return elementRange;

}

function getElementRangeConstants () {

   

    BEFORE_ELEMENT = 1;

    AFTER_ELEMENT = 2;

    BEGINNING_OF_BRANCH = 3;

    END_OF_BRANCH = 4;

    SELECT_ELEMENT = 5;

    SELECT_CONTENTS = 6;

}

Likes

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
Reply
Loading...
Feb 20, 2017 1
Enthusiast ,
Feb 20, 2017

Copy link to clipboard

Copied

Perfect thank you Rick.

This does exactly what I need and will be even more useful for my next project!

Ian

Likes

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
Reply
Loading...
Feb 20, 2017 0
Mentor ,
Feb 21, 2017

Copy link to clipboard

Copied

Rick, thanks for the attribution. The truth is, unless you are much smarter than me, you need to get a function like this set up and then just use it. You might look over that code and think "wow, Russ sure knows this stuff" but the truth is that it is still quite cryptic to me. All I did was spend an hour manually setting up the different types of selections, while querying the ElementSelection property to see how it was set up. Then I built the function to model the behavior that I saw. All these years later, I still don't really understand the logic of the ElementRange structure. I could probably get it if I really studied hard, but my ignorance has not hindered me yet. So, heck with it.

Russ

Likes

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
Reply
Loading...
Feb 21, 2017 0
Adobe Community Professional ,
Feb 21, 2017

Copy link to clipboard

Copied

I have a similar development style, except it usually takes me more than an hour :-). I keep banging on it until it works; my understanding of it may come later. Many of my functions are black boxes to me--I know they work so I use them with confidence, even if I sometimes forget the logic behind them.

That said, I appreciate all of your excellent plugins and code samples and your willingness to share them. You have helped me solve a lot of problems over the years.

Here is one from my wishlist. Saxon recently released its XSLT engine for JavaScript. It's meant primarily for use in a web server but it would be fantastic if it could be used with ExtendScript.

Likes

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
Reply
Loading...
Feb 21, 2017 0
Advocate ,
Feb 21, 2017

Copy link to clipboard

Copied

Hi Rick,

I was looking for that a while ago, as I am doing almost everything in XML and XSLT now. After much asking and not giving up, I did get the info from the dev team in India about calling the XSLT processor from ExtendScript. I will prepare a post about it. I can send you the code, too.

Ciao

Likes

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
Reply
Loading...
Feb 21, 2017 1
Adobe Community Professional ,
Feb 21, 2017

Copy link to clipboard

Copied

Jang, that would be fantastic. Thank you. -Rick

Likes

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
Reply
Loading...
Feb 21, 2017 0
Enthusiast ,
Feb 21, 2017

Copy link to clipboard

Copied

Hi Russ,

You did much better than me on getting a working understanding! I tried to match the reported element locations with a script that reported everything to the console. Unfortunately that didn't return the information that I expected...

Ian

Likes

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
Reply
Loading...
Feb 21, 2017 0
Mentor ,
Feb 22, 2017

Copy link to clipboard

Copied

Here is a general purpose function I use to report the current element selection, text selection, and/or graphic selection. I think every developer needs some version of something like this.

function cm_ReportIds(doMsgBox, doConsole)

{

    var msg = "";

    var doingBook = false;

    var file = app.ActiveDoc;

   

    if(!file.ObjectValid())

    {

        file = app.ActiveBook;

        doingBook = true;

    }

   

    if(!file.ObjectValid())

    {

        alert ("No active document or book.");

        return;

    }

    msg += "----------------------------------------\n";

    if(doingBook)   

        msg += "ACTIVE BOOK: ";

    else  

        msg += "ACTIVE DOCUMENT: \n\n"

    msg += file.Name + "\n(ID: " + file.id + ")\n";

    msg += "----------------------------------------\n\n";

  

    msg += "----------------------------------------\n";

    msg += "CURRENT ELEMENT SELECTION (er = file.ElementSelection):\n\n";

   

    var er = file.ElementSelection;

   

    if(!er.beg.parent.ObjectValid() &&

        !er.beg.child.ObjectValid() &&

        !er.end.parent.ObjectValid() &&

        !er.end.child.ObjectValid())

    {

        msg += "(No valid element selection)\n";

    }

   

    else

    {

        msg += "er.beg.parent (element):  ";

        if(er.beg.parent.ObjectValid())

            msg += "<" + er.beg.parent.ElementDef.Name + ">      (ID: " + er.beg.parent.id + ")\n";

        else msg += "-NONE-\n";

       

        msg += "er.beg.child (element):    ";

        if(er.beg.child.ObjectValid())

            msg += "<" + er.beg.child.ElementDef.Name + ">      (ID: " + er.beg.child.id + ")\n";

        else msg += "-NONE-\n";

       

        msg += "er.beg.offset: " + er.beg.offset + "\n\n";

       

        msg += "er.end.parent (element):  " ;

        if(er.end.parent.ObjectValid())

            msg += "<" + er.end.parent.ElementDef.Name + ">      (ID: " + er.end.parent.id + ")\n";

        else msg += "-NONE-\n";

       

        msg += "er.end.child (element):    ";

        if(er.end.child.ObjectValid())

            msg += "<" + er.end.child.ElementDef.Name + ">      (ID: " + er.end.child.id + ")\n";

        else msg += "-NONE-\n";

       

        msg += "er.end.offset: " + er.end.offset + "\n";

    }

    msg += "----------------------------------------\n\n";

    if(!doingBook)

    {

        msg += "----------------------------------------\n";

        msg += "CURRENT TEXT SELECTION (tr = file.TextSelection):\n\n";

       

        var tr = file.TextSelection;

       

        if(tr.beg.obj.ObjectValid())

        {

            msg += "tr.beg.obj (pgf ID):  " + tr.beg.obj.id + "\n";

            msg += "tr.beg.obj (pgf format):  " + tr.beg.obj.Name + "\n";

            msg += "tr.beg.offset:  " + tr.beg.offset + "\n\n";

           

            msg += "tr.end.obj (pgf ID):  " + tr.end.obj.id + "\n";

            msg += "tr.end.obj (pgf format):  " + tr.end.obj.Name + "\n";

            msg += "tr.end.offset:  " + tr.end.offset + "\n";

        }

        else msg += "(No valid text selection)\n";

        msg += "----------------------------------------\n\n";

        msg += "----------------------------------------\n";

        msg += "CURRENT GRAPHIC SELECTION:\n\n";

      

       var graphic = file.FirstSelectedGraphicInDoc;

       var path = "";

       

        if(graphic.ObjectValid())

        {

            msg += "Selected graphic object: ";

            if(graphic.constructor.name == "AFrame")

            {

                msg += "Anchored frame       (ID: " + graphic.id + ")\n";

                var elem = graphic.Element;

                if(elem.ObjectValid())

                {

                    msg += "Anchored frame element:  <" +

                        elem.ElementDef.Name + ">    (ID: " + elem.id + ")\n";

                }

                msg += "First graphic object in the frame: ";

               

                graphic = graphic.FirstGraphicInFrame;

                if(graphic.ObjectValid())

                {

                    msg += graphic.constructor.name + "       (ID: " + graphic.id + ")\n";

                    if(graphic.constructor.name == "Inset")

                        path = graphic.InsetFile;

                }

                else msg += "(none detected)";

            }

       

            else

            {

                msg += graphic.constructor.name + "       (ID: " + graphic.id + ")\n";

                if(graphic.constructor.name == "Inset")

                    path = graphic.InsetFile;

               

                msg += "Parent anchored frame: ";

               

                graphic = graphic.FrameParent;

                if(graphic.ObjectValid() && graphic.constructor.name == "AFrame")

                {

                    msg += "  ID: " + graphic.id + "\n";

                    var elem = graphic.Element;

                    if(elem.ObjectValid())

                    {

                        msg += "Anchored frame element:  <" +

                            elem.ElementDef.Name + ">    (ID: " + elem.id + ")\n";

                    }

                }

                else msg += "(none detected)";

            }

       

            if(path != "")

            {

                msg += "\nReferenced graphic filepath:\n" + path + "\n";

            }

        }           

       

        else msg += "(No valid graphic selection)\n";

        msg += "----------------------------------------\n\n";

    }

   

    if(doMsgBox)

        alert(msg);

    if(doConsole)

    {

        while(msg.length > 0)

        {

            Err(msg.substring(0, 255));

            msg = msg.substring(255, msg.length);

        }

    }

}

Likes

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
Reply
Loading...
Feb 22, 2017 2
Advocate ,
Feb 21, 2017

Copy link to clipboard

Copied

Ian,

There is also another method to get the copy into the right location, using text locations:

oPgf = oDoc.MainFlowInDoc.FirstTextFrameInFlow.FirstPgf;

if( oPgf.NextPgfInFlow.ObjectValid( )

     oTLoc = new TextLoc( oPgf.NextPgfInFlow, 0 );

else

     oTLoc = new TextLoc( oPgf, Constants.FV_OBJ_END_OFFSET);

oTRange = new TextRange( oTLoc, oTLoc );

oDoc.TextSelection = oTRange;

oDoc.Paste( true );

This may not work in generic cases where you want to get the element location just right, but in this particular case it is all the code you need.

Likes

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
Reply
Loading...
Feb 21, 2017 1