content: property pTitle : "TaskPaper Views"
  property pVer : "0.5"
  
  -- 	Copyright © 2014, Robin Trew
  --  All rights reserved.
  -- 	
  -- 	Redistribution and use in source and binary forms, with or without modification, 
  -- 	are permitted provided that the following conditions are met:
  -- 	
  -- 		- Redistributions of source code must retain the above copyright notice, 
  -- 		  this list of conditions and the following disclaimer.
  -- 		- Redistributions in binary form must reproduce the above copyright notice, 
  -- 		  this list of conditions and the following disclaimer in the documentation 
  -- 		  and/or other materials provided with the distribution.
  -- 		
  -- 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
  -- 	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
  -- 	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
  -- 	IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
  -- 	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  -- 	 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  -- 	 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  -- 	 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
  -- 	 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  -- DESCRIPTION 
  
  -- An editable menu of saved TaskPaper views, which can
  -- 1. Optionally appear on the menu in the form of View names rather than raw Taskpaper queries
  -- 2. Include relative (and natural language) date expressions, {enclosed in curly brackets}
  
  -- The relative date function requires installation of 
  -- Mike Taylor and Darshana Chhajed's Python parsedatetime module:
  
  --	1. Visit https://github.com/bear/parsedatetime
  --	2. Download and expand https://github.com/bear/parsedatetime/archive/master.zip
  --	3. in Terminal.app, cd to the unzipped folder 
  --		(e.g. type cd followed by a space, and drag/drop the folder to the Terminal.app command line, then tap return)
  --	4. Enter:		sudo python setup.py install
  
  -- MENU STORED IN A TEXT FILE (SAME FOLDER AS SCRIPT)
  
  --	The list of custom Views is kept in a simple text file (one query, optionally labelled, per line)
  --	The default file name is TPCustomViews.txt
  
  -- QUERIES CAN BE PREFIXED WITH A LABEL
  
  --	Each query can optionally be prefixed by a descriptive label. 
  --	The default separator after the label is a single pipe character |
  
  -- AN EDIT FUNCTION OFFERS A CHEAT SHEET FOR THE TASKPAPER QUERY LANGUAGE
  
  --	The menu text file can be edited directly.  Alternatively, an Edit option at the bottom of the script menu 
  --	allow allows you to add and customise optionally labelled query lines,
  --	seeing their effect in Taskpaper as soon as you save.
  
  --	To amend or delete existing queries, multiselect 'Edit' and or more queries (⌘-Click)
  --	To delete a query, place a pipe character at the end of the line, followed by no further printing characters
  --	A cheet sheet with a copy button is lists query language terms and current document tabs
  
  -- EXAMPLES
  
  --	Available | not @done and not @cancel and not @hold and (@start <=  {today} or @due <=  {today} or @today)
  --	Due this week | @due < {now +7d}
  --	In Progress | not @done and (@start < {today} or @draft or @waiting or @today)
  --	Overdue | @due < {now}
  
  
  -- OPTIONS
  --	display the queries themselves (in the menu) as well as any label ?
  --	(queries are listed in the menu anyway if they have no label)
  property pblnLabelOnly : true
  
  property pblnQuitOnViewSelect : true -- exit immediately after view is selected ?
  property pblnQuitOnViewEdit : false -- exit immediately after view is edited ?
  
  -- Brackets enclosing natural language date expressions
  property pstrRelTimeOpen : "{"
  property pstrRelTimeClose : "}"
  
  -- Delimiter to separate leading View name from subsequence query clauses
  property pstrLabelDelim : "|"
  
  -- Default to exclude seconds from 'now' based date/time expressions
  property pblnSeconds : false
  
  -- HELP STRINGS, CHEAT SHEETS, EXAMPLES
  
  property pstrEditPrompt : "NAMING A QUERY:
  You can optionally prefix the query with a view name,  using the  pipe character  | as a delimiter, as in:
  
          Overdue | @due < {now}
  
  USING A RELATIVE DATE/TIME EXPRESSION:
  
  Anything in curly brackets will be converted to a standard date/time format
  (defaulting to the current time, if parsing proves impossible)
  
  	@due < {4pm tomorrow}
  	@due < {now + 1 week}
  	@start > {now - 5 days}
  
  DELETING EXISTING QUERIES:
  To delete a query from the edit dialogue, terminate it (or just its label) with a pipe character
  
  	Overdue |
  	overdue | @due < {now} |
  	@due < {now} |
  
  CREATING ONE OR MORE NEW QUERIES
  Simply type more or more query lines
  (Optionally preceding each with a name and a pipe character |)
  
  Edit Query:"
  
  
  property plstSamples : {¬
  	"Available | not @done and not @cancel and not @hold and (@start <=  {today} or @due <=  {today} or @today)", ¬
  	"Due over the next 6 weeks | @due <= {now + 42d}", ¬
  	"Due this week | @due < {now +7d}", ¬
  	"In Progress | not @done and (@start <= {today} or @draft or @waiting or @today)", ¬
  	"Overdue | @due < {now}"}
  
  property pstrAll : "ALL projects & tasks"
  
  -- PARSING CURLY-BRACKETED NATURAL LANGUAGE DATE EXPRESSIONS DEPENDS
  -- ON THE INSTALLATION OF A PYTHON LIBRARY
  property pstrInstall : "
  To install Mike Taylor and Darshana Chhajed's Python parsedatetime module:
  
  1. Visit https://github.com/bear/parsedatetime
   
  (Licence: https://github.com/bear/parsedatetime/blob/master/LICENSE.txt)
   
  2. Download and expand https://github.com/bear/parsedatetime/archive/master.zip
  
  3. in Terminal.app, cd to the unzipped folder 
  
  (e.g. type cd followed by a space, and drag/drop the folder to the Terminal.app command line, then tap return)
  
  4. Enter:
  			sudo python setup.py install
  "
  
  -- Taskpaper query language: Cheat sheet
  
  property plstAttribute : {¬
  	{"type", "[project|task|note]"}, ¬
  	{"line", "entire literal text"}, ¬
  	{"content", "minus tags & formatting"}, ¬
  	{"level", "count of leading tabs"}, ¬
  	{"parent", "text of parent line"}, ¬
  	{"project", "text of all enclosing projects"}, ¬
  	{"index", "0-based position among peers"}, ¬
  	{"uniqueid", "(numeric string)"}, ¬
  	{"+d", "plus descendants"}}
  property plstRelation : {¬
  	{"contains", "(case-insensitive)"}, ¬
  	{"matches", "regular expression"}, ¬
  	{"=", "ignoring white space"}, ¬
  	{"==", "exact"}, ¬
  	{"<", "(alphabetically)"}, ¬
  	{">", "(alphabetically)"}}
  property plstConjunction : {¬
  	{"and", "join clauses (or bracketed clause groups)"}, ¬
  	{"or", ""}}
  property plstNegn : {¬
  	{"not", "tag absent, or clause negated"}}
  property plstDiv : {{"________________________________________________", ""}}
  
  -- UI Prompts
  property pMainPrompt : "To add view(s) click 'Edit'.
  
  To modify or delete,
  multiselect (⌘-click)
  existing view(s) and 'Edit'.
  
  Choose:"
  
  -- UI BUTTONS
  property pOK : "OK"
  property pCancel : "Cancel"
  property pHelp : "Cheat sheet"
  property pSave : "Save"
  property pbtnClip : "Copy to clipboard"
  property pEdit : "Edit"
  
  -- TEXT FILE OF SAVED VIEWS (same directory as this script)
  property pQueryFile : "TPCustomViews.txt"
  
  -- Cache for list of Views
  property plstCurrent : {}
  
  on run
  	tell application "TaskPaper"
  		set lstDocs to documents
  		if lstDocs ≠ {} then
  			set oDoc to item 1 of lstDocs
  			-- Read from a text file (in same folder as script) listing views
  			-- creating a sample file if not found
  			set strLibPath to my GetLibPath(pQueryFile)
  			
  			set blnContinue to true
  			set iDefault to 1
  			set strDeltaKey to pstrAll
  			repeat while blnContinue
  				set {blnContinue, iDefault, strDeltaKey} to my MainMenuLoop(oDoc, strLibPath, iDefault, strDeltaKey)
  				-- OPTIONALLY QUIT EVERY TIME A VIEW IS SELECTED
  				if pblnQuitOnViewSelect then
  					-- BUT NOT NECESSARILY AFTER A VIEW IS EDITED
  					if pblnQuitOnViewEdit then
  						set blnContinue to false
  					else
  						if strDeltaKey = pstrAll then set blnContinue to false
  					end if
  				end if
  			end repeat
  		else
  			activate
  			display dialog "Open a document in TaskPaper, and run again ..." buttons {"OK"} default button "OK" with title pTitle & "  ver. " & pVer
  		end if
  	end tell
  end run
  
  -- OFFER:
  --	1. CURRENT SET OF SAVED VIEWS,
  --	2. EDIT VIEWS
  -- UNTIL EXIT.
  
  on MainMenuLoop(oDoc, strLibPath, iDefault, strDeltaKey)
  	-- Initialize key for most recent query change
  	
  	-- ASSEMBLE A LIST OF MENU OPTIONS
  	-- (Clear filters, Choice of views, Edit views)
  	
  	set {lstMenu, iDelta} to MainMenuOptions(strLibPath, strDeltaKey)
  	
  	tell application "TaskPaper"
  		if strDeltaKey ≠ pstrAll then
  			set lngChoice to iDelta + 1
  			my ApplyFilter(oDoc, item 2 of item iDelta of plstCurrent)
  		else
  			set lngChoice to iDefault
  		end if
  		
  		-- GET CHOICE
  		set blnContinue to true
  		activate
  		try
  			set varChoice to choose from list lstMenu with title pTitle & tab & pVer with prompt ¬
  				pMainPrompt default items {item lngChoice of lstMenu} ¬
  				OK button name "OK" cancel button name ¬
  				"Cancel" with multiple selections allowed without empty selection allowed
  		on error
  			set blnContinue to false
  		end try
  		
  		if blnContinue then
  			-- Quit now if cancel button used
  			if varChoice = false then
  				set blnContinue to false
  			else
  				set lngChoice to length of varChoice
  				set blnSingle to (lngChoice = 1)
  			end if
  			
  			if blnContinue then
  				if blnSingle then set varChoice to (item 1 of varChoice)
  				
  				-- CHOICE = CHANGE FILTER OF FRONT WINDOW ?
  				set lngChoice to iDefault
  				if blnSingle and varChoice contains tab then
  					set {dlm, my text item delimiters} to {my text item delimiters, tab}
  					set lngChoice to (first text item of varChoice) as integer
  					set my text item delimiters to dlm
  					
  					if lngChoice > 0 then
  						set strChoice to item 2 of (item lngChoice of plstCurrent)
  					else
  						set strChoice to pstrAll
  					end if
  					-- PRE-PROCESS THE QUERY (CONVERTING RELATIVE TO ABSOLUTE DATES)
  					-- AND USE IT TO FILTER THE CURRENT TASKPAPER DOCUMENT
  					my ApplyFilter(oDoc, strChoice)
  					set lngChoice to lngChoice + 1
  					set strDeltaKey to pstrAll
  				else
  					-- CHOICE: EDIT CURRENT OR SELECTED FILTER(S)
  					set strDraft to my QueriesToEdit(oDoc, varChoice)
  					set strDeltaKey to my EditViews(oDoc, strDraft, strLibPath)
  				end if
  			end if
  		end if
  	end tell
  	
  	return {blnContinue, lngChoice, strDeltaKey}
  end MainMenuLoop
  
  -- EDIT CURRENT, SELECTED OR NEW VIEW(S)
  -- (CREATE, AMEND, DELETE, WITH OR WITHOUT LABEL)
  
  -- OPTIONALLY COPY TOKENS FROM A QUERY SYNTAX CHEAT SHEET
  -- AND UPDATE (AND BACK-UP) THE LIBRARY FILE ACCORDINGLY
  
  -- UPDATING THE TP VIEW IF ONLY ONE VIEW WAS SAVED
  -- AND CLEARING THE TP VIEW IF MORE
  on EditViews(oDoc, strDraft, strLibPath)
  	
  	tell application "TaskPaper"
  		set blnContinue to true
  		set strDeltaKey to pstrAll
  		repeat while blnContinue
  			try
  				activate
  				set varOption to display dialog pstrEditPrompt default answer strDraft & return & return buttons {pCancel, pHelp, pSave} cancel button pCancel default button pSave with title pTitle & "  ver. " & pVer
  			on error
  				set varOption to false
  				set blnContinue to false
  			end try
  			if varOption is not false then
  				set strDraft to text returned of varOption
  				if button returned of varOption ≠ pHelp then
  					set {lstNew, strDeltaKey} to my UpdateCache(strDraft)
  					my SaveViewFile(lstNew, strLibPath)
  					set blnContinue to false
  				else
  					
  					set strTokens to my ChooseTokens(oDoc)
  					set the clipboard to strTokens
  				end if
  			end if
  		end repeat
  	end tell
  	return strDeltaKey
  end EditViews
  
  -- Create an updated key/value (label/query) list of views
  on UpdateCache(strDeltas)
  	set lstCurrent to contents of plstCurrent
  	set lngCurrent to length of lstCurrent
  	set blnUpdated to false
  	
  	-- LIST OF NEW/UPDATED QUERIES AS  KEY/VALUE PAIRS
  	set lstLines to paragraphs of strDeltas
  	set lstDeltas to {}
  	set {dlm, my text item delimiters} to {my text item delimiters, pstrLabelDelim}
  	repeat with oLine in lstLines
  		set strLine to Trim(contents of oLine)
  		if strLine ≠ "" then
  			set lstParts to text items of strLine
  			if length of lstParts > 1 then
  				set {strKey, strValue} to {Trim(item 1 of lstParts), Trim((items 2 thru -1 of lstParts) as string)}
  			else
  				set strValue to Trim(item 1 of lstParts)
  				set strKey to strValue
  			end if
  			set end of lstDeltas to {strKey, strValue}
  		end if
  	end repeat
  	
  	
  	-- PROCESS ANY EDITS
  	set lngDeltas to length of lstDeltas
  	if lngDeltas > 0 then
  		-- GET A LIST OF THE DELTA KEYS
  		set lstDeltaKeys to contents of lstDeltas
  		repeat with i from 1 to lngDeltas
  			set item i of lstDeltaKeys to contents of (item 1 of item i of lstDeltaKeys)
  		end repeat
  		
  		set lstResult to {}
  		if lngCurrent > 0 then
  			
  			-- PASS THROUGH ANY EXISTING VIEWS UNMODIFIED,
  			-- UNLESS THEIR KEYS ARE IN THE DELTA LIST
  			repeat with oView in lstCurrent
  				set {strKey, strValue} to contents of oView
  				if (lstDeltaKeys) does not contain strKey then
  					set end of lstResult to {strKey, strValue}
  				end if
  			end repeat
  			
  			-- PASS THROUGH ALL DELTAS WITH VALUES
  			repeat with oDelta in lstDeltas
  				set {strKey, strValue} to contents of oDelta
  				if (strValue ≠ "") and last character of strValue ≠ pstrLabelDelim then
  					set end of lstResult to {strKey, strValue}
  					set blnUpdated to true
  				end if
  			end repeat
  		else
  			-- There are no current views – the deltas are all we have
  			set lstResult to lstDeltas
  		end if
  	else
  		-- There are no deltas – retain the current set of views
  		set lstResult to lstCurrent
  	end if
  	
  	-- Return the key of the last item of new query clauses
  	set my text item delimiters to dlm
  	if blnUpdated then
  		set strDeltaKey to strKey
  	else
  		set strDeltaKey to pstrAll
  	end if
  	return {lstResult, strDeltaKey}
  end UpdateCache
  
  -- Interpret request for query line(s) to use as editing draft
  -- Current query of front TP window, or one or more from library cache
  on QueriesToEdit(oDoc, varChoice)
  	set lstLibView to {}
  	if class of varChoice is list then
  		repeat with oChoice in varChoice
  			set strChoice to contents of oChoice
  			if strChoice contains tab then
  				set {dlm, my text item delimiters} to {my text item delimiters, tab}
  				set lstParts to text items of strChoice
  				set lngChoice to (item 1 of lstParts) as integer
  				if lngChoice > 0 then
  					set {strKey, strValue} to contents of (item lngChoice of plstCurrent)
  					if strKey ≠ strValue then
  						set my text item delimiters to space & pstrLabelDelim & space
  						set end of lstLibView to {strKey, strValue} as string
  					else
  						set end of lstLibView to strValue
  					end if
  				end if
  				set my text item delimiters to dlm
  			end if
  		end repeat
  	end if
  	
  	if lstLibView ≠ {} then
  		set {dlm, my text item delimiters} to {my text item delimiters, linefeed}
  		set strDraft to lstLibView as text
  		set my text item delimiters to dlm
  	else
  		tell application "TaskPaper"
  			tell oDoc to set strDraft to "View name | " & search field string
  		end tell
  	end if
  	return strDraft
  end QueriesToEdit
  
  
  -- Apply a filter to a Taskpaper document, 
  -- preprocessing any relative date expressions 
  -- to an absolute date expression
  on ApplyFilter(oDoc, strChoice)
  	set strView to strChoice
  	if strView ≠ pstrAll then
  		if strView contains pstrRelTimeOpen then ¬
  			set strView to Rel2Abs(strView)
  	else
  		set strView to ""
  	end if
  	tell application "TaskPaper"
  		tell oDoc to set search field string to strView
  	end tell
  	return true
  end ApplyFilter
  
  
  -- Build list of Main Menu Options
  -- CLEAR, CHOICE OF SAVED VIEWS, EDIT
  on MainMenuOptions(strLibPath, strDeltaKey)
  	-- READ VIEW SET TO CACHE as LABEL+QUERY KEY/VALUE PAIRS
  	set lngView to LoadViewFile(strLibPath)
  	set lstView to contents of plstCurrent
  	
  	if strDeltaKey ≠ pstrAll then
  		set iDelta to Key2Index(strDeltaKey, lstView)
  	else
  		set iDelta to 0
  	end if
  	
  	-- BUILD A LIST OF MENU CHOICE STRINGS
  	set lngDigits to (length of (lngView as string))
  	
  	if pblnLabelOnly then
  		repeat with i from 1 to lngView
  			set strLabel to item 1 of (item i of lstView)
  			set item i of lstView to my PadNum(i, lngDigits) & tab & strLabel
  		end repeat
  	else -- (display query itself after label)
  		repeat with i from 1 to lngView
  			set {strLabel, strQuery} to contents of (item i of lstView)
  			if strLabel ≠ strQuery then
  				set strSuffix to ":" & tab & tab & strQuery
  			else
  				set strSuffix to ""
  			end if
  			set item i of lstView to my PadNum(i, lngDigits) & tab & strLabel & strSuffix
  		end repeat
  	end if
  	
  	-- Prepend an option to clear filters,
  	set beginning of lstView to my PadNum(0, lngDigits) & tab & pstrAll
  	
  	-- and append an option to edit filters.
  	set lstView to lstView & {"____", pEdit}
  	
  	return {lstView, iDelta}
  end MainMenuOptions
  
  
  on Key2Index(strTargetKey, lstKeyValue)
  	set iIndex to 0
  	if lstKeyValue ≠ {} then
  		repeat with i from 1 to length of lstKeyValue
  			set {strKey, strValue} to contents of (item i of lstKeyValue)
  			if strKey = strTargetKey then
  				set iIndex to i
  				exit repeat
  			end if
  		end repeat
  	end if
  	return iIndex
  end Key2Index
  
  
  -- Replace any specially bracketed date time expressions 
  -- (including natural language expressions)
  -- with ParseDateTime results
  on Rel2Abs(strQuery)
  	set lstSectns to {}
  	set {dlm, my text item delimiters} to {my text item delimiters, pstrRelTimeOpen}
  	set lstParts to text items of strQuery
  	repeat with oPart in lstParts
  		set strPart to contents of oPart
  		if strPart contains pstrRelTimeClose then
  			set my text item delimiters to pstrRelTimeClose
  			set lstSubParts to text items of strPart
  			set end of lstSectns to ParseTime(item 1 of lstSubParts, false)
  			set end of lstSectns to (items 2 thru -1 of lstSubParts) as string
  		else
  			set end of lstSectns to strPart
  		end if
  	end repeat
  	set my text item delimiters to "\""
  	set strNewQuery to lstSectns as string
  	set my text item delimiters to dlm
  	return strNewQuery
  end Rel2Abs
  
  
  -- Get a sorted (unique) set of Tag names from the document
  on TagList(oDoc)
  	tell application "TaskPaper"
  		set lstTags to {}
  		tell oDoc
  			it
  			set refEntries to a reference to (entries where its text line contains "@")
  			repeat with lst in (name of tags of refEntries) as list
  				set lst to contents of lst
  				if lst ≠ {} then
  					repeat with strTag in lst
  						set strTag to strTag as string
  						if lstTags does not contain strTag then set end of lstTags to strTag
  					end repeat
  				end if
  			end repeat
  		end tell
  		if lstTags ≠ {} then
  			set {dlm, my text item delimiters} to {my text item delimiters, linefeed}
  			set strSort to do shell script "echo " & quoted form of (lstTags as string) & " | sort -f"
  			set my text item delimiters to return
  			set lstTags to text items of strSort
  			set my text item delimiters to return & "@"
  			set strSort to "@" & lstTags as string
  			set my text item delimiters to return
  			set lstTags to text items of strSort
  			set my text item delimiters to dlm
  		end if
  		return lstTags
  	end tell
  end TagList
  
  -- Use Mike Taylor and Darshana Chhajed's Python parsedatetime module 
  -- to get a parse of a natural language expression as a series of integers {year, month, day, hour, minute}
  -- (defaults, if parse fails, to current time)
  on ParseTime(strPhrase, blnSeconds)
  	set strSec to ""
  	if blnSeconds then set strSec to ":%S"
  	try
  		set str to do shell script ¬
  			"python -c 'import sys, time, parsedatetime as pdt; print time.strftime(\"%Y-%m-%d %H:%M" & ¬
  			strSec & "\", time.struct_time(pdt.Calendar().parse(sys.argv[1])[0]))' " & ¬
  			quoted form of strPhrase
  	on error
  		tell application "System Events"
  			activate
  			try
  				set varResult to (display dialog pstrInstall buttons {"Esc", pbtnClip} default button pbtnClip cancel button "Esc" with title pTitle & "  ver. " & pVer)
  				set the clipboard to pstrInstall
  			on error
  				return ""
  			end try
  		end tell
  	end try
  end ParseTime
  
  -- Adding leading zeros to specified width
  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
  
  -- Add trailling spaces to make target length
  on PadWord(strWord, lngTarget)
  	set lngChars to length of strWord
  	set lngDiff to lngTarget - lngChars
  	if lngDiff > 0 then
  		set strNew to strWord
  		repeat with i from 1 to lngDiff
  			set strNew to strNew & space
  		end repeat
  		return strNew
  	else
  		return strWord
  	end if
  end PadWord
  
  -- Remove leading and trailling whitespace
  on Trim(strText)
  	set lngChars to length of strText
  	if lngChars is 0 then return ""
  	set lstWhite to {space, tab, return, ASCII character 10, ASCII character 0}
  	
  	set blnFound to false
  	repeat with iChar from 1 to lngChars
  		if character iChar of strText is not in lstWhite then exit repeat
  	end repeat
  	set strText to text iChar thru lngChars of strText
  	
  	repeat with iChar from length of strText to 1 by -1
  		if character iChar of strText is not in lstWhite then exit repeat
  	end repeat
  	set strText to text 1 thru iChar of strText
  	return strText
  end Trim
  
  -- View Library file functions
  on GetLibPath(strLibFile)
  	set strFolder to ScriptFolder() as string
  	set strLibPath to POSIX path of (strFolder & "/" & strLibFile)
  	set strLibPath to EscapeSpace(strLibPath)
  	
  	set the clipboard to strLibPath
  	tell application "Finder"
  		if not my FileExists(strLibPath) then
  			my SaveViewFile(plstSamples, strLibPath)
  		end if
  	end tell
  	return strLibPath
  end GetLibPath
  
  on FileExists(strPath)
  	set strResult to (do shell script ("test -e " & strPath & "; echo $?"))
  	return strResult = "0"
  end FileExists
  
  -- Path containing this script
  on ScriptFolder()
  	set {dlm, my text item delimiters} to {my text item delimiters, "/"}
  	set strPath to (text items 1 thru -2 of (POSIX path of ((path to me) as alias))) as string
  	set my text item delimiters to dlm
  	return strPath
  end ScriptFolder
  
  -- Write out the list of TaskPaper (Label|Query Views) as delimited lines in a text file
  on SaveViewFile(lstViews, strLibFile)
  	set lstLines to contents of lstViews
  	set {dlm, my text item delimiters} to {my text item delimiters, space & pstrLabelDelim & space}
  	repeat with i from 1 to length of lstLines
  		set item i of lstLines to item i of lstLines as string
  	end repeat
  	
  	set my text item delimiters to linefeed
  	set strCMD to "echo " & quoted form of (lstLines as string) & " | sort  > " & strLibFile
  	do shell script strCMD
  	set my text item delimiters to dlm
  end SaveViewFile
  
  -- Read Taskpaper views from text file to key/value pairs in plstCurrent
  on LoadViewFile(strLibFile)
  	set lstLines to paragraphs of (do shell script "cat " & strLibFile)
  	
  	
  	-- Prune out empty lines and missing queries
  	set plstCurrent to {}
  	repeat with oLine in lstLines
  		set strLine to Trim(contents of oLine)
  		if strLine ≠ "" and character -1 of strLine ≠ pstrLabelDelim then
  			set end of plstCurrent to strLine
  		end if
  	end repeat
  	set lngView to length of plstCurrent
  	
  	set {dlm, my text item delimiters} to {my text item delimiters, pstrLabelDelim}
  	repeat with i from 1 to lngView
  		set lstParts to text items of (contents of (item i of plstCurrent))
  		set strLabel to item 1 of lstParts
  		if length of lstParts > 1 then
  			set strQuery to (items 2 thru -1 of lstParts) as string
  		else
  			set strQuery to strLabel -- (the 'label' was just the query)
  		end if
  		set item i of plstCurrent to {Trim(strLabel), Trim(strQuery)}
  	end repeat
  	set my text item delimiters to dlm
  	
  	return lngView
  end LoadViewFile
  
  -- Escape any spaces in a Posix path
  on EscapeSpace(strPath)
  	set {dlm, my text item delimiters} to {my text item delimiters, space}
  	set lstParts to text items of strPath
  	set my text item delimiters to "\\ "
  	set strEscaped to lstParts as string
  	set my text item delimiters to dlm
  	return strEscaped
  end EscapeSpace
  
  -- DISPLAY CHEAT SHEET,
  -- AND SELECT TOKENS TO COPY
  on ChooseTokens(oDoc)
  	tell application "TaskPaper"
  		set lstTags to my TagList(oDoc)
  		if lstTags ≠ {} then
  			repeat with i from 1 to length of lstTags
  				set item i of lstTags to {item i of lstTags, ""}
  			end repeat
  		end if
  	end tell
  	
  	-- BUILD CHEAT SHEET, INCLUDING ANY TAGS IN THE CURRENT DOCUMENT
  	set lstTokens to plstConjunction & plstNegn & plstDiv & plstAttribute & plstDiv
  	if lstTags ≠ {} then set lstTokens to lstTokens & lstTags & plstDiv
  	set lstTokens to lstTokens & plstRelation
  	
  	set lngTokens to length of lstTokens
  	set lngDigits to length of (lngTokens as string)
  	
  	set lstDisplay to {}
  	repeat with i from 1 to lngTokens
  		set {strToken, strGloss} to item i of lstTokens
  		set end of lstDisplay to "  " & my PadWord(strToken, 12) & tab & strGloss
  	end repeat
  	
  	activate
  	set varChoice to choose from list lstDisplay with title pTitle & tab & pVer with prompt ¬
  		"Choose item(s) to copy to the clipboard:" default items {} ¬
  		OK button name "Copy" cancel button name "Cancel" with empty selection allowed and multiple selections allowed
  	set cType to class of varChoice
  	
  	set strClip to ""
  	if cType ≠ boolean then
  		if cType ≠ list then
  			set strClip to varChoice as string
  		else
  			set {dlm, my text item delimiters} to {my text item delimiters, tab}
  			repeat with oToken in varChoice
  				set strToken to Trim(text item 1 of (contents of oToken))
  				if not (strToken begins with "__") then set strClip to strClip & space & strToken
  			end repeat
  			set my text item delimiters to dlm
  		end if
  	end if
  	return strClip
  end ChooseTokens
  
  
  
  