The following code I think is pretty clear.
//////////////////////////////////////////////////////////////////
// MenuDemo.jsx By Trevor http://creative-scripts.com 12 Apr 18 //
// Demonstrates how to control menu items on InDesign //
//////////////////////////////////////////////////////////////////
#targetengine foo
// If using CSTK best to comment out the targetengine line
// Look at the bottom of the "Layout" menu
var installMenuItem;
/**
* [installMenuItem description] Helper function for adding InDesign menu items by Trevor http://creative-scripts.com
* @param {String} title The words you want displayed
* @param {DOM Menu / Sub-menu} parentMenu The menu / sub-menu on which the item should appear
* @param {Function} beforeDisplay like function(ev){ev.target.enabled = !!app.properties.activeDocument}
* @param {Function} onInvoke like
* function(ev){
* ev.target.title =
* (app.properties.activeDocument && app.properties.activeDocument.name) || 'Nope';
* }
* @param {DOM Constant} position LocationOptions.BEFORE, LocationOptions.AFTER, LocationOptions.AT_END,
* LocationOptions.AT_BEGINNING, or LocationOptions.UNKNOWN
* [Optional] default LocationOptions.AT_END
* @param {DOM Menu / Sub-menu / Menu Item} reference [description]
* [Optional] only needed if position is set to LocationOptions.BEFORE or LocationOptions.AFTER
* @return {DOM menuAction} The created or existing menu Item
*/
installMenuItem = function(title, parentMenu, beforeDisplay, onInvoke, name, position, reference) {
var menuAction;
if (typeof title === 'object') {
parentMenu = title.parentMenu;
beforeDisplay = title.beforeDisplay;
onInvoke = title.onInvoke;
position = title.position;
reference = title.reference;
name = title.name || title.title;
title = title.title || title.name;
}
position = position || LocationOptions.AT_END;
parentMenu = parentMenu || app.menus.item("$ID/Main");
menuAction = app.scriptMenuActions.add({ title: title, name: name });
if (beforeDisplay) {
menuAction.eventListeners.add('beforeDisplay', beforeDisplay);
}
if (onInvoke) {
menuAction.eventListeners.add('onInvoke', onInvoke);
}
return parentMenu.menuItems.add(menuAction, position, reference);
};
/*
____ __ _ _ ____ __ ____ _ _ ____ __ ___ ____
/ ___) / _\ ( \/ )( _ \( ) ( __) / )( \/ ___) / _\ / __)( __)
\___ \/ \/ \/ \ ) __// (_/\ ) _) ) \/ (\___ \/ \( (_ \ ) _)
(____/\_/\_/\_)(_/(__) \____/(____) \____/(____/\_/\_/ \___/(____)
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This should add a "FabScript" submenu to the bottom of the "Layout" menu //
// It contains to menu items //
// Demonstrates how to change properties of the menu items like their text and enabled status //
// Keep playing with the menu until you understand how it's actions fit in with the code. //
// To remove the menu use either foo.remove() from the correct scriptEngine or use from any engine //
// app.menus.itemByName('$ID/Main').submenus.itemByName('$ID/&Layout').submenus.itemByName('FabScript').remove(); //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var foo;
foo = (function() {
var toggleScriptEnable, toggleYepNopeTitle, yepNopeBeforeDisplay, enableMyScriptOnInvoke, yepNopeOnInvoke, wl, say, count;
count = 0;
toggleScriptEnable = toggleYepNopeTitle = false;
/*
_ _ _ ___ _ _
| || |___| |_ __ ___ _ _ | __| _ _ _ __| |_(_)___ _ _ ___
| __ / -_) | '_ \/ -_) '_| | _| || | ' \/ _| _| / _ \ ' \(_-<
|_||_\___|_| .__/\___|_| |_| \_,_|_||_\__|\__|_\___/_||_/__/
|_|
*/
/**
* [wl description] Shortcut to output to either ESTK or CSTK console, applying / filtering the css
* @param {String} message
* @param {String} css [Optional] css to be applied to the message if sent to the CSTK console
* @return {String} The message without the css
*/
wl = function(message, css) {
if ($.engineName === 'CSTK') {
$.writeln(message, css);
} else {
$.writeln(message);
}
return message;
};
/**
* [say description] Speaks the message out and passes the message out so it can be forwarded to a log etc.
* @param {String} message the message to say
* @return {String} The message
*/
say = function(message) {
const DONTSAY = false; // Change to true to shut the computers mouth
if (DONTSAY) { return message; }
var isMac, script;
isMac = $.os[0] === 'M';
script = (isMac ? 'Say "' : 'CreateObject("sapi.spvoice").Speak "') + message + '"';
app.doScript(script, ScriptLanguage[isMac ? 'APPLESCRIPT_LANGUAGE' : 'VISUAL_BASIC']);
return message;
};
/*
__ __ _ _ _ _
| \/ |___ _ _ _ _ _____ _____ _ _| |_| | (_)__| |_ ___ _ _ ___ _ _ ___
| |\/| / -_) ' \ || | / -_) V / -_) ' \ _| |__| (_-< _/ -_) ' \/ -_) '_(_-<
|_| |_\___|_||_\_,_| \___|\_/\___|_||_\__|____|_/__/\__\___|_||_\___|_| /__/
*/
enableMyScriptOnInvoke = function(ev) {
wl(say('On Invoke ' + ev.target.name), 'color: blue;background:yellow;');
ev.target.title = ev.target.name = toggleScriptEnable ? 'Enable my script' : 'Dis-enable my script'; // ;-);
toggleScriptEnable = !toggleScriptEnable;
};
yepNopeBeforeDisplay = function(ev) {
count++;
ev.target.enabled = toggleScriptEnable; // toggleScriptEnable
ev.target.name = count + // increments on beforeDisplay events
(toggleYepNopeTitle ? ' Yep script' : ' Nope script') + // toggles on beforeDisplay events
(toggleScriptEnable ? ' is enabled' : ' is not enabled'); // changes on the enableMyScriptOnInvoke event
////////////////////////////////////////////////////////////////////
// NOTE: The Yep and Nope are not connected to the enabled status //
// take note of when they swap and when the count changes //
////////////////////////////////////////////////////////////////////
toggleYepNopeTitle = !toggleYepNopeTitle;
wl(say('Before display ' + ev.target.name), 'color: orange');
// NOTE: The before display is triggered when the menu item is invoked and before the parent menu is displayed
// In this example the parent menu is the "Layout" menu
};
yepNopeOnInvoke = function(ev) {
wl(say('On Invoke ' + ev.target.name), 'color: red;background:yellow;');
///////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE: before the onInvoke fires the beforeDisplay will fire //
// This is in addition to the beforeDisplay that fired on displaying the menu //
// In our case this will cause that it looks like the Yep Nope are not being toggled //
// To circumvent that one could place in the on Invoke `toggleYepNopeTitle = !toggleYepNopeTitle;` //
// Take note that the count changes with the onInvoke because of it's associated beforeDisplay event //
///////////////////////////////////////////////////////////////////////////////////////////////////////
};
/*
_ _ _ __ __
/_\ __| |__| | | \/ |___ _ _ _ _ ___
/ _ \/ _` / _` | | |\/| / -_) ' \ || (_-<
/_/ \_\__,_\__,_| |_| |_\___|_||_\_,_/__/
*/
var fooMenu = app.menus.itemByName('$ID/Main').submenus.itemByName('$ID/&Layout').submenus.itemByName('FabScript');
// For convenience we shall remove the menu if it already exists.
if (fooMenu !== null) fooMenu.remove();
// Add the FabScript submenu
fooMenu = app.menus.itemByName('$ID/Main').submenus.itemByName('$ID/&Layout').submenus.add('FabScript', LocationOptions.AT_END);
// Add the menu items with the event listeners
installMenuItem({
name: 'YepNope',
parentMenu: fooMenu,
onInvoke: yepNopeOnInvoke,
beforeDisplay: yepNopeBeforeDisplay
});
installMenuItem({
name: 'Enable my script',
parentMenu: fooMenu,
onInvoke: enableMyScriptOnInvoke
});
return fooMenu;
})();
/*
To remove the menu
foo.remove();
or
app.menus.itemByName('$ID/Main').submenus.itemByName('$ID/&Layout').submenus.itemByName('FabScript').remove();
*/