use scripting additions
use framework "Foundation"
property NSString : a reference to current application's NSString

-- Helper

on myRemoveLastPath(myPath)
set myString to NSString's stringWithString:myPath
set removedLastPathString to myString's stringByDeletingLastPathComponent
removedLastPathString as text
end myRemoveLastPath

-- Path

on myHomePath()
tell application "System Events"
"/Users/" & (name of current user)
end tell
end myHomePath

on myLibraryPath()
myHomePath() & "/Library"
end myLibraryPath

on myXcodePath()
myLibraryPath() & "/Developer/Xcode"
end myXcodePath

on myUserDataPath()
myXcodePath() & "/UserData"
end myUserDataPath

on myProjectPath()
tell application "Xcode"
tell active workspace document
set myPath to path
end tell
end tell
myRemoveLastPath(myPath)
end myProjectPath

-- Generic Open

on myOpenFolder(myPath)
tell application "Finder"
activate
open myPath as POSIX file
end tell
end myOpenFolder

on myOpeniTerm(myPath)
tell application "Finder"
do shell script "open -a iTerm " & quoted form of myPath
end tell
end myOpeniTerm

-- Open

on myOpenThemesFolder()
set myPath to myUserDataPath() & "/FontAndColorThemes/"
myOpenFolder(myPath)
end myOpenThemesFolder

on myOpenCodeSnippetsFolder()
set myPath to myUserDataPath() & "/CodeSnippets/"
myOpenFolder(myPath)
end myOpenCodeSnippetsFolder

on myOpenDeviceSupportFolder()
set myPath to "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"
myOpenFolder(myPath)
end myOpenDeviceSupportFolder

on myOpenArchivesFolder()
set myPath to myXcodePath() & "/Archives/"
myOpenFolder(myPath)
end myOpenArchivesFolder

on myOpenProvisioningProfileFolder()
set myPath to myLibraryPath() & "/MobileDevice/Provisioning Profiles"
myOpenFolder(myPath)
end myOpenProvisioningProfileFolder

on myOpenProjectFolder()
set myPath to myProjectPath()
myOpenFolder(myPath)
end myOpenProjectFolder

on myOpeniTermForCurrentProject()
set myPath to myProjectPath()
myOpeniTerm(myPath)
end myOpeniTermForCurrentProject

use scripting additions
use framework "Foundation"
property NSString : a reference to current application's NSString

-- Helper

on myRemoveLastPath(myPath)
set myString to NSString's stringWithString:myPath
set removedLastPathString to myString's stringByDeletingLastPathComponent
removedLastPathString as text
end myRemoveLastPath

on myFolderExists(myFolder) -- (String) as Boolean
tell application "System Events"
if exists folder myFolder then
return true
else
return false
end if
end tell
end myFolderExists

-- Path

on myHomePath()
tell application "System Events"
"/Users/" & (name of current user)
end tell
end myHomePath

on myLibraryPath()
myHomePath() & "/Library"
end myLibraryPath

on myXcodePath()
myLibraryPath() & "/Developer/Xcode"
end myXcodePath

on myUserDataPath()
myXcodePath() & "/UserData"
end myUserDataPath

on myProjectPath()
tell application "Xcode"
tell active workspace document
set myPath to path
end tell
end tell
myRemoveLastPath(myPath)
end myProjectPath

-- Generic Open

on myOpenFolder(myPath)
tell application "Finder"
activate
open myPath as POSIX file
end tell
end myOpenFolder

on myOpeniTerm(myPath)
tell application "Finder"
do shell script "open -a iTerm " & quoted form of myPath
end tell
end myOpeniTerm

-- Open

on myOpenThemesFolder()
set myPath to myUserDataPath() & "/FontAndColorThemes/"
myOpenFolder(myPath)
end myOpenThemesFolder

on myOpenCodeSnippetsFolder()
set myPath to myUserDataPath() & "/CodeSnippets/"
myOpenFolder(myPath)
end myOpenCodeSnippetsFolder

on myOpenDeviceSupportFolder()
set myPath to "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport"
myOpenFolder(myPath)
end myOpenDeviceSupportFolder

on myOpenArchivesFolder()
set myPath to myXcodePath() & "/Archives/"
myOpenFolder(myPath)
end myOpenArchivesFolder

on myOpenProvisioningProfileFolder()
set myPath to myLibraryPath() & "/MobileDevice/Provisioning Profiles"
myOpenFolder(myPath)
end myOpenProvisioningProfileFolder

on myOpenProjectFolder()
set myPath to myProjectPath()
myOpenFolder(myPath)
end myOpenProjectFolder

on myOpeniTermForCurrentProject()
set myPath to myProjectPath()
myOpeniTerm(myPath)
end myOpeniTermForCurrentProject

on myOpenDerivedDataFolder()
set myRelativePath to myProjectPath() & "/DerivedData/"
if myFolderExists(myRelativePath) then
myOpenFolder(myRelativePath)
else
myOpenFolder(myXcodePath() & "/DerivedData/")
end if
end myOpenDerivedDataFolder

myOpenDerivedDataFolder()

Update script to open DerivedData folder


set open_vagrant to "cd ~/uberVU/puppet && vagrant up"

tell application "iTerm"
	activate
	try
		set mysession to current session of current terminal
	on error
		set myterm to (make new terminal)
		tell myterm
			launch session "Default"
			set mysession to current session
		end tell
	end try

	tell mysession
		write text open_vagrant
	end tell
end tell

set open_vagrant to "cd ~/uberVU/puppet/hs_analytics_vagrant && git pull && vagrant up"

tell application "iTerm"
	activate
	try
		set mysession to current session of current terminal
	on error
		set myterm to (make new terminal)
		tell myterm
			launch session "Default"
			set mysession to current session
		end tell
	end try

	tell mysession
		write text open_vagrant
	end tell
end tell

Update Vagrant path

Update Vagrant path


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 modelIt in modelList
			if comments of modelIt contains "xmod" then
				set modelSrcDir to my modelSrcDirPath(full path of modelIt)
				set targetList to my everyTargetWithBuildFilePath(_project, full path of modelIt)

				-- Create the .xcdatamodel related source group if necessary.
				if not (exists (every item reference of group of modelIt whose full path is modelSrcDir)) then
					tell group of modelIt
						make new group with properties {full path:modelSrcDir, name:text 1 thru -13 of ((name of modelIt) as string)}
					end tell
				end if
				set modelSrcGroup to item 1 of (every item reference of group of modelIt whose full path is modelSrcDir)
				tell modelSrcGroup to delete every item reference -- clear it out for population in case we didn't just create it

				--	Meat.
				do shell script "/usr/bin/mogenerator --model '" & full path of modelIt & "' --output-dir '" & modelSrcDir & "'"

				--	Build a list of resulting source files.
				tell application "System Events"
					set humanFileList to (every file of (POSIX file modelSrcDir as alias) whose (name ends with ".m" or name ends with ".mm") and name does not start with "_")
					set machineFileList to (every file of (POSIX file modelSrcDir as alias) whose (name ends with ".m" or name ends with ".mm") and name starts with "_")
					set fileList to humanFileList & machineFileList
					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 model's source group and the model's targets.
				repeat with pathIt in pathList
					tell modelSrcGroup
						set modelSrcFileRef to make new file reference with properties {full path:pathIt, name:name of (info for POSIX file pathIt)}
						repeat with targetIndex from 1 to (count of targetList)
							set targetIt to item targetIndex of targetList
							add modelSrcFileRef to targetIt
						end repeat
					end tell
				end repeat
			end if
		end repeat
	end tell
end updateProjectXmod

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

on modelSrcDirPath(modelFileUnixPath)
	set modelFilePosixRef to POSIX file modelFileUnixPath
	set modelFileAlias to modelFilePosixRef as alias

	tell application "Finder"
		set modelFileFolder to folder of modelFileAlias
		set modelFileName to name of modelFileAlias
		set modelSrcFolderName to text 1 thru -13 of modelFileName -- pull off the .xcdatamodel extension
		if not (exists folder modelSrcFolderName of modelFileFolder) then
			make folder at modelFileFolder with properties {name:modelSrcFolderName}
		end if
		set modelSrcFolder to folder modelSrcFolderName of modelFileFolder
	end tell
	return text 1 thru -2 of (POSIX path of (modelSrcFolder as alias)) -- kill the trailing slash
end modelSrcDirPath

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

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 modelIt in modelList
			if comments of modelIt contains "xmod" then
				set modelSrcDir to my modelSrcDirPath(full path of modelIt)
				set targetList to my everyTargetWithBuildFilePath(_project, full path of modelIt)

				-- Create the .xcdatamodel related source group if necessary.
				if not (exists (every item reference of group of modelIt whose full path is modelSrcDir)) then
					tell group of modelIt
						make new group with properties {full path:modelSrcDir, name:text 1 thru -13 of ((name of modelIt) as string)}
					end tell
				end if
				set modelSrcGroup to item 1 of (every item reference of group of modelIt whose full path is modelSrcDir)
				tell modelSrcGroup to delete every item reference -- clear it out for population in case we didn't just create it

				--	Meat.
				do shell script "/usr/bin/mogenerator --model '" & full path of modelIt & "' --output-dir '" & modelSrcDir & "'"

				--	Build a list of resulting source files.
				tell application "System Events"
					set modelSrcDirAlias to POSIX file modelSrcDir as alias
					set humanHeaderFileList to (every file of modelSrcDirAlias whose name ends with ".h" and name does not start with "_")
					set humanSourceFileList to (every file of modelSrcDirAlias whose (name ends with ".m" or name ends with ".mm") and name does not start with "_")
					set machineHeaderFileList to (every file of modelSrcDirAlias whose name ends with ".h" and name starts with "_")
					set machineSourceFileList to (every file of modelSrcDirAlias whose (name ends with ".m" or name ends with ".mm") and name starts with "_")
					set fileList to humanHeaderFileList & humanSourceFileList & machineHeaderFileList & machineSourceFileList
					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 model's source group and the model's targets.
				repeat with pathIt in pathList
					tell modelSrcGroup
						set modelSrcFileRef to make new file reference with properties {full path:pathIt, name:name of (info for POSIX file pathIt)}
						repeat with targetIndex from 1 to (count of targetList)
							set targetIt to item targetIndex of targetList
							add modelSrcFileRef to targetIt
						end repeat
					end tell
				end repeat
			end if
		end repeat
	end tell
end updateProjectXmod

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

on modelSrcDirPath(modelFileUnixPath)
	set modelFilePosixRef to POSIX file modelFileUnixPath
	set modelFileAlias to modelFilePosixRef as alias

	tell application "Finder"
		set modelFileFolder to folder of modelFileAlias
		set modelFileName to name of modelFileAlias
		set modelSrcFolderName to text 1 thru -13 of modelFileName -- pull off the .xcdatamodel extension
		if not (exists folder modelSrcFolderName of modelFileFolder) then
			make folder at modelFileFolder with properties {name:modelSrcFolderName}
		end if
		set modelSrcFolder to folder modelSrcFolderName of modelFileFolder
	end tell
	return text 1 thru -2 of (POSIX path of (modelSrcFolder as alias)) -- kill the trailing slash
end modelSrcDirPath

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

[CHANGE] Xmo'd: add .h human+machine header files to project (in addition to current .m + .mm files). Closes #1.


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

l
O

O

T
k
l
Q1
r(
n&
4#&

sgrp
m$%
n#
4 #

uiel
 m!"
n !"
!4 #

scra
#m
"4$

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

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

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

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

prcs
km
llmm&Notification Center

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

prcs

cwin

scra

uiel

sgrp0	thewindow	theWindow

actT0
theactions
theActions

kocl

cobj
.corecnte********

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

pcap~$NotificationCenter

cwin}&Notification Center

scra

uiel

sgrprn

pcap$NotificationCenter

cwin&Notification Center

scra

uiel

sgrp

actT"AXScrollToVisiblen

pcap$NotificationCenter

cwin&Notification Center

scra

uiel

sgrp

actTAXShowMenu
boovtrueascr
ޭ

FasdUAS 1.101.10
k
i
I(0clearnotificationsclearNotifications
k

I
.miscactvnullnull
m

@alispMacintosh HDBDNotificationCenter.app
 cuCoreServices5/:System:Library:CoreServices:NotificationCenter.app/.NotificationCenter.appMacintosh HD2System/Library/CoreServices/NotificationCenter.app/
O
O

T
k
l
Q1
r(
n&
4#&

sgrp
 m$%
n#!"
!4 ##

uiel
#m!"
"n $%
$4 &

scra
&m
%4'

cwin
'm(())&Notification Center
o0	thewindow	theWindow
R
.ascrerr ********
S01*+
*l22+,
,Q2-.
-k5//01
0r5:23
2n5845
4268

actT
5o560	thewindow	theWindow
3o0
theactions
theActions167
6l;;789
8l;;:;:SM Try to close the whole group first. If that fails, close individual windows.;<< Try to close the whole group first. If that fails, close individual windows.9=>
=X;p?@
?ZKkAB
A=KTCD
CnKPEF
E1LP

desc
FoKL0	theaction	theAction
DmPSGGHHClear All
BkWgIIJK
JrWZLM
LmWX
boovtrue
Mo
0closedKNO
NO[ePQ
PI_dR
.prcsperfnullactT
Ro_`0	theaction	theAction
Qo[\0	thewindow	theWindowOS
SSfg0	theaction	theAction
@o>?0
theactions
theActions>TU
TlqqUVW
VXqXY
XZZ[
Z=\]
\n^_
^1

desc
_o0	theaction	theAction
]m``aa
Close
[kbbcd
cref
em
boovtrue
fo
0closeddgh
gOij
iIk
.prcsperfnullactT
ko0	theaction	theAction
jo0	thewindow	theWindowhl
lS0	theaction	theAction
Yotu0
theactions
theActionsWm
ml
.R
.ascrerr ********
4
n

prcs
nm
oopp&Notification Center
mqqsevsalis\Macintosh HDBDSystem Events.app
 cuCoreServices0/:System:Library:CoreServices:System Events.app/$System Events.appMacintosh HD-System/Library/CoreServices/System Events.app/rs
rlst
tlu
uI(0clearnotificationsclearNotificationsvwxv(0clearnotificationsclearNotifications
.aevtoappnull****wyz(0clearnotificationsclearNotificationsy0	thewindow	theWindow0
theactions
theActions0	theaction	theAction
0closedz
qo(G`
.miscactvnullnull

prcs

cwin

scra

uiel

sgrp

actT

kocl

cobj
.corecnte********

desc
.prcsperfnullactTjO*/hZ*/k/k/k/EWX
Ox-EO4[lkha,aeEOjUOYh[OYO4[lkha,aeEOjUOYh[OYOPWX
h[OYaUUx{|}
.aevtoappnull****
{k~~t|}(0clearnotificationsclearNotifications*j+ascrޭ

Fix up clear notifs to put all into a handler


############################
# How to generate the PDFs #
############################
# 1. Generate Cover.pdf with latest version date
#    a. Create "ebook" folder in ~/Downloads
#    b. update date in cover.html
#    c. open in safari file:///Users/frank/Sites/ServerlessStackCom/etc/cover.html
#    d. "Export to PDF…"
#    e. Place Cover.pdf in ~/ebook folder
# 2. Ensure "ebook" folder is an option when "Export to PDF…" in Safari
# 3. In terminal, run "osascript pdf.scpt"

set theChaptersStr to "index
who-is-this-guide-for
what-does-this-guide-cover
how-to-get-help
what-is-serverless
what-is-aws-lambda
why-create-serverless-apps
create-an-aws-account
create-an-iam-user
what-is-iam
what-is-an-arn
configure-the-aws-cli
create-a-dynamodb-table
create-an-s3-bucket-for-file-uploads
create-a-cognito-user-pool
create-a-cognito-test-user
setup-the-serverless-framework
add-support-for-es6-es7-javascript
add-a-create-note-api
add-a-get-note-api
add-a-list-all-the-notes-api
add-an-update-note-api
add-a-delete-note-api
deploy-the-apis
create-a-cognito-identity-pool
cognito-user-pool-vs-identity-pool
test-the-apis
create-a-new-reactjs-app
add-app-favicons
setup-custom-fonts
setup-bootstrap
handle-routes-with-react-router
create-containers
adding-links-in-the-navbar
handle-404s
configure-aws-amplify
create-a-login-page
login-with-aws-cognito
add-the-session-to-the-state
load-the-state-from-the-session
clear-the-session-on-logout
redirect-on-login-and-logout
give-feedback-while-logging-in
create-a-signup-page
create-the-signup-form
signup-with-aws-cognito
add-the-create-note-page
call-the-create-api
upload-a-file-to-s3
list-all-the-notes
call-the-list-api
display-a-note
render-the-note-form
save-changes-to-a-note
delete-a-note
setup-secure-pages
create-a-route-that-redirects
use-the-redirect-routes
redirect-on-login
deploy-the-frontend
create-an-s3-bucket
deploy-to-s3
create-a-cloudfront-distribution
setup-your-domain-with-cloudfront
setup-www-domain-redirect
setup-ssl
deploy-updates
update-the-app
deploy-again
getting-production-ready
initialize-the-backend-repo
organize-the-backend-repo
what-is-infrastructure-as-code
configure-dynamodb-in-serverless
configure-s3-in-serverless
configure-cognito-user-pool-in-serverless
configure-cognito-identity-pool-in-serverless
use-environment-variables-in-lambda-functions
deploy-your-serverless-infrastructure
working-with-3rd-party-apis
setup-a-stripe-account
add-a-billing-api
load-secrets-from-env-yml
test-the-billing-api
unit-tests-in-serverless
automating-serverless-deployments
setting-up-your-project-on-seed
configure-secrets-in-seed
deploying-through-seed
set-custom-domains-through-seed
test-the-configured-apis
monitoring-deployments-in-seed
initialize-the-frontend-repo
manage-environments-in-create-react-app
create-a-settings-page
add-stripe-keys-to-config
create-a-billing-form
connect-the-billing-form
automating-react-deployments
create-a-build-script
setting-up-your-project-on-netlify
custom-domain-in-netlify
frontend-workflow
wrapping-up
further-reading
giving-back
changelog
staying-up-to-date
organizing-serverless-projects
cross-stack-references-in-serverless
dynamodb-as-a-serverless-service
s3-as-a-serverless-service
api-gateway-domains-across-services
cognito-as-a-serverless-service
deploying-multiple-services-in-serverless
api-gateway-and-lambda-logs
debugging-serverless-api-issues
serverless-environment-variables
stages-in-serverless-framework
configure-multiple-aws-profiles
customize-the-serverless-iam-policy
mapping-cognito-identity-id-and-user-pool-id
connect-to-api-gateway-with-iam-auth
serverless-nodejs-starter
manage-user-accounts-in-aws-amplify
handle-forgot-and-reset-password
allow-users-to-change-passwords
allow-users-to-change-their-email
code-splitting-in-create-react-app
environments-in-create-react-app
facebook-login-with-cognito-using-aws-amplify
"

set text item delimiters to "
"

set theChapters to text items of theChaptersStr

########
# Main #
########
downloadPdfs(theChapters)
mergePdfs(theChapters)

##############
# Merge PDFs #
##############
on mergePdfs(theChapters)
  set outputFile to "~/Downloads/ebook/ServerlessStack.pdf"

  set pdfFiles to "~/Downloads/ebook/Cover.pdf"
  repeat with theCurrentChapter in theChapters
    set pdfFiles to pdfFiles & " ~/Downloads/ebook/" & theCurrentChapter & ".pdf"
  end repeat

  do shell script "/System/Library/Automator/Combine\\ PDF\\ Pages.action/Contents/Resources/join.py " & "-o " & outputFile & " " & pdfFiles
end mergePdfs

#################
# Download PDFs #
#################
on downloadPdfs(theChapters)
  repeat with theCurrentChapter in theChapters
    if not(checkFileExist(theCurrentChapter)) then
      downloadPdf(theCurrentChapter)
    end if
  end repeat
end downloadPdfs

on checkFileExist(theChapterName)
  set basePath to POSIX path of (path to home folder) & "Downloads/ebook/"

  tell application "Finder"
    return exists basePath & theChapterName & ".pdf" as POSIX file
  end tell
end checkFileExist

on downloadPdf(theChapterName)
  tell application "System Events"

    tell process "Safari"
      set frontmost to true

      if ((theChapterName as string) is equal to "index") then
        set urlPath to theChapterName
      else
        set urlPath to "chapters/" & theChapterName
      end if
      open location "https://serverless-stack.com/" & urlPath & ".html"

      delay 1

      repeat until menu item "Export as PDF…" of menu "File" of menu bar 1 exists
        delay 0.2
      end repeat
      repeat until menu item "Export as PDF…" of menu "File" of menu bar 1 is enabled
        delay 0.2
      end repeat
      click menu item "Export as PDF…" of menu "File" of menu bar 1

      tell window 1
        repeat until sheet 1 exists
        end repeat

        tell sheet 1
          keystroke theChapterName
          click pop up button "Where:"

          repeat until menu 1 of pop up button "Where:" exists
          end repeat
          click menu item "ebook" of menu 1 of pop up button "Where:"
          click button "Save"
        end tell

      end tell

      click menu item "Close Tab" of menu "File" of menu bar 1

    end tell

  end tell
end downloadPdf

############################
# How to generate the PDFs #
############################
# 1. Generate Cover.pdf with latest version date
#    a. Create "ebook" folder in ~/Downloads
#    b. update date in cover.html
#    c. open in safari file:///Users/frank/Sites/ServerlessStackCom/etc/cover.html
#    d. "Export to PDF…"
#    e. Place Cover.pdf in ~/ebook folder
# 2. Ensure "ebook" folder is an option when "Export to PDF…" in Safari
# 3. In terminal, run "osascript pdf.scpt"

set theChaptersStr to "index
who-is-this-guide-for
what-does-this-guide-cover
how-to-get-help
what-is-serverless
what-is-aws-lambda
why-create-serverless-apps
create-an-aws-account
create-an-iam-user
what-is-iam
what-is-an-arn
configure-the-aws-cli
create-a-dynamodb-table
create-an-s3-bucket-for-file-uploads
create-a-cognito-user-pool
create-a-cognito-test-user
setup-the-serverless-framework
add-support-for-es6-es7-javascript
add-a-create-note-api
add-a-get-note-api
add-a-list-all-the-notes-api
add-an-update-note-api
add-a-delete-note-api
deploy-the-apis
create-a-cognito-identity-pool
cognito-user-pool-vs-identity-pool
test-the-apis
create-a-new-reactjs-app
add-app-favicons
setup-custom-fonts
setup-bootstrap
handle-routes-with-react-router
create-containers
adding-links-in-the-navbar
handle-404s
configure-aws-amplify
create-a-login-page
login-with-aws-cognito
add-the-session-to-the-state
load-the-state-from-the-session
clear-the-session-on-logout
redirect-on-login-and-logout
give-feedback-while-logging-in
create-a-signup-page
create-the-signup-form
signup-with-aws-cognito
add-the-create-note-page
call-the-create-api
upload-a-file-to-s3
list-all-the-notes
call-the-list-api
display-a-note
render-the-note-form
save-changes-to-a-note
delete-a-note
setup-secure-pages
create-a-route-that-redirects
use-the-redirect-routes
redirect-on-login
deploy-the-frontend
create-an-s3-bucket
deploy-to-s3
create-a-cloudfront-distribution
setup-your-domain-with-cloudfront
setup-www-domain-redirect
setup-ssl
deploy-updates
update-the-app
deploy-again
getting-production-ready
initialize-the-backend-repo
organize-the-backend-repo
what-is-infrastructure-as-code
configure-dynamodb-in-serverless
configure-s3-in-serverless
configure-cognito-user-pool-in-serverless
configure-cognito-identity-pool-in-serverless
use-environment-variables-in-lambda-functions
deploy-your-serverless-infrastructure
working-with-3rd-party-apis
setup-a-stripe-account
add-a-billing-api
load-secrets-from-env-yml
test-the-billing-api
unit-tests-in-serverless
automating-serverless-deployments
setting-up-your-project-on-seed
configure-secrets-in-seed
deploying-through-seed
set-custom-domains-through-seed
test-the-configured-apis
monitoring-deployments-in-seed
initialize-the-frontend-repo
manage-environments-in-create-react-app
create-a-settings-page
add-stripe-keys-to-config
create-a-billing-form
connect-the-billing-form
automating-react-deployments
create-a-build-script
setting-up-your-project-on-netlify
custom-domain-in-netlify
frontend-workflow
wrapping-up
further-reading
giving-back
changelog
staying-up-to-date
organizing-serverless-projects
cross-stack-references-in-serverless
dynamodb-as-a-serverless-service
s3-as-a-serverless-service
api-gateway-domains-across-services
cognito-as-a-serverless-service
deploying-multiple-services-in-serverless
api-gateway-and-lambda-logs
debugging-serverless-api-issues
serverless-environment-variables
stages-in-serverless-framework
configure-multiple-aws-profiles
customize-the-serverless-iam-policy
mapping-cognito-identity-id-and-user-pool-id
connect-to-api-gateway-with-iam-auth
serverless-nodejs-starter
manage-user-accounts-in-aws-amplify
handle-forgot-and-reset-password
allow-users-to-change-passwords
allow-users-to-change-their-email
code-splitting-in-create-react-app
environments-in-create-react-app
facebook-login-with-cognito-using-aws-amplify"

set text item delimiters to "
"

set theChapters to text items of theChaptersStr

########
# Main #
########
downloadPdfs(theChapters)
mergePdfs(theChapters)

##############
# Merge PDFs #
##############
on mergePdfs(theChapters)
  set outputFile to "~/Downloads/ebook/ServerlessStack.pdf"

  set pdfFiles to "~/Downloads/ebook/Cover.pdf"
  repeat with theCurrentChapter in theChapters
    set pdfFiles to pdfFiles & " ~/Downloads/ebook/" & theCurrentChapter & ".pdf"
  end repeat

  do shell script "/System/Library/Automator/Combine\\ PDF\\ Pages.action/Contents/Resources/join.py " & "-o " & outputFile & " " & pdfFiles
end mergePdfs

#################
# Download PDFs #
#################
on downloadPdfs(theChapters)
  repeat with theCurrentChapter in theChapters
    if not(checkFileExist(theCurrentChapter)) then
      downloadPdf(theCurrentChapter)
    end if
  end repeat
end downloadPdfs

on checkFileExist(theChapterName)
  set basePath to POSIX path of (path to home folder) & "Downloads/ebook/"

  tell application "Finder"
    return exists basePath & theChapterName & ".pdf" as POSIX file
  end tell
end checkFileExist

on downloadPdf(theChapterName)
  tell application "System Events"

    tell process "Safari"
      set frontmost to true

      if ((theChapterName as string) is equal to "index") then
        set urlPath to theChapterName
      else
        set urlPath to "chapters/" & theChapterName
      end if
      open location "https://serverless-stack.com/" & urlPath & ".html"

      delay 1

      repeat until menu item "Export as PDF…" of menu "File" of menu bar 1 exists
        delay 0.2
      end repeat
      repeat until menu item "Export as PDF…" of menu "File" of menu bar 1 is enabled
        delay 0.2
      end repeat
      click menu item "Export as PDF…" of menu "File" of menu bar 1

      tell window 1
        repeat until sheet 1 exists
        end repeat

        tell sheet 1
          keystroke theChapterName
          click pop up button "Where:"

          repeat until menu 1 of pop up button "Where:" exists
          end repeat
          click menu item "ebook" of menu 1 of pop up button "Where:"
          click button "Save"
        end tell

      end tell

      click menu item "Close Tab" of menu "File" of menu bar 1

    end tell

  end tell
end downloadPdf

Remove extra line breaker in pdf script

Remove extra line breaker in pdf script


-- Original script from Murphy Mac at http://murphymac.com/itunes-alarm-clock/
-- Start iTunes from cron and slowly bring volume up
tell application "iTunes"
     set volume 1
     set sound volume to 1
     play playlist "Wake-Up"
     set x to 0

     repeat until x >= 100
         set x to x + 10
         if x <= 100 then set sound volume to x
         delay 30
     end repeat
end tell

-- Original script from Murphy Mac at http://murphymac.com/itunes-alarm-clock/
-- Start iTunes from cron and slowly bring volume up
tell application "System Events"
  tell process "iTunes"
     -- Switch to the speakers in my bedroom
     set frontmost to true
     click button 15 of window 1 -- Remote Speakers Menu
     key code 115                -- Home Key
     key code 125                -- Down Arrow
     key code 125                -- Down Arrow
     key code 36                 -- Return Key
  end tell
end tell

tell application "iTunes"
  set volume 1
  set sound volume to 1
  play playlist "Wake-Up"
  set x to 0

  repeat until x >= 100
    set x to x + 10
    if x <= 100 then set sound volume to x
    delay 30
  end repeat
end tell

Update wake-up script to switch to my remote speakers


-- core configuration --

-- configurable options --

-- limit number of songs to improve efficiency
property resultLimit : 15
-- whether or not to retrieve/display album artwork for each result
property albumArtEnabled : true

-- workflow parameters --

-- workflow folders
property libraryFolder : (path to library folder from user domain as text)
property cacheFolder : (libraryFolder & "Caches:")
property alfredWorkflowDataFolder : (cacheFolder & "com.runningwithcrayons.Alfred:Workflow Data:")
property bundleId : "com.calebevans.playsong"
property workflowCacheFolder : (alfredWorkflowDataFolder & bundleId & ":") as text

-- the default icon used for search results without album artwork
property defaultIconName : "resources/icon-noartwork.png"
-- the name of the playlist used by the workflow for playing songs
property workflowPlaylistName : "Alfred Play Song"
-- list of Alfred results
property resultList : {}

-- replaces substring in string with another substring
on replace(replaceThis, replaceWith, theString)

	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to replaceThis
	set strItems to text items of theString
	set AppleScript's text item delimiters to replaceWith
	set newString to strItems as text
	set AppleScript's text item delimiters to oldDelims
	return newString

end replace

-- retrieve the plural/singular form of a quantity based on the given number
on quantifyNumber(theNumber, quantityName, pluralQuantityName)

	if theNumber is 1 then

		set theString to (theNumber as text) & space & quantityName

	else

		set theString to (theNumber as text) & space & pluralQuantityName

	end if

	return theString

end quantifyNumber

-- encodes JSON reserved characters in the given string
on encodeFeedbackChars(theString)

	set theString to replace("\\", "\\\\", theString)
	set theString to replace("\"", "\\\"", theString)
	return theString

end encodeFeedbackChars

-- decodes JSON reserved characters in the given string
on decodeFeedbackChars(theString)

	set theString to replace("\\\\", "\\", theString)
	set theString to replace("\\\"", "\"", theString)
	return theString

end decodeFeedbackChars

-- adds Alfred result to result list
on addResult(theResult)

	copy theResult to the end of resultList

end addResult

-- adds item for "No Results" message
on addNoResultsItem(query, queryType)

	addResult({uid:"no-results", valid:"no", title:"No Results Found", subtitle:("No " & queryType & "s matching '" & query & "'"), icon:defaultIconName})

end addNoResultsItem

on resultListIsFull()

	return (length of resultList is resultLimit)

end resultListIsFull

on resultListIsEmpty()

	return (length of resultList is 0)

end resultListIsFull

-- builds Alfred result item as JSON
on getResultFeedback(theResult)

	-- encode reserved JSON characters
	set resultUid to encodeFeedbackChars(uid of theResult)
	set resultValid to (valid of theResult) as text
	set resultTitle to encodeFeedbackChars(title of theResult)
	set resultSubtitle to encodeFeedbackChars(subtitle of theResult)

	if (icon of theResult) contains ":" then

		set resultIcon to encodeFeedbackChars(POSIX path of icon of theResult)

	else

		set resultIcon to icon of theResult

	end if

	set json to "{"
	set json to json & "\"uid\":\"" & resultUid & "\","
	set json to json & "\"arg\":\"" & resultUid & "\","
	set json to json & "\"valid\":\"" & resultValid & "\","
	set json to json & "\"title\":\"" & resultTitle & "\","
	set json to json & "\"subtitle\":\"" & resultSubtitle & "\","
	set json to json & "\"icon\":{\"path\":\"" & resultIcon & "\"}"
	set json to json & "}"
	return json

end getResultFeedback

-- retrieves JSON document for Alfred results
on getResultListFeedback()

	set json to "{\"items\": ["

	repeat with theResult in resultList

		set json to json & getResultFeedback(theResult)
		set json to json & ","

	end repeat

	-- remove trailing comma after last item
	set json to text 1 thru (length of json - 1) of json
	set json to json & "]}"
	return json

end getResultListFeedback

-- query path to artwork image file cached natively by Music.app in Catalina
on getSongArtworkPath(theSong)

	try

		if albumArtEnabled is false then return defaultIconName

		tell application "Music"

			-- get persistent ID of song and use it to fetch album artwork
			set songId to persistent ID of theSong
			-- the base path to the artwork file (without extension)
			set artworkPath to (do shell script "./resources/get-song-artwork-path.sh" & space & songId & space & defaultIconName)

		end tell

		return artworkPath

	on error errorMessage

		log errorMessage

	end try

end getSongArtworkPath

-- creates album artwork cache
on createWorkflowPlaylist()

	tell application "Music"

		if not (user playlist workflowPlaylistName exists) then

			make new user playlist with properties {name:workflowPlaylistName, shuffle:false}

		end if

	end tell

end createWorkflowPlaylist

on clearQueue()

	tell application "Music"

		if user playlist workflowPlaylistName exists then

			delete tracks of user playlist workflowPlaylistName

		end if

	end tell

end clearQueue

on queueSongs(theSongs)

	tell application "Music"

		repeat with theSong in theSongs

			duplicate theSong to user playlist workflowPlaylistName

		end repeat

	end tell

end queueSongs

on playQueue()

	tell application "Music"

		if number of tracks in user playlist workflowPlaylistName is not 0 then

			play user playlist workflowPlaylistName

		end if

	end tell

end playQueue

on getPlaylist(playlistId)

	tell application "Music"

		return (first playlist whose id is playlistId)

	end tell

end getPlaylist

on getPlaylistSongs(playlistId)

	tell application "Music"

		set thePlaylist to getPlaylist(playlistId) of me
		set playlistSongs to every track of thePlaylist

	end tell

	return playlistSongs

end getPlaylistSongs

-- retrieves list of songs within the given genre, sorted by artist
on getGenreSongs(genreName)

	tell application "Music"

		set genreSongs to every track of playlist 2 whose genre is genreName

	end tell

	return genreSongs

end getGenreSongs

-- retrieves list of album names for the given artist
on getArtistAlbums(artistName)

    tell application "Music"

        set artistSongs to every track of playlist 2 whose artist is artistName
        set albumNames to {}

        repeat with theSong in artistSongs

            if (album of theSong) is not in albumNames then

                set albumNames to albumNames & (album of theSong)

            end if

        end repeat

    end tell

    return albumNames

end getArtistAlbums

-- retrieves list of songs by the given artist, sorted by album
on getArtistSongs(artistName)

	tell application "Music"

		set artistSongs to every track of playlist 2 whose artist is artistName

	end tell

	return artistSongs

end getArtistSongs

-- retrieves list of songs in the given album
on getAlbumSongs(albumName)

	tell application "Music"

		set albumSongs to every track of playlist 2 whose album is albumName

	end tell

	return albumSongs

end getAlbumSongs

-- retrieves the song with the given ID
on getSong(songId)

	tell application "Music"

		set theSong to first track of playlist 2 whose database ID is songId

	end tell

	return theSong

end getSong

-- retrieves a list of objects or names matching the given query and type
on getResultsFromQuery(query, queryType)

	set evalScript to run script "
	script

		on findResults(query, queryType, resultLimit)

			tell application \"Music\"

				set theSongs to (get every track in playlist 2 whose " & queryType & " starts with query)

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains (space & query) and " & queryType & " does not start with query)

				end if

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains query and " & queryType & " does not start with query and " & queryType & " does not contain (space & query))

				end if

				if length of theSongs is 0 then

					if queryType is \"name\" then

						set theSongs to theSongs & (search playlist 2 for query only songs)

					else if queryType is not \"genre\" then

						set theSongs to theSongs & (search playlist 2 for query only " & queryType & "s)

					end if

				end if

				if queryType is \"name\" then

					if length of theSongs > resultLimit then

						set theSongs to items 1 thru resultLimit of theSongs

					end if

					set theResults to theSongs

				else

					set theResults to {}

					repeat with theSong in theSongs

						if length of theResults is resultLimit then exit repeat

						set theResult to " & queryType & " of theSong

						if theResult is not in theResults then

							set theResults to theResults & theResult

						end if

					end repeat

				end if

			end tell

			return theResults

		end findResults

	end script
	"

	evalScript's findResults(query, queryType, resultLimit)

end getResultsFromQuery

-- returns the given string with leading and trailing whitespace removed
on trimWhitespace(theString)

	-- trim leading whitespace
	repeat while theString begins with space

		if length of theString is 1 then return ""
		set theString to text 2 thru end of theString

	end repeat

	-- trim trailing whitespace
	repeat while theString ends with space

		set theString to text 1 thru ((length of theString) - 1) of theString

	end repeat

	return theString

end trimWhitespace

-- queues the song with the given ID
on queueSong(songId)

	set theSong to getSong(songId)
	queueSongs({theSong})

end queueSong

-- queues all songs belonging to the given album
on queueAlbum(albumName)

	set albumName to decodeFeedbackChars(albumName)
	set albumSongs to getAlbumSongs(albumName)
	queueSongs(albumSongs)

end queueAlbum

-- queues all songs by the given artist
on queueArtist(artistName)

	set artistName to decodeFeedbackChars(artistName)
	set artistSongs to getArtistSongs(artistName)
	queueSongs(artistSongs)

end queueArtist

-- queues all songs within the given genre
on queueGenre(genreName)

	set genreName to decodeFeedbackChars(genreName)
	set genreSongs to getGenreSongs(genreName)
	queueSongs(genreSongs)

end queueGenre

-- queues all songs in the given playlist
on queuePlaylist(playlistId)

	set playlistSongs to getPlaylistSongs(playlistId)
	queueSongs(playlistSongs)

end queuePlaylist

-- parses the given result query to retrieve type and id of item to queue
on parseResultQuery(query)

	set pos to offset of "-" in query
	set theType to text 1 thru (pos - 1) of query
	set theId to text (pos + 1) thru end of query
	return {type:theType, id:theId}

end parseResultQuery

on queue(query)

	set typeAndId to parseResultQuery(query)
	set theType to type of typeAndId
	set theId to id of typeAndId

	createWorkflowPlaylist()

	if theType is "song" then
		queueSong(theId)
	else if theType is "album" then
		queueAlbum(theId)
	else if theType is "artist" then
		queueArtist(theId)
	else if theType is "genre" then
		queueGenre(theId)
	else if theType is "playlist" then
		queuePlaylist(theId)
	else
		log "Unsupported type: " & theType
	end if

end queue

on play(query)

	clearQueue()
	queue(query)
	playQueue()

end play

-- core configuration --

-- configurable options --

-- limit number of songs to improve efficiency
property resultLimit : 15
-- whether or not to retrieve/display album artwork for each result
property albumArtEnabled : true

-- workflow parameters --

-- workflow folders
property libraryFolder : (path to library folder from user domain as text)
property cacheFolder : (libraryFolder & "Caches:")
property alfredWorkflowDataFolder : (cacheFolder & "com.runningwithcrayons.Alfred:Workflow Data:")
property bundleId : "com.calebevans.playsong"
property workflowCacheFolder : (alfredWorkflowDataFolder & bundleId & ":") as text

-- the default icon used for search results without album artwork
property defaultIconName : "resources/icon-noartwork.png"
-- the name of the playlist used by the workflow for playing songs
property workflowPlaylistName : "Alfred Play Song"
-- list of Alfred results
property resultList : {}

-- replaces substring in string with another substring
on replace(replaceThis, replaceWith, theString)

	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to replaceThis
	set strItems to text items of theString
	set AppleScript's text item delimiters to replaceWith
	set newString to strItems as text
	set AppleScript's text item delimiters to oldDelims
	return newString

end replace

-- retrieve the plural/singular form of a quantity based on the given number
on quantifyNumber(theNumber, quantityName, pluralQuantityName)

	if theNumber is 1 then

		set theString to (theNumber as text) & space & quantityName

	else

		set theString to (theNumber as text) & space & pluralQuantityName

	end if

	return theString

end quantifyNumber

-- encodes JSON reserved characters in the given string
on encodeFeedbackChars(theString)

	set theString to replace("\\", "\\\\", theString)
	set theString to replace("\"", "\\\"", theString)
	return theString

end encodeFeedbackChars

-- decodes JSON reserved characters in the given string
on decodeFeedbackChars(theString)

	set theString to replace("\\\\", "\\", theString)
	set theString to replace("\\\"", "\"", theString)
	return theString

end decodeFeedbackChars

-- adds Alfred result to result list
on addResult(theResult)

	copy theResult to the end of resultList

end addResult

-- adds item for "No Results" message
on addNoResultsItem(query, queryType)

	addResult({uid:"no-results", valid:"no", title:"No Results Found", subtitle:("No " & queryType & "s matching '" & query & "'"), icon:defaultIconName})

end addNoResultsItem

on resultListIsFull()

	return (length of resultList is resultLimit)

end resultListIsFull

on resultListIsEmpty()

	return (length of resultList is 0)

end resultListIsFull

-- builds Alfred result item as JSON
on getResultFeedback(theResult)

	-- encode reserved JSON characters
	set resultUid to encodeFeedbackChars(uid of theResult)
	set resultValid to (valid of theResult) as text
	set resultTitle to encodeFeedbackChars(title of theResult)
	set resultSubtitle to encodeFeedbackChars(subtitle of theResult)

	if (icon of theResult) contains ":" then

		set resultIcon to encodeFeedbackChars(POSIX path of icon of theResult)

	else

		set resultIcon to icon of theResult

	end if

	set json to "{"
	set json to json & "\"uid\":\"" & resultUid & "\","
	set json to json & "\"arg\":\"" & resultUid & "\","
	set json to json & "\"valid\":\"" & resultValid & "\","
	set json to json & "\"title\":\"" & resultTitle & "\","
	set json to json & "\"subtitle\":\"" & resultSubtitle & "\","
	set json to json & "\"text\":{"
	set json to json & "\"copy\":\"" & resultTitle & "\","
	set json to json & "\"largetype\":\"" & resultTitle & "\""
	set json to json & "},"
	set json to json & "\"icon\":{\"path\":\"" & resultIcon & "\"}"
	set json to json & "}"
	return json

end getResultFeedback

-- retrieves JSON document for Alfred results
on getResultListFeedback()

	set json to "{\"items\": ["

	repeat with theResult in resultList

		set json to json & getResultFeedback(theResult)
		set json to json & ","

	end repeat

	-- remove trailing comma after last item
	set json to text 1 thru (length of json - 1) of json
	set json to json & "]}"
	return json

end getResultListFeedback

-- query path to artwork image file cached natively by Music.app in Catalina
on getSongArtworkPath(theSong)

	try

		if albumArtEnabled is false then return defaultIconName

		tell application "Music"

			-- get persistent ID of song and use it to fetch album artwork
			set songId to persistent ID of theSong
			-- the base path to the artwork file (without extension)
			set artworkPath to (do shell script "./resources/get-song-artwork-path.sh" & space & songId & space & defaultIconName)

		end tell

		return artworkPath

	on error errorMessage

		log errorMessage

	end try

end getSongArtworkPath

-- creates album artwork cache
on createWorkflowPlaylist()

	tell application "Music"

		if not (user playlist workflowPlaylistName exists) then

			make new user playlist with properties {name:workflowPlaylistName, shuffle:false}

		end if

	end tell

end createWorkflowPlaylist

on clearQueue()

	tell application "Music"

		if user playlist workflowPlaylistName exists then

			delete tracks of user playlist workflowPlaylistName

		end if

	end tell

end clearQueue

on queueSongs(theSongs)

	tell application "Music"

		repeat with theSong in theSongs

			duplicate theSong to user playlist workflowPlaylistName

		end repeat

	end tell

end queueSongs

on playQueue()

	tell application "Music"

		if number of tracks in user playlist workflowPlaylistName is not 0 then

			play user playlist workflowPlaylistName

		end if

	end tell

end playQueue

on getPlaylist(playlistId)

	tell application "Music"

		return (first playlist whose id is playlistId)

	end tell

end getPlaylist

on getPlaylistSongs(playlistId)

	tell application "Music"

		set thePlaylist to getPlaylist(playlistId) of me
		set playlistSongs to every track of thePlaylist

	end tell

	return playlistSongs

end getPlaylistSongs

-- retrieves list of songs within the given genre, sorted by artist
on getGenreSongs(genreName)

	tell application "Music"

		set genreSongs to every track of playlist 2 whose genre is genreName

	end tell

	return genreSongs

end getGenreSongs

-- retrieves list of album names for the given artist
on getArtistAlbums(artistName)

    tell application "Music"

        set artistSongs to every track of playlist 2 whose artist is artistName
        set albumNames to {}

        repeat with theSong in artistSongs

            if (album of theSong) is not in albumNames then

                set albumNames to albumNames & (album of theSong)

            end if

        end repeat

    end tell

    return albumNames

end getArtistAlbums

-- retrieves list of songs by the given artist, sorted by album
on getArtistSongs(artistName)

	tell application "Music"

		set artistSongs to every track of playlist 2 whose artist is artistName

	end tell

	return artistSongs

end getArtistSongs

-- retrieves list of songs in the given album
on getAlbumSongs(albumName)

	tell application "Music"

		set albumSongs to every track of playlist 2 whose album is albumName

	end tell

	return albumSongs

end getAlbumSongs

-- retrieves the song with the given ID
on getSong(songId)

	tell application "Music"

		set theSong to first track of playlist 2 whose database ID is songId

	end tell

	return theSong

end getSong

-- retrieves a list of objects or names matching the given query and type
on getResultsFromQuery(query, queryType)

	set evalScript to run script "
	script

		on findResults(query, queryType, resultLimit)

			tell application \"Music\"

				set theSongs to (get every track in playlist 2 whose " & queryType & " starts with query)

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains (space & query) and " & queryType & " does not start with query)

				end if

				if length of theSongs < resultLimit then

					set theSongs to theSongs & (get every track in playlist 2 whose " & queryType & " contains query and " & queryType & " does not start with query and " & queryType & " does not contain (space & query))

				end if

				if length of theSongs is 0 then

					if queryType is \"name\" then

						set theSongs to theSongs & (search playlist 2 for query only songs)

					else if queryType is not \"genre\" then

						set theSongs to theSongs & (search playlist 2 for query only " & queryType & "s)

					end if

				end if

				if queryType is \"name\" then

					if length of theSongs > resultLimit then

						set theSongs to items 1 thru resultLimit of theSongs

					end if

					set theResults to theSongs

				else

					set theResults to {}

					repeat with theSong in theSongs

						if length of theResults is resultLimit then exit repeat

						set theResult to " & queryType & " of theSong

						if theResult is not in theResults then

							set theResults to theResults & theResult

						end if

					end repeat

				end if

			end tell

			return theResults

		end findResults

	end script
	"

	evalScript's findResults(query, queryType, resultLimit)

end getResultsFromQuery

-- returns the given string with leading and trailing whitespace removed
on trimWhitespace(theString)

	-- trim leading whitespace
	repeat while theString begins with space

		if length of theString is 1 then return ""
		set theString to text 2 thru end of theString

	end repeat

	-- trim trailing whitespace
	repeat while theString ends with space

		set theString to text 1 thru ((length of theString) - 1) of theString

	end repeat

	return theString

end trimWhitespace

-- queues the song with the given ID
on queueSong(songId)

	set theSong to getSong(songId)
	queueSongs({theSong})

end queueSong

-- queues all songs belonging to the given album
on queueAlbum(albumName)

	set albumName to decodeFeedbackChars(albumName)
	set albumSongs to getAlbumSongs(albumName)
	queueSongs(albumSongs)

end queueAlbum

-- queues all songs by the given artist
on queueArtist(artistName)

	set artistName to decodeFeedbackChars(artistName)
	set artistSongs to getArtistSongs(artistName)
	queueSongs(artistSongs)

end queueArtist

-- queues all songs within the given genre
on queueGenre(genreName)

	set genreName to decodeFeedbackChars(genreName)
	set genreSongs to getGenreSongs(genreName)
	queueSongs(genreSongs)

end queueGenre

-- queues all songs in the given playlist
on queuePlaylist(playlistId)

	set playlistSongs to getPlaylistSongs(playlistId)
	queueSongs(playlistSongs)

end queuePlaylist

-- parses the given result query to retrieve type and id of item to queue
on parseResultQuery(query)

	set pos to offset of "-" in query
	set theType to text 1 thru (pos - 1) of query
	set theId to text (pos + 1) thru end of query
	return {type:theType, id:theId}

end parseResultQuery

on queue(query)

	set typeAndId to parseResultQuery(query)
	set theType to type of typeAndId
	set theId to id of typeAndId

	createWorkflowPlaylist()

	if theType is "song" then
		queueSong(theId)
	else if theType is "album" then
		queueAlbum(theId)
	else if theType is "artist" then
		queueArtist(theId)
	else if theType is "genre" then
		queueGenre(theId)
	else if theType is "playlist" then
		queuePlaylist(theId)
	else
		log "Unsupported type: " & theType
	end if

end queue

on play(query)

	clearQueue()
	queue(query)
	playQueue()

end play

Add support for cmd-c and cmd-l on results


#!/usr/bin/env osascript

-- Fist: Click Install Button
set timeoutSeconds to 1.0

my doWithTimeout("click UI Element \"Install\" of window 1 of application process \"Install Command Line Developer Tools\"", timeoutSeconds)
my doWithTimeout("click UI Element \"Agree\" of window 1 of application process \"Install Command Line Developer Tools\"", 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
set timeoutSeconds to 1.0

my doWithTimeout("click UI Element \"Install\" of window 1 of application process \"Install Command Line Developer Tools\"", timeoutSeconds)
my doWithTimeout("click UI Element \"Agree\" of window 1 of application process \"Install Command Line Developer Tools\"", 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 errorMessage & "; Can not " & uiScript
      end if
    end try
  end repeat
end doWithTimeout

Fix error message

Fix error message


global B
set B to my _load_bundler()
property the_bundler : missing value

(* ///
MAIN API FUNCTIONS
/// *)

on load_utility(_name, _version, _json)
	if the_bundler is not equal to true then
		my _bootstrap()
	end if

	try
		set utility to B's load_utility(_name, _version, _json)
		return utility
	on error
		set B to my _load_bundler()
		try
			set utility to B's load_utility(_name, _version, _json)
			return utility
		on error
			set msg to "Error retrieving utility `" & _name & "`"
			B's _log("get_icon", "DEBUG", msg)
			error msg number num
		end try
	end try
end load_utility

on get_icon(_font, _name, _color, _alter)
	if the_bundler is not equal to true then
		my _bootstrap()
	end if

	try
		set icon to B's get_icon(_font, _name, _color, _alter)
		return icon
	on error
		set B to my _load_bundler()
		try
			set icon to B's get_icon(_font, _name, _color, _alter)
			return "yes: " & icon
		on error msg number num
			set msg to "Error retrieving icon `" & _font & "`'s `{}`"
			B's _log("get_icon", "DEBUG", B's _format(msg, _name))
			error msg number num
		end try
	end try
end get_icon

(* ///
SUB-API FUNCTIONS
/// *)

on _bootstrap()
	(*Check if bundler bash bundlet is installed and install it if not.

    	:returns: ``None``

    	*)
	if the_bundler is not equal to missing value then
		return true
	else
		set shell_bundlet to quoted form of (my _load_shell_bundlet())
		set shell_cmd to shell_bundlet & " utility CocoaDialog"
		set cmd to my _prepare_cmd(shell_cmd)
		do shell script cmd
		set the_bundler to true
	end if

end _bootstrap

(* ///
HELPER FUNCTIONS
/// *)

on _load_bundler()
	set applescript_test_dir to my _pwd()
	set test_dir to my _dirname(applescript_test_dir)
	set bundler_dir to my _dirname(test_dir)
	set as_bundler to bundler_dir & "bundler/AlfredBundler.scpt"
	return load script as_bundler
end _load_bundler

on _load_shell_bundlet()
	set bundlet to (my _pwd()) & "alfred.bundler.sh"
end _load_shell_bundlet

on _prepare_cmd(cmd)
	set pwd to quoted form of (my _pwd())
	return "cd " & pwd & "; bash " & cmd
end _prepare_cmd

on _pwd()
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "/"}
	set _path to (text items 1 thru -2 of (POSIX path of (path to me)) as string) & "/"
	set AppleScript's text item delimiters to ASTID
	return _path
end _pwd

on _dirname(_file)
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "/"}
	set _path to (text items 1 thru -3 of _file as string) & "/"
	set AppleScript's text item delimiters to ASTID
	return _path
end _dirname

global B
set B to my _load_bundler()
property the_bundler : missing value

(* ///
MAIN API FUNCTIONS
/// *)

on load_utility(_name, _version, _json)
	if the_bundler is not equal to true then
		my _bootstrap()
	end if

	try
		set utility to B's load_utility(_name, _version, _json)
		return utility
	on error
		set B to my _load_bundler()
		try
			set utility to B's load_utility(_name, _version, _json)
			return utility
		on error
			set msg to "Error retrieving utility `" & _name & "`"
			B's _log("get_icon", "DEBUG", msg)
			error msg number num
		end try
	end try
end load_utility

on get_icon(_font, _name, _color, _alter)
	if the_bundler is not equal to true then
		my _bootstrap()
	end if

	try
		set icon to B's get_icon(_font, _name, _color, _alter)
		return icon
	on error
		set B to my _load_bundler()
		try
			set icon to B's get_icon(_font, _name, _color, _alter)
			return icon
		on error msg number num
			set msg to "Error retrieving icon `" & _font & "`'s `{}`"
			B's _log("get_icon", "DEBUG", B's _format(msg, _name))
			error msg number num
		end try
	end try
end get_icon

(* ///
SUB-API FUNCTIONS
/// *)

on _bootstrap()
	(*Check if bundler bash bundlet is installed and install it if not.

    	:returns: ``None``

    	*)
	--if the_bundler is not equal to missing value then
	--	return the_bundler
	--else
	set shell_bundlet to quoted form of (my _load_shell_bundlet())
	set shell_cmd to shell_bundlet & " utility CocoaDialog"
	set cmd to my _prepare_cmd(shell_cmd)
	do shell script cmd
	set the_bundler to true
	--end if

end _bootstrap

(* ///
HELPER FUNCTIONS
/// *)

on _load_bundler()
	set applescript_test_dir to my _pwd()
	set test_dir to my _dirname(applescript_test_dir)
	set bundler_dir to my _dirname(test_dir)
	set as_bundler to bundler_dir & "bundler/AlfredBundler.scpt"
	return load script as_bundler
end _load_bundler

on _load_shell_bundlet()
	set bundlet to (my _pwd()) & "alfred.bundler.sh"
end _load_shell_bundlet

on _prepare_cmd(cmd)
	set pwd to quoted form of (my _pwd())
	set testing_var to "export AB_BRANCH=devel; "
	return testing_var & "cd " & pwd & "; bash " & cmd
end _prepare_cmd

on _pwd()
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "/"}
	set _path to (text items 1 thru -2 of (POSIX path of (path to me)) as string) & "/"
	set AppleScript's text item delimiters to ASTID
	return _path
end _pwd

on _dirname(_file)
	set {ASTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "/"}
	set _path to (text items 1 thru -3 of _file as string) & "/"
	set AppleScript's text item delimiters to ASTID
	return _path
end _dirname

set AB_BRANCH for testing `_bootstrap()`


-- Update ctags file for currently active project if it already exists

-- Determine the project root directory
on getProjectRoot()
	tell application "BBEdit"
		set activeWindow to front window

		if (class of activeWindow is not project window) then
			return missing value
		end if

		set projectDocument to project document of activeWindow

		if ((count of items of projectDocument) > 0) then
			set firstItem to (item 1 of projectDocument) as alias
		else
			if file of document of activeWindow is not missing value then
				set firstItem to (file of document of activeWindow) as alias
			else
				-- File is unsaved
				return missing value
			end if
		end if

		if (on disk of projectDocument) then
			tell application "Finder"
				set projectDir to (container of (file of projectDocument))
				set firstItemDir to (container of firstItem)
			end tell

			if (firstItemDir is equal to projectDir) then
				-- Use project file
				return projectDir as alias
			end if
		end if

		-- Project is either insta-project or external
		-- Use first item
		return firstItem
	end tell
end getProjectRoot

on run
	set projectRoot to getProjectRoot()
	if projectRoot is missing value then
		return
	end if

	-- Check for existing tag file
	-- if it exists, rebuild it
	-- This allows a project to opt-in to tags by creating the tags file once
	tell application "Finder"
		if exists ((projectRoot as string) & "tags") then
			run script ((((container of (path to me)) as string) & "Scripts:Ctags:Create.scpt") as alias)
		end if
	end tell
end run

-- Update ctags file for currently active project if it already exists

-- Determine the project root directory
on getProjectRoot()
	tell application "BBEdit"
		set activeWindow to front window

		if (class of activeWindow is not project window) then
			return missing value
		end if

		set projectDocument to project document of activeWindow

		if ((count of items of projectDocument) > 0) then
			set firstItem to (item 1 of projectDocument) as alias
		else
			if file of document of activeWindow is not missing value then
				set firstItem to (file of document of activeWindow) as alias
			else
				-- File is unsaved
				return missing value
			end if
		end if

		if (on disk of projectDocument) then
			tell application "Finder"
				set projectDir to (container of (file of projectDocument))
				set firstItemDir to (container of firstItem)
			end tell

			if (firstItemDir is equal to projectDir) then
				-- Use project file
				return projectDir as alias
			end if
		end if

		-- Project is either insta-project or external
		-- Use first item
		return firstItem
	end tell
end getProjectRoot

on run
	set projectRoot to getProjectRoot()
	if projectRoot is missing value then
		return
	end if

	-- Check for existing tag file
	-- if it exists, rebuild it
	-- This allows a project to opt-in to tags by creating the tags file once
	tell application "Finder"
		if exists ((projectRoot as string) & "tags") then
			run script ((((container of (path to me)) as string) & "Create.scpt") as alias)
		end if
	end tell
end run

Fix ctags update script

Fix ctags update script

Path to create script was wrong


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

property pstrDBPath : "$HOME/Library/Caches/com.omnigroup.OmniFocus/OmniFocusDatabase2"
property pstrMinOSX : "10.6"

property pTimeOut : 20

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

-- Copyright  2012, 2013, 2014 Robin Trew
--
-- Permission is hereby granted, free of charge,
-- to any person obtaining a copy of this software
-- and associated documentation files (the "Software"),
-- to deal in the Software without restriction,
-- including without limitation the rights to use, copy,
-- modify, merge, publish, distribute, sublicense,
-- and/or sell copies of the Software, and to permit persons
-- to whom the Software is furnished to do so,
-- subject to the following conditions:
--
-- *******
-- The above copyright notice and this permission notice
-- shall be included in ALL copies
-- or substantial portions of the Software.
-- *******
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
-- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

property pToClipboard : "Copy list to clipboard"

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

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

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

	")

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

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

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

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

property pstrDBPath : "$HOME/Library/Caches/com.omnigroup.OmniFocus/OmniFocusDatabase2"
property pstrMinOSX : "10.6"

property pTimeOut : 20

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

-- Copyright  2012, 2013, 2014 Robin Trew
--
-- Permission is hereby granted, free of charge,
-- to any person obtaining a copy of this software
-- and associated documentation files (the "Software"),
-- to deal in the Software without restriction,
-- including without limitation the rights to use, copy,
-- modify, merge, publish, distribute, sublicense,
-- and/or sell copies of the Software, and to permit persons
-- to whom the Software is furnished to do so,
-- subject to the following conditions:
--
-- *******
-- The above copyright notice and this permission notice
-- shall be included in ALL copies
-- or substantial portions of the Software.
-- *******
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
-- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

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

property pToClipboard : "Copy list to clipboard"

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

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

if pstrDBPath  "" then
        set commands to "
	select 'INBOX GROUPS & ACTIONS', count(*) from task where (inInbox=1);
	select '    Inbox action groups', count(*) from task where (inInbox=1) and (childrenCount>0);
	select '    Inbox actions', count(*) from task where (inInbox=1) and (childrenCount=0);
	select null;
	select 'FOLDERS'	, count(*) from folder;
	select '    Active folders', count(*) from folder where effectiveActive=1;
	select '    Dropped folders', count(*) from folder where effectiveActive=0;
	select null;
	select 'PROJECTS', count(*) from projectInfo where containsSingletonActions=0;
	select '    Active projects', count(*) from projectInfo where (containsSingletonActions=0) and (status='active');
	select '            Current projects', count(*) from projectInfo p join task t on t.projectinfo=p.pk where (p.containsSingletonActions=0) and (p.folderEffectiveActive=1) and (p.status='active') and (t.dateToStart is null or t.dateToStart < (strftime('%s','now') - strftime('%s','2001-01-01')));
	select '            Pending projects', count(*) from projectInfo p join task t on t.projectinfo=p.pk where (p.containsSingletonActions=0) and (p.folderEffectiveActive=1) and (p.status='active') and (t.dateToStart > (strftime('%s','now') - strftime('%s','2001-01-01')));
	select '    On-hold projects', count(*) from projectInfo where (containsSingletonActions=0) and (status='inactive');
	select '    Completed projects', count(*) from projectInfo where (containsSingletonActions=0) and (status='done');
	select '    Dropped projects', count(*) from projectInfo where (containsSingletonActions=0) and (( status='dropped') or (folderEffectiveActive=0));
	select null;
	select 'SINGLE ACTION LISTS', count(*) from projectInfo where containsSingletonActions=1;
	select '    Active single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (status='active');
	select '    On-hold single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (status='inactive');
	select '    Completed single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (status='done');
	select '    Dropped single action lists', count(*) from projectInfo where (containsSingletonActions=1) and (( status='dropped') or (folderEffectiveActive=0));
	select null;
	select 'CONTEXTS', count(*) from context;
	select '    Active contexts', count(*) from context where (effectiveActive=1) and (allowsNextAction=1);
	select '    On-hold contexts', count(*) from context where (effectiveActive=1) and allowsNextAction=0;
	select '    Dropped contexts', count(*) from context where effectiveActive=0;
	select null;
	select 'ACTION GROUPS', count(*) from task where (projectinfo is null) and (childrenCount>0);
	select '    Remaining action groups', count(*) from task where (projectinfo is null) and (dateCompleted is null) and (childrenCount>0);
	select '    Completed action groups', count(dateCompleted) from task where (projectinfo is null) and (childrenCount>0);
	select null;
	select 'ACTIONS', count(*) from task where (projectinfo is null) and (childrenCount=0);
	select '    Completed actions', count(dateCompleted) from task where (projectinfo is null) and (childrenCount=0);
	select '    Dropped project actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp where (projectinfo is null) and (childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is not null and (tp.status='dropped' or tp.folderEffectiveActive=0));
	select '    Dropped context actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and c.effectiveActive= 0;
	select '    Remaining actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1);
	select '        Actions in Projects on hold', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is not null and tp.status='inactive');
	select '        Actions in Contexts on hold', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is not null and c.allowsNextAction=0);
	select '        Blocked actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=1;
	select '        	Blocked by future start date', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=1
				and tp.blockedByFutureStartDate=1;
	select '        	Sequentially blocked', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=1
				and tp.blockedByFutureStartDate=0;
	select '        Available actions', count(*) from (task t left join projectinfo p on t.containingProjectinfo=p.pk) tp left join context c on tp.context=c.persistentIdentifier where (projectinfo is null) and (tp.childrenCount=0)  and (dateCompleted is null)
				and (tp.containingProjectinfo is null or (tp.status !='dropped' and tp.folderEffectiveActive=1))
				and (tp.context is null or c.effectiveActive= 1)
				and (tp.containingProjectInfo is null or tp.status!='inactive')
				and (tp.context is null or c.allowsNextAction=1)
				and tp.blocked=0;

	"
	set strCmd to "sqlite3 -separator ': ' \"" & pstrDBPath & "\" " & quoted form of (commands)

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

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

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

simplify quoting

simplify quoting


-- iterm.applescript
---
-- this script requires an argument that represent the command to execute

on run argv

    set command to (item 1 of argv)

    tell application iTerm2
      tell current session of first window
        write text command
      end tell
    end tell
end run

on run argv
    set command to (item 1 of argv)

    tell application iTerm2
      set _window to (current window)

      if _window is equal to missing value then
        create window with default profile
      end if

      tell current window
        tell current session
          write text command
        end tell
      end tell
    end tell
end run

Update script

Update script


#!/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 Tools\"", timeoutSeconds)
my doWithTimeout("click UI Element \"Agree\" of window 1 of application process \"Install Command Line Developer Tools\"", 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
set timeoutSeconds to 1.0

my doWithTimeout("click UI Element \"Install\" of window 1 of application process \"Install Command Line Developer Tools\"", timeoutSeconds)
my doWithTimeout("click UI Element \"Agree\" of window 1 of application process \"Install Command Line Developer Tools\"", 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

Change delay

Change delay


FasdUAS 1.101.10
k
l

O

r

m
boovtrue
n

1

pisf

4

prcs
mcc3d_python
	msevsalisvTaoECeH+1System Events.appee	 CoreServicesE10/4Tao:System:Library:CoreServices:System Events.app$System Events.appTao-System/Library/CoreServices/System Events.app/
l'
O'
O&
O%
I$
.prcskprsnullctxt
mm

faal
m
eMdsKcmd
msevsalisvTaoECeH+1System Events.appee	 CoreServicesE10/4Tao:System:Library:CoreServices:System Events.app$System Events.appTao-System/Library/CoreServices/System Events.app/
4

prcs
 m!!""cc3d_python
m##sevsalisvTaoECeH+1System Events.appee	 CoreServicesE10/4Tao:System:Library:CoreServices:System Events.app$System Events.appTao-System/Library/CoreServices/System Events.app/$%$
.aevtoappnull****%&'(
.aevtoappnull****
&k'))**'(	!

prcs

pisf

faal
eMdsKcmd
.prcskprsnullctxt(
e*/,FUO*/	lUUUascrޭ

FasdUAS 1.101.10
k
l
I
.sysodelanullnmbr
m

	l
O

r

m

boovtrue
n
1

pisf
4

prcs
m
cc3d_python

msevsalisvTaoECeH+1System Events.appee	 CoreServicesE10/4Tao:System:Library:CoreServices:System Events.app$System Events.appTao-System/Library/CoreServices/System Events.app/

l-
O-
O,
O+
I#*
.prcskprsnullctxt
m#$  !!m"

faal
"m%&
eMdsKcmd
m ##sevsalisvTaoECeH+1System Events.appee	 CoreServicesE10/4Tao:System:Library:CoreServices:System Events.app$System Events.appTao-System/Library/CoreServices/System Events.app/
4$

prcs
$m%%&&cc3d_python
m''sevsalisvTaoECeH+1System Events.appee	 CoreServicesE10/4Tao:System:Library:CoreServices:System Events.app$System Events.appTao-System/Library/CoreServices/System Events.app/()(
.aevtoappnull****)*+,
.aevtoappnull****
*k---..	//+,%
.sysodelanullnmbr

prcs

pisf

faal
eMdsKcmd
.prcskprsnullctxt.jO
e*/,FUO*/	l
UUUascr
ޭ

Fix minor error on mac where autorun CompuCell would send the run simulation command to CompuCell before it was open


-- This Applescript changes all of the icon labels on the disk image to
--  a different color.
-- Usage: osascript set-labels.applescript <volume-name>
-- where <volume-name> is the volume name of the disk image.

on run argv
	tell application "Finder"
		set diskname to item 1 of argv
		set file_list to every file in disk diskname
		repeat with i in file_list
			if the name of i is "Applications" then
				set the position of i to {368, 135}
			else if the name of i ends with ".app" then
				set the position of i to {134, 135}
			end if
		-- The magic happens here: This will set the label color of
		--  the file icon. Change the 7 to change the color: 0 is no
		--  label, then red, orange, yellow, green, blue, purple, or gray.
			set the label index of i to 7
		end repeat
	end tell
end run

-- This Applescript changes all of the icon labels on the disk image to
--  a different color.
-- Usage: osascript set-labels.applescript <volume-name>
-- where <volume-name> is the volume name of the disk image.

on run argv
	tell application "Finder"
		set diskname to item 1 of argv
		tell disk diskname
			set file_list to every file
			repeat with i in file_list
				if the name of i is "Applications" then
					set the position of i to {368, 135}
				else if the name of i ends with ".app" then
					set the position of i to {134, 135}
				end if
			-- The magic happens here: This will set the label color of
			--  the file icon. Change the 7 to change the color: 0 is no
			--  label, then red, orange, yellow, green, blue, purple, or gray.
				set the label index of i to 7
			end repeat
			update without registering applications
			delay 4
		end tell
	end tell
end run

Make Mac disk image packager set application icon location more reliably.


-- vim-anywhere - use Vim whenever, wherever
-- Author: Chris Knadler
-- Homepage: https://www.github.com/cknadler/vim-anywhere
--
-- Open the keyboard shortcuts tab of keyboard system preferences

tell application "System Preferences"
  activate
  reveal anchor "shortcutsTab" of pane id "com.apple.preference.keyboard"
  keystroke tab
  keystroke key code 125
  keystroke key code 125
  keystroke key code 125
end tell

-- vim-anywhere - use Vim whenever, wherever
-- Author: Chris Knadler
-- Homepage: https://www.github.com/cknadler/vim-anywhere
--
-- Open the "Services" keyboard shortcuts tab of keyboard system preferences

tell application "System Preferences"
  activate
  reveal anchor "shortcutsTab" of pane id "com.apple.preference.keyboard"
  keystroke tab
  repeat 3 times
    key code 125
  end repeat
end tell

Fix open_shortcuts

Fix open_shortcuts


tell application "iTunes"
  set specified_tracks to (every track whose #{conditions})

	set json to "["

	repeat with specified_track in specified_tracks
    set props to {}

    set end of props to "{"
    set end of props to ("\"persistent_id\":\"" & persistent ID of specified_track & "\",")
    set end of props to ("\"name\":\"" & my escape_quote(name of specified_track) & "\",")
    set end of props to ("\"album\":\"" & my escape_quote(album of specified_track) & "\",")
    set end of props to ("\"artist\":\"" & my escape_quote(artist of specified_track) & "\",")
    set end of props to ("\"track_count\":\"" & track count of specified_track & "\",")
    set end of props to ("\"track_number\":\"" & track number of specified_track & "\"")
    set end of props to "}"

    set json to json & props as string & ","
  end repeat

  set json to json & "null]"
  return json

end tell

on escape_quote(someText)
 return replaceText(someText, "\"", "\\\"")
end escape_quote

(*
   https://discussions.apple.com/thread/4588230?start=0&tstart=0
*)
on replaceText(someText, oldItem, newItem)
     set {tempTID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, oldItem}
     try
          set {itemList, AppleScript's text item delimiters} to {text items of someText, newItem}
          set {someText, AppleScript's text item delimiters} to {itemList as text, tempTID}
     on error errorMessage number errorNumber -- oops
          set AppleScript's text item delimiters to tempTID
          error errorMessage number errorNumber -- pass it on
     end try

     return someText
end replaceText

Delete trailing whitespace

Delete trailing whitespace