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

Is there a way to create an event listener for the change of foonotes number?

Explorer ,
May 01, 2020 May 01, 2020

Copy link to clipboard

Copied

I'd like to use `app.addEventListener()` to listen when the number of foonotes in the currently open document change. There might be two types of change: the number might increase (when one or more footnotes are inserted) or decrease (when one or more footnotes are removed).


I'd like to use it in a startup script in InDesign CC 2018. When there is a change in number of footnotes, I'd like to run a script.


In my other startup script, I use `app.addEventListener('afterSave', saveIDML, false);` where `saveIDML` is a function that save the currently open document as IDML afer it is saved (as INDD). I am looking for similar way, but for different event. There might not be readily available event listener for this, therefore I am looking for anyway I could accomplish this.


Note: I have asked this very same question at StackOverflow.

TOPICS
Scripting

Views

1.2K

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
Engaged ,
May 01, 2020 May 01, 2020

Copy link to clipboard

Copied

I think what you need is a menuEventListener. Like this:

 

#targetengine session
app.menuActions.itemByID(47134).addEventListener('afterInvoke', afterNoteInserted);
function afterNoteInserted() {
    alert ("A footnote was inserted into " + app.activeDocument.name );
} // end function
 

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
Explorer ,
May 01, 2020 May 01, 2020

Copy link to clipboard

Copied

Thank you, @Robert_Kyle!

 

Your code looks promising, however, I have several questions:

- How do you get the actions menu ID (47134)? Do I need to have a specific ID? (I'd like to know if any footnote anywhere within the currently open document was inserted or removed.)

- It looks like the code listens only if a footnote is inserted, not when it is removed---or do I misunderstand the code?

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
Engaged ,
May 01, 2020 May 01, 2020

Copy link to clipboard

Copied

The easy question first: You can address a menuAction by name...

app.menuActions.item("Insert Footnote");

... the catch is that this won't work for someone using, say, the German version of InDesign. So ...

app.menuActions.item("Insert Footnote").id;

... gives you the id number. Actually, it gives you two: 47134, and 19699. But when I looked at 19699 I realized that it was the disabled version, so there didn't seem to be any point in attaching an event listener to it.

 

But I will admit to being stumped about how to detect when a footnote is removed. I don't work with footnotes at all, but my understanding is that you simply select the superscript number in the text and delete it. So there's no menu action to detect. 

You certainly could create an inventory of footnotes when the document is opened and compare it against the footnotes in place before it closes. Or maybe you could invent a scripted footnote-removal tool, if you think you can train/persuade your users to employ it.

It would help to know what you want to do after the user deletes a footnote. 

 

Bob

 

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
Explorer ,
May 02, 2020 May 02, 2020

Copy link to clipboard

Copied

@Robert_Kyle, where did your second post disappeared? Why did you deleted it?

 

---

 

The easy question first: You can address a menuAction by name...

app.menuActions.item("Insert Footnote");

... the catch is that this won't work for someone using, say, the German version of InDesign. So ...

app.menuActions.item("Insert Footnote").id;

... gives you the id number. Actually, it gives you two: 47134, and 19699. But when I looked at 19699 I realized that it was the disabled version, so there didn't seem to be any point in attaching an event listener to it.

 

You answered my first question perfectly. That was all I wanted and needed to know.

 

But I will admit to being stumped about how to detect when a footnote is removed. I don't work with footnotes at all, but my understanding is that you simply select the superscript number in the text and delete it. So there's no menu action to detect. 

You certainly could create an inventory of footnotes when the document is opened and compare it against the footnotes in place before it closes. Or maybe you could invent a scripted footnote-removal tool, if you think you can train/persuade your users to employ it.

 

I’d like to ask you about a different approach: what about counting footnotes? You can grep search for `~F` which stands for the footnote reference (important: do not search in the footnotes as the references there are matched too), count them, save them to a variable. Then listen (i.e. check the number of footnotes periodically) and if there is any difference between the old and new numbers, run a script. Is it possible? I know how to run grep find search, but I am not how it count the results (is there an array of reference points somewhere?) and have no idea how create an event listener.

 

It would help to know what you want to do after the user deletes a footnote.

 

What do I want to do after the inserts or removes one or more footnotes? See this StackExchange answer of a question on cross-referencing footnote references. I am in of such functionality, but InDesign lacks it. The answer suggests a workaround by creating a text frame object (so-called ‘sidenote’) per footnote reference outside the printable area (text frames are anchored after each footnote reference in the text body). In that frame, there is set a paragraph style with numbering enabled by default and numbering numbers are used for cross-references. For a workaround quite good.


I have scripted it the way it is suggested the answer, but it needs to updated each time the footnote number changes (the number of text frame objects must equal to the footnote numbers, otherwise the numbering numbers do not match the associated footnote references). I’d like to call this script whenever there is a change in the footnote number and thus ease the end-users (incl myself), not to burden them. Whatever way they insert or remove footnotes, should not change.

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
Explorer ,
May 02, 2020 May 02, 2020

Copy link to clipboard

Copied

Okay, now I can get the number of footnotes in the current document using the following code, but how do I make it a listener, i.e. how do I run a script each and every time the number of footnotes changes? Do I need to create an infinite while?

 

// Reset the GREP preferences
app.findGrepPreferences = NothingEnum.NOTHING;
app.changeGrepPreferences = NothingEnum.NOTHING;
app.findGrepPreferences = null;
app.changeGrepPreferences = null;
app.findChangeGrepOptions.properties = (
	{
		includeFootnotes: false,
		includeMasterPages: false,
		includeHiddenLayers: false,
		includeLockedLayersForFind: false,
		includeLockedStoriesForFind: false,
	}
);

// Set the GREP search preferences
app.findGrepPreferences.findWhat = '~F';

// Find all footnote references
var matches = app.properties.activeDocument.findGrep();

// Count the footnote references
var footnotesNum = matches.length;
alert (footnotesNum);

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
Explorer ,
May 02, 2020 May 02, 2020

Copy link to clipboard

Copied

Now I know that `$.sleep(10000);` within an infinite while won't help because the script does not run in the background. 😞

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
Engaged ,
May 02, 2020 May 02, 2020

Copy link to clipboard

Copied

Right. sleep() won't help. You might look into using an idleEvent... it will invoke a handler when InDesign has been idle for a period of time. It wouldn't be catching the deletion of a footnote directly, but it could check to see if the number of footnotes has changed.

 

What would your menuEvent listener do when a new footnote is inserted? Is this the time that you add the number referencing another footnote? If so, it might be possible to use the insertLabel() method to note the relationship between the two footnotes. One label might say, "linked to footnote a" and other "footnote b links here". You'd want some labeling system for the footnotes that didn't involve numbers since those are likely to change. This is a variation the idea of using a grep style to make type invisible. Not a bad idea, even invisible type takes up some space and it has a way of becoming visible at the wrong time. A script lable is really invisible.

 

The next step, in my mind, would be to run a script to check the footnote references. The alternative to a findGrep operation using the footnote character is to simply get an array of all the footnotes in the document: app.activeDocument.stories.everyItem().footnotes;

You could loop through the array to read the labels attached to each footnote. If the number of the "linked to" footnote has changed, your script could make that change to the "linked from note."

 

This check might work more or less automatically if it were executed by an onIdle() event handler. You could also try it with some of the standard event listeners: afterOpen(), beforeSave(), etc. 

 

Hope this helps

 

Bob

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
Explorer ,
May 04, 2020 May 04, 2020

Copy link to clipboard

Copied

HI @Robert_Kyle!

 

Did you read the StackExchange answer I have linked? there three animated GIFs that describe what I have scripted and what needs to be done when a foonote is inserted or removed.

 

What would your menuEvent listener do when a new footnote is inserted?

 

Basically: insert an anchored text frame object of Sidenote object style (which has preset Sidenote paragraph style with numbering and auto-size and auto-placement) with a space character in it. That anchored text frame object is inserted after each footnote reference within the document (but not in the footnotes) where it is not inserted yet. The paragraph numbering makes sure that it is in sync with the footnote reference numbers.

 

Is this the time that you add the number referencing another footnote?

 

No. The actual footnote cross-referencing is (and should be) done manually as the script cannot know by any means where should it insert the reference nor how should it format, nor which footnote should be referenced. The footnote cross-referencing is done via the Cross-References panel.

 

The alternative to a findGrep operation using the footnote character is to simply get an array of all the footnotes in the document: `app.activeDocument.stories.everyItem().footnotes;`

 

Thanks! That's much better approach! 🙂

 

You could also try it with some of the standard event listeners: afterOpen(), beforeSave(), etc.

 

That might a partial solution / workaround.

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
Engaged ,
May 04, 2020 May 04, 2020

Copy link to clipboard

Copied

The actual footnote cross-referencing is (and should be) done manually as the script cannot know by any means where should it insert the reference nor how should it format, nor which footnote should be referenced. The footnote cross-referencing is done via the Cross-References panel.

 

What I was suggesting was you used a custom scripted tool to replace thee Cross-References panel. It starts by extracting a list of the current footnotes in the document,

 

The interface would permit the user to insert a new footnote and link it to an existing footnote in one motion. The script itself would use labels to establish the relationship between the two footnotes.

 

The other option would be remove a footnote. When this happpens, the script would check to see if the selected note is related to another. If so, the internal references could be fixed with whatever amount of warning seems appropriate. You could still use a beforeSave event listener to check for any mismatches.

 

The whole idea depends on how much control you have over the users. If they are empowered to do things the hard-way, then this wouldn't work.

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
Explorer ,
May 05, 2020 May 05, 2020

Copy link to clipboard

Copied

LATEST

I don't know if it is better approach that the one from that StackExchange answer. Some other stuff to consider that must work if I re-work the whole footnote cross-referencing mechanism:

 

  1. I use cross-references for other stuff too, not just for the footnote cross-references. What you suggest should not interfere with any other cross-reference.
  2. I work with InDesign books (INDB file format), therefore it must use the _Start At_ number from the Footnote Options as the the first footnote reference number.
  3. The footnote reference number must auto-update whenever another footnote before it is inserted or removed.

 

---

 

Anyway, here's the current version of the script based on the StackExchange answer (it might not be perfect, but it works):

 

// Sidenotes preparation + update script

// author:  Tukusej's Sirs
// licence: GNU GPLv3
// date:    2 May 2020
// version: 1.0

// Presumptions:
// - If `paraStyleSidenotesName` paragraph style exists, the bulleting is set the way the user wants (i.e. this script does not modify it unless `footnotesStartAt` is modified (and then only `numberingStartAt` is modified)).
// - Each footnote reference in the document (but not in the footnotes) is not allowed to be followed by any other type of anchored object but the one we insert.

// Algorithm source: https://graphicdesign.stackexchange.com/a/129868/133961
// Other sites that helped me:
// - https://community.adobe.com/t5/indesign/how-to-format-the-numbering-number-in-a-paragraph-style-via-script/m-p/11095225#M184568

// TODO
// - does not work if a footnote is at the end of the document (workaround: additional search for `~F$`, but that also matches other footnotes at the end of the paragraph);
// - auto-update the inserted/removed footnotes;
// - auto-update the changed Start At footnote number in the _Sidenote_ paragraph style;


// Variables
var paraStyleSidenotesName = 'Sidenotes';
var objStyleSidenotesName = 'Sidenotes';
var numberingNumberFormat = '^#';
var objStyleXOffset = '20';  // Horizontal offset from page edge; in millimeters (or rather based on preferences? IDK)
var objStyleYOffset = '-5';  // Vertical offset from baseline; in millimeters (or rather based on preferences? IDK)
var textFrameHeight = 6;
var textFrameWidth = 13;
var textFrameText = ' ';  // Note: The frame has to contain at least a character, else the numbering number won't show up; A space won't show up in the inserted cross-reference
var doc = app.activeDocument;
var footnotesStartAt = doc.footnoteOptions.startAt;  // Get the footnote `Start At` number
var paraStyleSidenotes = doc.paragraphStyles.itemByName(paraStyleSidenotesName);
var objStyleSidenotes = doc.objectStyles.itemByName(objStyleSidenotesName);

// Create `paraStyleSidenotesName` paragraph style if missing
// Note: If a paragraph style with `paraStyleSidenotesName` already exists, it is not modified unless `footnotesStartAt` is modified (and then only `numberingStartAt` is modified)
if (paraStyleSidenotes.isValid) {
	if (paraStyleSidenotes.numberingStartAt != footnotesStartAt) {
		paraStyleSidenotes.numberingStartAt = footnotesStartAt;
	}
} else {
	var newStyle = app.documents[0].paragraphStyles.add(
		{
			name: paraStyleSidenotesName,
			bulletsAndNumberingListType: ListType.NUMBERED_LIST,
			numberingFormat: NumberingStyle.ARABIC,
			numberingExpression: numberingNumberFormat,
			numberingStartAt: footnotesStartAt
		}
	);
}

// Create `objStyleSidenotes` object style if missing
// Note: If a object style with `objStyleSidenotesName` already exists, it is not modified
if (! objStyleSidenotes.isValid) {
	var newStyle = app.documents[0].objectStyles.add(
		{
			name: objStyleSidenotesName,
			enableFill: false,
			enableFrameFittingOptions: false,
			enableExportTagging: false,
			enableObjectExportAltTextOptions: false,
			enableObjectExportEpubOptions: false,
			enableObjectExportTaggedPdfOptions: false,
			enableStoryOptions: false,
			// enableStroke: true,
			enableStrokeAndCornerOptions: false,
			enableTextFrameBaselineOptions: false,
			enableTextFrameFootnoteOptions: false,
			enableTextFrameGeneralOptions: false,
			enableTextWrapAndOthers: false,
			enableTransformAttributes: false,
			enableParagraphStyle: true,
			enableAnchoredObjectOptions: true,
			enableTextFrameAutoSizingOptions: true,
			appliedParagraphStyle: paraStyleSidenotes,
			strokeWeight: 0,
			anchoredObjectSettings: {
				anchoredPosition: AnchorPosition.ANCHORED,
				spineRelative: true,
				anchorPoint: AnchorPoint.TOP_RIGHT_ANCHOR,
				verticalReferencePoint: VerticallyRelativeTo.LINE_BASELINE,
				verticalAlignment: VerticalAlignment.TOP_ALIGN,
				horizontalReferencePoint: AnchoredRelativeTo.PAGE_EDGE,
				horizontalAlignment: HorizontalAlignment.LEFT_ALIGN,
				anchorXoffset: objStyleXOffset,
				anchorYoffset: objStyleYOffset,
				pinPosition: false
			},
			textFramePreferences: {
				autoSizingType: AutoSizingTypeEnum.HEIGHT_AND_WIDTH,
				useMinimumHeightForAutoSizing: true,
				useMinimumWidthForAutoSizing: true,
				minimumHeightForAutoSizing: textFrameHeight,
				minimumWidthForAutoSizing: textFrameWidth,
				useNoLineBreaksForAutoSizing: true,
				nonprinting: true
			}
		}
	);
}

// Insert a text frame object
var textFrame = doc.pages[0].textFrames.add();
textFrame.properties = {
	appliedObjectStyle: objStyleSidenotes,
	contents: textFrameText  // Note: The frame has to contain at least a character, else the numbering number won't show up; A space won't show up in the inserted cross-reference
};

// Clear the selection
app.select(null);

// Select the textFrame
doc.select(textFrame);

// Cut the text frame
app.cut();

// Reset the GREP preferences
app.findGrepPreferences = NothingEnum.NOTHING;
app.changeGrepPreferences = NothingEnum.NOTHING;
app.findGrepPreferences = null;
app.changeGrepPreferences = null;
app.findChangeGrepOptions.properties = (
	{
		includeFootnotes: false,
		includeMasterPages: false,
		includeHiddenLayers: false,
		includeLockedLayersForFind: false,
		includeLockedStoriesForFind: false,
	}
);

// Set the GREP search preferences for search footnote references that are not followed by an anchored object
app.findGrepPreferences.findWhat = '~F\\K[^~a]';
// app.findGrepPreferences.findWhat = '~F\\K([^~a]|$)';
app.changeGrepPreferences.properties = (
	{
		changeTo: '~c$0'
	}
);

app.findGrep();
doc.changeGrep();

// Set the GREP search preferences for search footnote references are at the end of a paragraph
// Note: This is used only for the footnote reference at the end of the document as footnote references at the end of paragraph but the last paragraph of the document are matched by the previous GREP search
// Note: This one places the anchored object _before_ the footnote references and thus creates a (potential) bug when the user inserts additional text either at the end of the last paragraph or a new paragraph after the last paragraph
app.findGrepPreferences.findWhat = '~F$';
app.changeGrepPreferences.properties = (
	{
		changeTo: '~c$0'
	}
);

app.findGrep();
doc.changeGrep();

 

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