Detect active document changing

In InDesign Scripting (UXP): What is the correct way to react to a change to the active Document. By this I mean:

  • A document is being opened
  • A document is being closed
  • The user switches from one opened document to another
  • any other change to the active document

Sadly, I found no documentation on all the events supported by ‘Application’.

Through deep research I stumbled upon the event ‘afterContextChanged’. I’m not sure what exactly “Context” means, but it seems to be something like what I’m looking for, although it seems to fire twice for every of the above actions. But as I don’t know what “Context” means exactly, I’m not sure if this event would fire at an unexpected time. E.g. changing a context could theoretically also mean opening the preferences or something like that.

Is this the correct event for a change of activeDocument, or is there a better one available?

Extra question: Is there a list of all events available for each internal object (like Application or Document etc.). The documentation points to a “list of available events” but this cannot be all events available, and isn’t providing context (Application, Document, etc.).

Good morning, @roman-edv

I know that activation is supported. I use it. 

var newestApplicationEventListenter_o = g_app_o.addEventListener(‘afterActivate’, idDomainTracking_checkDomainAsync);

where, for me, g_app_o is, I think, require(‘indesign’).app;

jsw

Thank you very much vor your answer!

The event afterActivate seems to fire when a document is getting active, so either when it is being opened or regains focus. This is very good to know! Here I noticed the same behaviour, that the event actually fires twice each time. But this could be due to a misconfiguration on my side, or a bug in multipanel-plugins.

In my case afterContextChanged seems to work better, as I can also detect when no document is open anymore (last document closed). I need to react to this as well, so this seems to be the better option for now. At least, if it doesn’t have any unexpected side-effects I don’t know about.

1 Like

It is a bit easier to find in this version of the Object Model:

https://www.indesignjs.de/extendscriptAPI/indesign-latest/

If, for instance, you look at Application or Document, you’ll see everything on the right divided into sections – Methods, Obects, Preferences, Property Listing, etc. Scroll down to the section Constants/Events to see the list of events that the object emits.

You are almost certainly seeing that because two obects are emitting the event. In the case of the afterActivate event, both Document and Layout Window emit that event.

The (stripped back) code I have been using is this:

const indesign = require("indesign");
const app = indesign.app;

const afterActivateListener = app.addEventListener("afterActivate", handleAfterActivate);

function handleAfterActivate(theEvent) {
	if (theEvent.target.constructor.name === 'Document') {	
		console.log('Document activated: ' + app.activeDocument.name);
	}
}

The line if (theEvent.target.constructor.name === 'Document') means I only do whatever I want to do when the Document emits the event, not when the Layout Window does.

In order to respond when there are no documents open, I have the above event listener paired with this event listener:

const beforeCloseListener = app.addEventListener("beforeClose", handleBeforeClose);

function handleBeforeClose(theEvent) {
	if (theEvent.constructor.name === 'DocumentEvent') {
		if (app.documents.length < 1) {
			console.log('No documents open');
		}
	}
}

Philip

Thank you very much for your thorough answer!

Thanks, I know and love this site. But I didn’t know how to read about events. So I just always convert the event constants from UPPER_SNAKE_CASE to lowerCamelCase? Is that the way to get the correct event name? I think that’s what got me confused, and I though it’s not documented at all.

Ah, I see. Thanks for the explanation! And thank you for the example code to check if the last document is closed. That’s all incredibly helpful for understanding how InDesign works.

I always struggle with scripting and plugin development because I haven’t yet found a good explanation for most things. There’s only the auto-generated API documentation (which just documents things without explaining them). The few tutorials and sample scripts only get you so far – beyond that, it feels like it’s all trial and error with no way to really read into it.

Short answer . . . Yes.

Long (rambling) answer . . . As I understand it (my terminology might not be quite right) . . .

They are constants of the Event class. They work like the enums that you get all over the place in the InDesign Object Model. The constant (or enum key) is written in UPPER_SNAKE_CASE and has a value. The value might be anything but in this case is a string which, as you have found, is always the constant written in lowerCamelCase.

You can see that this is the case if somewhere in your code you write:

console.dir(indesign.Event):

you’ll see a list of all the UPPER_SNAKE_CASE constants with their lowerCamelCase values.

Instead of:

addEventListener('afterActivate', ...

You could write:

addEventListener(indesign.Event.AFTER_ACTIVATE, ...

But in this case I can’t think of any good reason to write it like that. The first version is concise and perfectly readable.

This is in contrast to other enums where I think it does make sense to get the enum value by referencing the constant (enum key).

For instance, if I wanted to move one page after another page, I might write something like:

const pageToMove = ... // some code that references the page I want to move;
const pageAfter = ... // some code that references the page after which I want to move the page;
pageToMove.move(indesign.LocationOptions.AFTER, pageAfter); // move the page

AFTER is an enum of the LocationOptions class. It does have a value, 1634104421, so I think you could write:

pageToMove.move(1634104421, pageAfter);

But I would always write it out in the long form because if I ever had to troubleshoot my code, I wouldn’t have any idea what the numbers 1634104421 meant. Spelling out the enum in this case makes the code readable.

I feel your pain! :wink:

I could point you to some resources but I doubt if I know of resources that you haven’t already found. You might have to be a bit more specific with where you’re getting stuck.

Philip

Ah, thank you so much! Using Enums for this is a bit unexpected since JavaScript typically relies on strings, but now I know how to access it and where to look.

There are definitely a few things that seem to only be found this way. I’ve already extracted JSON files for info on all the panels and menu actions, and now I’ve got another one for the events.

No problem! That was more of a general moan ^^. Most of the things I was struggling with, I’ve either found answers for after endless hours of Googling and trial and error, or I’ve just worked around by now. Thanks so much for all your help so far!