Demo Using KM Custom HTML Prompt with AppleScript & JXA

learning
html_prompt
applescript
jxa
javascript

#1

Demo Using KM Custom HTML Prompt with AppleScript

First, let me give @peternlewis a huge thank you for providing the Custom HTML Prompt action as a new feature in KM7. This is really huge, and opens up all kinds of opportunities/solutions we didn't have before.

One opportunity I am very interested in is using this KM HTML form with scripts like AppleScript and JXA. Neither of these have support for HTML dialogs natively, and the dialogs available are very simple.

So, I have learned that I can create an AppleScript that executes a KM Macro with a Custom HTML Prompt action, and waits for the action to complete before continuing the script. So we start the process with AppleScript, and stay in AppleScript entirely, except for when we want to show the HTML form via KM. Again, this is huge, IMO.

Turns out it is not that hard to build such a script, once you know how. LOL I know, that sounds stupid, but what I mean is that the resulting script and the HTML file are pretty simple once you understand how to put the parts together.

So, I'm sharing today a complete example of:

  • AppleScript
  • HTML File
  • KM Macro

If anyone finds any bugs or issues, or have suggestions for improvements, please post here.

The KM Macro is extremely simple since it is just a placeholder that all scripts can execute. Each script just needs to set a KM variable with the POSIX path to the HTML file before executing the macro.

Download one Zip File with all 3 files:

[KM] DEMO KM HTML Form Set by AppleScript.zip (17.6 KB)

I have now added this JXA script file, which I really like better.

To whet your appetite, let me start with a screenshot of the HTML form itself:

(BTW, I reworked the sample KM file, using tables for layout instead of CSS, mainly because I don't understand how to use CSS for layout/positioning)

The KM Macro is extremely simple. The key is using a KM variable for the path to the HTML file:

KNOWN ISSUES:

  1. There is an apparent BUG in KM, causing it to return a value of "Password", instead of the actual value, for any KM variable that has been used in a HTML password form field.

  2. You can't use KM variables with an underscore ("_") in the name, since KM replaces "_" with a space when the HTML Form Field Name is returned to KM.

AppleScript Code

I saved the meat for the last, because it is the longest:

(*
===============================================================================
	DEMO Use of KM HTML Form Set by AppleScript 
===============================================================================

VER: 	2.0		LAST UPDATE:   2015-12-27

PURPOSE:
	β€’ Show how to use the KM Action "Custom HTML Prompt" with AppleScript

AUTHOR:		JMichaelTX
			Find any bugs/issues or have suggestions for improvement?
			Contact me via PM or at blog.jmichaeltx.com/contact/

REQUIRED:
	1.	Mac OS X Yosemite 10.10.5+
	2.	Mac Applications
			β€’ Keyboard Maestro Engine
				
	3.	EXTERNAL OSAX Additions/LIBRARIES/FUNCTIONS
			β€’ None
					
	4.	INTERNAL FUNCTIONS:
			β€’ setKMVar(pKMVarName, pKMVarValue)
			β€’ bolToNum(pbolVar)
			β€’ numToBol(pnumVar)

REF:  The following were used in some way in the writing of this script.

	1.	[Custom HTML Prompt action]
		(https://wiki.keyboardmaestro.com/action/Custom_HTML_Prompt)
		
	2. [Trigger Macro via AppleScript]
		(https://wiki.keyboardmaestro.com/trigger/Script)
	
KNOWN ISSUES:
	1. There is an apparent BUG in KM, causing it to return a value of "Password", instead of the actual value, for any
		KM variable that has been used in a HTML password form field.
	2. You can't use KM variables with an underscore ("_") in the name, since KM replaces "_" with " " when the HTML Form Field Name is returned to KM.
	
===============================================================================
   
*)
--- SET VALUES TO AS VARS THAT WILL BE USED ON FORM ---

set strASName to "Mr. No Name"
set strASEMail to "johnny@AppleScript.com"
set strASPW to "BadPassWord"
set bolASCheckbox to false
set strASMessage to "Now is the time for all good men to come to the aid of their country"
set strASPopup to "TX"
set strASSelection to "TX"
set strASRadio to "B"

--- SET POSIX FILE PATH FOR HTML FILE ---
set strHTMLFile to "/Users/Shared/Dropbox/SW/DEV/KM/KM7/Actions/Custom HTML Prompt/KM-HTML-Form-Set-by-Applescript.html"

-----------------------------------------------
--- SET KM VARS (Create if necessary) ---
-----------------------------------------------

--setKMVar("HTML Prompt File", strHTMLFile)
setKMVar("AS_HTML_Form_File", strHTMLFile)


--- MUST SET RESULT BUTTON, IF USER PRESSES ESC BUTTON IS NOT SET ---
setKMVar("HTML Result Button", "TBD")

--- SET INITIAL VALUE FOR FORM FIELD NAMES ---
setKMVar("ASName", strASName)
setKMVar("ASEmail", strASEMail)
setKMVar("ASPW", strASPW)
setKMVar("ASCheckbox", bolToNum(bolASCheckbox)) -- KM uses 1/0 for checkbox checked/unchecked
setKMVar("ASMessage", strASMessage)
setKMVar("ASPopup", strASPopup)
setKMVar("ASSelection", strASSelection)
setKMVar("ASRadio", strASRadio)

log strASPW


--------------------------------------------------------------
tell application "Keyboard Maestro Engine"
	----------------------------------------------------------
	
	--- EXECUTE MACRO TO DISPLAY HTML FORM ---
	do script "[SCRIPT] HTML Form for Scripts"
	
	## AppleScript will wait here until User submits form ##
	
	
	--- OK, FORM HAS BEEN SUBMITTED, GET DATA ---
	
	--- GET BUTTON CLICKED ---
	--		If form was closed by user pressing ESC, no button was clicked
	--		and the KM var "HTML Result Button" will NOT be set.
	--		But we will treat it as if the user cancelled.
	--		That's why before form was submitted, the "HTML Result Button" variable 
	--		was set to "TBD"
	set strKMButton to value of variable "HTML Result Button"
	log "Result Button: " & strKMButton
	
	if (strKMButton = "Save") then -- GET KM vars, & process 
		
		set strASName to value of variable "ASName"
		set strASEMail to value of variable "ASEmail"
		set strASPW to value of variable "ASPW" ## BUG - KM is returning "Password" instead of actual value ##
		set bolASCheckbox to my numToBol(value of variable "ASCheckbox")
		set strASMessage to value of variable "ASMessage"
		set strASPopup to value of variable "ASPopup"
		set strASSelection to value of variable "ASSelection"
		set strASRadio to value of variable "ASRadio"
		
		### INSERT YOUR PROCESSING OF FORM DATA ###
		
		set strAlertTitle to "SUCCESS"
		set strAlertMsg to "Variables Updated via KM Form"
		
		
	else -- User Canceled, so terminate script, or handle cancellation
		
		log "User Canceled"
		set strAlertTitle to "*** USER CANCELLED ***"
		set strAlertMsg to "What Now?"
		
	end if -- (strKMButton = "Save") 
	
	----------------------------------------------------------
end tell -- application "Keyboard Maestro Engine"
--------------------------------------------------------------

display alert strAlertTitle message strAlertMsg as critical Β¬
	buttons {"Cancel", "Ignore"} -- last button is default

--- LOG VARS AFTER FORM WAS SUBMITTED OR CANCELLED ---
log strASName
log strASEMail
log strASPW
log bolASCheckbox
log strASRadio
log strASPopup
log strASSelection
log strASMessage

--============================= END OF MAIN SCRIPT ====================

### β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
# 			setKMVar()			Sets KM Variable, Makes if needed
# 
# 			Ver 2.0				2015-12-27
### β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
on setKMVar(pKMVarName, pKMVarValue)
	
	--log ("setKMVar: " & pKMVarName & ": " & pKMVarValue)
	
	tell application "Keyboard Maestro Engine"
		
		try -- to set variable, will error if it doesn't exist
			set value of variable pKMVarName to pKMVarValue
			
		on error -- Make & Set Variable
			make new variable with properties {name:pKMVarName, value:pKMVarValue}
		end try
		
	end tell -- KM
	
end setKMVar
--β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”


### β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
# 			bolToNum()			Convert Boolean to Number (1/0)
# 
# 			Ver 1.0				2015-12-27
### β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
on bolToNum(pbolVar)
	
	if pbolVar then
		set nVar to 1
	else
		set nVar to 0
	end if
	
	return nVar
	
end bolToNum
--β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”


### β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
# 			numToBol()			Convert Number to Boolean
# 
# 			Ver 1.0				2015-12-27
### β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
on numToBol(pnumVar)
	
	if (pnumVar as integer) = 0 then
		set bolVar to false
	else
		set bolVar to true
	end if
	
	return bolVar
	
end numToBol
--β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”

Results of Script

(*BadPassWord*)
(*Result Button: Save*)
(*Mr. Big Boss*)
(*bossy@bigco.com*)
(*Password*)
(*true*)
(*A*)
(*CN*)
(*CO*)
(*Now is the time for all good men to come to the aid of their country*)

Best Macros by @JMichaelTX
Form inside KM
Keyboard Maestro supports Swift?!
Script Editor - resize the output pane
#2

I can now add to this a JXA script file, which I really like better.

####DOWNLOADS
Original 3 files with AppleScript
[KM] DEMO KM HTML Form Set by AppleScript.zip

JXA Script File, which uses the same HTML file and Macro as above:
[KM] DEMO Custom HTML Form using JXA.scpt.zip (3.7 KB)

Both script files have had limited testing, so please report any bugs, issues, or concerns that you may find with them. And if you have any suggestions for improvements, or a better or alternate script, then please post that as well.

None of these files are intended for production use. They are all just examples that will hopefully help you get started using the great KM Custom HTML Prompt action with your scripts.

Remember that the primary/lead tool in this process is the script. You start by running the script, not the macro. The script then calls the KM macro at the appropriate point.

I look forward to seeing your feedback, and hope that we call learn together.

###Here's the JXA script:
Note: the code alignment here looks weird. I think it's due to the number of spaces for a TAB char that the forum uses, vs what I use, which is 2 spaces/TAB.

/*
===============================================================================
	DEMO Use of KM HTML Form Set by JXA 
===============================================================================

VER: 	2.0		LAST UPDATE:   2015-12-28

PURPOSE:
	β€’ Show how to use the KM Action "Custom HTML Prompt" with JXA

AUTHOR:		JMichaelTX
					Find any bugs/issues or have suggestions for improvement?
					Contact me via PM or at blog.jmichaeltx.com/contact/

REQUIRED:
	1.	Mac OS X Yosemite 10.10.5+
	2.	Mac Applications
			β€’ Keyboard Maestro Engine
				
	3.	EXTERNAL OSAX Additions/LIBRARIES/FUNCTIONS
			β€’ None
					
	4.	INTERNAL FUNCTIONS:
			β€’ setKMVar(pKMVarName, pKMVarValue)
			β€’ getKMVar(pstrName)
			β€’ strToBool (pstrBool)

REF:  The following were used in some way in the writing of this script.

	1.	[Custom HTML Prompt action]
		(https://wiki.keyboardmaestro.com/action/Custom_HTML_Prompt)
		
	2. [Trigger Macro via AppleScript]
		(https://wiki.keyboardmaestro.com/trigger/Script)
	
KNOWN ISSUES:
	1. There is an apparent BUG in KM, causing it to return a value of "Password", instead of the actual value, for any
		KM variable that has been used in a HTML password form field.
	2. You can't use KM variables with an underscore ("_") in the name, since KM replaces "_" with " " when the HTML Form Field Name is returned to KM.
	
===============================================================================
*/
var app = Application.currentApplication()
app.includeStandardAdditions = true

var strAlertTitle
var strAlertMsg

var oKMV = {}		// Object that holds KM var name and value

//--- KM VARS USED ON HTML FORM ---
oKMV["ASName"] 	= "Johnny"
oKMV["ASEmail"] = "john@gmail.com"
oKMV["ASPW"] 	= "SetByJXA"
oKMV["ASCheckbox"] = "0"		// will be set to Boolean after form processing.
oKMV["ASPopup"] = "TX"
oKMV["ASMessage"] = "Now is the time for all good men to come to the aid of their country"
oKMV["ASSelection"] = "TX"
oKMV["ASRadio"] = "B"

//--- SET KM VAR FOR HTML FILE ---
setKMVar("AS_HTML_Form_File", 
	"/Users/Shared/Dropbox/SW/DEV/KM/KM7/Actions/Custom HTML Prompt/KM-HTML-Form-Set-by-Applescript.html")

//--- MUST SET RESULT BUTTON, IF USER PRESSES ESC BUTTON IS NOT SET ---
setKMVar("HTML Result Button","TBD")

console.log(oKMV.ASName)		// use this form for other processing in JXA

//--- SET KM VARS (Create if necessary) ---
for(var name in oKMV) {
		console.log(name + ": " + oKMV[name])
		setKMVar(name, oKMV[name])		// SET KM var
}

//--- CALL KM MACRO TO SHOW THE HTML FORM ---

var appKM = Application('Keyboard Maestro Engine')
appKM.doScript("[SCRIPT] HTML Form for Scripts");

//### SCRIPT WILL WAIT HERE UNTIL USER SUBMITS FORM ###

var strButton = getKMVar("HTML Result Button")

if (strButton === "Save") {		//--- GET HTML FORM FIELDS ---
	
	console.log ("--- RESULTS FROM FORM ---")
	
	for(var name in oKMV) {
	
		oKMV[name] = getKMVar(name)		// Get KM var
		console.log(name + ": " + oKMV[name])
			
	}	// END FOR
	

	//	### INSERT YOUR PROCESSING OF FORM DATA ###
	strAlertTitle = "SUCCESS!  User Saved Form"
	strAlertMsg		= "Now Process Form Data"

} else {

	console.log("*** USER CANCELLED FORM ***")
	strAlertTitle = "User Cancelled Form"
	strAlertMsg		= "What next?"


}	// END IF/else "Save" button

	//--- CONVERT STRING "0" or "1" to Boolean ---
	oKMV.ASCheckbox = strToBool(oKMV.ASCheckbox)
	console.log(oKMV.ASCheckbox)

app.beep()
var oAns = app.displayAlert(
	strAlertTitle, // main alert text
	{
		message: strAlertMsg,
		as:  "critical",
		buttons: ['Cancel', 'Ignore', 'OK'],
		defaultButton: 3,
		cancelButton: 1
	})
	
if (oAns.buttonReturned = "Cancel") {
	// Quit the script?
}


//=============== END OF MAIN SCRIPT =================================

//=====================================================================	
function boolToStr (pBoolVar) {
//=====================================================================	

	if (pBoolVar) {
		strVar = "1"
	} else {
		strVar = "0"
	}
	return strVar
	
}	// END function boolToStr
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––


//=====================================================================	
function strToBool (pstrBool) {
//=====================================================================	

	if (pstrBool === "0") {
		bolVar = false
	} else {
		bolVar = true
	}
	return bolVar
	
}	// END function strToBool
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––


//=====================================================================	
function setKMVar(pstrName, pstrValue) {
//=====================================================================	

	var app = Application.currentApplication()
	app.includeStandardAdditions = true

	var appKM = Application("Keyboard Maestro Engine")
		
	var oVars = appKM.variables
		
	try {
		oVars[pstrName].name();
		
	} catch (e) {
		appKM.variables.push(appKM.Variable({'name': pstrName	}));
		
		app.displayNotification(
			pstrName, 
			{
				withTitle: "Set KM Variable",
				subtitle:  "Variable was Created"
				//soundName: "Basso"
		  });

		}	// END try/catch
		
		oVars[pstrName].value = pstrValue
		
		return
		
}	// END function setKMVar
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––

//=====================================================================	
function getKMVar(pstrName) {
//=====================================================================	

	var app = Application.currentApplication()
	app.includeStandardAdditions = true

	var appKM = Application("Keyboard Maestro Engine")
		
	var oVars = appKM.variables
		
	try {
		var strValue = oVars[pstrName].value();
		
	} catch (e) {
		
		strValue = undefined
		
		app.beep()
		var oAns = app.displayAlert('KM Variable does NOT exist', {
				message: 'Var Name: ' + pstrName,
				as: 'critical'
			})

		}	// END try/catch
				
		return strValue
		
}	// END function getKMVar
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
//======================= END OF FILE =====================================

#3

So is this first link the best one to use or the last reference Here?[quote=β€œJMichaelTX, post:2, topic:2670”]
JXA Script File, which uses the same HTML file and Macro as above:[KM] DEMO Custom HTML Form using JXA.scpt.zip (3.7 KB)
[/quote]
Also @JMichaelTX what’s the process for installing this? e.g. Where do I put the HTML file? I put the AppleScript in /HD/Users/me/Library/Scripts/Keyboard Maestro UIs/


#4

If you want to display the HTML Prompt from JXA, then download the .zip files from here:

###Install/Setup Instructions

  • Unpack both zip files.
  • Install the KM Macro as normal -- just double-click on the .kmmacros file.
  • Put the .html file anywhere you like.
  • Open the JXA Script in Script Editor.
    • Change this line to use the path of the .html file:
setKMVar("AS_HTML_Form_File", 
  "/Users/Shared/Dropbox/SW/DEV/KM/KM7/Actions/Custom HTML Prompt/KM-HTML-Form-Set-by-Applescript.html")
  • Compile and run the script.

Please let me know if you have any further questions, or issues with running the script.


#5

Actually I got the form to open when firing the Keyboard Maestro macro by clicking on the folder icon in the 3rd action (Custom HTML Prompt β€˜Unknown’ and navigating to the file. Should I change the text back to what you have written?


#6

Yes, you should.

As it states, the macro is not intended to be run directly. It is to be run ONLY from a script. The script will set that KM variable that contains the path.
This allows different scripts to use different HTML files, but the same KM macro.


#7

Ok. So what can I use this for? Where can I direct the output?


#8

The only purpose of the macro and scripts in this topic is to show how to display a KM HTML prompt from a script, rather than having to start with a KM Macro.

How you use it is totally up to you.

I plan to use it for scripts where I need a more complex UI than AppleScript or JXA can provide natively (using display dialog commands).

Essentially, the HTML Prompt provides a very rich text, full featured UI that is only limited by your imagination and skill.

The output of the HTML Prompt is to KM Variables. So, after the user closes the HTML Prompt, the script needs to read those KM variables.

Since the latest update of KM which provides for a JavaScript in HTML Prompt action, this would allow you run a JavaScript, which returns data, from your main AppleScript or JXA script.


#9

I can think of some great uses but how to implement them is the question. For example since Keyboard Maestro has a webserver (which I haven’t explored at all) I can see creating a form to add records to a database I manage that others input data to. I could use this as a method of controlling user input which would get emailed to me and I could enter the record since there’s a problem with messy record entry. I could watch over stuff that comes down the pipe so to speak.

I am very interested in this @JMichaelTX so if there’s a particular thread you are updating on this, I think it’s one I’d like to watch. As I get more comfortable with JavaScript I am hoping to make better use of your custom HTML prompt.


#10

I haven’t planned to have one thread for this, but if I did this one would be it.
Most likely, I’ll post a new topic with a descriptive title, and tags of β€œhtml_prompt” and β€œscripting”. If you like you can search for my posts on this using this search:
tags:html_prompt @jmichaeltx scripting


#11

Sounds good.