content: -- Copyright (C) 2012 Robin Trew
  --
  -- Permission is hereby granted, free of charge, 
  -- to any person obtaining a copy of this software 
  -- and associated documentation files (the "Software"), 
  -- to deal in the Software without restriction, 
  -- including without limitation the rights to use, copy, 
  -- modify, merge, publish, distribute, sublicense, 
  -- and/or sell copies of the Software, and to permit persons 
  -- to whom the Software is furnished to do so, 
  -- subject to the following conditions:
  
  -- *******
  -- The above copyright notice and this permission notice 
  -- shall be included in ALL copies 
  -- or substantial portions of the Software.
  -- *******
  
  -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
  -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
  -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
  -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
  -- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  property pTitle : "Save from OmniFocus as FT MD"
  property pVer : "0.05"
  property pAuthor : "Robin Trew"
  
  
  -- PLACE THE FOLDINGTEXT VERSION IN THE CLIPBOARD ?
  property pblnSetClipboard : false
  
  -- SAVE TO A FILE ?
  property pblnWriteFile : true
  
  -- NOTIFY ON COPY/SAVE  ?
  property pblnNotify : true -- for Growl or Applescript confirmation of copy or save
  
  
  property pOutFolder : (path to desktop)
  property pstrDefaultName : "Tasks"
  property pstrExtn : "txt"
  
  property pblnTodo : true
  property pPROJECT : "project"
  property pstrProjectFlag : " .todo"
  
  -- Ver 0.02 corrected position of .todo in projects with notes. Enabled Context mode copying
  -- Ver 0.03 added WriteFile and notify options
  
  on run
  	set {lstNodes, blnContext} to SelectedInOF()
  	
  	if lstNodes ≠ {} then
  		set lstPaths to {}
  		repeat with oNode in lstNodes
  			set end of lstPaths to my GetNodePath(oNode, pstrProjectFlag, tab & tab)
  		end repeat
  		set strFT to MakeMD(lstPaths)
  		
  		
  		if pblnWriteFile then
  			set strPath to ChooseFilePathAndSave(strFT, POSIX path of pOutFolder, pstrDefaultName)
  			if pblnNotify then Notify("OmniFocus", "", "Saved", strPath)
  		end if
  		
  		
  		if pblnSetClipboard then
  			set the clipboard to strFT
  			if pblnNotify then Notify("OmniFocus", "", "Copied as FoldingText MD", strFT)
  		end if
  	end if
  end run
  
  -- if a string is new write it out, 
  -- otherwise write out a tab
  -- and terminate each path with a linefeed
  on MakeMD(lstPaths)
  	set str to ""
  	set lstCurrent to {}
  	set lngCurrent to 0
  	repeat with oPath in lstPaths
  		set lngParts to length of oPath
  		repeat with i from 1 to lngParts
  			set strIndent to my NumChar(i - 1, tab)
  			if strIndent ≠ "" then
  				set strIndent to strIndent & "- "
  			else
  				set strIndent to strIndent & "# "
  			end if
  			set strPart to contents of item i of oPath
  			if i ≤ lngCurrent then
  				if strPart ≠ item i of lstCurrent then
  					set str to str & strIndent & strPart & linefeed
  					set item i of lstCurrent to strPart
  				end if
  			else
  				set str to str & strIndent & strPart & linefeed
  				set end of lstCurrent to strPart
  				set lngCurrent to length of lstCurrent
  			end if
  		end repeat
  	end repeat
  	return str
  end MakeMD
  
  -- READ SELECTED OmniFocus Nodes
  on SelectedInOF()
  	tell application "OmniFocus"
  		set lstWindows to document windows of front document
  		if (count of lstWindows) < 1 then return {{}, false}
  		tell item 1 of lstWindows
  			if not visible then return {{}, false}
  			set blnContext to ((selected view mode identifier) is not equal to pPROJECT)
  			
  			repeat with oPanel in {content, sidebar}
  				set lstNodes to value of (selected trees of oPanel where class of its value ≠ item)
  				set lngNodes to count of lstNodes
  				if lngNodes > 0 then exit repeat
  			end repeat
  			set blnAll to (lngNodes < 1)
  			if blnAll then set lstNodes to value of (trees of content where class of its value ≠ item)
  		end tell
  		
  		return {lstNodes, blnContext}
  	end tell
  end SelectedInOF
  
  -- Return the path of an OF object as a list of strings, optionally flagging projects with a string
  on GetNodePath(oNode, strProjMarker, strIndent)
  	tell application id "OFOC"
  		set {cClass, oParent, strName} to {class, container, name} of oNode
  		set cParentClass to class of oParent
  		set strNote to ""
  		if {project, task, inbox task} contains cClass then
  			-- start date, due date, completed, duration, flagged, note
  			set {varContext, varStart, varDue, varDone, varDurn, blnFlagged, strNote} to {context, start date, due date, completion date, estimated minutes, flagged, note} of oNode
  			if varContext ≠ missing value then
  				set strContext to name of varContext
  				if strContext contains space then set strContext to my FixSpace(strContext)
  				set strName to strName & " @" & strContext
  			end if
  			if varStart ≠ missing value then set strName to strName & " @start(" & my FTDateFormat(varStart) & ")"
  			if varDue ≠ missing value then set strName to strName & " @due(" & my FTDateFormat(varDue) & ")"
  			if varDone ≠ missing value then set strName to strName & " @done(" & my FTDateFormat(varDone) & ")"
  			if varDurn ≠ missing value then set strName to strName & " @duration(" & my Mins2Str(varDurn) & ")"
  			if blnFlagged then set strName to strName & " @flagged"
  			
  		end if
  		
  		-- IF THIS IS THE PROJECT LEVEL, FLAG IT, AND CONTINUE UP THROUGH ANY ENCLOSING FOLDERS
  		if cParentClass is document or cParentClass is folder then
  			if cClass is task or cClass is project then
  				tell oNode
  					if (cClass is project) or (it is root task of its containing project) then
  						if strProjMarker ≠ "" then
  							set strName to strName & strProjMarker
  						end if
  						
  						set oFolder to folder of containing project of oNode
  						if oFolder is not missing value then
  							set cParentClass to folder
  							set oParent to oFolder
  						end if
  					end if
  				end tell
  			end if
  		end if
  		
  		-- NOW THAT WE HAVE ADDED ANY @KEY(VALUE) TAGS, AND ANY PROJECT MARKER (E.G. .TODO)
  		-- WE CAN APPEND ANY NOTE TEXT
  		if strNote ≠ "" then
  			repeat with oPara in paragraphs of strNote
  				set strName to strName & linefeed & strIndent & oPara
  			end repeat
  		end if
  		
  		-- RECURSE UPWARDS UNLESS WE'VE REACHED THE DOCUMENT CEILING
  		if cParentClass is not document then
  			return my GetNodePath(oParent, strProjMarker, strIndent & tab) & {strName}
  		else
  			return {strName}
  		end if
  	end tell
  end GetNodePath
  
  on NumChar(lngChars, strChar)
  	set str to ""
  	repeat with i from 1 to lngChars
  		set str to str & strChar
  	end repeat
  	return str
  end NumChar
  
  -- yyyy-mm-dd HH:MM
  on FTDateFormat(varDate)
  	set {dlm, my text item delimiters} to {my text item delimiters, "-"}
  	tell varDate
  		set strDate to {its year as string, my PadNum(its month as integer, 2), my PadNum(its day, 2)} as string
  		set my text item delimiters to ":"
  		set strTime to {my PadNum(its hours, 2), my PadNum(its minutes, 2)} as string
  	end tell
  	set my text item delimiters to dlm
  	return strDate & space & strTime
  end FTDateFormat
  
  on Mins2Str(lngMins)
  	set my text item delimiters to ""
  	return ((lngMins div 60) & "h " & (lngMins mod 60) & "m") as string
  end Mins2Str
  
  -- Replace spaces in context names with underscore
  on FixSpace(str)
  	set {dlm, my text item delimiters} to {my text item delimiters, space}
  	set lstParts to text items of str
  	set my text item delimiters to "_"
  	set strFixed to lstParts as string
  	set my text item delimiters to dlm
  	return strFixed
  end FixSpace
  
  on PadNum(lngNum, lngDigits)
  	set strNum to lngNum as string
  	set lngGap to (lngDigits - (length of strNum))
  	repeat while lngGap > 0
  		set strNum to "0" & strNum
  		set lngGap to lngGap - 1
  	end repeat
  	strNum
  end PadNum
  
  -- SAVE THE FT STRING TO A TEXT FILE
  on ChooseFilePathAndSave(strFT, strPath, strName)
  	tell application "Finder"
  		-- OFFER A DEFAULT FOLDER (if a valid one is specified)
  		if exists strPath then
  			set strOutFolder to strPath
  		else
  			set strOutFolder to POSIX path of (path to desktop)
  		end if
  	end tell
  	set {dlm, my text item delimiters} to {my text item delimiters, "."}
  	set strStem to first text item of strName
  	set my text item delimiters to dlm
  	tell application id "OFOC"
  		activate
  		set strOutPath to (POSIX path of ¬
  			(choose file name with prompt pTitle default name strStem & "." & pstrExtn default location strOutFolder))
  	end tell
  	WriteText2Path(strFT, strOutPath)
  	return strOutPath
  end ChooseFilePathAndSave
  
  -- WRITE UTF-8 TEXT FILE TO POSIX PATH
  on WriteText2Path(strText, strPosixPath)
  	set f to (POSIX file strPosixPath)
  	open for access f with write permission
  	write strText as «class utf8» to f
  	close access f
  end WriteText2Path
  
  on Notify(strAppName, strProcess, strTitle, strMsg)
  	tell application "System Events"
  		set strGrowlApp to ""
  		repeat with oGrowlApp in {"Growl", "GrowlHelperApp"}
  			if (count of (every process whose name = oGrowlApp)) > 0 then
  				set strGrowlApp to oGrowlApp
  				exit repeat
  			end if
  		end repeat
  		if strGrowlApp ≠ "" then
  			set strScript to "
  			tell application \"" & strGrowlApp & "\"
  				register as application \"Houthakker scripts\" all notifications {\"" & strProcess & "\"} default notifications {\"" & strProcess & "\"} icon of application \"" & strAppName & "\"
  				notify with name \"" & strProcess & "\" title \"" & strTitle & "\" application name \"Houthakker scripts\" description \"" & strMsg & "\"
  			end tell"
  			strScript
  			run script strScript
  		else
  			activate
  			display dialog strMsg buttons {"OK"} default button "OK" with title pTitle & tab & pVer
  		end if
  	end tell
  end Notify