Validate the dialog list types (for typos)

(*
	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

(*
	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 "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

	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..."

	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:
	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)
	-- :TODO: verify that 'cur_type' is in 'p_list_types'

	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 for issue #28, switch to bash before executing script

FasdUAS 1.101.10
l\
O\
k[
I
.miscactvnullnull

l

} TODO: test if ~/.boot2docker/boot2docker.iso exists, and if not, copy it from the install (need to ship it in the installer)
 TODO: test if ~/.boot2docker/boot2docker.iso exists, and if not, copy it from the install (need to ship it in the installer)
r

I

.coredoscnullctxt
o0	newwindow	newWindow
r
m&Boot2Docker for OSX
o0windowtitlewindowTitle
r
m
boovtrue
n
1

tdct
o0	newwindow	newWindow
r!!"
!o0windowtitlewindowTitle
"n#$
#1

titl
$o0	newwindow	newWindow %&
%r"%'(
'm"#))**4/usr/local/bin/boot2docker
(o0b2d&+,
+I&--.
.coredoscnullctxt
-m&'//00bash.1

kfil
1o()0	newwindow	newWindow,23
2I.545
.coredoscnullctxt
4m./6677.mkdir -p ~/.boot2docker58

kfil
8o010	newwindow	newWindow39:
9I6=;<
.coredoscnullctxt
;m67==>>if [ ! -f ~/.boot2docker/boot2docker.iso ]; then cp /usr/local/share/boot2docker/boot2docker.iso ~/.boot2docker/ ; fi<?

kfil
?o890	newwindow	newWindow:@A
@I>GBC
.coredoscnullctxt
Bb>ADE
Do>?0b2d
Em?@FFGG init CH

kfil
HoBC0	newwindow	newWindowAIJ
IIHQKL
.coredoscnullctxt
KbHKMN
MoHI0b2d
NmIJOOPP up && export DOCKER_HOST=tcp://$(/usr/local/bin/boot2docker ip 2>/dev/null):2375LQ

kfil
QoLM0	newwindow	newWindowJR
RIR[ST
.coredoscnullctxt
SmRUUUVVdocker versionTW

kfil
WoVW0	newwindow	newWindow
mXX@alislMacintosh HDH++Terminal.app_?O	 	Utilities?A+*2Macintosh HD:Applications:Utilities:Terminal.appTerminal.appMacintosh HD#Applications/Utilities/Terminal.app/YZY
.aevtoappnull****Z[\]
.aevtoappnull****
[k\^^\]X)/6=FOU
.miscactvnullnull
.coredoscnullctxt0	newwindow	newWindow0windowtitlewindowTitle

tdct

titl0b2d

kfil]Y*jO*jEOEOe,FO,FOEOlOlOlO%lO%lOalUascrޭ

FasdUAS 1.101.10
lR
OR
kQ
I
.miscactvnullnull

l

} TODO: test if ~/.boot2docker/boot2docker.iso exists, and if not, copy it from the install (need to ship it in the installer)
 TODO: test if ~/.boot2docker/boot2docker.iso exists, and if not, copy it from the install (need to ship it in the installer)
r

I

.coredoscnullctxt
o0	newwindow	newWindow
r
m&Boot2Docker for OSX
o0windowtitlewindowTitle
r
m
boovtrue
n
1

tdct
o0	newwindow	newWindow
r!!"
!o0windowtitlewindowTitle
"n#$
#1

titl
$o0	newwindow	newWindow %&
%r"%'(
'm"#))**4/usr/local/bin/boot2docker
(o0b2d&+,
+I&--.
.coredoscnullctxt
-m&'//00.mkdir -p ~/.boot2docker.1

kfil
1o()0	newwindow	newWindow,23
2I.545
.coredoscnullctxt
4m./6677if [ ! -f ~/.boot2docker/boot2docker.iso ]; then cp /usr/local/share/boot2docker/boot2docker.iso ~/.boot2docker/ ; fi58

kfil
8o010	newwindow	newWindow39:
9I6?;<
.coredoscnullctxt
;b69=>
=o670b2d
>m78??@@ init <A

kfil
Ao:;0	newwindow	newWindow:BC
BI@IDE
.coredoscnullctxt
Db@CFG
Fo@A0b2d
GmABHHII up && export DOCKER_HOST=tcp://$(/usr/local/bin/boot2docker ip 2>/dev/null):2375EJ

kfil
JoDE0	newwindow	newWindowCK
KIJQLM
.coredoscnullctxt
LmJKNNOOdocker versionMP

kfil
PoLM0	newwindow	newWindow
mQQ@alisZOSX-OSxH+PTerminal.app(>e	 	Utilitiesy`?APO,OSX-OS:Applications:Utilities:Terminal.appTerminal.appOSX-OS#Applications/Utilities/Terminal.app/RSR
.aevtoappnull****STUV
.aevtoappnull****
TkRWWUVQ)/6?HN
.miscactvnullnull
.coredoscnullctxt0	newwindow	newWindow0windowtitlewindowTitle

tdct

titl0b2d

kfilSO*jO*jEOEOe,FO,FOEOlOlO%lO%lOlUascrޭ


windowSize.applescript, from darwin11

windowSize.applescript, from darwin11

property DEBUG_LEVEL : 1

property DEF_LEFT_MARGIN : 10
property DEF_RIGHT_MARGIN : 10
property DEF_TOP_MARGIN : 10
property DEF_BOTTOM_MARGIN : 10

property GEEKTOOL_WINDOW : 1
property GEEKTOOL_MARGIN : 10

-- Window size main function
on windowSize(pars)
	-- Debug mode
	if DEBUG_LEVEL > 0 then
		log "debug mode = " & DEBUG_LEVEL
		if DEBUG_LEVEL > 1 then
			display dialog "debug mode = " & DEBUG_LEVEL
		end if
	end if

	-- Set Parameters
	set {xsize, ysize, xpos, ypos, leftmargin, rightmargin, topmargin, bottommargin, appName, windowNumber, monPosX, monPosY, resize} to {1, 1, 0, 0, DEF_LEFT_MARGIN, DEF_RIGHT_MARGIN, DEF_TOP_MARGIN, DEF_BOTTOM_MARGIN, "", "", "", "", 1}
	try
		set xsize to xsize of pars
	end try
	try
		set ysize to ysize of pars
	end try
	try
		set xpos to xpos of pars
	end try
	try
		set ypos to ypos of pars
	end try
	try
		set leftmargin to leftmargin of pars
	end try
	try
		set rightmargin to rightmargin of pars
	end try
	try
		set topmargin to topmargin of pars
	end try
	try
		set bottommargin to bottommargin of pars
	end try
	try
		set appName to appName of pars
	end try
	try
		set windowNumber to windowNumber of pars
	end try
	try
		set monPosX to monPosX of pars
	end try
	try
		set monPosY to monPosY of pars
	end try
	try
		set resize to resize of pars
	end try

	-- Get application name and window number, if appName is "", if windowNumber is not set, set 1
	if appName is "" then
		tell application "System Events"
			set pList to name of every process whose frontmost is true
			set appName to item 1 of pList
		end tell
	end if
	if windowNumber is "" then
		set windowNumber to 1
	end if

	if DEBUG_LEVEL > 0 then
		log "windowSize(" & xsize & ", " & ysize & ", " & xpos & ", " & ypos & ", " & leftmargin & ", " & rightmargin & ", " & topmargin & ", " & bottommargin & ", " & appName & ", " & windowNumber & ", " & monPosX & ", " & monPosY & "," & resize & ")"
		if DEBUG_LEVEL > 1 then
			display dialog "windowSize(" & xsize & ", " & ysize & ", " & xpos & ", " & ypos & ", " & leftmargin & ", " & rightmargin & ", " & topmargin & ", " & bottommargin & ", " & appName & ", " & windowNumber & ", " & monPosX & ", " & monPosY & "," & resize & ")"
		end if
	end if

	-- First, move monitoring applications to correct places, if available
	try
		tell application "Finder"
			set scriptPath to (path to me)'s folder as text
		end tell
		set moveForMonScpt to scriptPath & "moveForMon.scpt"
		set moveForMon to load script file moveForMonScpt
		moveForMon's moveForMon(false)
	end try

	-- Get window position
	tell application "System Events"
		tell process appName
			try
				set topWindow to item windowNumber of (every window whose focused is true)
			on error
				set topWindow to window windowNumber
			end try
			set winPos to position of topWindow
			set winSize to size of topWindow
			set winPosX to item 1 of winPos
			set winPosY to item 2 of winPos
			set winSizeX to item 1 of winSize
			set winSizeY to item 2 of winSize
			set winPosRT to {winPosX + winSizeX, winPosY}
			set winPosLB to {winPosX, winPosY + winSizeY}
			set winPosRB to {winPosX + winSizeX, winPosY + winSizeY}
			if DEBUG_LEVEL > 0 then
				log "winPos(" & winPosX & ", " & winPosY & ")"
				log "winSize(" & winSizeX & ", " & winSizeY & ")"
				if DEBUG_LEVEL > 1 then
					display dialog "winPos(" & winPosX & ", " & winPosY & ")"
					display dialog "winSize(" & winSizeX & ", " & winSizeY & ")"
				end if
			end if
		end tell
	end tell

	-- Get screen (display) size
	if monPosX is not "" and monPosY is not "" then
		set svs to getVisibleFrame(monPosX, monPosY)
		set dPosX to item 1 of svs
	else
		try
			set svs to getVisibleFrame(item 1 of winPos, item 2 of winPos)
			set dPosX to item 1 of svs

		on error
			try
				set svs to getVisibleFrame(item 1 of winPosRT, item 2 of winPosRT)
				set dPosX to item 1 of svs

			on error
				try
					set svs to getVisibleFrame(item 1 of winPosLB, item 2 of winPosLB)
					set dPosX to item 1 of svs

				on error
					set svs to getVisibleFrame(item 1 of winPosRB, item 2 of winPosRB)
					set dPosX to item 1 of svs
				end try
			end try
		end try
	end if

	set dPosX to item 1 of svs
	set dPosY to item 2 of svs
	set dWidth to item 3 of svs
	set dHeight to item 4 of svs
	if DEBUG_LEVEL > 0 then
		log "svs(" & dPosX & ", " & dPosY & ", " & dWidth & ", " & dHeight & ")"
		if DEBUG_LEVEL > 1 then
			display dialog "svs(" & dPosX & ", " & dPosY & ", " & dWidth & ", " & dHeight & ")"
		end if
	end if

	-- Get GeekTool's position
	--tell application "System Events"
	--	try
	--		tell process "GeekTool"
	--			set topWindow to window GEEKTOOL_WINDOW
	--			set gtPos to position of topWindow
	--		end tell
	--		set gtflag to 1
	--	on error
	--		set gtflag to 0
	--	end try
	--end tell

	-- Check if the window and GeekTool are in same screen
	--if gtflag is 1 then
	--	set gtsvs to getVisibleFrame(item 1 of gtPos, item 2 of gtPos)
	--	set gtdPosX to item 1 of gtsvs
	--	set gtdPosY to item 2 of gtsvs
	--	if dPosX is gtdPosX and dPosY is gtdPosY then
	--		try
	--			set gtSize to size of topWindow
	--			set geekToolWidth to item 1 of gtSize
	--			set geekToolWidth to geekToolWidth + GEEKTOOL_MARGIN -- additional margin
	--		on error
	--			set geekToolWidth to 0
	--		end try
	--	else
	--		set geekToolWidth to 0
	--	end if
	if item 1 of winPos < 0 then
		set geekToolWidth to 160
		set geekToolWidth to geekToolWidth + GEEKTOOL_MARGIN -- additional margin
	else
		set geekToolWidth to 0
	end if

	-- Subtract geekToolWidth from display width
	set dWidth to dWidth - geekToolWidth

	-- Resize/move
	tell application "System Events"
		tell process appName
			try
				set topWindow to item windowNumber of (every window whose focused is true)
			on error
				set topWindow to window windowNumber
			end try
			-- Set screen size w/o margins
			--set useSize to {dWidth - leftmargin - rightmargin, dHeight - topmargin - bottommargin}
			--if DEBUG_LEVEL > 0 then
			--	log "useSize(" & item 1 of useSize & ", " & item 2 of useSize & ")"
			--	if DEBUG_LEVEL > 1 then
			--		display dialog "useSize(" & item 1 of useSize & ", " & item 2 of useSize & ")"
			--	end if
			--end if
			-- No margin w/o edges of screen
			--set wpos to {dPosX + leftmargin + xpos * (item 1 of useSize), dPosY + topmargin + ypos * (item 2 of useSize)}
			--set wsize to {xsize * (item 1 of useSize), ysize * (item 2 of useSize)}

			-- Set margin in any place
			set wpos to {dPosX + leftmargin + xpos * dWidth, dPosY + topmargin + ypos * dHeight}
			set wsize to {xsize * dWidth - leftmargin - rightmargin, ysize * dHeight - topmargin - bottommargin}

			if DEBUG_LEVEL > 0 then
				log "wpos(" & item 1 of wpos & ", " & item 2 of wpos & ")"
				log "wsize(" & item 1 of wsize & ", " & item 2 of wsize & ")"
				if DEBUG_LEVEL > 1 then
					display dialog "wpos(" & item 1 of wpos & ", " & item 2 of wpos & ")"
					display dialog "wsize(" & item 1 of wsize & ", " & item 2 of wsize & ")"
				end if
			end if
			tell topWindow
				if DEBUG_LEVEL > 0 then
					log "preoperties before= "
					log properties
				end if
				set winSize to size
				if item 1 of winSize > dWidth or item 2 of winSize > dHeight then
					if DEBUG_LEVEL > 1 then
						display dialog "Curent size is larger than window size!"
					end if
					set position to {dPosX - ((item 1 of wpos) + (item 1 of wsize) - dWidth), dPosY - ((item 2 of wpos) + (item 2 of wsize) - dHeight)}
				else if (item 1 of wpos) + (item 1 of winSize) > dWidth or (item 2 of wpos) + (item 2 of winSize) > dHeight then
					if DEBUG_LEVEL > 1 then
						display dialog "Curent size is larger than window-windowpos!"
					end if
					set position to {dPosX, dPosY}
				else
					set position to wpos
				end if
				if DEBUG_LEVEL > 0 then
					log "preoperties after position change= "
					log properties
					if DEBUG_LEVEL > 1 then
						display dialog "set size"
					end if
				end if
				if resize is 1 then
					set size to wsize
				end if
				-- This shows an error for iTerm!
				-- maybe properties is changed when set size…???
				--if DEBUG_LEVEL > 0 then
				--	log "preoperties after resize= "
				--	properties
				--	if DEBUG_LEVEL > 1 then
				--		display dialog "set final position"
				--	end if
				--end if

				--end tell
				--try
				--	set topWindow to item windowNumber of (every window whose focused is true)
				--on error
				--	set topWindow to window windowNumber
				--end try
				--tell topWindow

				if DEBUG_LEVEL > 0 then
					log "preoperties after resize= "
					log properties
					if DEBUG_LEVEL > 1 then
						display dialog "set final position to (" & item 1 of wpos & ", " & item 2 of wpos & ")"
					end if
				end if
				set position to wpos

				if DEBUG_LEVEL > 2 then
					log "properties after = "
					log properties
				end if
			end tell

		end tell
	end tell
end windowSize

-- Function to get visible frame (w/o menu bar, dock)
on getVisibleFrame(x, y)
	set cocoaScript to "require 'osx/cocoa';
x = (ARGV[1].to_i);
y = (ARGV[2].to_i);

mf = OSX::NSScreen.mainScreen().frame();
mX = mf.origin.x
mY = mf.origin.y
mW = mf.size.width
mH = mf.size.height
sc = OSX::NSScreen.screens;
point = OSX::NSMakePoint(x, -y + mY + mH);
vf = 0
for i in 0..sc.count()-1
	f = sc[i].frame();
	if( OSX:: NSMouseInRect(point,f,0) )
		vf = sc[i].visibleFrame();
		break;
	end
end

if vf == 0
exit 0;
end

vX = vf.origin.x
vY = vf.origin.y
vW = vf.size.width
vH = vf.size.height

print (vX);
print '
';
print (-vY - vH + mH - mY);
print '
';
print vW;
print '
';
print vH;
"

	set ret to do shell script "/usr/bin/ruby -e " & quoted form of cocoaScript & " '' " & " " & x & " " & y
	if ret is "" then
		--display dialog "missing value"
		return 0
	end if
	return {(paragraph 1 of ret) as number, (paragraph 2 of ret) as number, (paragraph 3 of ret) as number, (paragraph 4 of ret) as number}
end getVisibleFrame

property DEBUG_LEVEL : 1

property DEF_LEFT_MARGIN : 10
property DEF_RIGHT_MARGIN : 10
property DEF_TOP_MARGIN : 10
property DEF_BOTTOM_MARGIN : 10

property GEEKTOOL_WINDOW : 1
property GEEKTOOL_MARGIN : 10

-- window size main function
on windowSize(pars)
	-- debug mode
	if DEBUG_LEVEL > 0 then
		log "debug mode = " & DEBUG_LEVEL
		if DEBUG_LEVEL > 1 then
			display dialog "debug mode = " & DEBUG_LEVEL
		end if
	end if

	-- set Parameters
	set {xsize, ysize, xpos, ypos, leftmargin, rightmargin, topmargin, bottommargin, appName, windowNumber, monPosX, monPosY, resize} to {1, 1, 0, 0, DEF_LEFT_MARGIN, DEF_RIGHT_MARGIN, DEF_TOP_MARGIN, DEF_BOTTOM_MARGIN, "", "", "", "", 1}
	try
		set xsize to xsize of pars
	end try
	try
		set ysize to ysize of pars
	end try
	try
		set xpos to xpos of pars
	end try
	try
		set ypos to ypos of pars
	end try
	try
		set leftmargin to leftmargin of pars
	end try
	try
		set rightmargin to rightmargin of pars
	end try
	try
		set topmargin to topmargin of pars
	end try
	try
		set bottommargin to bottommargin of pars
	end try
	try
		set appName to appName of pars
	end try
	try
		set windowNumber to windowNumber of pars
	end try
	try
		set monPosX to monPosX of pars
	end try
	try
		set monPosY to monPosY of pars
	end try
	try
		set resize to resize of pars
	end try

	-- get application name and window number, if appName is "", if windowNumber is not set, set 1
	if appName is "" then
		tell application "System Events"
			set pList to name of every process whose frontmost is true
			set appName to item 1 of pList
		end tell
	end if
	if windowNumber is "" then
		set windowNumber to 1
	end if

	if DEBUG_LEVEL > 0 then
		log "windowSize(" & xsize & ", " & ysize & ", " & xpos & ", " & ypos & ", " & leftmargin & ", " & rightmargin & ", " & topmargin & ", " & bottommargin & ", " & appName & ", " & windowNumber & ", " & monPosX & ", " & monPosY & "," & resize & ")"
		if DEBUG_LEVEL > 1 then
			display dialog "windowSize(" & xsize & ", " & ysize & ", " & xpos & ", " & ypos & ", " & leftmargin & ", " & rightmargin & ", " & topmargin & ", " & bottommargin & ", " & appName & ", " & windowNumber & ", " & monPosX & ", " & monPosY & "," & resize & ")"
		end if
	end if

	-- first, move monitoring applications to correct places, if available
	try
		tell application "Finder"
			set scriptPath to (path to me)'s folder as text
		end tell
		set moveForMonScpt to scriptPath & "moveForMon.scpt"
		set moveForMon to load script file moveForMonScpt
		moveForMon's moveForMon(false)
	end try

	-- get window position
	tell application "System Events"
		tell process appName
			try
				set topWindow to item windowNumber of (every window whose focused is true)
			on error
				set topWindow to window windowNumber
			end try
			set winPos to position of topWindow
			set winSize to size of topWindow
			set winPosX to item 1 of winPos
			set winPosY to item 2 of winPos
			set winSizeX to item 1 of winSize
			set winSizeY to item 2 of winSize
			set winPosRT to {winPosX + winSizeX, winPosY}
			set winPosLB to {winPosX, winPosY + winSizeY}
			set winPosRB to {winPosX + winSizeX, winPosY + winSizeY}
			if DEBUG_LEVEL > 0 then
				log "winPos(" & winPosX & ", " & winPosY & ")"
				log "winSize(" & winSizeX & ", " & winSizeY & ")"
				if DEBUG_LEVEL > 1 then
					display dialog "winPos(" & winPosX & ", " & winPosY & ")"
					display dialog "winSize(" & winSizeX & ", " & winSizeY & ")"
				end if
			end if
		end tell
	end tell

	-- get screen (display) size
	if monPosX is not "" and monPosY is not "" then
		set svs to getVisibleFrame(monPosX, monPosY)
		set dPosX to item 1 of svs
	else
		try
			set svs to getVisibleFrame(item 1 of winPos, item 2 of winPos)
			set dPosX to item 1 of svs

		on error
			try
				set svs to getVisibleFrame(item 1 of winPosRT, item 2 of winPosRT)
				set dPosX to item 1 of svs

			on error
				try
					set svs to getVisibleFrame(item 1 of winPosLB, item 2 of winPosLB)
					set dPosX to item 1 of svs

				on error
					set svs to getVisibleFrame(item 1 of winPosRB, item 2 of winPosRB)
					set dPosX to item 1 of svs
				end try
			end try
		end try
	end if

	set dPosX to item 1 of svs
	set dPosY to item 2 of svs
	set dWidth to item 3 of svs
	set dHeight to item 4 of svs
	if DEBUG_LEVEL > 0 then
		log "svs(" & dPosX & ", " & dPosY & ", " & dWidth & ", " & dHeight & ")"
		if DEBUG_LEVEL > 1 then
			display dialog "svs(" & dPosX & ", " & dPosY & ", " & dWidth & ", " & dHeight & ")"
		end if
	end if

	-- get GeekTool's position
	tell application "System Events"
		try
			tell process "GeekTool"
				set topWindow to window GEEKTOOL_WINDOW
				set gtPos to position of topWindow
			end tell
			set gtflag to 1
		on error
			set gtflag to 0
		end try
	end tell

	-- check if the window and GeekTool are in same screen
	if gtflag is 1 then
		set gtsvs to getVisibleFrame(item 1 of gtPos, item 2 of gtPos)
		set gtdPosX to item 1 of gtsvs
		set gtdPosY to item 2 of gtsvs
		if dPosX is gtdPosX and dPosY is gtdPosY then
			try
				set gtSize to size of topWindow
				set geekToolWidth to item 1 of gtSize
				set geekToolWidth to geekToolWidth + GEEKTOOL_MARGIN -- additional margin
			on error
				set geekToolWidth to 0
			end try
		else
			set geekToolWidth to 0
		end if
	else
		set geekToolWidth to 0
	end if

	-- subtract geekToolWidth from display width
	set dWidth to dWidth - geekToolWidth

	-- resize/move
	tell application "System Events"
		tell process appName
			try
				set topWindow to item windowNumber of (every window whose focused is true)
			on error
				set topWindow to window windowNumber
			end try
			-- set screen size w/o margins
			--set useSize to {dWidth - leftmargin - rightmargin, dHeight - topmargin - bottommargin}
			--if DEBUG_LEVEL > 0 then
			--	log "useSize(" & item 1 of useSize & ", " & item 2 of useSize & ")"
			--	if DEBUG_LEVEL > 1 then
			--		display dialog "useSize(" & item 1 of useSize & ", " & item 2 of useSize & ")"
			--	end if
			--end if
			-- no margin w/o edges of screen
			--set wpos to {dPosX + leftmargin + xpos * (item 1 of useSize), dPosY + topmargin + ypos * (item 2 of useSize)}
			--set wsize to {xsize * (item 1 of useSize), ysize * (item 2 of useSize)}

			-- set margin in any place
			set wpos to {dPosX + leftmargin + xpos * dWidth, dPosY + topmargin + ypos * dHeight}
			set wsize to {xsize * dWidth - leftmargin - rightmargin, ysize * dHeight - topmargin - bottommargin}

			if DEBUG_LEVEL > 0 then
				log "wpos(" & item 1 of wpos & ", " & item 2 of wpos & ")"
				log "wsize(" & item 1 of wsize & ", " & item 2 of wsize & ")"
				if DEBUG_LEVEL > 1 then
					display dialog "wpos(" & item 1 of wpos & ", " & item 2 of wpos & ")"
					display dialog "wsize(" & item 1 of wsize & ", " & item 2 of wsize & ")"
				end if
			end if
			tell topWindow
				if DEBUG_LEVEL > 0 then
					log "preoperties before= "
					log properties
				end if
				set winSize to size
				if item 1 of winSize > dWidth or item 2 of winSize > dHeight then
					if DEBUG_LEVEL > 1 then
						display dialog "Curent size is larger than window size!"
					end if
					set position to {dPosX - ((item 1 of wpos) + (item 1 of wsize) - dWidth), dPosY - ((item 2 of wpos) + (item 2 of wsize) - dHeight)}
				else if (item 1 of wpos) + (item 1 of winSize) > dWidth or (item 2 of wpos) + (item 2 of winSize) > dHeight then
					if DEBUG_LEVEL > 1 then
						display dialog "Curent size is larger than window-windowpos!"
					end if
					set position to {dPosX, dPosY}
				else
					set position to wpos
				end if
				if DEBUG_LEVEL > 0 then
					log "preoperties after position change= "
					log properties
					if DEBUG_LEVEL > 1 then
						display dialog "set size"
					end if
				end if
				if resize is 1 then
					set size to wsize
				end if
				-- this shows an error for iTerm!
				-- maybe properties is changed when set size…???
				--if DEBUG_LEVEL > 0 then
				--	log "preoperties after resize= "
				--	properties
				--	if DEBUG_LEVEL > 1 then
				--		display dialog "set final position"
				--	end if
				--end if

				--end tell
				--try
				--	set topWindow to item windowNumber of (every window whose focused is true)
				--on error
				--	set topWindow to window windowNumber
				--end try
				--tell topWindow

				if DEBUG_LEVEL > 0 then
					log "preoperties after resize= "
					log properties
					if DEBUG_LEVEL > 1 then
						display dialog "set final position to (" & item 1 of wpos & ", " & item 2 of wpos & ")"
					end if
				end if
				set position to wpos

				if DEBUG_LEVEL > 2 then
					log "properties after = "
					log properties
				end if
			end tell

		end tell
	end tell
end windowSize

-- function to get visible frame (w/o menu bar, dock)
on getVisibleFrame(x, y)
	set cocoaScript to "require 'osx/cocoa';
x = (ARGV[1].to_i);
y = (ARGV[2].to_i);

mf = OSX::NSScreen.mainScreen().frame();
mX = mf.origin.x
mY = mf.origin.y
mW = mf.size.width
mH = mf.size.height
sc = OSX::NSScreen.screens;
point = OSX::NSMakePoint(x, -y + mY + mH);
vf = 0
for i in 0..sc.count()-1
	f = sc[i].frame();
	if( OSX:: NSMouseInRect(point,f,0) )
		vf = sc[i].visibleFrame();
		break;
	end
end

if vf == 0
exit 0;
end

vX = vf.origin.x
vY = vf.origin.y
vW = vf.size.width
vH = vf.size.height

print (vX);
print '
';
print (-vY - vH + mH - mY);
print '
';
print vW;
print '
';
print vH;
"

	set ret to do shell script "/usr/bin/ruby -e " & quoted form of cocoaScript & " '' " & " " & x & " " & y
	if ret is "" then
		--display dialog "missing value"
		return 0
	end if
	return {(paragraph 1 of ret) as number, (paragraph 2 of ret) as number, (paragraph 3 of ret) as number, (paragraph 4 of ret) as number}
end getVisibleFrame


tdf#90298 rescan for bundled extensions when installing Mac langpack

LO only scans for new extensions when the folder's timestamp is newer
than a timestamp file in the user's profile. When installing a
languagepack, the folder is set to the build-time of the package. touch
the dir to set it to install time and thus allow LO to pick up newly
installed dictionaries and thesaurus/hyphenation data.

Change-Id: I888f830d2325774cd98e3624c19d2be19d4d6db2

(*

 This file is part of the LibreOffice project.

 This Source Code Form is subject to the terms of the Mozilla Public
 License, v. 2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.

 This file incorporates work covered by the following license notice:

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements. See the NOTICE file distributed
   with this work for additional information regarding copyright
   ownership. The ASF licenses this file to you under the Apache
   License, Version 2.0 (the "License"); you may not use this file
   except in compliance with the License. You may obtain a copy of
   the License at http://www.apache.org/licenses/LICENSE-2.0 .

This script is meant to
	1) Identify installed instances of the product
	2) check whether the user has write-access (and if not
	ask for authentification)
	3) install the shipped tarball
*)

-- strings for localisations - to be meant to be replaced
-- by a makefile or similar
set OKLabel to "[OKLabel]"
set InstallLabel to "[InstallLabel]"
set AbortLabel to "[AbortLabel]"
set intro to "[IntroText1]

[IntroText2]

[IntroText3]"
set chooseMyOwn to "[ChooseMyOwnText]"
set listPrompt to "[ListPromptText]"
set chooseManual to "[ChooseManualText]"
set listOKLabel to "[ListOKLabelText]"
set listCancelLabel to "[ListCancelLabel]"
set appInvalid to "[AppInvalidText1]

[AppInvalidText2]" -- string will begin with the chosen application's name
set startInstall to "[StartInstallText1]

[StartInstallText2]"
set IdentifyQ to "[IdentifyQText]

[IdentifyQText2]"
set IdentifyYES to "[IdentifyYES]"
set IdentifyNO to "[IdentifyNO]"
set installFailed to "[InstallFailedText]"
set installComplete to "[InstallCompleteText]

[InstallCompleteText2]"

set sourcedir to (do shell script "dirname " & quoted form of POSIX path of (path to of me))

activate
display dialog intro buttons {AbortLabel, InstallLabel} default button 2

if (button returned of result) is AbortLabel then
	return 2
end if

set found_ooos_all to ""
-- command might return an error if spotlight is disabled completely
try
	set found_ooos_all to (do shell script "mdfind \"kMDItemContentType == 'com.apple.application-bundle' && kMDItemDisplayName == '[PRODUCTNAME]*' && kMDItemDisplayName != '[FULLAPPPRODUCTNAME].app'\"")
end try
set found_ooos_all to found_ooos_all & "
" & chooseMyOwn

set found_ooos_all_paragraphs to paragraphs in found_ooos_all

set found_ooos to {}
repeat with currentApp in found_ooos_all_paragraphs
	if currentApp does not start with "/Volumes" then
		copy currentApp to the end of found_ooos
	end if
end repeat

-- repeat with oneApp in found_ooos
--  display dialog oneApp
-- end repeat

-- the choice returned is of type "list"
-- Show selection dialog only if more than one or no product was found
-- The first item is an empty string, if no app was found and no app started with "/Volumes"
-- The first item is chooseMyOwn, if no app was found and at least one app started with "/Volumes"
if (get first item of found_ooos as string) is "" then
  set the choice to (choose from list found_ooos default items (get second item of found_ooos) with prompt listPrompt OK button name listOKLabel cancel button name listCancelLabel)
  if choice is false then
	  -- do nothing, the user cancelled the installation
    	return 2 --aborted by user
  else if (choice as string) is chooseMyOwn then
	  -- yeah, one needs to use "choose file", otherwise
	  -- the user would not be able to select the .app
	  set the choice to POSIX path of (choose file with prompt chooseManual of type "com.apple.application-bundle" without showing package contents and invisibles)
  end if
else if (get first item of found_ooos as string) is chooseMyOwn then
  set the choice to (choose from list found_ooos default items (get first item of found_ooos) with prompt listPrompt OK button name listOKLabel cancel button name listCancelLabel)
  if choice is false then
	  -- do nothing, the user cancelled the installation
    	return 2 --aborted by user
  else if (choice as string) is chooseMyOwn then
	  -- yeah, one needs to use "choose file", otherwise
	  -- the user would not be able to select the .app
	  set the choice to POSIX path of (choose file with prompt chooseManual of type "com.apple.application-bundle" without showing package contents and invisibles)
  end if
else if (get second item of found_ooos as string) is chooseMyOwn then
  -- set choice to found installation
  -- set the choice to (get first paragraph of found_ooos)
  set the choice to (get first item of found_ooos)
else
  set the choice to (choose from list found_ooos default items (get first item of found_ooos) with prompt listPrompt OK button name listOKLabel cancel button name listCancelLabel)
  if choice is false then
	  -- do nothing, the user cancelled the installation
    	return 2 --aborted by user
  else if (choice as string) is chooseMyOwn then
	  -- yeah, one needs to use "choose file", otherwise
	  -- the user would not be able to select the .app
	  set the choice to POSIX path of (choose file with prompt chooseManual of type "com.apple.application-bundle" without showing package contents and invisibles)
  end if
end if

-- now only check whether the path is really from [PRODUCTNAME]
try
	do shell script "grep '<string>[PRODUCTNAME] [PRODUCTVERSION]'   " & quoted form of (choice as string) & "/Contents/Info.plist"
on error
	display dialog (choice as string) & appInvalid buttons {InstallLabel} default button 1 with icon 0
	return 3 --wrong target-directory
end try

(*
display dialog startInstall buttons {AbortLabel, InstallLabel} default button 2

if (button returned of result) is AbortLabel then
	return 2
end if
*)

-- touch extensions folder to have LO register bundled dictionaries
set tarCommand to "/usr/bin/tar -C " & quoted form of (choice as string) & " -xjf " & quoted form of sourcedir & "/tarball.tar.bz2 && touch " & quoted form of (choice as string) & "/Contents/Resources/extensions"
try
	do shell script tarCommand

on error errMSG number errNUM
	display dialog IdentifyQ buttons {IdentifyYES, IdentifyNO} with icon 2
	if (button returned of result) is IdentifyYES then
		try
			do shell script tarCommand with administrator privileges
		on error errMSG number errNUM
			display dialog installFailed buttons {OKLabel} default button 1 with icon 0
			-- -60005 username/password wrong
			-- -128   aborted by user
			-- 2 error from tar - tarball not found (easy to test)
			return errNUM
		end try
	else
		return 2 -- aborted by user
	end if
end try

display dialog installComplete buttons {OKLabel} default button 1

(*

 This file is part of the LibreOffice project.

 This Source Code Form is subject to the terms of the Mozilla Public
 License, v. 2.0. If a copy of the MPL was not distributed with this
 file, You can obtain one at http://mozilla.org/MPL/2.0/.

 This file incorporates work covered by the following license notice:

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements. See the NOTICE file distributed
   with this work for additional information regarding copyright
   ownership. The ASF licenses this file to you under the Apache
   License, Version 2.0 (the "License"); you may not use this file
   except in compliance with the License. You may obtain a copy of
   the License at http://www.apache.org/licenses/LICENSE-2.0 .

This script is meant to
	1) Identify installed instances of the product
	2) check whether the user has write-access (and if not
	ask for authentification)
	3) install the shipped tarball
*)

-- strings for localisations - to be meant to be replaced
-- by a makefile or similar
set OKLabel to "[OKLabel]"
set InstallLabel to "[InstallLabel]"
set AbortLabel to "[AbortLabel]"
set intro to "[IntroText1]

[IntroText2]

[IntroText3]"
set chooseMyOwn to "[ChooseMyOwnText]"
set listPrompt to "[ListPromptText]"
set chooseManual to "[ChooseManualText]"
set listOKLabel to "[ListOKLabelText]"
set listCancelLabel to "[ListCancelLabel]"
set appInvalid to "[AppInvalidText1]

[AppInvalidText2]" -- string will begin with the chosen application's name
set startInstall to "[StartInstallText1]

[StartInstallText2]"
set IdentifyQ to "[IdentifyQText]

[IdentifyQText2]"
set IdentifyYES to "[IdentifyYES]"
set IdentifyNO to "[IdentifyNO]"
set installFailed to "[InstallFailedText]"
set installComplete to "[InstallCompleteText]

[InstallCompleteText2]"

set sourcedir to (do shell script "dirname " & quoted form of POSIX path of (path to of me))

activate
display dialog intro buttons {AbortLabel, InstallLabel} default button 2

if (button returned of result) is AbortLabel then
	return 2
end if

set found_ooos_all to ""
-- command might return an error if spotlight is disabled completely
try
	set found_ooos_all to (do shell script "mdfind \"kMDItemContentType == 'com.apple.application-bundle' && kMDItemDisplayName == '[PRODUCTNAME]*' && kMDItemDisplayName != '[FULLAPPPRODUCTNAME].app'\"")
end try
set found_ooos_all to found_ooos_all & "
" & chooseMyOwn

set found_ooos_all_paragraphs to paragraphs in found_ooos_all

set found_ooos to {}
repeat with currentApp in found_ooos_all_paragraphs
	if currentApp does not start with "/Volumes" then
		copy currentApp to the end of found_ooos
	end if
end repeat

-- repeat with oneApp in found_ooos
--  display dialog oneApp
-- end repeat

-- the choice returned is of type "list"
-- Show selection dialog only if more than one or no product was found
-- The first item is an empty string, if no app was found and no app started with "/Volumes"
-- The first item is chooseMyOwn, if no app was found and at least one app started with "/Volumes"
if (get first item of found_ooos as string) is "" then
  set the choice to (choose from list found_ooos default items (get second item of found_ooos) with prompt listPrompt OK button name listOKLabel cancel button name listCancelLabel)
  if choice is false then
	  -- do nothing, the user cancelled the installation
    	return 2 --aborted by user
  else if (choice as string) is chooseMyOwn then
	  -- yeah, one needs to use "choose file", otherwise
	  -- the user would not be able to select the .app
	  set the choice to POSIX path of (choose file with prompt chooseManual of type "com.apple.application-bundle" without showing package contents and invisibles)
  end if
else if (get first item of found_ooos as string) is chooseMyOwn then
  set the choice to (choose from list found_ooos default items (get first item of found_ooos) with prompt listPrompt OK button name listOKLabel cancel button name listCancelLabel)
  if choice is false then
	  -- do nothing, the user cancelled the installation
    	return 2 --aborted by user
  else if (choice as string) is chooseMyOwn then
	  -- yeah, one needs to use "choose file", otherwise
	  -- the user would not be able to select the .app
	  set the choice to POSIX path of (choose file with prompt chooseManual of type "com.apple.application-bundle" without showing package contents and invisibles)
  end if
else if (get second item of found_ooos as string) is chooseMyOwn then
  -- set choice to found installation
  -- set the choice to (get first paragraph of found_ooos)
  set the choice to (get first item of found_ooos)
else
  set the choice to (choose from list found_ooos default items (get first item of found_ooos) with prompt listPrompt OK button name listOKLabel cancel button name listCancelLabel)
  if choice is false then
	  -- do nothing, the user cancelled the installation
    	return 2 --aborted by user
  else if (choice as string) is chooseMyOwn then
	  -- yeah, one needs to use "choose file", otherwise
	  -- the user would not be able to select the .app
	  set the choice to POSIX path of (choose file with prompt chooseManual of type "com.apple.application-bundle" without showing package contents and invisibles)
  end if
end if

-- now only check whether the path is really from [PRODUCTNAME]
try
	do shell script "grep '<string>[PRODUCTNAME] [PRODUCTVERSION]'   " & quoted form of (choice as string) & "/Contents/Info.plist"
on error
	display dialog (choice as string) & appInvalid buttons {InstallLabel} default button 1 with icon 0
	return 3 --wrong target-directory
end try

(*
display dialog startInstall buttons {AbortLabel, InstallLabel} default button 2

if (button returned of result) is AbortLabel then
	return 2
end if
*)

set tarCommand to "/usr/bin/tar -C " & quoted form of (choice as string) & " -xjf " & quoted form of sourcedir & "/tarball.tar.bz2"
try
	do shell script tarCommand

on error errMSG number errNUM
	display dialog IdentifyQ buttons {IdentifyYES, IdentifyNO} with icon 2
	if (button returned of result) is IdentifyYES then
		try
			do shell script tarCommand with administrator privileges
		on error errMSG number errNUM
			display dialog installFailed buttons {OKLabel} default button 1 with icon 0
			-- -60005 username/password wrong
			-- -128   aborted by user
			-- 2 error from tar - tarball not found (easy to test)
			return errNUM
		end try
	else
		return 2 -- aborted by user
	end if
end try

display dialog installComplete buttons {OKLabel} default button 1