I am guessing that the only way to be able to get this to work is to reference a file to the following JavaScript code since it is not pure JXA
JavaScript for OmniFocus Using Keyboard Maestro
(() => {
'use strict';
// Get the KM variable in the JXA context BEFORE passing to OmniJS
const kmApp = Application("Keyboard Maestro Engine");
const daysOffset = parseInt(kmApp.getvariable("DaysOffset")) || 1;
// OMNI JS CODE ---------------------------------------
const omniJSContext = (daysOffsetValue) => {
const main = () => {
// Get selected tasks first
const seln = document.windows[0].selection.tasks;
if (seln.length === 0) {
return "No tasks selected in OmniFocus";
}
// Use the passed in offset value directly
const daysOffset = daysOffsetValue;
// Calculate target date with offset
const targetDate = new Date();
targetDate.setDate(targetDate.getDate() + daysOffset);
const formattedDate = targetDate.getFullYear() + '-' +
String(targetDate.getMonth() + 1).padStart(2, '0') + '-' +
String(targetDate.getDate()).padStart(2, '0');
// Find target project with the date
let targetProject = flattenedProjects.find(p => p.name.includes(formattedDate));
if (!targetProject) {
return `No Project Found With the Date of ${formattedDate}`;
}
// Process each selected task
for (const task of seln) {
try {
let parentGroup = targetProject.tasks.find(t => t.name === task.parent.parent.name);
if (!parentGroup) {
parentGroup = new Task(task.parent.parent.name, targetProject);
}
let subGroup = parentGroup.children.find(t => t.name === task.parent.name);
if (!subGroup) {
subGroup = new Task(task.parent.name, parentGroup);
}
if (typeof moveTasks === 'function') {
moveTasks([task], subGroup);
} else {
task.move(subGroup);
}
} catch (error) {
// Silent error handling
}
}
return `Successfully Moved to the Project Named ${targetProject.name}`;
};
return main();
};
// OmniJS Context Evaluation ------------------------------------------------
return 0 < Application('OmniFocus').documents.length ? (
Application('OmniFocus').evaluateJavascript(
`(${omniJSContext})(${daysOffset})`
)
) : 'No documents open in OmniFocus';
})();
Execute JavaScript For Automation - Pasted Code Doesn't Work
The above OmniFocus script is using TWO different JavaScript contexts:
The outer JXA context (which can talk to Keyboard Maestro)
The inner OmniJS context (which runs inside OmniFocus)
When I save the script as a file and reference it, the entire script runs properly through both contexts. But when you I paste it directly into "Execute JavaScript For Automation", it doesn't properly handle the nested OmniJS context evaluation.
So do I need to break up the code or is there some other Keyboard Maestro action that will allow me to keep this all in Keyboard Maestro and not have to reference another file which is my preference.
Possibly just that you need to make a couple of adjustments for the Modern Syntax option which is now the default in the menu behind the small chevron to the left of the Execute JavaScript for Automation text field:
You need to prefix your IIFE with return
You don't need the .getvariable method.
Assuming that in the Chevron menu you have not only selected "Modern Syntax" but have also checked the name of the KM Variable value that you want to use (DaysOffset here)
You can write:
return (() => {
'use strict';
const daysOffset = parseInt(kmvar.DaysOffset) || 1;
// OMNI JS CODE ---------------------------------------
etc. etc.
In full:
Expand disclosure triangle to view JS source
return (() => {
'use strict';
const daysOffset = parseInt(kmvar.DaysOffset) || 1;
// OMNI JS CODE ---------------------------------------
const omniJSContext = (daysOffsetValue) => {
const main = () => {
// Get selected tasks first
const seln = document.windows[0].selection.tasks;
if (seln.length === 0) {
return "No tasks selected in OmniFocus";
}
// Use the passed in offset value directly
const daysOffset = daysOffsetValue;
// Calculate target date with offset
const targetDate = new Date();
targetDate.setDate(targetDate.getDate() + daysOffset);
const formattedDate = targetDate.getFullYear() + '-' +
String(targetDate.getMonth() + 1).padStart(2, '0') + '-' +
String(targetDate.getDate()).padStart(2, '0');
// Find target project with the date
let targetProject = flattenedProjects.find(p => p.name.includes(formattedDate));
if (!targetProject) {
return `No Project Found With the Date of ${formattedDate}`;
}
// Process each selected task
for (const task of seln) {
try {
let parentGroup = targetProject.tasks.find(t => t.name === task.parent.parent.name);
if (!parentGroup) {
parentGroup = new Task(task.parent.parent.name, targetProject);
}
let subGroup = parentGroup.children.find(t => t.name === task.parent.name);
if (!subGroup) {
subGroup = new Task(task.parent.name, parentGroup);
}
if (typeof moveTasks === 'function') {
moveTasks([task], subGroup);
} else {
task.move(subGroup);
}
} catch (error) {
// Silent error handling
}
}
return `Successfully Moved to the Project Named ${targetProject.name}`;
};
return main();
};
// OmniJS Context Evaluation ------------------------------------------------
return 0 < Application('OmniFocus').documents.length ? (
Application('OmniFocus').evaluateJavascript(
`(${omniJSContext})(${daysOffset})`
)
) : 'No documents open in OmniFocus';
})();
That works great here as well. Thank you for the information and making that work. It is interesting that Keyboard Maestro doesn't the code look all nice like it does with AppleScript and it is more difficult to get the error messages and "Save Results to a Clipboard" then choosing System Clipboard.
You get code formatting if you switch off Modern Syntax.
The difficulty with achieving that in the latter may, I think, be a price paid for wrapping the code in an IIFE and preparing a kmvar object (informed with the selected KM Variable key:value pairs).
Okay, yay that is the best of both worlds and I can get better readable results using Script Editor and I can see the formatting and can just copy and paste the code into "Execute JavaScript For Automation" and disable Modern Syntax.
Thank you that was quick and easy and I will not have to modify the dozens of Script files I have made (though the changes were relatively simple) this is much easier to read.