Pages: 1
Print
Author Topic: Get nodelist from Selection/Range?  (Read 9541 times)
schwamkrug
Member

Posts: 33


« on: September 11, 2012, 10:50:39 AM »

I feel like I'm missing something obvious, but is there an API call to get a node list/array from a selction? Something along these lines:

var rng = Application.ActiveDocument.Range;
var nodeList = rng.GetNodeList();
//iterate over the nodeList and do things with it

So, if I select a couple of <p> elements, that nodeList would have DOMNodes for those p tags? Of course, that doesn't quite make sense if I've selected part of a text node.
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #1 on: September 12, 2012, 10:26:29 AM »

Does getNodesByXPath() help you here? That is the first thing I can think of that requires the least amount of code.
The DOMElement object has this as a method. You can use Selection.ContainerNode to get the node for the current selection's immediate parent.

Logged
schwamkrug
Member

Posts: 33


« Reply #2 on: September 12, 2012, 10:36:39 AM »

I don't think that would help because it wouldn't give me the ability to see which specific nodes are selected. Imagine this chunk of content:

<body>
   <p>Paragraph 1</p>
   <p>Paragraph 2</p>
   <p>Paragraph 3</p>
   <p>Paragraph 4</p>
   <p>Paragraph 5</p>
</body>

And assume I have paragraph 2 and paragraph 3 selected. Selection.ContainerNode returns the <body> element. I don't see how I could use XPath to get the two specific paragraphs that are selected.
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #3 on: September 12, 2012, 12:02:44 PM »

What do you need to do with these nodes once you have them?
Perhaps knowing that will help me provide a better answer.
Logged
schwamkrug
Member

Posts: 33


« Reply #4 on: September 12, 2012, 12:19:27 PM »

Sure. I'm working on a macro to enable cutting/pasting content as a conref. It was pretty easy to do this with a single element...with the cursor inside a given element, get Selection.ContainerNode, then do typical DOM stuff to add a conref to the clipboard. But what I want to do is extend that to also do conref range. Consider the following (assume all these elements have ids):

<body>
   <p>Paragraph 1, with some <ph><b>inline</b></ph> elements <ph>included</ph> in them.</p>
   <p>Paragraph 2</p>
   <p>Paragraph 3</p>
   <p>Paragraph 4</p>
   <p>Paragraph 5</p>
</body>

If the following is selected:
   <p>Paragraph 2</p>
   <p>Paragraph 3</p>
   <p>Paragraph 4</p>

I'd use the id from Paragraph 2 for the conref attribute, and the id from paragraph 4 for the conrefend attribute.

However, if I selected this:
   <ph><b>inline</b></ph> elements <ph>included</ph>
Or this:
    some <ph><b>inline</b></ph> elements <ph>included</ph> in them.

Those would be error conditions. Those wouldn't be the only error conditions. The main thing is that I'd need to get a node list and look at it to see.
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #5 on: September 12, 2012, 02:05:28 PM »

So this is actually modifying the DITA authoring functionality in XMetaL Author Enterprise?
Logged
schwamkrug
Member

Posts: 33


« Reply #6 on: September 12, 2012, 03:00:53 PM »

I guess it depends on what you mean by modifying... I'm just adding a macro to fill the clipboard with a conref. It's written as an application macro, so it's not really aware of whether it's in a DITA doc or not. It just looks for elements with id attributes.  I'm not looking to hook into or override any of the functionality you guys provide.
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #7 on: September 12, 2012, 03:14:29 PM »

This is very likely incomplete but you might build on it. There are probably test cases I have not thought of.
If you want to capture other types of nodes (text, comment, PI, etc) then this would probably need to be substantially rewritten.

Code:
//XMetaL Script Language JScript:
//debugger;
var nodes = Array();
var rngS = ActiveDocument.Range;
var rngE = rngS.Duplicate;
rngS.Collapse(sqCollapseStart);
rngE.Collapse(sqCollapseEnd);
var i = 0;
rngS.MoveToElement();
while(rngS.IsLessThan(rngE,false)) {
if(rngS.ContainerNode.nodeType == 1) {
nodes[i] = rngS.ContainerNode;
i++;
}
if(rngS.MoveToElement() == false) {
break;
}
}

var msg = "";
for(i=0;i<nodes.length;i++) {
msg += nodes[i].nodeName + "\n";
}
Application.Alert(msg);
Logged
schwamkrug
Member

Posts: 33


« Reply #8 on: September 12, 2012, 03:38:54 PM »

Thanks, Derek. That helps a lot.

Slight change of subject...is there a way to reload .mcr files without restarting XMetaL so I can test changes to my code?
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #9 on: September 12, 2012, 05:17:44 PM »

If you have XMetaL Developer installed then you can use Visual Studio's "edit and continue" feature though in some cases that is impossible (depends on the changes you have made to your script). Microsoft doesn't seem to document this feature for scripting languages that use WSH but this comes close: http://msdn.microsoft.com/en-us/library/ba77s56w%28v=vs.80%29.aspx

If you don't have XMetaL Developer then calling the API Application.RefreshMacros() will do what you want, depending on which events the macros are in. Some events will still require a restart because they just won't fire any longer (mostly events that fire at application start up obviously). By default there is a macro included that calls that API (it is in xmetal.mcr) and pressing Ctrl+Alt+R will run it.
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #10 on: September 12, 2012, 05:21:23 PM »

"Edit and continue" in Visual Studio is described well here: http://www.codeproject.com/Articles/359801/10plus-powerful-debugging-tricks-with-Visual-Studi

Most of the other features work as described for JScript / VBScript as well.
Logged
schwamkrug
Member

Posts: 33


« Reply #11 on: September 12, 2012, 09:02:59 PM »

Awesome, thanks.

So, I think an easier answer just slapped me across the face:

Code:
var rng = Application.ActiveDocument.Range;
var selectionXMLString = "<selectionroot>"+rng.Text+"</selectionroot>";
var xmlDoc = new ActiveXObject("msxml2.DOMDocument");
xmlDoc.loadXML(selectionXMLString);
var nodes = xmlDoc.documentElement.childNodes;
Logged
Pages: 1
Print
Jump to: