Repaired the not equal signs

Repaired the not equal signs

the "eachCharNum...45" and following conditions are supposed to be not equals
Applescript likes the single not equal symbol, which is entered via Option= on the mac

on dlog(myObj)
	set txt to quoted form of (myObj as string)
	log txt
	do shell script "logger -t 'LaunchBar.MailMate' " & txt
end dlog

on urlencode(theText)
	set theTextEnc to ""
	repeat with eachChar in characters of theText
		set useChar to eachChar
		set eachCharNum to ASCII number of eachChar
		if eachCharNum = 32 or eachCharNum > 127 then
			set useChar to "%20"
		else if (eachCharNum ≠ 42) and (eachCharNum ≠ 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
			set firstDig to round (eachCharNum / 16) rounding down
			set secondDig to eachCharNum mod 16
			if firstDig > 9 then
				set aNum to firstDig + 55
				set firstDig to ASCII character aNum
			end if
			if secondDig > 9 then
				set aNum to secondDig + 55
				set secondDig to ASCII character aNum
			end if
			set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
			set useChar to numHex
		end if
		set theTextEnc to theTextEnc & useChar as string
	end repeat
	return theTextEnc
end urlencode

on makeTo(_emailAddresses)
	set _to to ""
	repeat with addr in _emailAddresses
		set _to to _to & "&to=" & urlencode(addr)
	end repeat
	return _to
end makeTo

on basename(thePath) -- Requires POSIX path
	set ASTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to "/"
	set thePath to text item -1 of thePath
	set AppleScript's text item delimiters to ASTID
	return thePath
end basename

on sendFiles(_files, _emailAddresses)
	try
		set _mailto to "mailto:?send-now=no" & makeTo(_emailAddresses)
		set _names to ""
		repeat with _file in _files
			set _filePath to POSIX path of _file
			set _names to _names & " " & basename(_filePath)
			set _mailto to _mailto & "&attachment-url=file://" & urlencode(_filePath)
		end repeat
		set _mailto to _mailto & "&subject=File:" & urlencode(_names) & "&body=" & urlencode("File attached")
		tell application "MailMate" to open location _mailto with trust
		tell application "MailMate" to activate
	on error error_message number error_number
		set msg to "LaunchBar.MailMate.sendFiles ERROR: " & error_message & " #" & error_number
		dlog(msg)
		display dialog msg
	end try
end sendFiles

on sendText(txt, _emailAddresses)
	try
		if txt contains " http" then
			set myName to text 1 thru ((offset of " http" in txt) - 5) of txt
			set myURL to text ((offset of " http" in txt) + 1) thru end of txt
			set mySubj to "Link: " & myName
			set myBody to myName & return & myURL & return & return & "Enjoy"
			set _mailto to "mailto:?send-now=yes&subject=" & urlencode(mySubj) & makeTo(_emailAddresses) & "&body=" & urlencode(myBody)
			tell application "MailMate" to open location _mailto with trust
		else
			set _mailto to "mailto:?send-now=no&" & makeTo(_emailAddresses) & "&body=" & urlencode(txt)
			tell application "MailMate" to open location _mailto
			tell application "MailMate" to activate
		end if
	on error error_message number error_number
		set msg to "LaunchBar.MailMate.sendText ERROR: " & error_message & " #" & error_number
		dlog(msg)
		display dialog msg
	end try
end sendText

on dlog(myObj)
	set txt to quoted form of (myObj as string)
	log txt
	do shell script "logger -t 'LaunchBar.MailMate' " & txt
end dlog

on urlencode(theText)
	set theTextEnc to ""
	repeat with eachChar in characters of theText
		set useChar to eachChar
		set eachCharNum to ASCII number of eachChar
		if eachCharNum = 32 or eachCharNum > 127 then
			set useChar to "%20"
		else if (eachCharNum  42) and (eachCharNum  95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
			set firstDig to round (eachCharNum / 16) rounding down
			set secondDig to eachCharNum mod 16
			if firstDig > 9 then
				set aNum to firstDig + 55
				set firstDig to ASCII character aNum
			end if
			if secondDig > 9 then
				set aNum to secondDig + 55
				set secondDig to ASCII character aNum
			end if
			set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
			set useChar to numHex
		end if
		set theTextEnc to theTextEnc & useChar as string
	end repeat
	return theTextEnc
end urlencode

on makeTo(_emailAddresses)
	set _to to ""
	repeat with addr in _emailAddresses
		set _to to _to & "&to=" & urlencode(addr)
	end repeat
	return _to
end makeTo

on basename(thePath) -- Requires POSIX path
	set ASTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to "/"
	set thePath to text item -1 of thePath
	set AppleScript's text item delimiters to ASTID
	return thePath
end basename

on sendFiles(_files, _emailAddresses)
	try
		set _mailto to "mailto:?send-now=no" & makeTo(_emailAddresses)
		set _names to ""
		repeat with _file in _files
			set _filePath to POSIX path of _file
			set _names to _names & " " & basename(_filePath)
			set _mailto to _mailto & "&attachment-url=file://" & urlencode(_filePath)
		end repeat
		set _mailto to _mailto & "&subject=File:" & urlencode(_names) & "&body=" & urlencode("File attached")
		tell application "MailMate" to open location _mailto with trust
		tell application "MailMate" to activate
	on error error_message number error_number
		set msg to "LaunchBar.MailMate.sendFiles ERROR: " & error_message & " #" & error_number
		dlog(msg)
		display dialog msg
	end try
end sendFiles

on sendText(txt, _emailAddresses)
	try
		if txt contains " http" then
			set myName to text 1 thru ((offset of " http" in txt) - 5) of txt
			set myURL to text ((offset of " http" in txt) + 1) thru end of txt
			set mySubj to "Link: " & myName
			set myBody to myName & return & myURL & return & return & "Enjoy"
			set _mailto to "mailto:?send-now=yes&subject=" & urlencode(mySubj) & makeTo(_emailAddresses) & "&body=" & urlencode(myBody)
			tell application "MailMate" to open location _mailto with trust
		else
			set _mailto to "mailto:?send-now=no&" & makeTo(_emailAddresses) & "&body=" & urlencode(txt)
			tell application "MailMate" to open location _mailto
			tell application "MailMate" to activate
		end if
	on error error_message number error_number
		set msg to "LaunchBar.MailMate.sendText ERROR: " & error_message & " #" & error_number
		dlog(msg)
		display dialog msg
	end try
end sendText


Warn about invalid list item selection (hor. rule)

(*
	Log Page - Log categorized web page bookmarks to a text file

	Version: @@VERSION@@
	Date:    2012-12-20
	Author:  Steve Wheeler

	Get the title, URL, current date and time, and a user-definable
	category for the frontmost Safari window and log the info to a text
	file.

	This program is free software available under the terms of a
	BSD-style (3-clause) open source license detailed below.
*)

(*
Copyright (c) 2011-2012 Steve Wheeler
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.

* Neither the name of the copyright holder nor the names of
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

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 HOLDER 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.
*)

(*
	TODO: Change the following local-only config before any public release.
	The log file should be: $GTD_IN/logs/urls/urls.YYYY.txt
	Or just: $GTD_IN/logs/urls.txt

	TODO: The edit_log() function is currently incomplete. It will only launch
	an editor from the shell and only if that editor is a GUI editor (in this
	case, MacVim). If I release this script publicly, I will still need to
	implement funtionality to launch a GUI editor normally (with the 'open'
	AppleScript commmand) as well as a command-line editor run in the Terminal.
*)

property script_name : "Log Page"
property script_version : "@@VERSION@@"

property uRule : data utxt2500 as Unicode text -- BOX DRAWINGS LIGHT HORIZONTAL

property p_list_types : {"top", "sub", "all"}

global g_top_categories, g_all_categories, g_previous_categories
global g_list_type, g_previous_list_type
global g_prompt_count, g_prompt_total

set g_list_type to missing value
set g_previous_list_type to missing value

---------- USER CONFIGURATION ----------------------------------------
-- Default log directory to use if one is not read from config file:
--set log_dir to POSIX path of (path to application support folder from user domain) & "Jazzhead/Log Page/"
set log_dir to POSIX path of (path to desktop folder) & "Log Page/" -- :DEBUG:

-- Log file name:
set log_name to "urls.txt"

-- Default categories/labels if none are found in (or there is no)
-- URL file. Modify as desired:
set default_cats to "Development
Development:AppleScript
Development:AppleScript:Mail
Design
Productivity:GTD
Apple:Mac OS X
Apple:iOS
Career
Education
Health
Health:Diet
Health:Fitness
General"

-- Text editor to use if manually editing the log file.
--
-- This example:
--	1. Opens the file using MacVim ('mvim' as installed by MacPorts)
--	2. Switches the local working directory for just that buffer to the
--	    directory containing the file ('lcd')
--	3. Gives the MacVim window a name ('--servername').
--
set text_editor to "/opt/local/bin/mvim \"+lcd %:p:h\" --servername LOG_PAGE"

-- Should the specified editor be launched from the unix shell?
set should_use_shell to true
---------- END User Configuration ----------------------------------------

---------- ADVANCED (EXPERIMENTAL) ---------------------------------------------
-- For advanced users familiar with the command-line, override the default log
-- directory path. This probably won't be in a public release.
--
-- Grab an exported shell environment variable from a config file to use as the
-- log directory path. For this example, I have a "~/.gtdrc" config file that I
-- source from my "~/.bash_profile" file so that it exportss environment
-- variables relating to my GTD system. That makes those variables available to
-- any of the shell scripts and tools that I use. For instance, when I'm
-- editing files in Vim, I can write paths to other files using one of the
-- environment variables rather than a full or relative path and the 'gf'
-- command will work for jumping to that file. The config file looks like this:
--
--         # ~/.gtdrc
--         #
--         # File locations for GTD documents
--         #
--         export GTD_DIR=$HOME/Documents/org  # GTD root directory
--         export GTD_IN=$GTD_DIR/content      # source/input files
--         export GTD_OUT=$GTD_DIR/html        # generated HTML output files
--         export GTD_LIB=$GTD_DIR/lib         # code library files
--         export GTD_BIN=$GTD_LIB/script      # scripts
--
-- Remove (or comment out) the "(*" and "*)" comment markers below to use this
-- section. Make sure you also configure the two variables for your system.
--(*
--------------- USER CONFIGURATION ----------
-- Path to your config file:
set cfg_file to POSIX path of (path to home folder) & ".gtdrc"
-- The environment variable you're interested in:
set env_var to "GTD_IN"
--------------- END User Configuration ---------

-- Check for environment variable in config file:
set s to "egrep '\\<" & env_var & "=' " & quoted form of cfg_file &
	" > /dev/null 2>&1; echo $?"
set shell_status to (do shell script s) as integer
-- Source the config file and echo the desired variable:
if shell_status is 0 then -- (means that grep found a match)
	set s to ". " & quoted form of cfg_file & " && echo $" & env_var
	set gtd_in_dir to do shell script s
	set log_dir to gtd_in_dir & "/logs/"

	--------------------
	-- OPTIONAL: Override the Text editor to integrate with my GTD system.
	--
	-- This example:
	--	1. Opens the file using MacVim ('mvim' as installed by MacPorts)
	--	2. Switches the local working directory ('lcd') for just that buffer to
	--	    my GTD content directory ($GTD_IN after escaping potential spaces)
	--	3. Gives the MacVim window a name ('--servername').

	----- First, escape any potential spaces in the directory path:
	--
	--set gtd_in_dir to gtd_in_dir & " foo    bar" -- :DEBUG: spaces in path
	--
	set s to "echo " & quoted form of gtd_in_dir & " | sed 's/ \\{1,\\}/\\\\ /g'" -- escape spaces
	set gtd_in_dir to do shell script s
	--
	--log gtd_in_dir -- :DEBUG: need to use 'log' to see the value w/o AppleScript escapes

	----- Finally, set the editor with all of the options:
	--
	set text_editor to "/opt/local/bin/mvim \"+lcd " & gtd_in_dir & "\" --servername GTD --remote-silent"
	--
	--log text_editor -- :DEBUG:
	--return text_editor -- :DEBUG:
	--------------------
end if
---------- END Advanced --------------------------------------------------------
--*)

--return log_dir -- :DEBUG:

--
-- Get URL and title from front browser window
--
tell application "Safari"
	--activate
	try
		set this_url to URL of front document
		set this_title to name of first tab of front window whose visible is true
	on error
		return false
	end try
end tell
-- Transliterate non-ASCII characters to ASCII
set this_title to convert_to_ascii(this_title)

--return this_title -- :DEBUG:

--
-- Format the record separator between log entries
--
set rule_char to "-"
set rule_width to 80
set field_width to 7
set field_sep to " | "
set rec_sep to "" & multiply_text(rule_char, field_width - 1) &
	"+" & multiply_text(rule_char, rule_width - field_width)

--
-- Format date-time string as "YYYY-MM-DD HH:MM:SS"
--
set {year:y, month:m, day:d, hours:hh, minutes:mm, seconds:ss} to current date
set m to m as integer -- coerce month string
-- Pad numbers with leading zeros:
set tmp_list to {}
repeat with this_item in {m, d, hh, mm, ss}
	set this_item to text 2 thru -1 of (100 + this_item as text)
	set end of tmp_list to this_item
end repeat
set {m, d, hh, mm, ss} to tmp_list
-- Final string:
set date_time to join_list({y, m, d}, "-") & " " & join_list({hh, mm, ss}, ":")

-- Log file name
--set log_name to "urls." & y & ".txt"

--
-- Create log directory if it doesn't already exist. (Go ahead and use a shell
-- command since it's easy to create intermediate directories all in one step.):
--
try
	set s to "mkdir -pm700 " & quoted form of log_dir
	do shell script s
on error err_msg number err_num
	error "Can't create log directory: " & err_msg number err_num
	return "Fatal Error: Can't create log directory"
end try

set log_file_posix to "" & log_dir & log_name
set log_file to POSIX file log_file_posix

--
-- Add header to new or empty log file
--
set should_init to false
tell application "Finder"
	if (not (exists log_file)) or (_IO's read_file(log_file) is "") then
		set should_init to true
	end if
end tell

set head_sep to multiply_text("#", 80)
if should_init then
	set file_header to head_sep & "
# urls.txt - Timestamped and categorized URL archive
# ==================================================
# Optionally list some categories/labels of URLs below that might not yet
# be used. Any categories/lables listed here or in the web page records
# will be presented as a list to select from when saving new URLs.
" & head_sep & linefeed & default_categories & linefeed & head_sep & "
# END: category/label list
" & head_sep & linefeed
	tell _IO to write_file(log_file, file_header)
	set all_category_txt to default_categories
else
	-- Read categories from file
	(*
		:TODO: Error handling.
	*)
	set all_category_txt to text 2 thru -2 of (item 3 of split_text(_IO's read_file(log_file), head_sep))
end if

-- Parse any existing labels from the "Label" fields of the URLs file:
set s to "LANG=C sed -n 's/^Label | //p' " & quoted form of log_file_posix & " | sort | uniq"
--return s
set categories_used to do shell script s without altering line endings
-- Sort those along with the manually entered categories/labels:
set s to "echo \"" & all_category_txt & linefeed & categories_used & "\" | egrep -v '^$' | sort | uniq"
set all_category_txt to do shell script s without altering line endings
--return all_category_txt -- :DEBUG:
-- Coerce the lines into a list:
set g_all_categories to paragraphs of all_category_txt
if g_all_categories's last item is "" then set g_all_categories to g_all_categories's items 1 thru -2
--return g_all_categories -- :DEBUG:

-- Get top-level categories:
set s to "LANG=C echo \"" & all_category_txt & "\" | sed -n 's/^\\([^:]\\{1,\\}\\).*/\\1/p' | uniq"
set top_category_txt to do shell script s without altering line endings
--return top_category_txt -- :DEBUG:
-- Coerce the lines into a list:
set g_top_categories to paragraphs of top_category_txt
if g_top_categories's last item is "" then set g_top_categories to g_top_categories's items 1 thru -2
--return g_top_categories -- :DEBUG:

--
-- Prompt for title, category/subcategories and optional note for URL
--

-- The number of dialog prompts will vary depending on if a subcategory list is requested.
set g_prompt_count to 1 -- increment with every dialog or list prompt, decrement if going back
set g_prompt_total to 4 -- increment if sub/full lists are displayed, decrement if going back

-- Accept or modify URL title
set b to {"Manually Edit Log File", "Cancel", "Next..."}
set t to "" & script_name & ": Title (" & g_prompt_count & "/" & g_prompt_total & ")"
set m to "To log the URL for:" & return & return
	& tab & "\"" & this_title & "\"" & return & return &
	"first accept or edit the title."
display dialog m default answer this_title with title t buttons b default button last item of b cancel button second item of b
set {this_title, btn_pressed} to {text returned of result, button returned of result}
set g_prompt_count to g_prompt_count + 1
if btn_pressed is first item of b then
	if should_use_shell then
		set this_log_file to quoted form of log_file_posix
	else
		set this_log_file to log_file
	end if
	edit_log(this_log_file, text_editor, should_use_shell)
	return "Script ended with '" & (first item of b as text) & "'"
end if

-- Select a category
set chosen_category to choose_category(g_top_categories, "top")
if chosen_category is false then error number -128 -- User canceled

-- Modify selected category or enter a new category
set t to "" & script_name & ": Category (" & g_prompt_count & "/" & g_prompt_total & ")"
set m to "Please provide a category and any optional subcategories (or edit your selected category) for the URL. Example: \"Development:AppleScript:Mail\""
repeat --10 times -- limit loops as a precaution during development
	display dialog m default answer chosen_category with title t buttons b default button last item of b cancel button second item of b
	set {this_label, btn_pressed} to {text returned of result, button returned of result}
	if this_label is not "" then
		exit repeat
	else
		display alert "Category required" message "Please supply a category for the URL." as warning
	end if
end repeat
set g_prompt_count to g_prompt_count + 1

-- Optionally add note
if btn_pressed is last item of b then
	set last item of b to "Append URL to Log File"
	set t to "" & script_name & ": Note (" & g_prompt_count & "/" & g_prompt_total & ")"
	set m to "Optionally add a short note. Just leave the field blank if you don't want to add a note."
	display dialog m default answer "" with title t buttons b default button last item of b
	set {this_note, btn_pressed} to {text returned of result, button returned of result}
end if

--
-- Append or edit the log file
--
if btn_pressed is last item of b then
	set final_text to join_list({
		"Date " & field_sep & date_time,
		"Label" & field_sep & this_label,
		"Title" & field_sep & this_title,
		"URL  " & field_sep & this_url}, linefeed) & linefeed
	if this_note is not "" then
		set final_text to final_text & "Note " & field_sep & this_note & linefeed
	end if
	set final_text to final_text & rec_sep & linefeed
	--return "[DEBUG]" & return & final_text
	_IO's append_file(log_file, final_text)
else
	if should_use_shell then
		set this_log_file to quoted form of log_file_posix
	else
		set this_log_file to log_file
	end if
	edit_log(this_log_file, text_editor, should_use_shell)
end if

--display alert "Log Page" message "DEBUG: Script complete."

(************************************************************
 *	Subroutines
 ************************************************************)

-- ===== Main Functions =====

----- Dialog Views

on choose_category(cur_list, cur_list_type)
	--log "[debug] in choose_category(_list_, \"" & cur_list_type & "\")"

	if cur_list_type is not in p_list_types then
		set t to "Error: Bad list type"
		set m to "choose_category():  The supplied list type parameter, '" & cur_list_type & "', is not a valid list type from the 'p_list_types' property. Please report this bug to the developer."
		-- This bug is probably the result of a typo in the recursive section below.
		display alert t message m buttons {"Cancel"} cancel button 1 as critical
	end if

	if cur_list_type is not "all" then copy cur_list to g_previous_categories

	--log "[debug] g_previous_categories: " & joinList(g_previous_categories, ", ")

	set_list_type(cur_list_type) -- set global current and previous list types

	--
	-- Configure the non-category navigation selections for the category list dialogs
	--
	set extra_items to get_extra_items(cur_list_type)
	set list_rule to extra_items's last item

	--
	-- Customize list dialog properties
	--
	set t to "" & script_name & ": Category (" & g_prompt_count & "/" & g_prompt_total & ")"
	if cur_list_type is "top" then
		set m to "Please select a top-level category for the URL you want to log. Next you will be able to select subcategories unless you are creating a new category."
	else if cur_list_type is in {"sub", "all"} then
		set m to "Please select a category or subcategory for the URL you want to log. You will have a chance to edit your choice (to add a new category or subcategory)."
	end if
	set b to "Next..."

	--
	-- Prompt the user for category and/or subcategory choices
	--
	repeat --10 times -- limit loops as a precaution during development
		set chosen_category to choose from list extra_items & cur_list with title t with prompt m OK button name b
		if chosen_category as text is not list_rule then
			exit repeat
		else
			display alert "Invalid selection" message "Please select a category." as warning
		end if
	end repeat
	if chosen_category is false then return false

	--
	-- Call this handler recursively if another list was requested.
	-- When specifying a 'cur_list_type' parameter to choose_category(), make
	-- sure it is one of the allowed types from the 'p_list_types' property.
	--
	if cur_list_type is in {"top", "sub"} and chosen_category as text is extra_items's first item then
		--log "[debug] incrementing both prompt count and total"
		set g_prompt_count to g_prompt_count + 1
		set g_prompt_total to g_prompt_total + 1
		--log "[debug] recurse to choose_category(g_all_categories, \"all\")"
		set chosen_category to choose_category(g_all_categories, "all")
	else if cur_list_type is "sub" and chosen_category as text is extra_items's second item then
		--log "[debug] decrementing both prompt count and total"
		set g_prompt_count to g_prompt_count - 1
		set g_prompt_total to g_prompt_total - 1
		--log "[debug] recurse to choose_category(g_top_categories, \"top\")"
		set chosen_category to choose_category(g_top_categories, "top")
	else if cur_list_type is "all" and chosen_category as text is extra_items's first item then
		--log "[debug] decrementing both prompt count and total"
		set g_prompt_count to g_prompt_count - 1
		set g_prompt_total to g_prompt_total - 1
		if g_previous_list_type is "sub" then
			--log "[debug] recurse to choose_category(g_previous_categories, \"sub\")"
			set chosen_category to choose_category(g_previous_categories, "sub")
		else if g_previous_list_type is "top" then
			--log "[debug] recurse to choose_category(g_top_categories, \"top\")"
			set chosen_category to choose_category(g_top_categories, "top")
		end if
	else if cur_list_type is "top" and chosen_category as text is extra_items's second item then
		set g_prompt_count to g_prompt_count + 1
		return ""
	else if cur_list_type is "top" then
		set sub_categories to get_subcategories(chosen_category)
		--log "[debug] incrementing both prompt count and total"
		set g_prompt_count to g_prompt_count + 1
		set g_prompt_total to g_prompt_total + 1
		--log "[debug] recurse to choose_category(sub_categories, \"sub\")"
		set chosen_category to choose_category(sub_categories, "sub")
	else
		--log "[debug] incrementing prompt count"
		set g_prompt_count to g_prompt_count + 1
	end if

	return chosen_category
end choose_category

on get_subcategories(chosen_category)
	set sub_categories to {}
	repeat with this_cat in g_all_categories
		if (this_cat as text) is (chosen_category as text)
			or (this_cat as text) starts with (chosen_category & ":") then
			set end of sub_categories to this_cat as text
		end if
	end repeat
	return sub_categories
end get_subcategories

on set_list_type(cur_type)
	set g_previous_list_type to g_list_type
	set g_list_type to cur_type
end set_list_type

on get_extra_items(list_type)
	if list_type is "top" then
		return {"Show full list with subcategories", "Create a new category...", multiplyTxt(uRule, 20)}
	else if list_type is "sub" then
		return {"Show full list of categories...", "Go back to previous list...", multiplyTxt(uRule, 35)}
	else if list_type is "all" then
		return {"Go back to previous list...", multiplyTxt(uRule, 35)}
	else
		return false
	end if
end get_extra_items

----- External Views

on edit_log(log_file, text_editor, should_use_shell)
	(*
		TODO:MAYBE:
		Have a configuration variable in the main script to specify which text
		editor to use. Have another variable for configuring whether or not
		it's a GUI application. Then this function would use 'open' to launch a
		GUI app or script the Terminal to launch a command-line editor.
		Optionally use 'do shell script' to launch a compatible GUI app from a
		command line (so that the working directory can be changed to that of
		the file first), but I don't know if that will really be necessary
		since I have a Vim shortcut for quickly changing the working directory
		if need be.
	*)
	if should_use_shell then
		set s to text_editor & space & log_file & space & "> /dev/null 2>&1"
		do shell script s
		return
	else
		set t to script_name
		set m to ":TODO: edit_log() function: use non-shell-invoked editor."
		set b to {"Cancel"}
		display alert t message m buttons b cancel button 1 as critical
		return false
	end if
end edit_log

-- ===== Utility Functions =====

on convert_to_ascii(non_ascii_txt)
	--
	-- From 'man iconv_open':
	--
	--    When  the string "//TRANSLIT" is appended to tocode, transliteration is
	--    activated. This means that when a character cannot  be  represented  in
	--    the target character set, it can be approximated through one or several
	--    characters that look similar to the original character.
	--
	set s to "iconv -f UTF-8 -t US-ASCII//TRANSLIT <<<" & quoted form of non_ascii_txt
	do shell script s
end convert_to_ascii

on multiply_text(str, n)
	if n < 1 or str = "" then return ""
	set lst to {}
	repeat n times
		set end of lst to str
	end repeat
	return lst as string
end multiply_text

on split_text(txt, delim)
	set old_tids to AppleScript's text item delimiters
	try
		set AppleScript's text item delimiters to (delim as string)
		set lst to every text item of (txt as string)
		set AppleScript's text item delimiters to old_tids
		return lst
	on error err_msg number err_num
		set AppleScript's text item delimiters to old_tids
		error "Can't split_text(): " & err_msg number err_num
	end try
end split_text

on join_list(lst, delim)
	set old_tids to AppleScript's text item delimiters
	try
		set AppleScript's text item delimiters to (delim as string)
		set txt to lst as string
		set AppleScript's text item delimiters to old_tids
		return txt
	on error err_msg number err_num
		set AppleScript's text item delimiters to old_tids
		error "Can't join_list(): " & err_msg number err_num
	end try
end join_list

(************************************************************
 *	Script Objects
 ************************************************************)

script _IO

	(* == PUBLIC == *)

	on write_file(file_path, this_data)
		_write_file(file_path, this_data, string, false) -- overwrite existing file
	end write_file

	on append_file(file_path, this_data)
		_write_file(file_path, this_data, string, true) -- append new data to end of existing file
	end append_file

	on read_file(file_path)
		_read_file(file_path, string)
	end read_file

	(* == PRIVATE == *)

	on _write_file(file_path, this_data, data_class, should_append_data)
		try
			set file_path to file_path as text
			set file_handle to
				open for access file file_path with write permission
			if not should_append_data then
				set eof of the file_handle to 0
			write this_data as data_class to file_handle starting at eof
			close access file_handle
			return true
		on error err_msg number err_num
			try
				close access file file_path
			end try
			error err_msg number err_num
			return false
		end try
	end _write_file

	on _read_file(file_path, data_class)
		try
			set file_path to file_path as text
			if (get eof file file_path) is 0 then
				set file_contents to ""
			else
				set file_contents to read file file_path as data_class
			end if
			return file_contents
		on error
			return 0
		end try
	end _read_file
end script

(*
	Log Page - Log categorized web page bookmarks to a text file

	Version: @@VERSION@@
	Date:    2012-12-20
	Author:  Steve Wheeler

	Get the title, URL, current date and time, and a user-definable
	category for the frontmost Safari window and log the info to a text
	file.

	This program is free software available under the terms of a
	BSD-style (3-clause) open source license detailed below.
*)

(*
Copyright (c) 2011-2012 Steve Wheeler
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.

* Neither the name of the copyright holder nor the names of
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

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 HOLDER 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.
*)

(*
	TODO: Change the following local-only config before any public release.
	The log file should be: $GTD_IN/logs/urls/urls.YYYY.txt
	Or just: $GTD_IN/logs/urls.txt

	TODO: The edit_log() function is currently incomplete. It will only launch
	an editor from the shell and only if that editor is a GUI editor (in this
	case, MacVim). If I release this script publicly, I will still need to
	implement funtionality to launch a GUI editor normally (with the 'open'
	AppleScript commmand) as well as a command-line editor run in the Terminal.
*)

property script_name : "Log Page"
property script_version : "@@VERSION@@"

property uRule : data utxt2500 as Unicode text -- BOX DRAWINGS LIGHT HORIZONTAL

property p_list_types : {"top", "sub", "all"}

global g_top_categories, g_all_categories, g_previous_categories
global g_list_type, g_previous_list_type
global g_prompt_count, g_prompt_total

set g_list_type to missing value
set g_previous_list_type to missing value

---------- USER CONFIGURATION ----------------------------------------
-- Default log directory to use if one is not read from config file:
--set log_dir to POSIX path of (path to application support folder from user domain) & "Jazzhead/Log Page/"
set log_dir to POSIX path of (path to desktop folder) & "Log Page/" -- :DEBUG:

-- Log file name:
set log_name to "urls.txt"

-- Default categories/labels if none are found in (or there is no)
-- URL file. Modify as desired:
set default_cats to "Development
Development:AppleScript
Development:AppleScript:Mail
Design
Productivity:GTD
Apple:Mac OS X
Apple:iOS
Career
Education
Health
Health:Diet
Health:Fitness
General"

-- Text editor to use if manually editing the log file.
--
-- This example:
--	1. Opens the file using MacVim ('mvim' as installed by MacPorts)
--	2. Switches the local working directory for just that buffer to the
--	    directory containing the file ('lcd')
--	3. Gives the MacVim window a name ('--servername').
--
set text_editor to "/opt/local/bin/mvim \"+lcd %:p:h\" --servername LOG_PAGE"

-- Should the specified editor be launched from the unix shell?
set should_use_shell to true
---------- END User Configuration ----------------------------------------

---------- ADVANCED (EXPERIMENTAL) ---------------------------------------------
-- For advanced users familiar with the command-line, override the default log
-- directory path. This probably won't be in a public release.
--
-- Grab an exported shell environment variable from a config file to use as the
-- log directory path. For this example, I have a "~/.gtdrc" config file that I
-- source from my "~/.bash_profile" file so that it exportss environment
-- variables relating to my GTD system. That makes those variables available to
-- any of the shell scripts and tools that I use. For instance, when I'm
-- editing files in Vim, I can write paths to other files using one of the
-- environment variables rather than a full or relative path and the 'gf'
-- command will work for jumping to that file. The config file looks like this:
--
--         # ~/.gtdrc
--         #
--         # File locations for GTD documents
--         #
--         export GTD_DIR=$HOME/Documents/org  # GTD root directory
--         export GTD_IN=$GTD_DIR/content      # source/input files
--         export GTD_OUT=$GTD_DIR/html        # generated HTML output files
--         export GTD_LIB=$GTD_DIR/lib         # code library files
--         export GTD_BIN=$GTD_LIB/script      # scripts
--
-- Remove (or comment out) the "(*" and "*)" comment markers below to use this
-- section. Make sure you also configure the two variables for your system.
--(*
--------------- USER CONFIGURATION ----------
-- Path to your config file:
set cfg_file to POSIX path of (path to home folder) & ".gtdrc"
-- The environment variable you're interested in:
set env_var to "GTD_IN"
--------------- END User Configuration ---------

-- Check for environment variable in config file:
set s to "egrep '\\<" & env_var & "=' " & quoted form of cfg_file &
	" > /dev/null 2>&1; echo $?"
set shell_status to (do shell script s) as integer
-- Source the config file and echo the desired variable:
if shell_status is 0 then -- (means that grep found a match)
	set s to ". " & quoted form of cfg_file & " && echo $" & env_var
	set gtd_in_dir to do shell script s
	set log_dir to gtd_in_dir & "/logs/"

	--------------------
	-- OPTIONAL: Override the Text editor to integrate with my GTD system.
	--
	-- This example:
	--	1. Opens the file using MacVim ('mvim' as installed by MacPorts)
	--	2. Switches the local working directory ('lcd') for just that buffer to
	--	    my GTD content directory ($GTD_IN after escaping potential spaces)
	--	3. Gives the MacVim window a name ('--servername').

	----- First, escape any potential spaces in the directory path:
	--
	--set gtd_in_dir to gtd_in_dir & " foo    bar" -- :DEBUG: spaces in path
	--
	set s to "echo " & quoted form of gtd_in_dir & " | sed 's/ \\{1,\\}/\\\\ /g'" -- escape spaces
	set gtd_in_dir to do shell script s
	--
	--log gtd_in_dir -- :DEBUG: need to use 'log' to see the value w/o AppleScript escapes

	----- Finally, set the editor with all of the options:
	--
	set text_editor to "/opt/local/bin/mvim \"+lcd " & gtd_in_dir & "\" --servername GTD --remote-silent"
	--
	--log text_editor -- :DEBUG:
	--return text_editor -- :DEBUG:
	--------------------
end if
---------- END Advanced --------------------------------------------------------
--*)

--return log_dir -- :DEBUG:

--
-- Get URL and title from front browser window
--
tell application "Safari"
	--activate
	try
		set this_url to URL of front document
		set this_title to name of first tab of front window whose visible is true
	on error
		return false
	end try
end tell
-- Transliterate non-ASCII characters to ASCII
set this_title to convert_to_ascii(this_title)

--return this_title -- :DEBUG:

--
-- Format the record separator between log entries
--
set rule_char to "-"
set rule_width to 80
set field_width to 7
set field_sep to " | "
set rec_sep to "" & multiply_text(rule_char, field_width - 1) &
	"+" & multiply_text(rule_char, rule_width - field_width)

--
-- Format date-time string as "YYYY-MM-DD HH:MM:SS"
--
set {year:y, month:m, day:d, hours:hh, minutes:mm, seconds:ss} to current date
set m to m as integer -- coerce month string
-- Pad numbers with leading zeros:
set tmp_list to {}
repeat with this_item in {m, d, hh, mm, ss}
	set this_item to text 2 thru -1 of (100 + this_item as text)
	set end of tmp_list to this_item
end repeat
set {m, d, hh, mm, ss} to tmp_list
-- Final string:
set date_time to join_list({y, m, d}, "-") & " " & join_list({hh, mm, ss}, ":")

-- Log file name
--set log_name to "urls." & y & ".txt"

--
-- Create log directory if it doesn't already exist. (Go ahead and use a shell
-- command since it's easy to create intermediate directories all in one step.):
--
try
	set s to "mkdir -pm700 " & quoted form of log_dir
	do shell script s
on error err_msg number err_num
	error "Can't create log directory: " & err_msg number err_num
	return "Fatal Error: Can't create log directory"
end try

set log_file_posix to "" & log_dir & log_name
set log_file to POSIX file log_file_posix

--
-- Add header to new or empty log file
--
set should_init to false
tell application "Finder"
	if (not (exists log_file)) or (_IO's read_file(log_file) is "") then
		set should_init to true
	end if
end tell

set head_sep to multiply_text("#", 80)
if should_init then
	set file_header to head_sep & "
# urls.txt - Timestamped and categorized URL archive
# ==================================================
# Optionally list some categories/labels of URLs below that might not yet
# be used. Any categories/lables listed here or in the web page records
# will be presented as a list to select from when saving new URLs.
" & head_sep & linefeed & default_categories & linefeed & head_sep & "
# END: category/label list
" & head_sep & linefeed
	tell _IO to write_file(log_file, file_header)
	set all_category_txt to default_categories
else
	-- Read categories from file
	(*
		:TODO: Error handling.
	*)
	set all_category_txt to text 2 thru -2 of (item 3 of split_text(_IO's read_file(log_file), head_sep))
end if

-- Parse any existing labels from the "Label" fields of the URLs file:
set s to "LANG=C sed -n 's/^Label | //p' " & quoted form of log_file_posix & " | sort | uniq"
--return s
set categories_used to do shell script s without altering line endings
-- Sort those along with the manually entered categories/labels:
set s to "echo \"" & all_category_txt & linefeed & categories_used & "\" | egrep -v '^$' | sort | uniq"
set all_category_txt to do shell script s without altering line endings
--return all_category_txt -- :DEBUG:
-- Coerce the lines into a list:
set g_all_categories to paragraphs of all_category_txt
if g_all_categories's last item is "" then set g_all_categories to g_all_categories's items 1 thru -2
--return g_all_categories -- :DEBUG:

-- Get top-level categories:
set s to "LANG=C echo \"" & all_category_txt & "\" | sed -n 's/^\\([^:]\\{1,\\}\\).*/\\1/p' | uniq"
set top_category_txt to do shell script s without altering line endings
--return top_category_txt -- :DEBUG:
-- Coerce the lines into a list:
set g_top_categories to paragraphs of top_category_txt
if g_top_categories's last item is "" then set g_top_categories to g_top_categories's items 1 thru -2
--return g_top_categories -- :DEBUG:

--
-- Prompt for title, category/subcategories and optional note for URL
--

-- The number of dialog prompts will vary depending on if a subcategory list is requested.
set g_prompt_count to 1 -- increment with every dialog or list prompt, decrement if going back
set g_prompt_total to 4 -- increment if sub/full lists are displayed, decrement if going back

-- Accept or modify URL title
set b to {"Manually Edit Log File", "Cancel", "Next..."}
set t to "" & script_name & ": Title (" & g_prompt_count & "/" & g_prompt_total & ")"
set m to "To log the URL for:" & return & return
	& tab & "\"" & this_title & "\"" & return & return &
	"first accept or edit the title."
display dialog m default answer this_title with title t buttons b default button last item of b cancel button second item of b
set {this_title, btn_pressed} to {text returned of result, button returned of result}
set g_prompt_count to g_prompt_count + 1
if btn_pressed is first item of b then
	if should_use_shell then
		set this_log_file to quoted form of log_file_posix
	else
		set this_log_file to log_file
	end if
	edit_log(this_log_file, text_editor, should_use_shell)
	return "Script ended with '" & (first item of b as text) & "'"
end if

-- Select a category
set chosen_category to choose_category(g_top_categories, "top")
if chosen_category is false then error number -128 -- User canceled

-- Modify selected category or enter a new category
set t to "" & script_name & ": Category (" & g_prompt_count & "/" & g_prompt_total & ")"
set m to "Please provide a category and any optional subcategories (or edit your selected category) for the URL. Example: \"Development:AppleScript:Mail\""
repeat 10 times -- limit loops as a precaution
	display dialog m default answer chosen_category with title t buttons b default button last item of b cancel button second item of b
	set {this_label, btn_pressed} to {text returned of result, button returned of result}
	if this_label is not "" then
		exit repeat
	else
		display alert "Category required" message "Please supply a category for the URL."
	end if
end repeat
set g_prompt_count to g_prompt_count + 1

-- Optionally add note
if btn_pressed is last item of b then
	set last item of b to "Append URL to Log File"
	set t to "" & script_name & ": Note (" & g_prompt_count & "/" & g_prompt_total & ")"
	set m to "Optionally add a short note. Just leave the field blank if you don't want to add a note."
	display dialog m default answer "" with title t buttons b default button last item of b
	set {this_note, btn_pressed} to {text returned of result, button returned of result}
end if

--
-- Append or edit the log file
--
if btn_pressed is last item of b then
	set final_text to join_list({
		"Date " & field_sep & date_time,
		"Label" & field_sep & this_label,
		"Title" & field_sep & this_title,
		"URL  " & field_sep & this_url}, linefeed) & linefeed
	if this_note is not "" then
		set final_text to final_text & "Note " & field_sep & this_note & linefeed
	end if
	set final_text to final_text & rec_sep & linefeed
	--return "[DEBUG]" & return & final_text
	_IO's append_file(log_file, final_text)
else
	if should_use_shell then
		set this_log_file to quoted form of log_file_posix
	else
		set this_log_file to log_file
	end if
	edit_log(this_log_file, text_editor, should_use_shell)
end if

--display alert "Log Page" message "DEBUG: Script complete."

(************************************************************
 *	Subroutines
 ************************************************************)

-- ===== Main Functions =====

----- Dialog Views

on choose_category(cur_list, cur_list_type)
	--log "[debug] in choose_category(_list_, \"" & cur_list_type & "\")"

	if cur_list_type is not in p_list_types then
		set t to "Error: Bad list type"
		set m to "choose_category():  The supplied list type parameter, '" & cur_list_type & "', is not a valid list type from the 'p_list_types' property. Please report this bug to the developer."
		-- This bug is probably the result of a typo in the recursive section below.
		display alert t message m buttons {"Cancel"} cancel button 1 as critical
	end if

	if cur_list_type is not "all" then copy cur_list to g_previous_categories

	--log "[debug] g_previous_categories: " & joinList(g_previous_categories, ", ")

	set_list_type(cur_list_type) -- set global current and previous list types

	--
	-- Configure the non-category navigation selections for the category list dialogs
	--
	set extra_items to get_extra_items(cur_list_type)
	set list_rule to extra_items's last item

	--
	-- Customize list dialog properties
	--
	set t to "" & script_name & ": Category (" & g_prompt_count & "/" & g_prompt_total & ")"
	if cur_list_type is "top" then
		set m to "Please select a top-level category for the URL you want to log. Next you will be able to select subcategories unless you are creating a new category."
	else if cur_list_type is in {"sub", "all"} then
		set m to "Please select a category or subcategory for the URL you want to log. You will have a chance to edit your choice (to add a new category or subcategory)."
	end if
	set b to "Next..."

	--
	-- Prompt the user for category and/or subcategory choices
	--
	repeat 10 times -- limit loops as a precaution
		set chosen_category to choose from list extra_items & cur_list with title t with prompt m OK button name b
		if chosen_category as text is not list_rule then exit repeat
	end repeat
	if chosen_category is false then return false

	--
	-- Call this handler recursively if another list was requested.
	-- When specifying a 'cur_list_type' parameter to choose_category(), make
	-- sure it is one of the allowed types from the 'p_list_types' property.
	--
	if cur_list_type is in {"top", "sub"} and chosen_category as text is extra_items's first item then
		--log "[debug] incrementing both prompt count and total"
		set g_prompt_count to g_prompt_count + 1
		set g_prompt_total to g_prompt_total + 1
		--log "[debug] recurse to choose_category(g_all_categories, \"all\")"
		set chosen_category to choose_category(g_all_categories, "all")
	else if cur_list_type is "sub" and chosen_category as text is extra_items's second item then
		--log "[debug] decrementing both prompt count and total"
		set g_prompt_count to g_prompt_count - 1
		set g_prompt_total to g_prompt_total - 1
		--log "[debug] recurse to choose_category(g_top_categories, \"top\")"
		set chosen_category to choose_category(g_top_categories, "top")
	else if cur_list_type is "all" and chosen_category as text is extra_items's first item then
		--log "[debug] decrementing both prompt count and total"
		set g_prompt_count to g_prompt_count - 1
		set g_prompt_total to g_prompt_total - 1
		if g_previous_list_type is "sub" then
			--log "[debug] recurse to choose_category(g_previous_categories, \"sub\")"
			set chosen_category to choose_category(g_previous_categories, "sub")
		else if g_previous_list_type is "top" then
			--log "[debug] recurse to choose_category(g_top_categories, \"top\")"
			set chosen_category to choose_category(g_top_categories, "top")
		end if
	else if cur_list_type is "top" and chosen_category as text is extra_items's second item then
		set g_prompt_count to g_prompt_count + 1
		return ""
	else if cur_list_type is "top" then
		set sub_categories to get_subcategories(chosen_category)
		--log "[debug] incrementing both prompt count and total"
		set g_prompt_count to g_prompt_count + 1
		set g_prompt_total to g_prompt_total + 1
		--log "[debug] recurse to choose_category(sub_categories, \"sub\")"
		set chosen_category to choose_category(sub_categories, "sub")
	else
		--log "[debug] incrementing prompt count"
		set g_prompt_count to g_prompt_count + 1
	end if

	return chosen_category
end choose_category

on get_subcategories(chosen_category)
	set sub_categories to {}
	repeat with this_cat in g_all_categories
		if (this_cat as text) is (chosen_category as text)
			or (this_cat as text) starts with (chosen_category & ":") then
			set end of sub_categories to this_cat as text
		end if
	end repeat
	return sub_categories
end get_subcategories

on set_list_type(cur_type)
	set g_previous_list_type to g_list_type
	set g_list_type to cur_type
end set_list_type

on get_extra_items(list_type)
	if list_type is "top" then
		return {"Show full list with subcategories", "Create a new category...", multiplyTxt(uRule, 20)}
	else if list_type is "sub" then
		return {"Show full list of categories...", "Go back to previous list...", multiplyTxt(uRule, 35)}
	else if list_type is "all" then
		return {"Go back to previous list...", multiplyTxt(uRule, 35)}
	else
		return false
	end if
end get_extra_items

----- External Views

on edit_log(log_file, text_editor, should_use_shell)
	(*
		TODO:MAYBE:
		Have a configuration variable in the main script to specify which text
		editor to use. Have another variable for configuring whether or not
		it's a GUI application. Then this function would use 'open' to launch a
		GUI app or script the Terminal to launch a command-line editor.
		Optionally use 'do shell script' to launch a compatible GUI app from a
		command line (so that the working directory can be changed to that of
		the file first), but I don't know if that will really be necessary
		since I have a Vim shortcut for quickly changing the working directory
		if need be.
	*)
	if should_use_shell then
		set s to text_editor & space & log_file & space & "> /dev/null 2>&1"
		do shell script s
		return
	else
		set t to script_name
		set m to ":TODO: edit_log() function: use non-shell-invoked editor."
		set b to {"Cancel"}
		display alert t message m buttons b cancel button 1 as critical
		return false
	end if
end edit_log

-- ===== Utility Functions =====

on convert_to_ascii(non_ascii_txt)
	--
	-- From 'man iconv_open':
	--
	--    When  the string "//TRANSLIT" is appended to tocode, transliteration is
	--    activated. This means that when a character cannot  be  represented  in
	--    the target character set, it can be approximated through one or several
	--    characters that look similar to the original character.
	--
	set s to "iconv -f UTF-8 -t US-ASCII//TRANSLIT <<<" & quoted form of non_ascii_txt
	do shell script s
end convert_to_ascii

on multiply_text(str, n)
	if n < 1 or str = "" then return ""
	set lst to {}
	repeat n times
		set end of lst to str
	end repeat
	return lst as string
end multiply_text

on split_text(txt, delim)
	set old_tids to AppleScript's text item delimiters
	try
		set AppleScript's text item delimiters to (delim as string)
		set lst to every text item of (txt as string)
		set AppleScript's text item delimiters to old_tids
		return lst
	on error err_msg number err_num
		set AppleScript's text item delimiters to old_tids
		error "Can't split_text(): " & err_msg number err_num
	end try
end split_text

on join_list(lst, delim)
	set old_tids to AppleScript's text item delimiters
	try
		set AppleScript's text item delimiters to (delim as string)
		set txt to lst as string
		set AppleScript's text item delimiters to old_tids
		return txt
	on error err_msg number err_num
		set AppleScript's text item delimiters to old_tids
		error "Can't join_list(): " & err_msg number err_num
	end try
end join_list

(************************************************************
 *	Script Objects
 ************************************************************)

script _IO

	(* == PUBLIC == *)

	on write_file(file_path, this_data)
		_write_file(file_path, this_data, string, false) -- overwrite existing file
	end write_file

	on append_file(file_path, this_data)
		_write_file(file_path, this_data, string, true) -- append new data to end of existing file
	end append_file

	on read_file(file_path)
		_read_file(file_path, string)
	end read_file

	(* == PRIVATE == *)

	on _write_file(file_path, this_data, data_class, should_append_data)
		try
			set file_path to file_path as text
			set file_handle to
				open for access file file_path with write permission
			if not should_append_data then
				set eof of the file_handle to 0
			write this_data as data_class to file_handle starting at eof
			close access file_handle
			return true
		on error err_msg number err_num
			try
				close access file file_path
			end try
			error err_msg number err_num
			return false
		end try
	end _write_file

	on _read_file(file_path, data_class)
		try
			set file_path to file_path as text
			if (get eof file file_path) is 0 then
				set file_contents to ""
			else
				set file_contents to read file file_path as data_class
			end if
			return file_contents
		on error
			return 0
		end try
	end _read_file
end script


Fix get state for locales that have commas as decimal delimiters

When your current set locale uses "," as decimal delimiter, AppleScript
will automatically use this when you convert the number to a string. This
will break the JSON parsing for the callback, as "," is invalid when not
in quotes.

on replace_chars(this_text, search_string, replacement_string)
  set AppleScript's text item delimiters to the search_string
  set the item_list to every text item of this_text
  set AppleScript's text item delimiters to the replacement_string
  set this_text to the item_list as string
  set AppleScript's text item delimiters to ""
  return this_text
end replace_chars

tell application "Spotify"
  -- force string coercion and locale format
  set position to "" & player position

  set cstate to "{"
  set cstate to cstate & "\"track_id\": \"" & current track's id & "\""
  set cstate to cstate & ",\"volume\": " & sound volume
  -- replace , with . when locale has , as decimal delimiter
  set cstate to cstate & ",\"position\": " & my replace_chars(position, ",", ".")
  set cstate to cstate & ",\"state\": \"" & player state & "\""
  set cstate to cstate & "}"

  return cstate
end tell

tell application "Spotify"
  set cstate to "{"
  set cstate to cstate & "\"track_id\": \"" & current track's id & "\""
  set cstate to cstate & ",\"volume\": " & sound volume
  set cstate to cstate & ",\"position\": " & player position
  set cstate to cstate & ",\"state\": \"" & player state & "\""
  set cstate to cstate & "}"

  return cstate
end tell


Perms

Perms

FasdUAS 1.101.10
k
l
I
.miscactvnullnull
m		@alispMacintosh HDBDNotificationCenter.app
 cuCoreServices5/:System:Library:CoreServices:NotificationCenter.app/.NotificationCenter.appMacintosh HD2System/Library/CoreServices/NotificationCenter.app/

l
O

O

T
k
l
Q1
r(
n&
4#&

sgrp
m$%
n#
4 #

uiel
 m!"
n !"
!4 #

scra
#m
"4$

cwin
$m%%&&&Notification Center
o0	thewindow	theWindow
R
.ascrerr ********
S01'(
'l22()
)Q2*+
*k5,,-.
-r5:/0
/n5812
1268

actT
2o560	thewindow	theWindow
0o0
theactions
theActions.34
3l;;456
5l;;787SM Try to close the whole group first. If that fails, close individual windows.899 Try to close the whole group first. If that fails, close individual windows.6:;
:X;t<=
<ZMo>?
>=MV@A
@nMRBC
B1NR

desc
CoMN0	theaction	theAction
AmRUDDEEClear All
?kYkFFGH
GrY^IJ
ImYZ
boovtrue
Jo
0closedHKL
KO_iMN
MIchO
.prcsperfnullactT
Oocd0	theaction	theAction
No_`0	thewindow	theWindowLP
PSjk0	theaction	theAction
=o>?0
theactions
theActions;QR
QluuRST
SXuUV
UZWX
W=YZ
Yn[\
[1

desc
\o0	theaction	theAction
Zm]]^^
Close
Xk__`a
`rbc
bm
boovtrue
co
0closedade
dOfg
fIh
.prcsperfnullactT
ho0	theaction	theAction
go0	thewindow	theWindowei
iS0	theaction	theAction
Voxy0
theactions
theActionsTj
jl
+R
.ascrerr ********
4
k

prcs
km
llmm&Notification Center

mnnsevsalis\Macintosh HDBDSystem Events.app
 cuCoreServices0/:System:Library:CoreServices:System Events.app/$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/opqro
.aevtoappnull****0	thewindow	theWindow0
theactions
theActions
0closedpstu
.aevtoappnull****
skvvww
t0	theaction	theActionu	nl%D]
.miscactvnullnull

prcs

cwin

scra

uiel

sgrp0	thewindow	theWindow

actT0
theactions
theActions

kocl

cobj
.corecnte********

desc
0closed
.prcsperfnullactTjO*/hZ*/k/k/k/EWXO-EO8[alkha,aeE`OjUOYh[OYO8[alkha,aeE`OjUOYh[OYOPWXh[OYYUUqxxyyzz{{|}|n~

pcap~$NotificationCenter

cwin}&Notification Center

scra

uiel

sgrprn

pcap$NotificationCenter

cwin&Notification Center

scra

uiel

sgrp

actT"AXScrollToVisiblen

pcap$NotificationCenter

cwin&Notification Center

scra

uiel

sgrp

actTAXShowMenu
boovtrueascr
ޭ

FasdUAS 1.101.10
k
l
I
.miscactvnullnull
m		@alispMacintosh HDBDNotificationCenter.app
 cuCoreServices5/:System:Library:CoreServices:NotificationCenter.app/.NotificationCenter.appMacintosh HD2System/Library/CoreServices/NotificationCenter.app/

l
O

O

T
k
l
Q1
r(
n&
4#&

sgrp
m$%
n#
4 #

uiel
 m!"
n !"
!4 #

scra
#m
"4$

cwin
$m%%&&&Notification Center
o0	thewindow	theWindow
R
.ascrerr ********
S01'(
'l22()
)Q2*+
*k5,,-.
-r5:/0
/n5812
1268

actT
2o560	thewindow	theWindow
0o0
theactions
theActions.34
3l;;456
5l;;787SM Try to close the whole group first. If that fails, close individual windows.899 Try to close the whole group first. If that fails, close individual windows.6:;
:X;t<=
<ZMo>?
>=MV@A
@nMRBC
B1NR

desc
CoMN0	theaction	theAction
AmRUDDEEClear All
?kYkFFGH
GrY^IJ
ImYZ
boovtrue
Jo
0closedHKL
KO_iMN
MIchO
.prcsperfnullactT
Oocd0	theaction	theAction
No_`0	thewindow	theWindowLP
PSjk0	theaction	theAction
=o>?0
theactions
theActions;QR
QluuRST
SXuUV
UZWX
W=YZ
Yn[\
[1

desc
\o0	theaction	theAction
Zm]]^^
Close
Xk__`a
`rbc
bm
boovtrue
co
0closedade
dOfg
fIh
.prcsperfnullactT
ho0	theaction	theAction
go0	thewindow	theWindowei
iS0	theaction	theAction
Voxy0
theactions
theActionsTj
jl
+R
.ascrerr ********
4
k

prcs
km
llmm&Notification Center

mnnsevsalis\Macintosh HDBDSystem Events.app
 cuCoreServices0/:System:Library:CoreServices:System Events.app/$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/opqro
.aevtoappnull****0	thewindow	theWindow0
theactions
theActions
0closedpstu
.aevtoappnull****
skvvww
t0	theaction	theActionu	nl%D]
.miscactvnullnull

prcs

cwin

scra

uiel

sgrp0	thewindow	theWindow

actT0
theactions
theActions

kocl

cobj
.corecnte********

desc
0closed
.prcsperfnullactTjO*/hZ*/k/k/k/EWXO-EO8[alkha,aeE`OjUOYh[OYO8[alkha,aeE`OjUOYh[OYOPWXh[OYYUUqxxyyzz{{|}|n~

pcap~$NotificationCenter

cwin}&Notification Center

scra

uiel

sgrprn

pcap$NotificationCenter

cwin&Notification Center

scra

uiel

sgrp

actT"AXScrollToVisiblen

pcap$NotificationCenter

cwin&Notification Center

scra

uiel

sgrp

actTAXPress~}|{zynx
x
pcap$NotificationCenter
y
cwin&Notification Center
{
scraz
}
uiel|

sgrp~

actTJName:Close
Target:0x0
Selector:(null)wvutsrqpno
o
pcap$NotificationCenter
p
cwin&Notification Center
r
scraq
t
uiels
v
sgrpu
w
actTJName:Reply
Target:0x0
Selector:(null)
boovtrueascr
ޭ


Simplify filterer code so it actually works.

on run
	tell application "BibDesk"
		try
			set alertResult to display alert ("Really remove all linked files and private notes from " & name of first document as string) & "?" buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" as warning

			if button returned of alertResult is "OK" then
				set theDocument to first document

				save theDocument

				set theFile to (theDocument's file) as alias

				tell application "Finder"
					set theContainer to (container of theFile) as alias
					set newFile to make new file with properties {container:theFile, name:"Filtered " & ((name of theDocument) as string)}
				end tell
				return

				save first document in (newFile as file)
				repeat with thePub in first document's publications
					set thePub's note to my splitNote(thePub's note)
					repeat with theFile in thePub's linked files
						remove theFile from thePub
					end repeat
				end repeat
				save first document
			end if
		on error -128
			display dialog "User cancelled."
		end try
	end tell
end run

to splitNote(theNoteText)
	try
		set oldDelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to {"---"}

		set theParts to every text item of theNoteText

		if length of theParts is greater than 1 then
			set outText to (text 2 through end) of last item of theParts
		else
			set outText to ""
		end if
		set AppleScript's text item delimiters to oldDelims
	on error
		set AppleScript's text item delimiters to oldDelims
	end try
	return outText
end splitNote

on run
	tell application "BibDesk"
		try
			set alertResult to display alert ("Really remove all linked files and private notes from " & name of first document as string) & "?" buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" as warning

			if button returned of alertResult is "OK" then
				save first document in "Filtered " & (name of first document as string) & ".bib"
				repeat with thePub in first document's publications
					set thePub's note to my splitNote(thePub's note)
					repeat with theFile in thePub's linked files
						remove theFile from thePub
					end repeat
				end repeat
				save first document
			end if
		on error -128
			display dialog "User cancelled."
		end try
	end tell
end run

to splitNote(theNoteText)
	try
		set oldDelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to {"---"}

		set theParts to every text item of theNoteText

		if length of theParts is greater than 1 then
			set outText to text 2 through end of last item of theParts
		else
			set outText to ""
		end if
		set AppleScript's text item delimiters to oldDelims
	on error
		set AppleScript's text item delimiters to oldDelims
	end try
	return outText
end splitNote


trailing whitespace

trailing whitespace

property pTitle : "OmniFocus: Quick Stats"
property pVersion : "2.07.brett"

property pstrDBPath : "$HOME/Library/Containers/com.omnigroup.OmniFocus2/Data/Library/Caches/com.omnigroup.OmniFocus/OmniFocusDatabase2"
property pstrMinOSX : "10.9"

property pTimeOut : 30

-- Original version: https://github.com/RobTrew/tree-tools/blob/master/OmniFocus%20scripts/Statistics/QuickStats.applescript

-- Copyright  2012, 2013, 2014 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.

-- Ver 0.8 adds clipboard option to dialogue
-- Ver 0.9 gives an error message if the cache schema has changed, leading to an SQL error in the script
-- Ver 1.0 slight simplification of the code
-- Ver 1.1 added count of Pending projects
-- Ver 1.2 added a count of available actions
-- Ver 1.3 added a break-down of unavailable actions
-- Ver 1.4 added count of Current projects to complement Pending projects
-- ver 1.5 replaced Applescript time function with SQL time expression
-- Ver 1.7 Reorganizes menu, and attempts to enable access for .macappstore installations of OF
--Ver 1.8 Adjusts handling of variant bundle identifiers generally
-- Ver 2.00 Redraft of task breakdown, with progressive narrowing of criteria ...

property pToClipboard : "Copy list to clipboard"

if not FileExists(pstrDBPath) then set pstrDBPath to GetCachePath()

tell application "Finder"
	if (version < pstrMinOSX) then
		display dialog "This script requires OSX " & pstrMinOSX & " or higher" buttons {"OK"} default button 1 with title pTitle & "Ver. " & pVersion
		return
	end if

	set screen_resolution to bounds of window of desktop
	set height to item 4 of screen_resolution
	if height < 1024 then
		set brief to true
	else
		set brief to false
	end if
end tell

if pstrDBPath  "" then
	set inbox_cmd to "
	select 'INBOX GROUPS & ACTIONS', count(*) from task where (inInbox=1);
	select '    Inbox action groups', count(*) from task where (inInbox=1) and (childrenCount>0);
	select '    Inbox actions', count(*) from task where (inInbox=1) and (childrenCount=0);
	select null;
	"
	set folders_cmd to "
	select 'FOLDERS'	, count(*) from folder;
	select '    Active folders', count(*) from folder where effectiveActive=1;
	select '    Dropped folders', count(*) from folder where effectiveActive=0;
	select null;
	"
	set projects_cmd to "
	select 'PROJECTS', count(*) from projectInfo where containsSingletonActions=0;
	select '    Active projects', count(*) from projectInfo where (containsSingletonActions=0) and (status='active');
	select '            Current projects', count(*) from projectInfo p join task t on t.projectinfo=p.pk where (p.containsSingletonActions=0) and (p.folderEffectiveActive=1) and (p.status='active') and (t.dateToStart is null or t.dateToStart < (strftime('%s','now') - strftime('%s','2001-01-01')));
	select '            Pending projects', count(*) from projectInfo p join task t on t.projectinfo=p.pk where (p.containsSingletonActions=0) and (p.folderEffectiveActive=1) and (p.status='active') and (t.dateToStart > (strftime('%s','now') - strftime('%s','2001-01-01')));
	select '    On-hold projects', count(*) from projectInfo where (containsSingletonActions=0) and (status='inactive');
	select '    Completed projects', count(*) from projectInfo where (containsSingletonActions=0) and (status='done');
	select '    Dropped projects', count(*) from projectInfo where (containsSingletonActions=0) and (( status='dropped') or (folderEffectiveActive=0));
	select null;
	"
	set lists_cmd to "
	select 'SINGLE ACTION LISTS', count(*) from projectInfo where containsSingletonActions=1;
	select '    Active single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (status='active');
	select '    On-hold single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (status='inactive');
	select '    Completed single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (status='done');
	select '    Dropped single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (( status='dropped') or (folderEffectiveActive=0));
	select null;
	"
	set contexts_cmd to "
	select 'CONTEXTS', count(*) from context;
	select '    Active contexts', count(*) from context where (effectiveActive=1) and (allowsNextAction=1);
	select '    On-hold contexts', count(*) from context where (effectiveActive=1) and allowsNextAction=0;
	select '    Dropped contexts', count(*) from context where effectiveActive=0;
	select null;
	"
	set groups_cmd to "
	select 'ACTION GROUPS', count(*) from task where (projectinfo is null) and (childrenCount>0);
	select '    Remaining action groups', count(*) from task where (projectinfo is null) and (dateCompleted is null) and (childrenCount>0);
	select '    Completed action groups', count(dateCompleted) from task where (projectinfo is null) and (childrenCount>0);
	select null;
	"
	set tasks_cmd to "
	select 'ALL TASKS', count(*) from task;
	select null;
	"
	set actions_cmd to "
	select 'ACTIONS', count(*) from task where (projectinfo is null) and (childrenCount=0);
	select '    Completed actions', count(dateCompleted) from task where (projectinfo is null) and (childrenCount=0);
	select '    Dropped project actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp where (projectinfo is null) and (childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is not null and (tp.status='dropped' or tp.folderEffectiveActive=0));
	select '    Dropped context actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and c.effectiveActive= 0;
	select '    Remaining actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1);
	select '        Actions in Projects on hold', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is not null and tp.status='inactive');
	select '        Actions in Contexts on hold', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is not null and c.allowsNextAction=0);
	select '        Blocked actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=1;
	select '        	Blocked by future start date', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=1
				and tp.blockedByFutureStartDate=1;
	select '        	Sequentially blocked', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=1
				and tp.blockedByFutureStartDate=0;
	select '        Available actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=0;
	select null;
	"
	set summary_cmd to "
	select 'SUMMARY';
	select '    Inbox available actions', count(*) from task where (inInbox=1) and (dateCompleted is null);
	select '    Due in next week', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				/* and (tp.context is null or c.allowsNextAction=1) */
				and tp.blockedByFutureStartDate=0
				and effectiveDateDue <= strftime('%s','now','+7 days') - strftime('%s','2001-01-01');
				/* and dateDue < strftime('%s','now','+7 days') - strftime('%s','2001-01-01'); */
	select '    Flagged projects and actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (((projectinfo is null) and (tp.childrenCount=0))  or containsSingletonActions=0) and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				/* and (tp.context is null or c.allowsNextAction=1) */
				and tp.blockedByFutureStartDate=0
				and tp.effectiveFlagged;
	select '    Due or Flagged', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (((projectinfo is null) and (tp.childrenCount=0))  or containsSingletonActions=0) and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				/* and (tp.context is null or c.allowsNextAction=1) */
				and tp.blockedByFutureStartDate=0
				and (tp.effectiveFlagged or (effectiveDateDue <= strftime('%s','now','+7 days') - strftime('%s','2001-01-01')));
	select null;
	"
	if brief then
		set commands to summary_cmd & actions_cmd & projects_cmd & contexts_cmd
	else
		set commands to summary_cmd & actions_cmd & inbox_cmd & folders_cmd & projects_cmd & lists_cmd & contexts_cmd & groups_cmd & tasks_cmd
	end if
	set strCmd to "sqlite3 -separator ': ' \"" & pstrDBPath & "\" " & quoted form of (commands)

	-- 		try
	set strList to do shell script strCmd
	-- 		on error
	-- 			display dialog "The SQL schema for the OmniFocus cache may have changed in a recent update of OF." & return & return &
	-- 				"Look on the OmniFocus user forums for an updated version of this script." buttons {"OK"} with title pTitle & "Ver. " & pVersion
	-- 			return
	-- 		end try
	tell application id "sevs"
		activate
		if button returned of (display dialog strList buttons {pToClipboard, "OK"} default button "OK" giving up after pTimeOut with title pTitle & " Ver. " & pVersion)
			is pToClipboard then tell application id "com.apple.finder" to set the clipboard to strList
	end tell
else
	tell application id "sevs"
		activate
		display dialog "OmniFocus cache not found ..." buttons {"OK"} default button 1 with title pTitle & " Ver. " & pVersion
	end tell
end if

on FileExists(strPath)
	(do shell script "test -e " & strPath & " ; echo $?") = "0"
end FileExists

on GetCachePath()
	try
		tell application "Finder" to tell (application file id "OFOC") to "$HOME/Library/Containers/com.omnigroup.OmniFocus2/Data/Library/Caches/" & its id & "/OmniFocusDatabase2"
	on error
		error "OmniFocus not installed ..."
	end try
end GetCachePath