Thanks for the information, Brian! I changed “activate” to “open” within the QE entry in the URI script and the bookmarklet functions correctly now. Here is the the modified script if anyone else is interested.
Code:
(*
# OMNIFOCUS URI HANDLER
This applet enables you to create new tasks using an "x-omnifocus" URL. This is particularly designed to make it easy to add tasks from a web browser via a bookmarklet, without having to code a separate script for each browser. This also works in browsers that don't support AppleScript. (I'm looking at you, Firefox!!!)
It also supports more advanced URLs containing context and project assignments that you're likely to create from web page bookmarklets. This functionality can be utilized to make it easier to add OmniFocus tasks from other applications, shell scripts, etc., without having to hook into complicated AppleScript.
## HOW TO CREATE PROPER URLS
All URLs must use the "x-omnifocus:" URI. You can have "whack whacks" after the URI, or just a colon, your choice. You must then follow the URI with the following methods:
*x-omnifocus:newtask*
This creates a new task. You can pass variables, following the example below. The only required element is "name".
x-omnifocus://newtask?name=**task name**&project=**project name**&context=**context name**¬e=**task note text**&quickentry=**1/0**
**Project Name** and **Context Name** will "fuzzy match" an existing project or context, so you don't need a full or exact name.
a **Quickentry** value of "1" will cause the task to go into the Quick Entry window, which will be activated, rather than straight into the inbox.
*x-omnifocus:parsetasks*
This parses tasks either as a single task or as multiple tasks, per the usual parsing syntax. You can pass two variables, the text to parse and whether to parse it as a single task, as below. The only required element is the text to parse.
x-omnifocus://parsetasks?text=**Text to Parse**&single=**1/0**
A *single* value of "1" will make the tasks parse as single tasks. Otherwise, tasks will be parsed line-by-line, potentially as multiple tasks.
All strings should be URL encoded to eliminate any ambiguity in URLs and whatnot.
*** BOOKMARKLETS TO GET YOU STARTED ***
Here's some sample bookmarklets you can put into your browser's toolbar to make this all go:
*Add the current page as a task with the page's URL and any selected text as the note:*
javascript:window.location='x-omnifocus://newtask?name='+encodeURIComponent(document.title)+'¬e='+encodeURIComponent(window.location+'\n\n')+encodeURIComponent(getSelection())+'&quickentry=0';
*Same as above, but route to the Quick Entry window instead of directly into the inbox:*
javascript:window.location='x-omnifocus://newtask?name='+encodeURIComponent(document.title)+'¬e='+encodeURIComponent(window.location+'\n\n')+encodeURIComponent(getSelection())+'&quickentry=1';
*Parse tasks in the selection:*
javascript:window.location='x-omnifocus://parsetasks?text='+encodeURIComponent(getSelection())+'&single=0';
## NOTES
The HTML entity decoding routine falls down on certain characters, particularly Unicode characters.
If you use the quick entry window with the "newtask" method, the project and context values will be ignored, as these are not scriptable in the quick entry window.
## VERSION HISTORY
• 1.0 - 06/20/2008: Initial release. Supports parsetasks, newtask
• 1.x - 09/03/2009: Modified iNik's script to support new AppleScript terminology used after OF 1.7 update. "activate" changed to "open" for QuickEntry. This version corrects an issue with the QuickEntry window failing to open when the bookmarklet is used in Firefox 3.5.2. Refer to http://forums.omnigroup.com/showthread.php?t=8251 for details. - kaijin
*)
property theURI : "x-omnifocus"
global tid -- save text item delimiters
-- Test it here
on run
open location "x-omnifocus://newtask?name=JavaScript%20escape%28%29%20Function¬e=The%20escape%28%29%20function%20encodes%20a%20string%2C%20so%20it%20can%20be%20read%20on%20all%20computers.&quickentry=0&context=P"
--open location "x-omnifocus:parsetasks?text=foo
--bar
--rawr&single=0"
-- open location "ofinbox://?title=JavaScript%20escape%28%29%20Function&selection=kittykat"
end run
(* Open the URI: link *)
on open location sURL
try
if sURL does not start with (theURI & ":") then error "Malformed URL: " & sURL number 400
set tid to AppleScript's text item delimiters
set theSplitURL to splitURL(sURL)
set theMethod to (decode_text(item 1 of theSplitURL))
if item 2 of theSplitURL is not missing value then
set theArgs to my parseArgs(item 2 of theSplitURL)
end if
if theMethod is "newtask" then
my newTask(theArgs)
else if theMethod is "parsetasks" then
my parseTasks(theArgs)
else
error "Method \"" & theMethod & "\" not allowed." number 405
end if
on error errMsg number errNum
display alert errMsg & " (" & errNum & ")"
error number -128
end try
end open location
on newTask(theArgs)
-- x-omnifocus://newtask?name=[task name, required]&project=[project name]&context=[context name]¬e=[task note text]&quickentry=[1: Use quickentry]
log "newTask"
log theArgs
set useQuickEntry to false -- default to no quick entry
set hasName to false -- Check to see if it has a name, otherwise it'll break
set theParameters to {}
set theNote to missing value
set theContext to missing value
set theProject to missing value
set theName to missing value
repeat with x in theArgs
set xKey to key of x
set xVal to value of x
-- Name
if xKey is "name" then
set theName to xVal
-- Note
else if xKey is "note" then
set theNote to xVal
-- QuickEntry
else if xKey is "quickentry" and xVal is "1" then
set useQuickEntry to true
-- Project
-- Match the project to the closest match in the document, error if something doesn't match
else if xKey is "project" then
try
tell application "OmniFocus" to tell default document to get name of first item of (complete xVal as project maximum matches 1)
set theProject to (result as text)
on error errMsg number errNum
if errNum is -1728 then
error "Project \"" & xVal & "\" not found." number 404
else
error errMsg number errNum
end if
end try
-- Context
-- Match the context to the closest match in the document, error if something doesn't match
else if xKey is "context" then
try
tell application "OmniFocus" to tell default document to get name of first item of (complete xVal as context maximum matches 1)
set theContext to (result as text)
on error errMsg number errNum
if errNum is -1728 then
error "Context \"" & xVal & "\" not found." number 404
else
error errMsg number errNum
end if
end try
end if
end repeat -- We have all the arguments now
log "Arguments repeat finished"
if theName is missing value then -- no name, no new task
error "No name specified. New tasks must have a name." number 406
else if useQuickEntry is true then
my makeQuickEntryTask(theName, theNote) -- Context and Project are not scriptable in QE mode
else
my makeInboxTask(theName, theProject, theContext, theNote)
end if
end newTask
on parseTasks(theArgs)
-- x-omnifocus://parsetasks?text=[Text to Parse, HTTP escaped, required]&single=[1: Parse as a single task]
set theText to missing value
set single to false
repeat with x in theArgs
set xKey to key of x
set xVal to value of x
if xKey is "text" then
set theText to xVal
else if xKey is "single" and xVal is "1" then
set single to true
end if
end repeat
if theText is missing value then -- error, we need transport text
error "No text specified. New tasks must include text to parse." number 406
else if single is true then
tell application "OmniFocus" to tell default document to parse tasks with transport text theText with as single task
else
tell application "OmniFocus" to tell default document to parse tasks with transport text theText without as single task
end if
end parseTasks
on makeQuickEntryTask(theName, theNote)
tell application "OmniFocus" to tell default document
tell quick entry
set theTask to make new inbox task with properties {name:theName}
if theNote is not missing value then set note of theTask to theNote
select {theTask}
open
end tell
end tell
end makeQuickEntryTask
on makeInboxTask(theName, theProject, theContext, theNote)
tell application "OmniFocus" to tell default document
set theTask to make new inbox task with properties {name:theName}
if theNote is not missing value then set note of theTask to theNote
end tell
end makeInboxTask
(* Convert a URL into a record set *)
on splitURL(theURL)
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 {(text 1 through (aN - 1) of theURL), (text (aN + 1) through (count of characters of theURL) of theURL)}
else
return {theURL, missing value}
end if
end splitURL
on parseArgs(sArgs)
set text item delimiters to "&"
set theArgs to every text item of sArgs
set text item delimiters to tid
set rArgs to {}
repeat with xArg in theArgs
set yArg to (text of xArg)
set eqOff to offset of "=" in yArg
if eqOff > 0 then
set argKey to text 1 through (eqOff - 1) of yArg
set argValueEncoded to text (eqOff + 1) through (count of characters of yArg) of yArg
set argValue to my decode_text(argValueEncoded)
set rArgs to rArgs & {{key:argKey, value:argValue}}
else
error "Parameter \"" & yArg & "\" has no value." number 406
end if
end repeat
return rArgs
end parseArgs
(* From Apple's sub-routines page *)
-- this sub-routine is used to decode a three-character hex string
on decode_chars(these_chars)
copy these_chars to {indentifying_char, multiplier_char, remainder_char}
set the hex_list to "123456789ABCDEF"
if the multiplier_char is in "ABCDEF" then
set the multiplier_amt to the offset of the multiplier_char in the hex_list
else
set the multiplier_amt to the multiplier_char as integer
end if
if the remainder_char is in "ABCDEF" then
set the remainder_amt to the offset of the remainder_char in the hex_list
else
set the remainder_amt to the remainder_char as integer
end if
set the ASCII_num to (multiplier_amt * 16) + remainder_amt
return (ASCII character ASCII_num)
end decode_chars
-- this sub-routine is used to decode text strings
on decode_text(this_text)
set flag_A to false
set flag_B to false
set temp_char to ""
set the character_list to {}
repeat with this_char in this_text
set this_char to the contents of this_char
if this_char is "%" then
set flag_A to true
else if flag_A is true then
set the temp_char to this_char
set flag_A to false
set flag_B to true
else if flag_B is true then
set the end of the character_list to my decode_chars(("%" & temp_char & this_char) as string)
set the temp_char to ""
set flag_A to false
set flag_B to false
else
set the end of the character_list to this_char
end if
end repeat
return the character_list as string
end decode_text