Sunday, October 17, 2021

#1 2010-10-21 04:10:16 pm

From:: Colorado
Registered: 2005-11-10
Posts: 59

Use HTML forms instead of AppleScript dialogs

I've recently put a fair bit of work into integrating HTML forms with client-side AppleScripts, so that I can use much more complicated UIs in my scripts. It's turned out to be an incredibly useful addition to my scripting arsenal, since it gets me away from the nightmare of sequential dialog boxes filled with half a dozen buttons a piece.

The basics are as follows:

Create an applet with a custom location handler (e.g. myscript://)

Create a web form that sends a GET request to that same handler

Process the URL in the applet, and use the form arguments to drive script behavior and/or write to script properties

The first two steps are fairly straightforward, but the parsing of the request is the really difficult part. But here's the code to handle that:

You can find a full discussion of how it works and a sample on my blog:

Here's the generic code to use in your scripts:


(* Nik's generic URI handling scriptlet

By Nik at [url][/url]

This script snippet has a script object that handles any URI/URL using the "open location" Applescript handler. This allows custom URI schemes to activate this script. It's a great way to fire off AppleScripts from web browsers that don't have script support or from shell scripts. If it can make a URL, it can run the applet.

This script takes a URL in the format of "theURI://theMethod?Arg1&Arg2&...ArgN". It handles the parsing of the individual URL arguments and the method, and generates an object containing the results as easy-to-access properties.

The "open location" handler is deliberately simplified to just generate the URL object -- nothing more. From there you can refer to the object to get specific properties, e.g. "get description of myURLObject's args" to get the "description" argument out of the original URL.

There is no built-in validation, so you'll have to manage that after parsing the initial URL.

For this script to work, you must save it as an Application Bundle, and edit the enclosed plist as follows (copied from Apple's website):


When you save the script as an application bundle, it will contain the standard Mac OS X bundle elements including an XML property list file defining important aspects of the script application.

To access the Info.plist property list, click on the script application with the Control key held down to access the Finder Contextual Menu. Choose Show Package Contents from this menu to open the script application bundle in a new window. Open the Contents folder within the new window to reveal the Info.plist file. Open this file in a text or property list editor and add the following XML keys and values to the property list.

To identify the Application, add the following lines to the property list, replacing the net.mysite.appname text shown here with a unique bundle identifier for your application:


To identify the URL handler that triggers the applet, add the following item to the property list, replacing the App Name and theURI text with title of your URL protocol and the URL scheme of your protocol:

       <string>App Name</string>


You can find documentation on how this works on Apple's website:



This script is free to share with anybody you like for any purpose. I'd appreciate it if you'd attribute it back to me (Nik) if you can.


-- Test it here
on run
   open location "x-custom-uri://check+this+out?arg1=I%20love%20AppleScript&"
end run

on open location sURL
   set myURLObject to newURI(sURL)
   display alert "The following values were passed: " & return & "SCHEME: " & scheme of myURLObject & return & "LOCATION: " & location of myURLObject & return & "ARG1: " & arg1 of args of myURLObject & return & "ARG2: " & arg2 of args of myURLObject
end open location

(* newURI(): URI Object Initialization Script

   This returns a URL script object, containing properties for the URL scheme, location, and arguments, as passed through an HTML-encoded URL. *)

on newURI(u)
   script uriObject
       property rawURL : missing value
       property scheme : missing value
       property location : missing value
       property args : {}
       (* initialize()        
       This handler initializes the URL object, breaking it out into its constituent parts, and assigns them to the various script object properties. It will also replace and overwrite any existing URL properties on the script object *)

       on initialize(aURL)
               set u to aURL
               -- Break out the URL into its various components
               set theSplitURL to splitURL(aURL)
               log result
               --Get the URI-Scheme from the URI
               set scheme to item 1 of theSplitURL
               -- Get the location from the URI
               set location to (decode_text(item 2 of theSplitURL))
               -- parse arguments
               if item 2 of theSplitURL is not missing value then
                   set args to argsToRecord(item 3 of theSplitURL)
               end if
               -- All went well, let's reset our text item delimiters and send back the arguments
               set AppleScript's text item delimiters to ""
               return {scheme:scheme, location:location, args:args}
           on error errMsg number errNum
               display alert errMsg & " (" & errNum & ")"
               error number -128
           end try
       end initialize
       (* Convert a URL into a record set *)
       on splitURL(theURL)
           set text item delimiters to ":"
           set theURI to text item 1 of theURL
           set text item delimiters to ""
           set uriN to (count of characters of theURI) + 1 -- account for the ":"
           -- Get rid of the url protocol string
           set pN to offset of (theURI & "://") in theURL -- is it a mailto:// style?
           if pN > 0 then -- a URI:// url
               set theURL to text (uriN + 3) through (count of characters of theURL) of theURL
           else -- or just a URI: url
               set theURL to text (uriN + 1) through (count of characters of theURL) of theURL
           end if
           -- See if there's any arguments being passed, pass 'em back if there are
           set aN to offset of "?" in theURL
           if aN = 1 then -- no base url, just arguments
               return {missing value, (text (aN + 1) through (count of characters of theURL) of theURL)}
           else if aN > 1 then
               return {theURI, (text 1 through (aN - 1) of theURL), (text (aN + 1) through (count of characters of theURL) of theURL)}
               return {theURI, theURL, theArgs}
           end if
       end splitURL
       (* Splits ?key=value&key2=value2 type arguments from the URI and turns them into a {key:value,key2:value2} record set *)
       on argsToRecord(argString)
           set rStringArray to {}
           set text item delimiters to "&"
           set splitArgs to text items of argString
           set text item delimiters to "="
           repeat with a in splitArgs
               set ax to text items of a
               set axKey to item 1 of ax
               set axValue to my decode_text(item 2 of ax)
               set rStringArray to rStringArray & {axKey & ":\"" & axValue & "\""}
           end repeat
           set text item delimiters to ","
           run script ("return {" & rStringArray as string) & "}"
           return result
       end argsToRecord
       (* Simple HTML decode routine *)
       on decode_text(encodedstring)
           do shell script "echo " & quoted form of ¬
               encodedstring & " | /usr/bin/ruby -r cgi -e \"print CGI.unescape('+',' ')\""
       end decode_text
   end script
   tell uriObject to initialize(u)
   return uriObject
end newURI



Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)