I created my first scripting action today! It was also the first time I’ve ever tried to learn/use javascript, and I’m far more proud of it than is warranted.
My goal was to create a prompt that would allow me to choose from several formats to insert the current date into my current draft. I started with the Prompt action, but couldn’t understand how to take the output of the prompt and change it into anything useful. All I could get out of the prompt was the text that made up the button I pressed.
So…I went down the (admittedly) much harder route and tried to figure out how to use a script to display a prompt and do what I wanted. It took a while, and with much referencing the example actions in the Directory, the Draft Script Reference, and this very forum.
And here’s the script. It’s my first one, so I have a lot to learn. If anyone has suggestions on how to streamline the code, I’d love to learn. As Dani Rojas says in Ted Lasso, “Roast me, amigo!”
// # Insert a formatted date in Drafts.app using a prompt.
// Store selection
const [st, len] = editor.getSelectedRange();
// Create prompt
var p = Prompt.create();
p.title = "Insert Formatted Date";
// Arrange the buttons in the order in which you prefer. The top button will be highlighted by default and can be selected by pressing the 'Return' key when prompted.
p.addButton("yyyy-mm-dd");
p.addButton("Month d, yyyy");
p.addButton("mm/dd/yyyy");
p.addButton("mm-dd-yyyy");
p.addButton("mm/d/yy");
p.addButton("mm-d-yy");
// Show prompt
p.show();
var b = (p.buttonPressed)
var ymd = draft.processTemplate("2021-07-18")
var mdy = draft.processTemplate("[[date|%B %e, %Y]]")
// Process dates based on button selection and set variable for the processed date (pd).
if (b == "yyyy-mm-dd") {
var pd = draft.processTemplate("2021-07-18")
}
if (b == "Month d, yyyy") {
var pd = draft.processTemplate("[[date|%B %e, %Y]]")
}
if (b == "mm/dd/yyyy") {
var pd = draft.processTemplate("[[date|%m/%d/%Y]]")
}
if (b == "mm-dd-yyyy") {
var pd = draft.processTemplate("[[date|%m-%d-%Y]]")
}
if (b == "mm/d/yy") {
var pd = draft.processTemplate("[[date|%m/%e/%y]]")
}
if (b == "mm-d-yy") {
var pd = draft.processTemplate("[[date|%m-%e-%y]]")
}
// Take action
if (pd != undefined) {
// Insert processed date template into the draft at the selected range or point
editor.setTextInRange(st, len, pd);
// Reactivate the editor with the insertion point at the end of the newly added date
editor.setSelectedRange(st + pd.length, 0);
}
editor.activate();
Congratulations for your first action! Gotta love that feeling of having learned/achieved something.
I’m no JavaScript pro, but what about this?
// # Insert a formatted date in Drafts.app using a prompt.
// Store selection
const [st, len] = editor.getSelectedRange();
// Create prompt
var p = Prompt.create();
p.title = "Insert Formatted Date";
// Arrange the buttons in the order in which you prefer. The top button will be highlighted by default and can be selected by pressing the 'Return' key when prompted.
p.addButton("yyyy-MM-dd");
p.addButton("MMMM d, yyyy");
p.addButton("MM/dd/yyyy");
p.addButton("MM-dd-yyyy");
p.addButton("MM/d/yy");
p.addButton("MM-d-yy");
// Show prompt
p.show();
var b = (p.buttonPressed)
// are these two leftover from something?
var ymd = draft.processTemplate("2021-07-18")
var mdy = draft.processTemplate("[[date|%B %e, %Y]]")
// if you use Date.js, you can use the prompt to as a variable for the date template (you've already done the work up there...)
var pd = new Date().toString(b)
// Take action
if (pd != undefined) {
// Insert processed date template into the draft at the selected range or point
editor.setTextInRange(st, len, pd);
// Reactivate the editor with the insertion point at the end of the newly added date
editor.setSelectedRange(st + pd.length, 0);
}
editor.activate();
Basically, if you use Date.js to parse the date string, you can use the output of the prompt as a variable to supply the template, which you’ve pretty much already defined in the prompt’s options (though note the difference between m and M for Date.js— specifying minutes vs months…). Saves a few lines of code and the need to write out those conditional statements…
This is great as-is (except the hard-coded "2021-07-18", oops!), but if you simply asking for way to make minor improvements, I would suggest abstracting the options into an object to make it a little easier to manage if you wanted to add/remove options in the future…something like:
Thank you! I like the idea of being able to just write the format that I want without necessarily needing to know the conversion. Then if other people download it, they can easily add their preferred formats, too.
How would I got about including Date.js? Would I add another action and copy the code from GitHub into it?
Thank you, Greg! I had wanted to separate out those options so that they could easily be edited by myself or others. I just wasn’t sure if they would work across different steps.
I appreciate you providing the example, and I updated the action with it.
Whoops! Luckily that part didn’t make it into the action I shared to the directory.
Based on the excellent suggestions here, I think I’ve arrived at my ultimate vision for this action: a prompt that is easily configurable (and, therefore, necessarily understandable) for anyone who downloads it.
I ended up using both @agiletortoise’s tip for separating out the options (but used an array instead of a dictionary), and @jsamlarose’s advice to implement Date.js so that the selected button text itself could be used when processing the date’s format.
Anyone can reference Date.js to add their preferred date and time format to the options array, and the syntax for those date and time format specifiers are pretty human-readable so they work fine as buttons.
Here’s the result
Step 1:
// Arrange the buttons in the order in which you prefer. The top button will be highlighted by default and can be selected by pressing the 'Return' key when prompted.
const options = [
"yyyy-MM-dd",
"MMMM d, yyyy",
"MM/dd/yyyy",
"MM-dd-yyyy",
"MM/d/yy",
"MM-d-yy"
]
// Additional date formats can be configured by referencing the Datejs documentation on GitHub (https://github.com/datejs/Datejs)
Step 2:
// Store selection
const [st, len] = editor.getSelectedRange();
// Create prompt
var p = Prompt.create();
p.title = "Insert Formatted Date";
// Build buttons using the array elements from the previous script
for (let i = 0; i < options.length; i++) {
p.addButton(options[i]);
} // First statement executes once before running the for loop (in this case, sets the initial variable i for the first element in the array), Second statement defines the condition for executing the code block (ITC, keep running as long as i is less than the number of elements in the options array), Third statement executes every time after the code block has been executed (ITC, increases the value of i by 1)
// Show prompt
p.show();
// Process dates based on button selection (b) and set variable for the processed date (pd) using Date.js
let b = (p.buttonPressed)
let pd = new Date().toString(b);
// Take action
if (pd != undefined) {
editor.setTextInRange(st, len, pd); // Inserts processed date template into the draft at the selected range or point
editor.setSelectedRange(st + pd.length, 0); // Reactivates the editor with the insertion point at the end of the newly added date
}
editor.activate();
I’ve updated the action in the directory. Thanks for helping me through this first creation!
Just to add another alternative take to the mix, take a peek at this version. It uses an even simpler option for users to maintain the list in the action (no array, no object definition, just plain text), deals with the user cancelling, and technically accounts for any options where the insertion date string cannot be defined (pops up an app level error message).
//Create prompt
let promptDate = Prompt.create();
promptDate.title = "Insert Formatted Date";
draft.getTemplateTag("OPTIONS").split("\n").map(strDateOption => promptDate.addButton(strDateOption));
//Process the prompt response
if (promptDate.show())
{
let strDate = new Date().toString(promptDate.buttonPressed);
if (strDate != undefined)
{
//Replace selection
const [intSelStart, intSelLen] = editor.getSelectedRange();
editor.setTextInRange(intSelStart, intSelLen, strDate);
//Position cursor at end of insertion
editor.setSelectedRange(intSelStart + strDate.length, 0);
}
else app.displayErrorMessage(`"${promptDate.buttonPressed}" could not be converted to a date.`);
}
//Activate editor
editor.activate();
Wow! Good stuff! I think I understand everything except for the last bit of this line:
I see that it get the tag defined in the first step, splits it by new lines, and maps it to a new array in order to make the buttons. This may be obvious, but what is the strDateOption =>...? I don’t see it defined before it gets called there.
strDateOption is the placeholder for the current line being processed. Each is an option for which date format to used and is expressed as a string, therefore the variable name. I like to try and use the names to tell me not only what a variable is/if for, but also what type it is.
=> is what defines it as a “fat arrow function”, and here it is just a compact way of writing an anonymous function like function (strDateOption){promptDate.addButton(strDateOption)}
You can pick out any elements you want to make use of, but it was intended just to show you some alternative approaches. You don’t have to change anything about your original one. It was functional as it was.
The version I created is on the directory, but it is unlisted meaning that unless someone enters the address or uses the link above, they will not discover it.