recompile bundlet script

recompile bundlet script

reduces file size by recompiling without running

FasdUAS 1.101.10
k
l& # Current Alfred-Bundler version		@# Current Alfred-Bundler version

j"0bundler_versionBUNDLER_VERSION
m

devel
l%# Path to user's home directory># Path to user's home directory
j	0_home
n

1

psxp
l

I

.earsffdralis afdr
mcusr

rtyp
m

ctxt
l!"!/)# Path to Alfred-Bundler's root directory"##R# Path to Alfred-Bundler's root directory $%
$j&0bundler_dirBUNDLER_DIR
&b'(
'b)*
)l+
+o	0_home
*m,,--Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-
(o"0bundler_versionBUNDLER_VERSION%./
.j00	cache_dir	CACHE_DIR
0b12
1b34
3l5
5o	0_home
4m6677Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-
2o"0bundler_versionBUNDLER_VERSION/89
8l9:;
:l;<=
<l>?> MAIN API FUNCTION ?@@& MAIN API FUNCTION =AB
AlBCD
Ci EF
EI0load_bundler
Fk/GGHI
HlJKJ Load `AlfredBundler.scpt` from the Alfred-Bundler directory as a script object.
	If the Alfred-Bundler directory does not exist, install it (using `_bootstrap()`).

	:returns: the script object of `AlfredBundler.scpt`
	:rtype: ``script object``

	KLL Load `AlfredBundler.scpt` from the Alfred-Bundler directory as a script object.
	If the Alfred-Bundler directory does not exist, install it (using `_bootstrap()`).

	:returns: the script object of `AlfredBundler.scpt`
	:rtype: ``script object``

	IMN
MlOPO,&# Check if Alfred-Bundler is installedPQQL# Check if Alfred-Bundler is installedNRS
RZTU
T>VW
Vl
X
Xn
YZ
YI
[0_folder_exists[\
\o0bundler_dirBUNDLER_DIR
Zf
Wm

boovtrue
Uk]]^_
^l`a`# install it if notabb&# install it if not_c
cnde
dI0
_bootstrap
efSfg
fIh
.sysodelanullnmbr
hmii?gjk
jllml?9# Path to `AlfredBundler.scpt` in Alfed-Bundler directorymnnr# Path to `AlfredBundler.scpt` in Alfed-Bundler directorykop
or(qr
ql&s
sb&tu
to$0bundler_dirBUNDLER_DIR
um$%vvww6/bundler/AlfredBundler.scpt
ro0
as_bundlerpxy
xl))z{z# Return script object{||,# Return script objecty}
}L)/~
~I).
.sysoloadscptfile
o)*0
as_bundlerD
l
l AUTO-DOWNLOAD BUNDLER . AUTO-DOWNLOAD BUNDLER
l
i!$
I0
_bootstrap
k
l`Z Check if bundler bash bundlet is installed and install it if not.

	:returns: ``None``

	 Check if bundler bash bundlet is installed and install it if not.

	:returns: ``None``

l"# Ask to install the Bundler8# Ask to install the Bundler
Q
n
I0_install_confirmation
f
R
.ascrerr ********
k
l71# Cannot continue to install the bundler, so stopb# Cannot continue to install the bundler, so stop
L
m
boovfals
l# Download the bundler,# Download the bundler
r)
J'
b
b
mhhttps://github.com/shawnrice/alfred-bundler/archive/
o"0bundler_versionBUNDLER_VERSION
m.zip
b%
b#
mfhttps://bitbucket.org/shawnrice/alfred-bundler/get/
o""0bundler_versionBUNDLER_VERSION
m#$.zip
o0urlsURLs
l**@:# Save Alfred-Bundler zipfile to this location temporarilyt# Save Alfred-Bundler zipfile to this location temporarily
r*5
b*3
l*1
n*1
1/1

strq
o*/0	cache_dir	CACHE_DIR
m12,/installer/bundler.zip
o0_zipfile
X6h
kFc
rFU
lFS
IFS
.sysoexecTEXTTEXT
bFO
bFM
bFK
bFI
mFGZcurl -fsSL --create-dirs --connect-timeout 5
oGH0_url
mIJ -o
oKL0_zipfile
mMN && echo $?
o0_status
ZVc
=V[
oVW0_status
mWZ0
S^_0_url
o9:0urlsURLs
lii## Could not download the file:# Could not download the file
Zi
>in
oij0_status
mjm0
Rq}
.ascrerr ********
my|NCould not download bundler install file

errn
mux
l	LF# Ensure directory tree already exists for bundler to be moved into it

# Ensure directory tree already exists for bundler to be moved into it
n

I0
_check_dir
o0bundler_dirBUNDLER_DIR
f
l;5# Unzip the bundler and move it to its data directoryj# Unzip the bundler and move it to its data directory
r
b
b
b
m  !!cd
l"
"n#$
#1

strq
$o0	cache_dir	CACHE_DIR
m%%&&l; cd installer; unzip -qo bundler.zip; mv ./*/bundler
l'
'n()
(1~
~
strq
)o}}0bundler_dirBUNDLER_DIR
o||0_cmd*+
*I{,z
{.sysoexecTEXTTEXT
,oyy0_cmdz+-.
-lx/0x/QK# Wait until bundler is fully unzipped and written to disk before finishing011# Wait until bundler is fully unzipped and written to disk before finishing.23
2r45
4l6wv
6b78
7ouu0bundler_dirBUNDLER_DIR
8m99::6/bundler/AlfredBundler.scptwv
5ott0
as_bundler3;<
;V=>
=Is?r
s.sysodelanullnmbr
?m@@?ər
>HA
AlBqp
BnCD
CIoEno0_path_existsEFm
Foll0
as_bundlermn
Dfqp<Gk
GLjjkHI
HlihgihgIJK
JlfLMfL;5# Function to get confirmation to install the bundlerMNNj# Function to get confirmation to install the bundlerKOP
Oi%(QR
QIedce0_install_confirmationdc
RkSSTU
TlbVWbV Ask user for permission to install Alfred-Bundler.
	Allow user to go to website for more information, or even to cancel download.

	:returns: ``True`` or raises Error

	WXXV Ask user for permission to install Alfred-Bundler.
	Allow user to go to website for more information, or even to cancel download.

	:returns: ``True`` or raises Error

	UYZ
Yla[\a[0*# Get path to workflow's `info.plist` file\]]T# Get path to workflow's `info.plist` fileZ^_
^r	`a
`bbc
bnde
dI`_^`0_pwd_^
ef
cmffgginfo.plist
ao]]
0_plist_hi
hl

\jk\j5/# Get name of workflow's from `info.plist` filekll^# Get name of workflow's from `info.plist` fileimn
mr
op
ob
qr
qb

st
sm
uuvvT/usr/libexec/PlistBuddy -c 'Print :name' '
to[[
0_plist
rm
wwxx'
poZZ0_cmdnyz
yr{|
{IY}X
Y.sysoexecTEXTTEXT
}oWW0_cmdX
|oVV	0_namez~
~lUU60# Get workflow's icon, or default to system icon`# Get workflow's icon, or default to system icon
r#
b!
n
ITSRT0_pwdSR
f
m icon.png
oQQ	0_icon
r$,
n$*
I%*POP0_check_iconN
o%&MM	0_iconNO
f$%
oLL	0_icon
l--KK/)# Prepare explanation text for dialog boxR# Prepare explanation text for dialog box
r-6
b-4
b-2
b-0
o-.JJ	0_name
m./ needs to install additional components, which will be placed in the Alfred storage directory and will not interfere with your system.

You may be asked to allow some components to run, depending on your security settings.

You can decline this installation, but
o01II	0_name
m23 may not work without them. There will be a slight delay after accepting.
oHH	0_text
l77GFEGFE
r7Y
n7W
1SWD
D
bhit
l7SCB
I7SA
A.sysodlogaskrTEXT
o78@@	0_text?
?
btns
J9>
m9:More Info
m:;Cancel>
m;<Proceed>=
=
dflt
m?@<<;
;
appr
bAD
mABSetup
oBC::	0_name98
9
disp
4GM7
7
psxf
oKL66	0_icon8CB
o55
0	_response
lZZ440*# If permission granted, continue downloadT# If permission granted, continue download
ZZh32
=Z_
oZ[11
0	_response
m[^Proceed
Lbd
mbc0
0boovtrue32
lii//60# If more info requested, open webpage and error`# If more info requested, open webpage and error
Zi.-
=in
oij,,
0	_response
mjmMore Info
kq
Oq
Iw~+*
+.GURLGURLnullTEXT
mwzhttps://github.com/shawnrice/alfred-bundler/wiki/What-is-the-Alfred-Bundler*
mqtsevsalisMacintosh HDH+ҍKSystem Events.appU'A	 CoreServices*A9ҍKҍHҍG=Macintosh HD:System:Library:CoreServices:System Events.app$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/)
R(
(.ascrerr ********
mFUser looked sought more information'&
'
errn
m%%&).-
l$$,&# If permission denied, stop and errorL# If permission denied, stop and error#
Z"!
=
o
0	_response
mCancel
R
.ascrerr ********
m4User canceled installation

errn
m"!#P
l
l

l
 HELPER HANDLERS
" HELPER HANDLERS
l
i),
I0_pwd
k8
l Get path to "present working directory", i.e. the workflow's root directory.

	:returns: Path to this script's parent directory
	:rtype: ``string`` (POSIX path)

	J Get path to "present working directory", i.e. the workflow's root directory.

	:returns: Path to this script's parent directory
	:rtype: ``string`` (POSIX path)

l

=7# Save default AS delimiters, and set delimiters to "/"n# Save default AS delimiters, and set delimiters to "/" !
 r"#
"J$$%&
%n'(
'1

txdl
(1

ascr&)

)m**++/

#J,,-.
-o		0astidASTID./
/n01
01

txdl
11

ascr!23
2l454,&# Get POSIX path of script's directory566L# Get POSIX path of script's directory378
7r/9:
9b-;<
;l+=
=c+>?
>n)@A
@7)BC

citm
Bm#%
Cm&(
AlD
DnEF
E1

psxp
FlG
GIH
.earsffdralis afdr
Hf
?m)*

TEXT
<m+,IIJJ/
:o	0_path8KL
Kl00MNM.(# Reset AS delimiters to original valuesNOOP# Reset AS delimiters to original valuesLPQ
Pr05RS
Ro010astidASTID
SnTU
T124

txdl
U112

ascrQV
VL68W
Wo67	0_pathXY
XlYZ[
Zi-0\]
\I^0_prepare_cmd^_
_o0_cmd
]k``ab
alcdc,& Ensure shell `_cmd` is working from the property directory.
	For testing purposes, it also sets the `AB_BRANCH` environmental variable.

	:param _cmd: Shell command to be run in `do shell script`
	:type _cmd: ``string``
	:returns: Shell command with `pwd` set properly
	:rtype: ``string``

	deeL Ensure shell `_cmd` is working from the property directory.
	For testing purposes, it also sets the `AB_BRANCH` environmental variable.

	:param _cmd: Shell command to be run in `do shell script`
	:type _cmd: ``string``
	:returns: Shell command with `pwd` set properly
	:rtype: ``string``

	bfg
flhih93# Ensure `pwd` is properly quoted for shell commandijjf# Ensure `pwd` is properly quoted for shell commandgkl
kr	mn
mnop
o1

strq
plq
qnrs
rI0_pwd
sf
no0pwdltu
tl

vwv& # Declare environmental variablewxx@# Declare environmental variableuyz
yl

{|{%#TODO: remove for final release|}}>#TODO: remove for final releasez~
~r

m
0export AB_BRANCH=devel;
o0testing_var
l71# return shell script where `pwd` is properly setb# return shell script where `pwd` is properly set
L
b
b
b
b
o0testing_var
mcd
o0pwd
m; bash
o0_cmd[
l
i14
I0_check_icon
o	0_icon
k
l Check if `_icon` exists, and if not revert to system download icon.

	:returns: POSIX path to `_icon`
	:rtype: ``string`` (POSIX path)

	 Check if `_icon` exists, and if not revert to system download icon.

	:returns: POSIX path to `_icon`
	:rtype: ``string`` (POSIX path)

Q
k
c
4

psxf
o	0_icon
m

alis
L

o
	0_icon
R
.ascrerr ********
L
m/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/SidebarDownloadsFolder.icns
l
i58
I0
_check_dir
o0_folder
k
l Check if `_folder` exists, and if not create it, including any sub-directories.

	:returns: POSIX path to `_folder`
	:rtype: ``string`` (POSIX path)

	0 Check if `_folder` exists, and if not create it, including any sub-directories.

	:returns: POSIX path to `_folder`
	:rtype: ``string`` (POSIX path)

Z
H
n
I0_folder_exists
o0_folder
f
I

.sysoexecTEXTTEXT
b

m
mkdir -p
l
n
1

strq
o0_folder
L
o0_folder
l
l
i9<
I0_folder_exists
o0_folder
k
l Return ``true`` if `_folder` exists, else ``false``

	:param _folder: Full path to directory
	:type _folder: ``string`` (POSIX path)
	:returns: ``Boolean``

	> Return ``true`` if `_folder` exists, else ``false``

	:param _folder: Full path to directory
	:type _folder: ``string`` (POSIX path)
	:returns: ``Boolean``

Z
n
I0_path_exists
o0_folder
f
O
L

l

=

n

m

pcls
l

4

ditm
o0_folder
m

cfol
m
sevsalisMacintosh HDH+ҍKSystem Events.appU'A	 CoreServices*A9ҍKҍHҍG=Macintosh HD:System:Library:CoreServices:System Events.app$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/
L
m
boovfals
l
i=@
I0_path_exists
o	0_path
kY
l Return ``true`` if `_path` exists, else ``false``

	:param _path: Any POSIX path (file or folder)
	:type _path: ``string`` (POSIX path)
	:returns: ``Boolean``

	D Return ``true`` if `_path` exists, else ``false``

	:param _path: Any POSIX path (file or folder)
	:type _path: ``string`` (POSIX path)
	:returns: ``Boolean``

Z

G

=
o	0_path
m

msng

n
I
0	_is_empty
o	0_path
f
L
m
boovfals
QY
kO
Z)
=
n !
 m

pcls
!o	0_path
m

alis
L#%"
"m#$
boovtrue#~
#Z*O$%&'
$E*-()
(o*+}}	0_path
)m+,**++:
%k08,,-.
-405|/
|
alis
/o23{{	0_path.0z
0L681
1m67y
yboovtruez&23
2E;>45
4o;<xx	0_path
5m<=6677/38w
8kAJ99:;
:cAG<=
<4AEv>
v
psxf
>oCDuu	0_path
=mEFt
t
alis;?s
?LHJ@
@mHIr
rboovtruesw
'LMOA
AmMNq
qboovfals~
RpBo
p.ascrerr ********
Bonn0msgo
LWYC
CmWXm
mboovfalsDE
DllkjlkjEFG
FiADHI
HIiJhi
0	_is_emptyJKg
Koff0_objgh
Ik(LLMN
MleOPeO Return ``true`` if `_obj ` is empty, else ``false``

	:param _obj: Any Applescript type
	:type _obj: (optional)
	:returns: ``Boolean``

	PQQ Return ``true`` if `_obj ` is empty, else ``false``

	:param _obj: Any Applescript type
	:type _obj: (optional)
	:returns: ``Boolean``

	NRS
RldTUdT!# Is `_obj ` a ``Boolean``?UVV6# Is `_obj ` a ``Boolean``?SWX
WZYZcb
YE[\
[J]]^_
^ma
aboovtrue_``
`m_
_boovfals`
\o^^0_obj
ZL	a
am
]
]boovfalscbXbc
bl\de\d'!# Is `_obj ` a ``missing value``?effB# Is `_obj ` a ``missing value``?cgh
gZij[Z
i=kl
koYY0_obj
lmX
X
msng
jLm
mmW
Wboovtrue[Zhno
nlVpqVp"# Is `_obj ` a empty string?qrr8# Is `_obj ` a empty string?osU
sL(t
t='uv
un%wx
w1#%T
T
leng
xl#ySR
yn#z{
zI#Q|PQ	0_trim|}O
}oNN0_objOP
{fSR
vm%&MMUG~
~lLKJLKJI
iEH
IHGH	0_trimF
oEE0_strFG
k
lDD Remove white space from beginning and end of `_str`

	:param _str: A text string
	:type _str: ``string``
	:returns: trimmed string

	 Remove white space from beginning and end of `_str`

	:param _str: A text string
	:type _str: ``string``
	:returns: trimmed string

Z CB
G
G
>
n
mA
A
pcls
o@@0_str
m?
?
ctxt
>

n
m	>
>
pcls
o	==0_str
m<
<
TEXT
=
o;;0_str
m:
:
msng
L
o990_strCB
Z!-87
=!$
o!"660_str
m"#
L')
o'(550_str87
V.W
Q6R
r9H
c9F
n9D
7:D4
4
cobj
m>@33
mAC22
o9:110_str
mDE0
0
ctxt
o//0_str
R.-
..ascrerr ********
o,,0msg-
LPR
mPQ
C25
o23++0_str
m34
VX
Q`|
rcr
ccp
ncn
7dn*
*
cobj
mhj))
mkm((
ocd''0_str
mno&
&
ctxt
o%%0_str
R$#"
$.ascrerr ********#"
Lz|
mz{
D\_
o\]!!0_str
m]^
L
o0_str I
"0bundler_versionBUNDLER_VERSION	0_home0bundler_dirBUNDLER_DIR0	cache_dir	CACHE_DIR0load_bundler0
_bootstrap0_install_confirmation0_pwd0_prepare_cmd0_check_icon0
_check_dir0_folder_exists0_path_exists
0	_is_empty	0_trim"/Users/smargheim//Users/smargheim/Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-devel/Users/smargheim/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-develF
0load_bundler

0
as_bundler	iv	0_folder_exists0
_bootstrap
.sysodelanullnmbr
.sysoloadscptfile0)bk+e
)j+YhOjOb%EOj0
_bootstrap0urlsURLs0_zipfile0_url0_status0_cmd0
as_bundler %9@0_install_confirmation

strq

kocl

cobj
.corecnte********
.sysoexecTEXTTEXT

errn0
_check_dir0_path_exists
.sysodelanullnmbr
)j+W	XfOb%%b%%lvEOb,%EO1[lkh%%%%jEOaYh[OYOa)aalaYhO)bk+Oab,%a%b,%EOjOba%EOh)k+ajYOhR0_install_confirmation
0_plist0_cmd	0_name	0_icon	0_text
0	_responsefuw0_pwd
.sysoexecTEXTTEXT0_check_icon

btns

dflt

appr

disp

psxf
.sysodlogaskrTEXT

bhit
.GURLGURLnullTEXT

errn)j+%EO%%EOjEO)j+%EO)k+EO%%%EOmvm%a*a/aa,EOaeYhOa a	ajUO)aalaYhOa)aalaYh0_pwd0astidASTID	0_path
*I

ascr

txdl

cobj
.earsffdralis afdr

psxp

citm

TEXT9,lvE[k/EZ[l/,FZO)j,[\[Zk\Z2&%EO,FO]0_prepare_cmd0_cmd0_cmd0pwd0testing_var0_pwd

strq)j+,EOEO%%%%0_check_icon	0_icon	0_icon

psxf

alis*/&OW	X0
_check_dir0_folder0_folder0_folder_exists

strq
.sysoexecTEXTTEXT)k+,%jYhO0_folder_exists0_folder0_folder0_path_exists

ditm

pcls

cfol)k+*/,UYhOf0_path_exists	0_path	0_path0msg
*6

msng
0	_is_empty

bool

pcls

alis

psxf0msgZ

)k+&fYhO9,eYhO
*/EOeY*/&OeYfW	X	fI
0	_is_empty0_obj0_obj

msng	0_trim

leng)eflvfYhOeYhO)k+,j	0_trim~~0_str}|}0_str|0msg{zyxwvutsr
{
pcls
z
ctxt
y
TEXT
x
bool
w
msng
v
cobju0msgtsr,
	,&
&YhOYhO(h[\[Zl\Zi2&EW	X	[OYO(h[\[Zk\Z2&EW	X
	[OYOascrޭ

FasdUAS 1.101.10
k
l& # Current Alfred-Bundler version		@# Current Alfred-Bundler version

j"0bundler_versionBUNDLER_VERSION
m

devel
l%# Path to user's home directory># Path to user's home directory
j	0_home
n

1

psxp
l

I

.earsffdralis afdr
mcusr

rtyp
m

ctxt
l!"!/)# Path to Alfred-Bundler's root directory"##R# Path to Alfred-Bundler's root directory $%
$j&0bundler_dirBUNDLER_DIR
&b'(
'b)*
)l+
+o	0_home
*m,,--Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-
(o"0bundler_versionBUNDLER_VERSION%./
.j00	cache_dir	CACHE_DIR
0b12
1b34
3l5
5o	0_home
4m6677Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-
2o"0bundler_versionBUNDLER_VERSION/89
8l9:;
:l<=< MAIN API FUNCTION =>>& MAIN API FUNCTION ;?@
?l@AB
Ai CD
CI0load_bundler
Dk/EEFG
FlHIH Load `AlfredBundler.scpt` from the Alfred-Bundler directory as a script object.
	If the Alfred-Bundler directory does not exist, install it (using `_bootstrap()`).

	:returns: the script object of `AlfredBundler.scpt`
	:rtype: ``script object``

	IJJ Load `AlfredBundler.scpt` from the Alfred-Bundler directory as a script object.
	If the Alfred-Bundler directory does not exist, install it (using `_bootstrap()`).

	:returns: the script object of `AlfredBundler.scpt`
	:rtype: ``script object``

	GKL
KlMNM,&# Check if Alfred-Bundler is installedNOOL# Check if Alfred-Bundler is installedLPQ
PZRS
R>TU
Tl
V
Vn
WX
WI
Y0_folder_existsYZ
Zo0bundler_dirBUNDLER_DIR
Xf
Um

boovtrue
Sk[[\]
\l^_^# install it if not_``&# install it if not]a
anbc
bI0
_bootstrap
cfQde
dIf
.sysodelanullnmbr
fmgg?ehi
hljkj?9# Path to `AlfredBundler.scpt` in Alfed-Bundler directorykllr# Path to `AlfredBundler.scpt` in Alfed-Bundler directoryimn
mr(op
ol&q
qb&rs
ro$0bundler_dirBUNDLER_DIR
sm$%ttuu6/bundler/AlfredBundler.scpt
po0
as_bundlernvw
vl))xyx# Return script objectyzz,# Return script objectw{
{L)/|
|I).}
.sysoloadscptfile
}o)*0
as_bundlerB~
~l
l AUTO-DOWNLOAD BUNDLER . AUTO-DOWNLOAD BUNDLER
l
i!$
I0
_bootstrap
k
l`Z Check if bundler bash bundlet is installed and install it if not.

	:returns: ``None``

	 Check if bundler bash bundlet is installed and install it if not.

	:returns: ``None``

l"# Ask to install the Bundler8# Ask to install the Bundler
Q
n
I0_install_confirmation
f
R
.ascrerr ********
k
l71# Cannot continue to install the bundler, so stopb# Cannot continue to install the bundler, so stop
L
m
boovfals
l# Download the bundler,# Download the bundler
r)
J'
b
b
mhhttps://github.com/shawnrice/alfred-bundler/archive/
o"0bundler_versionBUNDLER_VERSION
m.zip
b%
b#
mfhttps://bitbucket.org/shawnrice/alfred-bundler/get/
o""0bundler_versionBUNDLER_VERSION
m#$.zip
o0urlsURLs
l**@:# Save Alfred-Bundler zipfile to this location temporarilyt# Save Alfred-Bundler zipfile to this location temporarily
r*5
b*3
l*1
n*1
1/1

strq
o*/0	cache_dir	CACHE_DIR
m12,/installer/bundler.zip
o0_zipfile
X6h
kFc
rFU
lFS
IFS
.sysoexecTEXTTEXT
bFO
bFM
bFK
bFI
mFGZcurl -fsSL --create-dirs --connect-timeout 5
oGH0_url
mIJ -o
oKL0_zipfile
mMN && echo $?
o0_status
ZVc
=V[
oVW0_status
mWZ0
S^_0_url
o9:0urlsURLs
lii## Could not download the file:# Could not download the file
Zi
>in
oij0_status
mjm0
Rq}
.ascrerr ********
my|NCould not download bundler install file

errn
mux
lLF# Ensure directory tree already exists for bundler to be moved into it# Ensure directory tree already exists for bundler to be moved into it

	n
I
0
_check_dir

o0bundler_dirBUNDLER_DIR
f

l;5# Unzip the bundler and move it to its data directoryj# Unzip the bundler and move it to its data directory
r
b
b
b
mcd
l
 n!"
!1

strq
"o0	cache_dir	CACHE_DIR
m##$$l; cd installer; unzip -qo bundler.zip; mv ./*/bundler
l%
%n&'
&1

strq
'o0bundler_dirBUNDLER_DIR
o0_cmd()
(I~*}
~.sysoexecTEXTTEXT
*o||0_cmd})+,
+l{-.{-QK# Wait until bundler is fully unzipped and written to disk before finishing.//# Wait until bundler is fully unzipped and written to disk before finishing,01
0r23
2l4zy
4b56
5oxx0bundler_dirBUNDLER_DIR
6m77886/bundler/AlfredBundler.scptzy
3oww0
as_bundler19:
9V;<
;Iv=u
v.sysodelanullnmbr
=m>>?əu
<H?
?l@ts
@nAB
AIrCqr0_path_existsCDp
Dooo0
as_bundlerpq
Bfts:EF
ElnGHnG,&#TODO: remove bundler files from cacheHIIL#TODO: remove bundler files from cacheFJm
JLllmKL
KlkjikjiLMN
MlhOPhO;5# Function to get confirmation to install the bundlerPQQj# Function to get confirmation to install the bundlerNRS
Ri%(TU
TIgfeg0_install_confirmationfe
UkVVWX
WldYZdY Ask user for permission to install Alfred-Bundler.
	Allow user to go to website for more information, or even to cancel download.

	:returns: ``True`` or raises Error

	Z[[V Ask user for permission to install Alfred-Bundler.
	Allow user to go to website for more information, or even to cancel download.

	:returns: ``True`` or raises Error

	X\]
\lc^_c^0*# Get path to workflow's `info.plist` file_``T# Get path to workflow's `info.plist` file]ab
ar	cd
cbef
engh
gIba`b0_pwda`
hf
fmiijjinfo.plist
do__
0_plistbkl
kl

^mn^m5/# Get name of workflow's from `info.plist` filenoo^# Get name of workflow's from `info.plist` filelpq
pr
rs
rb
tu
tb

vw
vm
xxyyT/usr/libexec/PlistBuddy -c 'Print :name' '
wo]]
0_plist
um
zz{{'
so\\0_cmdq|}
|r~
~I[Z
[.sysoexecTEXTTEXT
oYY0_cmdZ
oXX	0_name}
lWW60# Get workflow's icon, or default to system icon`# Get workflow's icon, or default to system icon
r#
b!
n
IVUTV0_pwdUT
f
m icon.png
oSS	0_icon
r$,
n$*
I%*RQR0_check_iconP
o%&OO	0_iconPQ
f$%
oNN	0_icon
l--MM/)# Prepare explanation text for dialog boxR# Prepare explanation text for dialog box
r-6
b-4
b-2
b-0
o-.LL	0_name
m./ needs to install additional components, which will be placed in the Alfred storage directory and will not interfere with your system.

You may be asked to allow some components to run, depending on your security settings.

You can decline this installation, but
o01KK	0_name
m23 may not work without them. There will be a slight delay after accepting.
oJJ	0_text
l77IHGIHG
r7Y
n7W
1SWF
F
bhit
l7SED
I7SC
C.sysodlogaskrTEXT
o78BB	0_textA
A
btns
J9>
m9:More Info
m:;Cancel@
m;<Proceed@?
?
dflt
m?@>>=
=
appr
bAD
mABSetup
oBC<<	0_name;:
;
disp
4GM9
9
psxf
oKL88	0_icon:ED
o77
0	_response
lZZ660*# If permission granted, continue downloadT# If permission granted, continue download
ZZh54
=Z_
oZ[33
0	_response
m[^Proceed
Lbd
mbc2
2boovtrue54
lii1160# If more info requested, open webpage and error`# If more info requested, open webpage and error
Zi0/
=in
oij..
0	_response
mjmMore Info
kq
Oq
Iw~-,
-.GURLGURLnullTEXT
mwzhttps://github.com/shawnrice/alfred-bundler/wiki/What-is-the-Alfred-Bundler,
mqtsevsalisMacintosh HDH+ҍKSystem Events.appU'A	 CoreServices*A9ҍKҍHҍG=Macintosh HD:System:Library:CoreServices:System Events.app$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/+
R*
*.ascrerr ********
mFUser looked sought more information)(
)
errn
m''(+0/
l&&,&# If permission denied, stop and errorL# If permission denied, stop and error%
Z$#
=
o""
0	_response
mCancel
R!
!.ascrerr ********
m4User canceled installation

errn
m$#%S

	l

l

l HELPER HANDLERS " HELPER HANDLERS
l
i),
I0_pwd
k8
l Get path to "present working directory", i.e. the workflow's root directory.

	:returns: Path to this script's parent directory
	:rtype: ``string`` (POSIX path)

	J Get path to "present working directory", i.e. the workflow's root directory.

	:returns: Path to this script's parent directory
	:rtype: ``string`` (POSIX path)

l ! =7# Save default AS delimiters, and set delimiters to "/"!""n# Save default AS delimiters, and set delimiters to "/"#$
#r%&
%J''()
(n*+
*1

txdl
+1

ascr),
,m--../
&J//01
0o0astidASTID12

2n34
31

txdl
41

ascr
$56
5l787,&# Get POSIX path of script's directory899L# Get POSIX path of script's directory6:;
:r/<=
<b->?
>l+@
@c+AB
An)CD
C7)EF

citm
Em#%
Fm&(
DlG
GnHI
H1

psxp
IlJ
JIK
.earsffdralis afdr
Kf
Bm)*

TEXT
?m+,LLMM/
=o	0_path;NO
Nl00PQP.(# Reset AS delimiters to original valuesQRRP# Reset AS delimiters to original valuesOST
Sr05UV
Uo010astidASTID
VnWX
W124

txdl
X112

ascrTY
YL68Z
Zo67	0_path[\
[l\]^
]i-0_`
_Ia0_prepare_cmdab
bo0_cmd
`kccde
dlfgf,& Ensure shell `_cmd` is working from the property directory.
	For testing purposes, it also sets the `AB_BRANCH` environmental variable.

	:param _cmd: Shell command to be run in `do shell script`
	:type _cmd: ``string``
	:returns: Shell command with `pwd` set properly
	:rtype: ``string``

	ghhL Ensure shell `_cmd` is working from the property directory.
	For testing purposes, it also sets the `AB_BRANCH` environmental variable.

	:param _cmd: Shell command to be run in `do shell script`
	:type _cmd: ``string``
	:returns: Shell command with `pwd` set properly
	:rtype: ``string``

	eij
ilklk93# Ensure `pwd` is properly quoted for shell commandlmmf# Ensure `pwd` is properly quoted for shell commandjno
nr	pq
pnrs
r1

strq
slt
tnuv
uI0_pwd
vf
qo0pwdowx
wl

yzy& # Declare environmental variablez{{@# Declare environmental variablex|}
|l

~~%#TODO: remove for final release>#TODO: remove for final release}
r

m
0export AB_BRANCH=devel;
o0testing_var
l71# return shell script where `pwd` is properly setb# return shell script where `pwd` is properly set
L
b
b
b
b
o0testing_var
mcd
o0pwd
m; bash
o0_cmd^
l
i14
I0_check_icon
o	0_icon
k
l Check if `_icon` exists, and if not revert to system download icon.

	:returns: POSIX path to `_icon`
	:rtype: ``string`` (POSIX path)

	 Check if `_icon` exists, and if not revert to system download icon.

	:returns: POSIX path to `_icon`
	:rtype: ``string`` (POSIX path)

Q
k
c
4

psxf
o	0_icon
m

alis
L

o
	0_icon
R
.ascrerr ********
L
m/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/SidebarDownloadsFolder.icns
l
i58
I0
_check_dir
o0_folder
k
l Check if `_folder` exists, and if not create it, including any sub-directories.

	:returns: POSIX path to `_folder`
	:rtype: ``string`` (POSIX path)

	0 Check if `_folder` exists, and if not create it, including any sub-directories.

	:returns: POSIX path to `_folder`
	:rtype: ``string`` (POSIX path)

Z
H
n
I0_folder_exists
o0_folder
f
I

.sysoexecTEXTTEXT
b

m
mkdir -p
l
n
1

strq
o0_folder
L
o0_folder
l
l
i9<
I0_folder_exists
o0_folder
k
l Return ``true`` if `_folder` exists, else ``false``

	:param _folder: Full path to directory
	:type _folder: ``string`` (POSIX path)
	:returns: ``Boolean``

	> Return ``true`` if `_folder` exists, else ``false``

	:param _folder: Full path to directory
	:type _folder: ``string`` (POSIX path)
	:returns: ``Boolean``

Z
n
I0_path_exists
o0_folder
f
O
L

l

=

n

m

pcls
l

4

ditm
o0_folder
m

cfol
m
sevsalisMacintosh HDH+ҍKSystem Events.appU'A	 CoreServices*A9ҍKҍHҍG=Macintosh HD:System:Library:CoreServices:System Events.app$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/
L
m
boovfals
l
i=@
I0_path_exists
o	0_path
kY
l	 Return ``true`` if `_path` exists, else ``false``

	:param _path: Any POSIX path (file or folder)
	:type _path: ``string`` (POSIX path)
	:returns: ``Boolean``

D Return ``true`` if `_path` exists, else ``false``

	:param _path: Any POSIX path (file or folder)
	:type _path: ``string`` (POSIX path)
	:returns: ``Boolean``

Z

G
=
o	0_path
m

msng
n
I
0	_is_empty
o	0_path
f
L
m
boovfals
QY
kO
Z)
= !"
!n#$
#m

pcls
$o	0_path
"m

alis
 L#%%
%m#$
boovtrue&
&Z*O'()*
'E*-+,
+o*+	0_path
,m+,--..:
(k08//01
0405~2
~
alis
2o23}}	0_path13|
3L684
4m67{
{boovtrue|)56
5E;>78
7o;<zz	0_path
8m<=99::/6;y
;kAJ<<=>
=cAG?@
?4AExA
x
psxf
AoCDww	0_path
@mEFv
v
alis>Bu
BLHJC
CmHIt
tboovtrueuy
*LMOD
DmMNs
sboovfals
RrEq
r.ascrerr ********
Eopp0msgq
LWYF
FmWXo
oboovfalsGH
GlnmlnmlHIJ
IiADKL
KIkMjk
0	_is_emptyMNi
Nohh0_objij
Lk(OOPQ
PlgRSgR Return ``true`` if `_obj ` is empty, else ``false``

	:param _obj: Any Applescript type
	:type _obj: (optional)
	:returns: ``Boolean``

	STT Return ``true`` if `_obj ` is empty, else ``false``

	:param _obj: Any Applescript type
	:type _obj: (optional)
	:returns: ``Boolean``

	QUV
UlfWXfW!# Is `_obj ` a ``Boolean``?XYY6# Is `_obj ` a ``Boolean``?VZ[
ZZ\]ed
\E^_
^J``ab
amc
cboovtruebcb
cma
aboovfalsb
_o``0_obj
]L	d
dm
_
_boovfalsed[ef
el^gh^g'!# Is `_obj ` a ``missing value``?hiiB# Is `_obj ` a ``missing value``?fjk
jZlm]\
l=no
no[[0_obj
omZ
Z
msng
mLp
pmY
Yboovtrue]\kqr
qlXstXs"# Is `_obj ` a empty string?tuu8# Is `_obj ` a empty string?rvW
vL(w
w='xy
xn%z{
z1#%V
V
leng
{l#|UT
|n#}~
}I#SRS	0_trimQ
oPP0_objQR
~fUT
ym%&OOWJ
lNMLNMLK
iEH
IJIJ	0_trimH
oGG0_strHI
k
lFF Remove white space from beginning and end of `_str`

	:param _str: A text string
	:type _str: ``string``
	:returns: trimmed string

	 Remove white space from beginning and end of `_str`

	:param _str: A text string
	:type _str: ``string``
	:returns: trimmed string

Z ED
G
G
>
n
mC
C
pcls
oBB0_str
mA
A
ctxt
>

n
m	@
@
pcls
o	??0_str
m>
>
TEXT
=
o==0_str
m<
<
msng
L
o;;0_strED
Z!-:9
=!$
o!"880_str
m"#
L')
o'(770_str:9
V.W
Q6R
r9H
c9F
n9D
7:D6
6
cobj
m>@55
mAC44
o9:330_str
mDE2
2
ctxt
o110_str
R0/
0.ascrerr ********
o..0msg/
LPR
mPQ
C25
o23--0_str
m34
VX
Q`|
rcr
ccp
ncn
7dn,
,
cobj
mhj++
mkm**
ocd))0_str
mno(
(
ctxt
o''0_str
R&%$
&.ascrerr ********%$
Lz|
mz{
D\_
o\]##0_str
m]^ "
L
o!!0_str"K
 "0bundler_versionBUNDLER_VERSION	0_home0bundler_dirBUNDLER_DIR0	cache_dir	CACHE_DIR0load_bundler0
_bootstrap0_install_confirmation0_pwd0_prepare_cmd0_check_icon0
_check_dir0_folder_exists0_path_exists
0	_is_empty	0_trim"/Users/smargheim//Users/smargheim/Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-devel/Users/smargheim/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-develD
0load_bundler0
as_bundler
g	t0_folder_exists
0
_bootstrap
	.sysodelanullnmbr
.sysoloadscptfile
0)bk+e
)j+YhOjOb%EOj0
_bootstrap0urlsURLs0_zipfile0_url0_status0_cmd0
as_bundler#7>0_install_confirmation

strq

kocl

cobj
.corecnte********
.sysoexecTEXTTEXT

errn0
_check_dir0_path_exists
.sysodelanullnmbr
)j+W	XfOb%%b%%lvEOb,%EO1[lkh%%%%jEOaYh[OYOa)aalaYhO)bk+Oab,%a%b,%EOjOba%EOh)k+ajYOhU0_install_confirmation
0_plist0_cmd	0_name	0_icon	0_text
0	_responseixz0_pwd
.sysoexecTEXTTEXT0_check_icon

btns

dflt

appr

disp

psxf
.sysodlogaskrTEXT

bhit
.GURLGURLnullTEXT

errn)j+%EO%%EOjEO)j+%EO)k+EO%%%EOmvm%a*a/aa,EOaeYhOa a	ajUO)aalaYhOa)aalaYh0_pwd0astidASTID	0_path
-L

ascr

txdl

cobj
.earsffdralis afdr

psxp

citm

TEXT9,lvE[k/EZ[l/,FZO)j,[\[Zk\Z2&%EO,FO`0_prepare_cmd0_cmd0_cmd0pwd0testing_var0_pwd

strq)j+,EOEO%%%%0_check_icon	0_icon	0_icon

psxf

alis*/&OW	X0
_check_dir0_folder0_folder0_folder_exists

strq
.sysoexecTEXTTEXT)k+,%jYhO0_folder_exists0_folder0_folder0_path_exists

ditm

pcls

cfol)k+*/,UYhOf0_path_exists	0_path	0_path0msg
-9

msng
0	_is_empty

bool

pcls

alis

psxf0msgZ

)k+&fYhO9,eYhO
*/EOeY*/&OeYfW	X	fL
0	_is_empty0_obj0_obj

msng	0_trim

leng)eflvfYhOeYhO)k+,j	0_trim0_str~0_str~0msg}|{zyxwvut
}
pcls
|
ctxt
{
TEXT
z
bool
y
msng
x
cobjw0msgvut,
	,&
&YhOYhO(h[\[Zl\Zi2&EW	X	[OYO(h[\[Zk\Z2&EW	X
	[OYOascrޭ


Revert ":wrench: notifyme script: use HyperTerm instead of iTerm"

This reverts commit 17cba7446fc7fffc5bb5d09646fc11f3a5a68bdc.

#!/usr/bin/env osascript

-- see http://frantic.im/notify-on-completion
on run argv
  tell application "System Events"
    set frontApp to name of first application process whose frontmost is true
    if frontApp is not "iTerm2" then
      set notifTitle to item 1 of argv
      set notifBody to "succeded"
      set errorCode to item 2 of argv
      if errorCode is not "0"
        set notifBody to "failed with error code " & errorCode
      end if
      display notification notifBody with title notifTitle
    end if
  end tell
end run

#!/usr/bin/env osascript

-- see http://frantic.im/notify-on-completion
on run argv
  tell application "System Events"
    set frontApp to name of first application process whose frontmost is true
    if frontApp is not "HyperTerm" then
      set notifTitle to item 1 of argv
      set notifBody to "succeded"
      set errorCode to item 2 of argv
      if errorCode is not "0"
        set notifBody to "failed with error code " & errorCode
      end if
      display notification notifBody with title notifTitle
    end if
  end tell
end run


♻  Make minor improvements

♻  Make minor improvements

tell application "Terminal"

    local allOpenedWindows
    local initialOpenedWindows
    local windowID

    set themeFileName to "solarized_dark.terminal"
    set themeName to "Solarized Dark"

    (* Store the IDs of all the open terminal windows *)
    set initialOpenedWindows to id of every window

    (* Open the custom theme so that it gets added to the list
       of available terminal themes (note: this will open two
       additional terminal windows) *)
    do shell script "open " & themeFileName

    (* Wait a little bit to ensure that the custom theme is added *)
    delay 1

    (* Set the custom theme as the default terminal theme *)
    set default settings to settings set themeName

    (* Get the IDs of all the currently opened terminal windows *)
    set allOpenedWindows to id of every window

    repeat with windowID in allOpenedWindows

        (* Close the additional windows that were opened in order
           to add the custom theme to the list of terminal themes *)
        if initialOpenedWindows does not contain windowID then
            close (every window whose id is windowID)

        (* Change the theme for the initial opened terminal windows
           to remove the need to close them in order for the custom
           theme to be applied *)
        else
            set current settings of tabs of (every window whose id is windowID) to settings set themeName
        end if

    end repeat

end tell

tell application "Terminal"

    local allOpenedWindows
    local initialOpenedWindows
    local windowID

    set themeFileName to "solarized_dark.terminal"
    set themeName to "Solarized Dark"

    (* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *)

    (* Store the IDs of all the open terminal windows *)
    set initialOpenedWindows to id of every window

    (* Open the custom theme so that it gets added to the list
       of available terminal themes (note: this will open two
       additional terminal windows) *)
    do shell script "open " & themeFileName

    (* Wait a little bit to ensure that the custom theme is added *)
    delay 1

    (* Set the custom theme as the default terminal theme *)
    set default settings to settings set themeName

    (* Get the IDs of all the currently opened terminal windows *)
    set allOpenedWindows to id of every window

    repeat with windowID in allOpenedWindows

        (* Close the additional windows that were opened in order
           to add the custom theme to the list of terminal themes *)
        if initialOpenedWindows does not contain windowID then
            close (every window whose id is windowID)

        (* Change the theme for the initial opened terminal windows
           to remove the need to close them in order for the custom
           theme to be applied *)
        else
            set current settings of tabs of (every window whose id is windowID) to settings set themeName
        end if

    end repeat

end tell


Refer to playlist contents as songs, not tracks

This is only a UI presentation change, not a behavior change.

-- playplaylist filter --

on loadConfig()
	return (load script POSIX file (do shell script "./resources/compile-config.sh"))
end loadConfig

on getPlaylistResultListFeedback(query)

	global config

	set query to trimWhitespace(query) of config

	tell application "Music"

		-- retrieve list of playlists matching query (ordered by relevance)
		set thePlaylists to (get playlists whose name starts with query and name is not config's workflowPlaylistName and special kind is none and size is not 0)

		if length of thePlaylists < config's resultLimit then

			set thePlaylists to thePlaylists & (get playlists whose name contains (space & query) and name does not start with query and name is not config's workflowPlaylistName and special kind is none and size is not 0)

		end if

		if length of thePlaylists < config's resultLimit then

			set thePlaylists to thePlaylists & (get playlists whose name contains query and name does not start with query and name does not contain (space & query) and name is not config's workflowPlaylistName and special kind is none and size is not 0)

		end if

		if length of thePlaylists > config's resultLimit then

			set thePlaylists to items 1 thru (config's resultLimit) of thePlaylists

		end if

		repeat with thePlaylist in thePlaylists

			set playlistName to name of thePlaylist
			set playlistId to id of thePlaylist
			set songCount to number of tracks in thePlaylist
			set playlistDuration to time of thePlaylist

			try

				set theSong to first track in thePlaylist
				set songArtworkPath to getSongArtworkPath(theSong) of config

				set itemSubtitle to (quantifyNumber(songCount, "song", "songs") of config) & ", " & playlistDuration & " in length"

				-- Play Song does not support queueing Apple Music playlists
				-- because they can contain songs which the user has not added
				-- to their library (and such non-library songs cannot be
				-- programmatically added to a non-Apple Music playlist);
				-- therefore, we need to separate Apple Music playlists from
				-- other types of playlists
				if class of thePlaylist is subscription playlist then
					set prefixedPlaylistId to "subscription_playlist-" & playlistId
				else
					set prefixedPlaylistId to "playlist-" & playlistId
				end if
				addResult({uid:prefixedPlaylistId as text, valid:"yes", title:playlistName, subtitle:itemSubtitle, icon:songArtworkPath}) of config

			on error number -1728
			end try

		end repeat

		if config's resultListIsEmpty() then

			addNoResultsItem(query, "playlist") of config

		end if

	end tell

	return getResultListFeedback() of config

end getPlaylistResultListFeedback

on run query
	set config to loadConfig()
	getPlaylistResultListFeedback(query as text)
end run

-- playplaylist filter --

on loadConfig()
	return (load script POSIX file (do shell script "./resources/compile-config.sh"))
end loadConfig

on getPlaylistResultListFeedback(query)

	global config

	set query to trimWhitespace(query) of config

	tell application "Music"

		-- retrieve list of playlists matching query (ordered by relevance)
		set thePlaylists to (get playlists whose name starts with query and name is not config's workflowPlaylistName and special kind is none and size is not 0)

		if length of thePlaylists < config's resultLimit then

			set thePlaylists to thePlaylists & (get playlists whose name contains (space & query) and name does not start with query and name is not config's workflowPlaylistName and special kind is none and size is not 0)

		end if

		if length of thePlaylists < config's resultLimit then

			set thePlaylists to thePlaylists & (get playlists whose name contains query and name does not start with query and name does not contain (space & query) and name is not config's workflowPlaylistName and special kind is none and size is not 0)

		end if

		if length of thePlaylists > config's resultLimit then

			set thePlaylists to items 1 thru (config's resultLimit) of thePlaylists

		end if

		repeat with thePlaylist in thePlaylists

			set playlistName to name of thePlaylist
			set playlistId to id of thePlaylist
			set songCount to number of tracks in thePlaylist
			set playlistDuration to time of thePlaylist

			try

				set theSong to first track in thePlaylist
				set songArtworkPath to getSongArtworkPath(theSong) of config

				set itemSubtitle to (quantifyNumber(songCount, "track", "tracks") of config) & ", " & playlistDuration & " in length"

				-- Play Song does not support queueing Apple Music playlists
				-- because they can contain songs which the user has not added
				-- to their library (and such non-library songs cannot be
				-- programmatically added to a non-Apple Music playlist);
				-- therefore, we need to separate Apple Music playlists from
				-- other types of playlists
				if class of thePlaylist is subscription playlist then
					set prefixedPlaylistId to "subscription_playlist-" & playlistId
				else
					set prefixedPlaylistId to "playlist-" & playlistId
				end if
				addResult({uid:prefixedPlaylistId as text, valid:"yes", title:playlistName, subtitle:itemSubtitle, icon:songArtworkPath}) of config

			on error number -1728
			end try

		end repeat

		if config's resultListIsEmpty() then

			addNoResultsItem(query, "playlist") of config

		end if

	end tell

	return getResultListFeedback() of config

end getPlaylistResultListFeedback

on run query
	set config to loadConfig()
	getPlaylistResultListFeedback(query as text)
end run


fixed typo in error in displayFileMakerDatabase

-- displayFileMakerDatabase({dbName:"", waitCycleDelaySeconds:"", waitSaveTotalSeconds:""})
-- Dan Shockley, NYHTC
-- If open, then display first window this finds and return true, else return false.

(*
HISTORY:
	1.6.4 - 2020-05-20 ( dshockley ): fixed typo in error message.
	1.6.3 - 2019-02-27 ( eshagdar ): throw up a dialog if there is a privSet violation.
	1.6.2 - 2018-12-07 ( eshagdar ): don't both using case, terms, or bundle ID - just talk to the application directly.
	1.6.1 - 2018-10-16 ( eshagdar ): remove remaining FMA references.
	1.6 - 2018-09-20 ( eshagdar ): FileMaker 17 has only version so talk to it by name.
	1.5.3 - 2018-01-18 ( eshagdar ): capture privSet violation error
	1.5.2 - 2017-11-20 ( eshagdar ): disable logging
	1.5.1 - 2017-10-25 ( eshagdar ): updated defaultPrefs. updated helper handlers. added delay, more debugging messages. get list of document names instead of documents - we don't need to get the name later.
	1.5 - added a time-out loop for dealing with delay in being able to get list of databases.
	1.4 - added error-handling
	1.3 -
	1.2 -
	1.1 -
	1.0 - created

REQUIRES:
	coerceToString
	logConsole
*)

property debugMode : true
property ScriptName : "displayFileMakerDatabase_TEST"

on run
	displayFileMakerDatabase({dbName:"a01_PERSON"})
end run

--------------------
-- START OF CODE
--------------------

on displayFileMakerDatabase(prefs)
	-- version 1.6.3

	try
		set defaultPrefs to {dbName:null, waitCycleDelaySeconds:5, waitSaveTotalSeconds:2 * minutes}
		set prefs to prefs & defaultPrefs

		--if debugMode then logConsole(ScriptName, "displayFileMakerDatabase prefs: " & coerceToString(prefs))

		set dbName to dbName of prefs

		set waitSaveTotalSeconds to waitSaveTotalSeconds of prefs
		set waitCycleDelaySeconds to waitCycleDelaySeconds of prefs
		set waitCycleMax to round (waitSaveTotalSeconds / waitCycleDelaySeconds) rounding down

		-- get name of all documents
		delay 0.25
		repeat waitCycleMax times
			set docNameList to null
			try
				using terms from application "FileMaker Pro Advanced"
					tell application ID "com.filemaker.client.pro12"
						set docNameList to name of every document
					end tell
				end using terms from

				if docNameList is not null then exit repeat
			on error errMsg number errNum
				if errNum is -10011 then
					-- just keep trying - this error can be caused when a database is opening and the open script takes a bit too long to finish, or something in the FM interface is slow to respond.
				else if errNum is -10004 then
					activate
					display dialog "unable to displayFileMakerDatabase because of privSet violation in some FM file errNum: " & errNum buttons "OK" default button "OK"
					error "privSet violation in some FM file..." & errNum number errNum
				else
					error "Error getting list of databases already open - " & errMsg number errNum
				end if
			end try

			delay waitCycleDelaySeconds
		end repeat
		--if debugMode then my logConsole(ScriptName, "displayFileMakerDatabase docNameList: " & my coerceToString(docNameList))

		--loop through available document and bring the specified to the front
		repeat with oneDocName in docNameList
			set oneDocName to contents of oneDocName

			-- get the name WITHOUT the file extension:
			(reverse of characters of oneDocName) as string
			text ((offset of "." in result) + 1) thru -1 of result
			set oneDocName to (reverse of characters of result) as string

			-- apparently these two TEXT variables have some difference (formatting?) even when they are identical STRINGS:
			if oneDocName is equal to (dbName as string) then
				tell application "FileMaker Pro Advanced"
					--if debugMode then my logConsole(ScriptName, "displayFileMakerDatabase about to show oneDocName: " & oneDocName)
					show document oneDocName
					if debugMode then my logConsole(ScriptName, "displayFileMakerDatabase: " & oneDocName)
				end tell
				return true
			end if
		end repeat

		return false
	on error errMsg number errNum
		error "unable to displayFileMakerDatabase - " & errMsg number errNum
	end try
end displayFileMakerDatabase

--------------------
-- END OF CODE
--------------------

on logConsole(processName, consoleMsg)
	tell application "htcLib" to logConsole(processName, consoleMsg)
end logConsole

on coerceToString(incomingObject)
	-- 2017-07-12 ( eshagdar ): bootstrap code to bring a coerceToString into this file for the sample to run ( instead of having a copy of the handler locally ).

	tell application "Finder" to set coercePath to (container of (container of (path to me)) as text) & "text parsing:coerceToString.applescript"
	set codeCoerce to read file coercePath as text
	tell application "htcLib" to set codeCoerce to "script codeCoerce " & return & getTextBetween({sourceText:codeCoerce, beforeText:"-- START OF CODE", afterText:"-- END OF CODE"}) & return & "end script" & return & "return codeCoerce"
	set codeCoerce to run script codeCoerce
	tell codeCoerce to coerceToString(incomingObject)
end coerceToString

-- displayFileMakerDatabase({dbName:"", waitCycleDelaySeconds:"", waitSaveTotalSeconds:""})
-- Dan Shockley, NYHTC
-- If open, then display first window this finds and return true, else return false.

(*
HISTORY:
	1.6.3 - 2019-02-27 ( eshagdar ): throw up a dialog if there is a privSet violation.
	1.6.2 - 2018-12-07 ( eshagdar ): don't both using case, terms, or bundle ID - just talk to the application directly.
	1.6.1 - 2018-10-16 ( eshagdar ): remove remaining FMA references.
	1.6 - 2018-09-20 ( eshagdar ): FileMaker 17 has only version so talk to it by name.
	1.5.3 - 2018-01-18 ( eshagdar ): capture privSet violation error
	1.5.2 - 2017-11-20 ( eshagdar ): disable logging
	1.5.1 - 2017-10-25 ( eshagdar ): updated defaultPrefs. updated helper handlers. added delay, more debugging messages. get list of document names instead of documents - we don't need to get the name later.
	1.5 - added a time-out loop for dealing with delay in being able to get list of databases.
	1.4 - added error-handling
	1.3 -
	1.2 -
	1.1 -
	1.0 - created

REQUIRES:
	coerceToString
	logConsole
*)

property debugMode : true
property ScriptName : "displayFileMakerDatabase_TEST"

on run
	displayFileMakerDatabase({dbName:"a01_PERSON"})
end run

--------------------
-- START OF CODE
--------------------

on displayFileMakerDatabase(prefs)
	-- version 1.6.3

	try
		set defaultPrefs to {dbName:null, waitCycleDelaySeconds:5, waitSaveTotalSeconds:2 * minutes}
		set prefs to prefs & defaultPrefs

		--if debugMode then logConsole(ScriptName, "displayFileMakerDatabase prefs: " & coerceToString(prefs))

		set dbName to dbName of prefs

		set waitSaveTotalSeconds to waitSaveTotalSeconds of prefs
		set waitCycleDelaySeconds to waitCycleDelaySeconds of prefs
		set waitCycleMax to round (waitSaveTotalSeconds / waitCycleDelaySeconds) rounding down

		-- get name of all documents
		delay 0.25
		repeat waitCycleMax times
			set docNameList to null
			try
				using terms from application "FileMaker Pro Advanced"
					tell application ID "com.filemaker.client.pro12"
						set docNameList to name of every document
					end tell
				end using terms from

				if docNameList is not null then exit repeat
			on error errMsg number errNum
				if errNum is -10011 then
					-- just keep trying - this error can be caused when a database is opening and the open script takes a bit too long to finish, or something in the FM interface is slow to respond.
				else if errNum is -10004 then
					activate
					display dialog "unable to displayFileMakerDatabase becuase of privSet violation in some FM file errNum: " & errNum buttons "OK" default button "OK"
					error "privSet violation in some FM file..." & errNum number errNum
				else
					error "Error getting list of databases already open - " & errMsg number errNum
				end if
			end try

			delay waitCycleDelaySeconds
		end repeat
		--if debugMode then my logConsole(ScriptName, "displayFileMakerDatabase docNameList: " & my coerceToString(docNameList))

		--loop through available document and bring the specified to the front
		repeat with oneDocName in docNameList
			set oneDocName to contents of oneDocName

			-- get the name WITHOUT the file extension:
			(reverse of characters of oneDocName) as string
			text ((offset of "." in result) + 1) thru -1 of result
			set oneDocName to (reverse of characters of result) as string

			-- apparently these two TEXT variables have some difference (formatting?) even when they are identical STRINGS:
			if oneDocName is equal to (dbName as string) then
				tell application "FileMaker Pro Advanced"
					--if debugMode then my logConsole(ScriptName, "displayFileMakerDatabase about to show oneDocName: " & oneDocName)
					show document oneDocName
					if debugMode then my logConsole(ScriptName, "displayFileMakerDatabase: " & oneDocName)
				end tell
				return true
			end if
		end repeat

		return false
	on error errMsg number errNum
		error "unable to displayFileMakerDatabase - " & errMsg number errNum
	end try
end displayFileMakerDatabase

--------------------
-- END OF CODE
--------------------

on logConsole(processName, consoleMsg)
	tell application "htcLib" to logConsole(processName, consoleMsg)
end logConsole

on coerceToString(incomingObject)
	-- 2017-07-12 ( eshagdar ): bootstrap code to bring a coerceToString into this file for the sample to run ( instead of having a copy of the handler locally ).

	tell application "Finder" to set coercePath to (container of (container of (path to me)) as text) & "text parsing:coerceToString.applescript"
	set codeCoerce to read file coercePath as text
	tell application "htcLib" to set codeCoerce to "script codeCoerce " & return & getTextBetween({sourceText:codeCoerce, beforeText:"-- START OF CODE", afterText:"-- END OF CODE"}) & return & "end script" & return & "return codeCoerce"
	set codeCoerce to run script codeCoerce
	tell codeCoerce to coerceToString(incomingObject)
end coerceToString


extract model relative code into updateModel method

property kExplicitlyProcessedOptions : {"--human-dir ", "--machine-dir ", "--output-dir ", "--log-command"}

tell application "Xcode"
	if not (exists active project document) then
		error "No active project. Please open an Xcode project and re-run the script."
	try
		my updateProjectXmod(project of active project document)
	on error errMsg
		my logger("Xmod.scpt exception: " & errMsg)
	end try
end tell

on updateProjectXmod(_project)
	tell application "Xcode"
		-- Iterate over every .xcdatamodel in the project.
		set modelList to every file reference of _project whose file kind is "wrapper.xcdatamodel"
		repeat with modelItr in modelList
			set theComments to the comments of modelItr
			if theComments contains "xmod" then
				my updateModel(_projet, modelItr, theComments)
			end if
		end repeat
	end tell
end updateProjectXmod

on updateModel(_project, modelItr, theComments)
	tell application "Xcode"

				set modelInfo to my getModelInfo(full path of modelItr, theComments)
				set humanGroupRef to null
				set machineGroupRef to null

				-- Figure out the model's parent group.
				-- Unversioned models are simple files so their group is the answer.
				-- Version models are bundles, which are represented as pseudo-groups in Xcode. You can't ask a pseudo-group for its parent group,
				-- so we discover it by querying project-wide all groups that contain the pseudo-group and using the first result.
				set modelItemRef to item 1 of (every item reference of _project whose full path is (full path of modelItr))
				set modelGroupRef to group of modelItemRef
				if isBundle of modelInfo then
					set modelGroupRef to item 1 of (every group of _project whose groups contains modelGroupRef)
				end if

				-- look for a group in the project that has the full path of the human manageable files
				set groupRefs to (every group of _project whose full path is (humanDirPath of modelInfo))
				-- if we don't find one, create it in the same group as the model file
				if (count of groupRefs) = 0 then
					tell modelGroupRef
						-- if we have different human and machine paths, then append '_human' to the model name for the group name
						if (humanDirPath of modelInfo) is not equal to (machineDirPath of modelInfo) then
							make new group with properties {full path:(humanDirPath of modelInfo), name:(name of modelInfo & "_human")}
						else
							make new group with properties {full path:(humanDirPath of modelInfo), name:(name of modelInfo)}
						end if
						set groupRefs to (every group of _project whose full path is (humanDirPath of modelInfo))
					end tell
				end if
				set humanGroupRef to item 1 of groupRefs

				-- if we have different human and machine paths, then look for a group for the machine files
				if (humanDirPath of modelInfo) is not equal to (machineDirPath of modelInfo) then
					set groupRefs to (every group of _project whose full path is (machineDirPath of modelInfo))
					-- if we don't find one, create it in the same group as the model file... named with '_machine' appended to the model name
					if (count of groupRefs) = 0 then
						tell modelGroupRef
							make new group with properties {full path:(machineDirPath of modelInfo), name:(name of modelInfo & "_machine")}
						end tell
						set groupRefs to (every group of _project whose full path is (machineDirPath of modelInfo))
					end if
					set machineGroupRef to item 1 of groupRefs
				end if

				-- Create the do shell script string and append any custom per model options to it, skipping the ones we've already parsed out
				set logCommand to false
				set theScript to "/usr/bin/mogenerator --model '" & full path of modelItr & "' --human-dir '" & (humanDirPath of modelInfo) & "' --machine-dir '" & (machineDirPath of modelInfo) & "'"
				set theParagraphs to every paragraph of theComments
				repeat with theParagraph in theParagraphs
					if theParagraph starts with "--" then
						set isExplicitlyProcessedOption to false
						repeat with explicitlyProcessedOption in kExplicitlyProcessedOptions
							if theParagraph starts with explicitlyProcessedOption then set isExplicitlyProcessedOption to true
						end repeat
						if not isExplicitlyProcessedOption then
							set theScript to theScript & " " & the text of theParagraph
						end if
						if theParagraph starts with "--log-command" then
							set logCommand to true
						end if
					end if
				end repeat

				--	Meat.
				if logCommand then
					my logger(theScript)
				end if
				do shell script theScript

				my addFilesFromPathToGroup(_project, modelItr, humanGroupRef)
				if machineGroupRef is not null then
					my addFilesFromPathToGroup(_project, modelItr, machineGroupRef)
				end if
	end tell
end updateModel

on everyTargetWithBuildFilePath(_project, _buildFilePath)
	set theResult to {}
	tell application "Xcode"
		repeat with targetItr in (every target of _project)
			repeat with buildFileItr in build files of targetItr
				if full path of file reference of buildFileItr is _buildFilePath then set theResult to theResult & {(targetItr as anything)}
			end repeat
		end repeat
	end tell
	return theResult
end everyTargetWithBuildFilePath

on getModelInfo(modelFileUnixPath, theComments)
	set modelFilePosixRef to POSIX file modelFileUnixPath
	set modelFileAlias to modelFilePosixRef as alias
	set theOutputDir to ""
	set theHumanDir to ""
	set theMachineDir to ""

	-- check for any directory path options specified in the commments
	set theParagraphs to every paragraph of theComments
	repeat with theParagraph in theParagraphs
		set paragraphLength to (length of theParagraph)
		if (offset of "--human-dir " in theParagraph) = 1 then
			set remainderOffset to (length of "--human-dir ") + 1
			if remainderOffset > paragraphLength then
				set theHumanDir to ""
			else
				set theHumanDir to text remainderOffset thru -1 of theParagraph
			end if
		else if (offset of "--machine-dir " in theParagraph) = 1 then
			set remainderOffset to (length of "--machine-dir ") + 1
			if remainderOffset > paragraphLength then
				set theMachineDir to ""
			else
				set theMachineDir to text remainderOffset thru -1 of theParagraph
			end if
		else if (offset of "--output-dir " in theParagraph) = 1 then
			set remainderOffset to (length of "--output-dir ") + 1
			if remainderOffset > paragraphLength then
				set theOutputDir to ""
			else
				set theOutputDir to text remainderOffset thru -1 of theParagraph
			end if
		end if
	end repeat

	-- remove any leading/trailing spaces or tabs from the paths
	if (length of theHumanDir) > 0 then
		repeat while character 1 of theHumanDir = tab or character 1 of theHumanDir = space
			set theHumanDir to text 2 thru -1 of theHumanDir
		end repeat
		repeat while last character of theHumanDir = tab or last character of theHumanDir = space
			set theHumanDir to text 1 thru -2 of theHumanDir
		end repeat
	end if

	if (length of theMachineDir) > 0 then
		repeat while character 1 of theMachineDir = tab or character 1 of theMachineDir = space
			set theMachineDir to text 2 thru -1 of theMachineDir
		end repeat
		repeat while last character of theMachineDir = tab or last character of theMachineDir = space
			set theMachineDir to text 1 thru -2 of theMachineDir
		end repeat
	end if

	if (length of theOutputDir) > 0 then
		repeat while character 1 of theOutputDir = tab or character 1 of theOutputDir = space
			set theOutputDir to text 2 thru -1 of theOutputDir
		end repeat
		repeat while last character of theOutputDir = tab or last character of theOutputDir = space
			set theOutputDir to text 1 thru -2 of theOutputDir
		end repeat
	end if

	-- get actual the model info and modify value for theOutputDir if necessary
	tell application "Finder"
		set modelFileFolder to folder of modelFileAlias

		set isModelBundle to name of modelFileFolder ends with ".xcdatamodeld"
		if isModelBundle then
			set modelFileAlias to (folder of modelFileAlias) as alias -- it's a bundle, go up one folder
			set modelFileFolder to folder of modelFileAlias
		end if

		set modelFileName to name of modelFileAlias
		if isModelBundle then
			set extensionLength to length of ".xcdatamodeld"
		else
			set extensionLength to length of ".xcdatamodel"
		end if

		set modelName to text 1 thru -(extensionLength + 1) of modelFileName -- pull off the extension

		-- if we didn't find any directory specifiers in the comments, then make theOutputDir the folder containg the model file
		if ((length of theOutputDir) = 0) and ((length of theHumanDir) = 0) and ((length of theMachineDir) = 0) then
			if not (exists folder modelName of modelFileFolder) then
				make folder at modelFileFolder with properties {name:modelName}
			set modelSrcFolder to folder modelName of modelFileFolder
			set theOutputDir to text 1 thru -2 of (POSIX path of (modelSrcFolder as alias))
			-- otherwise, if theOutputDir isn't a full path itself already, set theOutputDir to a full path relative to the model file
		else if theOutputDir does not start with "/" then
			set modelFolderPath to text 1 thru -2 of (POSIX path of (modelFileFolder as alias))
			if (length of theOutputDir) = 0 then
				set theOutputDir to modelFolderPath
			else
				copy modelFolderPath & "/" & theOutputDir to theOutputDir
			end if
		end if

	end tell

	-- if theHumanDir is empty, use theOutputDir value instead, if theHumanDir not empty and it isn't a full path, then treat it as a relative path to theOutputDir
	if (length of theHumanDir) = 0 then
		set theHumanDir to theOutputDir
	else if theHumanDir does not start with "/" then
		copy theOutputDir & "/" & theHumanDir to theHumanDir
	end if

	-- if theMachineDir is empty, use theOutputDir value instead, if theMachineDir not empty and it isn't a full path, then treat it as a relative path to theOutputDir
	if (length of theMachineDir) = 0 then
		set theMachineDir to theOutputDir
	else if theMachineDir does not start with "/" then
		copy theOutputDir & "/" & theMachineDir to theMachineDir
	end if

	-- ensure the directories exist
	do shell script "mkdir -p '" & theHumanDir & "'"
	do shell script "mkdir -p '" & theMachineDir & "'"

	-- the following should resolve any symlinks, '..', or '~' in the paths
	tell application "System Events"
		set dirAlias to POSIX file theHumanDir as alias
		set theHumanDir to (POSIX path of (dirAlias as alias))
		set dirAlias to POSIX file theMachineDir as alias
		set theMachineDir to (POSIX path of (dirAlias as alias))
	end tell

	return {name:modelName, isBundle:isModelBundle, machineDirPath:theMachineDir, humanDirPath:theHumanDir}

end getModelInfo

on logger(msg)
	do shell script "logger '" & msg & "'"
end logger

on addFilesFromPathToGroup(_project, modelItr, groupRef)
	tell application "Xcode"
		-- get the full path of the directory that the groupRef represents
		set groupPath to full path of groupRef

		--	Build a list of source files in the group's directory
		tell application "System Events"
			set modelSrcDirAlias to POSIX file groupPath as alias
			set fileList to (every file of modelSrcDirAlias whose (name ends with ".m" or name ends with ".mm" or name ends with ".h"))
			set pathList to {}
			repeat with fileItem in fileList
				set pathList to pathList & POSIX path of fileItem
			end repeat
		end tell

		--	Add the source files to the group and the model's targets, but only if they don't exist already
		set targetList to null
		repeat with pathItr in pathList
			tell groupRef
				set fileRefs to (every file reference of _project whose full path is pathItr)
				if (count of fileRefs) = 0 then
					set modelSrcFileRef to make new file reference with properties {full path:pathItr, name:name of (info for POSIX file pathItr)}
					if targetList is null then
						set targetList to my everyTargetWithBuildFilePath(_project, full path of modelItr)
					end if
					repeat with targetIndex from 1 to (count of targetList)
						set targetItr to item targetIndex of targetList
						add modelSrcFileRef to targetItr
					end repeat
				end if
			end tell
		end repeat
	end tell
end addFilesFromPathToGroup

property kExplicitlyProcessedOptions : {"--human-dir ", "--machine-dir ", "--output-dir ", "--log-command"}

tell application "Xcode"
	if not (exists active project document) then
		error "No active project. Please open an Xcode project and re-run the script."
	try
		my updateProjectXmod(project of active project document)
	on error errMsg
		my logger("Xmod.scpt exception: " & errMsg)
	end try
end tell

on updateProjectXmod(_project)
	tell application "Xcode"
		-- Iterate over every .xcdatamodel in the project.
		set modelList to every file reference of _project whose file kind is "wrapper.xcdatamodel"
		repeat with modelItr in modelList
			set theComments to the comments of modelItr
			if theComments contains "xmod" then

				set modelInfo to my getModelInfo(full path of modelItr, theComments)
				set humanGroupRef to null
				set machineGroupRef to null

				-- Figure out the model's parent group.
				-- Unversioned models are simple files so their group is the answer.
				-- Version models are bundles, which are represented as pseudo-groups in Xcode. You can't ask a pseudo-group for its parent group,
				-- so we discover it by querying project-wide all groups that contain the pseudo-group and using the first result.
				set modelItemRef to item 1 of (every item reference of _project whose full path is (full path of modelItr))
				set modelGroupRef to group of modelItemRef
				if isBundle of modelInfo then
					set modelGroupRef to item 1 of (every group of _project whose groups contains modelGroupRef)
				end if

				-- look for a group in the project that has the full path of the human manageable files
				set groupRefs to (every group of _project whose full path is (humanDirPath of modelInfo))
				-- if we don't find one, create it in the same group as the model file
				if (count of groupRefs) = 0 then
					tell modelGroupRef
						-- if we have different human and machine paths, then append '_human' to the model name for the group name
						if (humanDirPath of modelInfo) is not equal to (machineDirPath of modelInfo) then
							make new group with properties {full path:(humanDirPath of modelInfo), name:(name of modelInfo & "_human")}
						else
							make new group with properties {full path:(humanDirPath of modelInfo), name:(name of modelInfo)}
						end if
						set groupRefs to (every group of _project whose full path is (humanDirPath of modelInfo))
					end tell
				end if
				set humanGroupRef to item 1 of groupRefs

				-- if we have different human and machine paths, then look for a group for the machine files
				if (humanDirPath of modelInfo) is not equal to (machineDirPath of modelInfo) then
					set groupRefs to (every group of _project whose full path is (machineDirPath of modelInfo))
					-- if we don't find one, create it in the same group as the model file... named with '_machine' appended to the model name
					if (count of groupRefs) = 0 then
						tell modelGroupRef
							make new group with properties {full path:(machineDirPath of modelInfo), name:(name of modelInfo & "_machine")}
						end tell
						set groupRefs to (every group of _project whose full path is (machineDirPath of modelInfo))
					end if
					set machineGroupRef to item 1 of groupRefs
				end if

				-- Create the do shell script string and append any custom per model options to it, skipping the ones we've already parsed out
				set logCommand to false
				set theScript to "/usr/bin/mogenerator --model '" & full path of modelItr & "' --human-dir '" & (humanDirPath of modelInfo) & "' --machine-dir '" & (machineDirPath of modelInfo) & "'"
				set theParagraphs to every paragraph of theComments
				repeat with theParagraph in theParagraphs
					if theParagraph starts with "--" then
						set isExplicitlyProcessedOption to false
						repeat with explicitlyProcessedOption in kExplicitlyProcessedOptions
							if theParagraph starts with explicitlyProcessedOption then set isExplicitlyProcessedOption to true
						end repeat
						if not isExplicitlyProcessedOption then
							set theScript to theScript & " " & the text of theParagraph
						end if
						if theParagraph starts with "--log-command" then
							set logCommand to true
						end if
					end if
				end repeat

				--	Meat.
				if logCommand then
					my logger(theScript)
				end if
				do shell script theScript

				my addFilesFromPathToGroup(_project, modelItr, humanGroupRef)
				if machineGroupRef is not null then
					my addFilesFromPathToGroup(_project, modelItr, machineGroupRef)
				end if
			end if
		end repeat
	end tell
end updateProjectXmod

on everyTargetWithBuildFilePath(_project, _buildFilePath)
	set theResult to {}
	tell application "Xcode"
		repeat with targetItr in (every target of _project)
			repeat with buildFileItr in build files of targetItr
				if full path of file reference of buildFileItr is _buildFilePath then set theResult to theResult & {(targetItr as anything)}
			end repeat
		end repeat
	end tell
	return theResult
end everyTargetWithBuildFilePath

on getModelInfo(modelFileUnixPath, theComments)
	set modelFilePosixRef to POSIX file modelFileUnixPath
	set modelFileAlias to modelFilePosixRef as alias
	set theOutputDir to ""
	set theHumanDir to ""
	set theMachineDir to ""

	-- check for any directory path options specified in the commments
	set theParagraphs to every paragraph of theComments
	repeat with theParagraph in theParagraphs
		set paragraphLength to (length of theParagraph)
		if (offset of "--human-dir " in theParagraph) = 1 then
			set remainderOffset to (length of "--human-dir ") + 1
			if remainderOffset > paragraphLength then
				set theHumanDir to ""
			else
				set theHumanDir to text remainderOffset thru -1 of theParagraph
			end if
		else if (offset of "--machine-dir " in theParagraph) = 1 then
			set remainderOffset to (length of "--machine-dir ") + 1
			if remainderOffset > paragraphLength then
				set theMachineDir to ""
			else
				set theMachineDir to text remainderOffset thru -1 of theParagraph
			end if
		else if (offset of "--output-dir " in theParagraph) = 1 then
			set remainderOffset to (length of "--output-dir ") + 1
			if remainderOffset > paragraphLength then
				set theOutputDir to ""
			else
				set theOutputDir to text remainderOffset thru -1 of theParagraph
			end if
		end if
	end repeat

	-- remove any leading/trailing spaces or tabs from the paths
	if (length of theHumanDir) > 0 then
		repeat while character 1 of theHumanDir = tab or character 1 of theHumanDir = space
			set theHumanDir to text 2 thru -1 of theHumanDir
		end repeat
		repeat while last character of theHumanDir = tab or last character of theHumanDir = space
			set theHumanDir to text 1 thru -2 of theHumanDir
		end repeat
	end if

	if (length of theMachineDir) > 0 then
		repeat while character 1 of theMachineDir = tab or character 1 of theMachineDir = space
			set theMachineDir to text 2 thru -1 of theMachineDir
		end repeat
		repeat while last character of theMachineDir = tab or last character of theMachineDir = space
			set theMachineDir to text 1 thru -2 of theMachineDir
		end repeat
	end if

	if (length of theOutputDir) > 0 then
		repeat while character 1 of theOutputDir = tab or character 1 of theOutputDir = space
			set theOutputDir to text 2 thru -1 of theOutputDir
		end repeat
		repeat while last character of theOutputDir = tab or last character of theOutputDir = space
			set theOutputDir to text 1 thru -2 of theOutputDir
		end repeat
	end if

	-- get actual the model info and modify value for theOutputDir if necessary
	tell application "Finder"
		set modelFileFolder to folder of modelFileAlias

		set isModelBundle to name of modelFileFolder ends with ".xcdatamodeld"
		if isModelBundle then
			set modelFileAlias to (folder of modelFileAlias) as alias -- it's a bundle, go up one folder
			set modelFileFolder to folder of modelFileAlias
		end if

		set modelFileName to name of modelFileAlias
		if isModelBundle then
			set extensionLength to length of ".xcdatamodeld"
		else
			set extensionLength to length of ".xcdatamodel"
		end if

		set modelName to text 1 thru -(extensionLength + 1) of modelFileName -- pull off the extension

		-- if we didn't find any directory specifiers in the comments, then make theOutputDir the folder containg the model file
		if ((length of theOutputDir) = 0) and ((length of theHumanDir) = 0) and ((length of theMachineDir) = 0) then
			if not (exists folder modelName of modelFileFolder) then
				make folder at modelFileFolder with properties {name:modelName}
			set modelSrcFolder to folder modelName of modelFileFolder
			set theOutputDir to text 1 thru -2 of (POSIX path of (modelSrcFolder as alias))
			-- otherwise, if theOutputDir isn't a full path itself already, set theOutputDir to a full path relative to the model file
		else if theOutputDir does not start with "/" then
			set modelFolderPath to text 1 thru -2 of (POSIX path of (modelFileFolder as alias))
			if (length of theOutputDir) = 0 then
				set theOutputDir to modelFolderPath
			else
				copy modelFolderPath & "/" & theOutputDir to theOutputDir
			end if
		end if

	end tell

	-- if theHumanDir is empty, use theOutputDir value instead, if theHumanDir not empty and it isn't a full path, then treat it as a relative path to theOutputDir
	if (length of theHumanDir) = 0 then
		set theHumanDir to theOutputDir
	else if theHumanDir does not start with "/" then
		copy theOutputDir & "/" & theHumanDir to theHumanDir
	end if

	-- if theMachineDir is empty, use theOutputDir value instead, if theMachineDir not empty and it isn't a full path, then treat it as a relative path to theOutputDir
	if (length of theMachineDir) = 0 then
		set theMachineDir to theOutputDir
	else if theMachineDir does not start with "/" then
		copy theOutputDir & "/" & theMachineDir to theMachineDir
	end if

	-- ensure the directories exist
	do shell script "mkdir -p '" & theHumanDir & "'"
	do shell script "mkdir -p '" & theMachineDir & "'"

	-- the following should resolve any symlinks, '..', or '~' in the paths
	tell application "System Events"
		set dirAlias to POSIX file theHumanDir as alias
		set theHumanDir to (POSIX path of (dirAlias as alias))
		set dirAlias to POSIX file theMachineDir as alias
		set theMachineDir to (POSIX path of (dirAlias as alias))
	end tell

	return {name:modelName, isBundle:isModelBundle, machineDirPath:theMachineDir, humanDirPath:theHumanDir}

end getModelInfo

on logger(msg)
	do shell script "logger '" & msg & "'"
end logger

on addFilesFromPathToGroup(_project, modelItr, groupRef)
	tell application "Xcode"
		-- get the full path of the directory that the groupRef represents
		set groupPath to full path of groupRef

		--	Build a list of source files in the group's directory
		tell application "System Events"
			set modelSrcDirAlias to POSIX file groupPath as alias
			set fileList to (every file of modelSrcDirAlias whose (name ends with ".m" or name ends with ".mm" or name ends with ".h"))
			set pathList to {}
			repeat with fileItem in fileList
				set pathList to pathList & POSIX path of fileItem
			end repeat
		end tell

		--	Add the source files to the group and the model's targets, but only if they don't exist already
		set targetList to null
		repeat with pathItr in pathList
			tell groupRef
				set fileRefs to (every file reference of _project whose full path is pathItr)
				if (count of fileRefs) = 0 then
					set modelSrcFileRef to make new file reference with properties {full path:pathItr, name:name of (info for POSIX file pathItr)}
					if targetList is null then
						set targetList to my everyTargetWithBuildFilePath(_project, full path of modelItr)
					end if
					repeat with targetIndex from 1 to (count of targetList)
						set targetItr to item targetIndex of targetList
						add modelSrcFileRef to targetItr
					end repeat
				end if
			end tell
		end repeat
	end tell
end addFilesFromPathToGroup


Long timeout

Long timeout

#!/usr/bin/env osascript

-- Fist: Click Install Button
set timeoutSeconds to 8.0

my doWithTimeout("click UI Element \"Install\" of window 1 of application process \"Install Command Line Developer Toold\"", timeoutSeconds)
my doWithTimeout("click UI Element \"Agree\" of window 1 of application process \"Install Command Line Developer Toold\"", timeoutSeconds)

on doWithTimeout(uiScript, timeoutSeconds)
  set endDate to (current date) + timeoutSeconds
  repeat
    try
      run script "tell application \"System Events\"
" & uiScript & "
end tell"
      exit repeat
    on error errorMessage
      if ((current date) > endDate) then
        error "Can not " & uiScript
      end if
    end try
  end repeat
end doWithTimeout

#!/usr/bin/env osascript

-- Fist: Click Install Button
delay 0.4
tell application "System Events"
  set installer to application process "Install Command Line Developer Tools"
  click UI Element "Install" of window 1 of installer
  click UI Element "Agree" of window 1 of installer
end tell