├── .github
├── FUNDING.yml
└── workflows
│ └── codeql-analysis.yml
├── .gitignore
├── Caffeinate.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ └── Coffee.icns
│ └── Scripts
│ ├── caffeinate.sh
│ └── stop.sh
├── Dismiss Notifications.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ ├── dismiss.applescript
│ └── dismiss.js
├── Dump Item.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ ├── equipment.icns
│ ├── headphones.icns
│ └── microphone.icns
│ └── Scripts
│ └── dump.js
├── Expand URL.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ ├── expand.js
│ └── resolve.sh
├── Forecast.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ ├── Cloud-Fog.png
│ ├── Cloud-Hail-Alt.png
│ ├── Cloud-Hail.png
│ ├── Cloud-Moon.png
│ ├── Cloud-Rain.png
│ ├── Cloud-Sun.png
│ ├── Cloud.png
│ ├── Moon.png
│ ├── Snowflake.png
│ ├── Sun-Low.png
│ ├── Sun.png
│ ├── Sunrise.png
│ ├── Sunset.png
│ ├── Thermometer-25.png
│ ├── Thermometer-50.png
│ ├── Thermometer-75.png
│ ├── Tornado.png
│ ├── Umbrella.png
│ ├── Wind.png
│ ├── airplane.png
│ ├── bosnia.icns
│ ├── canada.icns
│ ├── en.lproj
│ │ └── default.strings
│ ├── forecastio.png
│ ├── france.icns
│ ├── germany.icns
│ ├── indonesia.icns
│ ├── italy.icns
│ ├── netherlands.icns
│ ├── poland.icns
│ ├── portugal.icns
│ ├── russia.icns
│ ├── si.png
│ ├── spain.icns
│ ├── uk.icns
│ └── us.icns
│ └── Scripts
│ ├── forecast-main.js
│ ├── forecast.js
│ ├── location.js
│ ├── moment-timezone.js
│ ├── moment.js
│ ├── search.js
│ └── settings.js
├── Generate Password.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ ├── generate.js
│ └── hsxkpasswd-config.json
├── Incognito.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ └── incognito.png
│ └── Scripts
│ └── incognito.applescript
├── JIRA.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ └── jira.png
│ └── Scripts
│ └── jira.js
├── LICENSE
├── MailMate.applescript
├── README.md
├── Read-Later.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ └── readlater.js
├── Recents.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ └── recents.js
├── Screenshot.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ ├── imgcopy
│ ├── screenshot.js
│ ├── trash
│ └── trash.swift
├── Share Safari Link.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ └── share.applescript
├── Share.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ ├── share.js
│ ├── share.sh
│ └── zip.sh
├── Switch Audio.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ ├── equipment.icns
│ ├── headphones.icns
│ └── microphone.icns
│ └── Scripts
│ ├── audio.js
│ └── audio.sh
├── Things.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ └── things.applescript
├── Timer.lbaction
└── Contents
│ ├── Info.plist
│ └── Scripts
│ └── timer.js
├── Today.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ └── icalb.png
│ └── Scripts
│ ├── call.applescript
│ ├── moment-with-locales.js
│ └── today.js
├── Updates.lbaction
└── Contents
│ ├── Info.plist
│ ├── Resources
│ ├── actionTemplate.png
│ ├── alertTemplate.png
│ ├── cautionTemplate.png
│ ├── checkTemplate.png
│ ├── downloadTemplate.png
│ ├── logTemplate.png
│ ├── prefTemplate.png
│ ├── skipTemplate.png
│ └── urlTemplate.png
│ └── Scripts
│ ├── README.md
│ └── updates.js
└── docs
├── index.html
└── updates.html
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: "https://www.buymeacoffee.com/padraic"
14 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '26 0 * * 4'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v3
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v2
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v2
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v2
71 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _CodeSignature
2 | *.scpt
3 | *.dSYM
4 | bin
5 | .DS_Store
6 | .localized
7 |
8 | Caffeinate.lbaction/Contents/Scripts/0
9 |
--------------------------------------------------------------------------------
/Caffeinate.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Caffeinate
7 | CFBundleName
8 | Caffeinate
9 | CFBundleVersion
10 | 4.2
11 | CFBundleIconFile
12 | font-awesome:fa-coffee
13 | LBDebugLogEnabled
14 |
15 | LBTextInputTitle
16 | Duration e.g. 0/10m/2h
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | caffeinate.sh
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBAcceptedArgumentTypes
28 |
29 | string
30 |
31 | LBReturnsResult
32 |
33 |
34 |
35 | LBDescription
36 |
37 | LBSummary
38 | Keep system awake, as caffeine does, for a specified period of time. Uses built in system caffeinate command.
39 | LBAuthor
40 | Padraic Renaghan
41 | LBEmail
42 | prenagha@renaghan.com
43 | LBWebsiteURL
44 | https://renaghan.com/launchbar/caffeinate/
45 | LBTwitter
46 | @prenagha
47 | LBArgument
48 | Duration to stay awake, e.g. "10m", "2h". Hours by default. Use "0" to caffeinate forever.
49 | LBUpdateURL
50 | https://raw.githubusercontent.com/prenagha/launchbar/master/Caffeinate.lbaction/Contents/Info.plist
51 | LBDownloadURL
52 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Caffeinate.lbaction
53 | LBChangelog
54 |
55 | 4.2: Fix download URL
56 | 4.0: More aggressive use of caffeinate, changed -u to -dimsu
57 | 3.2: Fix search for existing caffeinate
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Caffeinate.lbaction/Contents/Resources/Coffee.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Caffeinate.lbaction/Contents/Resources/Coffee.icns
--------------------------------------------------------------------------------
/Caffeinate.lbaction/Contents/Scripts/caffeinate.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | STP="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
3 | CLK="Coffee.icns"
4 |
5 | # orig idea and some code from https://github.com/shawnrice/alfred-2-caffeinate-workflow
6 | # Coffee.icns from http://www.everaldo.com
7 |
8 | toSeconds() {
9 | arg=$1
10 | (( sec=60*60*2 ))
11 | if [[ $arg =~ ([0-9]+)$ ]]
12 | then
13 | (( sec=$arg*60*60 ))
14 | else
15 | [[ $arg =~ ([0-9]+)([hHmM])$ ]]
16 | time=${BASH_REMATCH[1]}
17 | unit=${BASH_REMATCH[2]}
18 | if [[ $unit =~ ([hH]) ]]
19 | then
20 | (( sec=$time*60*60 ))
21 | elif [[ $unit =~ ([mM]) ]]
22 | then
23 | (( sec=$time*60 ))
24 | fi
25 | fi
26 | echo $sec
27 | }
28 |
29 | toString() {
30 | sec=$1
31 | if [ $sec == 0 ]
32 | then
33 | echo "ever"
34 | else
35 | hour=$(($sec/3600))
36 | min=$((($sec/60)%60))
37 | printf " %02d:%02d" $hour $min
38 | fi
39 | }
40 |
41 | # if no argument then show status
42 | if [ -z "$1" ]
43 | then
44 | PROC=`ps -eo etime,args | grep caffeinate | grep -v grep | grep -v caffeinate.sh | head -n 1`
45 | if [ -z "$PROC" ]
46 | then
47 | echo "[{\"title\":\"Not caffeinated\",\"icon\":\"NotFound.icns\"}]"
48 | else
49 | # 03:26 caffeinate -u -t 1200
50 | # 01:03:26 caffeinate -u -t 1200
51 | [[ "$PROC" =~ ([0-9]+)$ ]]
52 | dur=${BASH_REMATCH[1]}
53 |
54 | if [[ "$PROC" =~ (-w [0-9]+) ]]
55 | then
56 | # if caffeinate running while another process is running
57 | str=" running PID ${dur}"
58 | elif [[ "$PROC" =~ ([0-9]+):([0-9]+):([0-9]+) ]]
59 | then
60 | (( elapsed=${BASH_REMATCH[1]}*60*60 + ${BASH_REMATCH[2]}*60 ))
61 | else
62 | [[ $PROC =~ ([0-9]+):([0-9]+) ]]
63 | (( elapsed=${BASH_REMATCH[1]}*60 ))
64 | fi
65 | if [ -z "${str}" ]
66 | then
67 | if [ -z "${dur}" -o "${dur}" == 0 ]
68 | then
69 | remain=0
70 | else
71 | (( remain=$dur - $elapsed ))
72 | fi
73 | str=`toString $remain`
74 | fi
75 | echo "[{\"title\":\"Caffeinated awake for${str}\",\"icon\":\"$CLK\"},\
76 | {\"title\":\"Stop caffeinating\",\"icon\":\"$STP\",\"action\":\"stop.sh\"}]"
77 | fi
78 | else
79 | # if we have an argument then process it as a new caffeinate time
80 | secArg=
81 | sec=`toSeconds $1`
82 | if [ $sec > 0 ]
83 | then
84 | secArg="-t $sec"
85 | fi
86 | str=`toString $sec`
87 | killall caffeinate 2>/dev/null
88 | caffeinate -dimsu $secArg 1>/dev/null 2>&1 &
89 | echo "[{\"title\":\"Caffeinated awake for${str}\",\"icon\":\"$CLK\"}]"
90 | fi
91 | exit 0
--------------------------------------------------------------------------------
/Caffeinate.lbaction/Contents/Scripts/stop.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | STP="/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
3 | killall caffeinate 2>/dev/null
4 | echo "[{\"title\":\"Caffeinating stopped\",\"icon\":\"$STP\"}]"
5 | exit 0
--------------------------------------------------------------------------------
/Dismiss Notifications.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Dismiss
7 | CFBundleName
8 | Dismiss Notifications
9 | CFBundleVersion
10 | 2.5
11 | CFBundleIconFile
12 | /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Notifications.icns
13 | LBDebugLogEnabled
14 |
15 | LBScripts
16 |
17 | LBDefaultScript
18 |
19 | LBScriptName
20 | dismiss.js
21 | LBRunInBackground
22 |
23 | LBRequiresArgument
24 |
25 | LBReturnsResult
26 |
27 |
28 |
29 | LBDescription
30 |
31 | LBSummary
32 | Dismiss all notifications from Notification Center. Will "Snooze" calendar items, "Close" on all others. Adjust preferences for other languages.
33 | LBAuthor
34 | Padraic Renaghan
35 | LBEmail
36 | prenagha@renaghan.com
37 | LBWebsiteURL
38 | https://renaghan.com/launchbar/dismiss-notifications/
39 | LBTwitter
40 | @prenagha
41 | LBUpdateURL
42 | https://raw.githubusercontent.com/prenagha/launchbar/master/Dismiss%20Notifications.lbaction/Contents/Info.plist
43 | LBDownloadURL
44 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Dismiss%20Notifications.lbaction&fileName=Dismiss-Notifications&rootDirectory=Dismiss-Notifications.lbaction
45 | LBChangelog
46 |
47 | 2.5: Fix download URL
48 | 2.3: Support other languages via preferences
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Dismiss Notifications.lbaction/Contents/Scripts/dismiss.applescript:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Dismiss Notifications.lbaction/Contents/Scripts/dismiss.applescript
--------------------------------------------------------------------------------
/Dismiss Notifications.lbaction/Contents/Scripts/dismiss.js:
--------------------------------------------------------------------------------
1 |
2 | function run(arg) {
3 | try {
4 | if (!Action.preferences.snoozeButtonName)
5 | Action.preferences.snoozeButtonName = "Snooze";
6 | if (!Action.preferences.whenIsNowText)
7 | Action.preferences.whenIsNowText = "now";
8 | if (!Action.preferences.closeButtonName)
9 | Action.preferences.closeButtonName = "Close";
10 | if (!Action.preferences.okButtonName)
11 | Action.preferences.okButtonName = "OK";
12 | if (!Action.preferences.numbersInConferenceCall)
13 | Action.preferences.numbersInConferenceCall = 14;
14 |
15 | let ascript = Action.path + '/Contents/Scripts/dismiss.scpt';
16 | if (!File.exists(ascript)) {
17 | ascript = Action.path + '/Contents/Scripts/dismiss.applescript';
18 | }
19 | if (!File.exists(ascript)) {
20 | LaunchBar.alert('Error', 'Applescript not found ' + ascript);
21 | }
22 |
23 | LaunchBar.executeAppleScriptFile(ascript,
24 | Action.preferences.snoozeButtonName
25 | ,Action.preferences.whenIsNowText
26 | ,Action.preferences.closeButtonName
27 | ,Action.preferences.okButtonName
28 | ,Action.preferences.numbersInConferenceCall
29 | );
30 |
31 | } catch (exception) {
32 | LaunchBar.log('Error ' + exception);
33 | LaunchBar.alert('Error', exception);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Dump Item.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.DumpItem
7 | CFBundleName
8 | Dump Item
9 | LBAbbreviation
10 | debug log
11 | CFBundleVersion
12 | 2.3
13 | CFBundleIconFile
14 | UnixExecutable.icns
15 | LBDebugLogEnabled
16 |
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | dump.js
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBReturnsResult
28 |
29 | LBAcceptedArgumentTypes
30 |
31 | string
32 | path
33 | url
34 | item
35 |
36 |
37 |
38 | LBDescription
39 |
40 | LBSummary
41 | Dumps contents of passed in item back as LaunchBar results. Useful for LaunchBar action developers to see the kind and type of data is being passed around.
42 | LBAuthor
43 | Padraic Renaghan
44 | LBEmail
45 | prenagha@renaghan.com
46 | LBWebsiteURL
47 | https://renaghan.com/launchbar/dump-item/
48 | LBTwitter
49 | @prenagha
50 | LBUpdateURL
51 | https://raw.githubusercontent.com/prenagha/launchbar/master/Dump%20Item.lbaction/Contents/Info.plist
52 | LBDownloadURL
53 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Dump%20Item.lbaction&fileName=Dump-Item&rootDirectory=Dump-Item.lbaction
54 | LBChangelog
55 |
56 | 2.3: Fix download URL
57 | Support action updates
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Dump Item.lbaction/Contents/Resources/equipment.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Dump Item.lbaction/Contents/Resources/equipment.icns
--------------------------------------------------------------------------------
/Dump Item.lbaction/Contents/Resources/headphones.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Dump Item.lbaction/Contents/Resources/headphones.icns
--------------------------------------------------------------------------------
/Dump Item.lbaction/Contents/Resources/microphone.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Dump Item.lbaction/Contents/Resources/microphone.icns
--------------------------------------------------------------------------------
/Dump Item.lbaction/Contents/Scripts/dump.js:
--------------------------------------------------------------------------------
1 | var ICON = "GrayInfo.tiff";
2 |
3 | function dumpAction() {
4 | var items = [];
5 | items.push({title:'path: ' + Action.path, label: typeStr(Action.path)});
6 | items.push({title:'scriptType: ' + Action.scriptType, label: typeStr(Action.scriptType)});
7 | items.push({title:'version: ' + Action.version, label: typeStr(Action.version)});
8 | items.push({title:'bundleIdentifier: ' + Action.bundleIdentifier, label: typeStr(Action.bundleIdentifier)});
9 | items.push({title:'cachePath: ' + Action.cachePath, label: typeStr(Action.cachePath)});
10 | items.push({title:'supportPath: ' + Action.supportPath, label: typeStr(Action.supportPath)});
11 | items.push({title:'debugLogEnabled: ' + Action.debugLogEnabled, label: typeStr(Action.debugLogEnabled)});
12 | items.push({title:'preferences ➔', label: typeStr(Action.preferences), children:printItem(Action.preferences)});
13 |
14 | var out = [];
15 | out.push(dumpLaunchBar());
16 | out.push({title:'Action ➔',children:items});
17 | return out;
18 | }
19 |
20 | function dumpLaunchBar() {
21 | var items = [];
22 | items.push({title:'systemVersion: ' + LaunchBar.systemVersion, label: typeStr(LaunchBar.systemVersion)});
23 | items.push({title:'currentLocale: ' + LaunchBar.currentLocale, label: typeStr(LaunchBar.currentLocale)});
24 | items.push({title:'path: ' + LaunchBar.path, label: typeStr(LaunchBar.path)});
25 | items.push({title:'version: ' + LaunchBar.version, label: typeStr(LaunchBar.version)});
26 | items.push({title:'bundleIdentifier: ' + LaunchBar.bundleIdentifier, label: typeStr(LaunchBar.bundleIdentifier)});
27 | items.push({title:'homeDirectory: ' + LaunchBar.homeDirectory, label: typeStr(LaunchBar.homeDirectory)});
28 | items.push({title:'userName: ' + LaunchBar.userName, label: typeStr(LaunchBar.userName)});
29 | items.push({title:'userID: ' + LaunchBar.userID, label: typeStr(LaunchBar.userID)});
30 | items.push({title:'hostName: ' + LaunchBar.hostName, label: typeStr(LaunchBar.hostName)});
31 | items.push({title:'computerName: ' + LaunchBar.computerName, label: typeStr(LaunchBar.computerName)});
32 | items.push({title:'options ➔', label: typeStr(LaunchBar.options), children: printItem(LaunchBar.options)});
33 |
34 | return {title:'LaunchBar ➔',children:items};
35 | }
36 |
37 | function printArg(arg) {
38 | if (typeof arg === 'undefined') {
39 | return '*undefined*';
40 | } else if (arg === null) {
41 | return '*null*';
42 | } else {
43 | return '"' + arg + '"';
44 | }
45 | }
46 |
47 | function typeStr(arg) {
48 | if (typeof(arg) === 'undefined') {
49 | return '*undefined*';
50 | } else if (arg === null) {
51 | return '*null*';
52 | } else {
53 | return typeof(arg);
54 | }
55 | }
56 |
57 | function printItem(arg) {
58 | if (typeof(arg) === 'undefined') {
59 | return [{title: '*undefined*', icon:ICON}];
60 | } else if (arg === null) {
61 | return [{title: '*null*', icon:ICON}];
62 | } else if (typeof(arg) !== 'object') {
63 | return [{title: '"' + arg + '"', icon:ICON}];
64 | }
65 |
66 | var empty = true;
67 | var items = [];
68 | for (var p in arg) {
69 | if (!arg.hasOwnProperty(p))
70 | continue;
71 | empty = false;
72 | var v = arg[p];
73 | var t = typeof(v);
74 | if (t === 'object') {
75 | items.push({title: p + ' ➔', label: t, icon: ICON, children: printItem(v)});
76 | } else {
77 | items.push({title: p + ': ' + printArg(v), label: typeStr(v), icon:ICON});
78 | }
79 | }
80 | if (empty)
81 | return [{title: '*empty*', icon:ICON}];
82 | return items;
83 | }
84 |
85 | function run(arg) {
86 | var items = dumpAction();
87 | items.push({title: 'run() handler called', icon:"GrayInfoPressed.tiff"});
88 | items.push({title: 'Argument ➔', label: typeStr(arg), icon:ICON, children:printItem(arg)});
89 | return items;
90 | }
91 |
92 | function runWithItem(item) {
93 | var items = dumpAction();
94 | items.push({title: 'runWithItem() handler called', icon:"ClipObject.icns"});
95 | items.push({title: 'Argument ➔', label: typeStr(item), icon:ICON, children:printItem(item)});
96 | return items;
97 | }
98 |
99 | function runWithPaths(paths) {
100 | var items = dumpAction();
101 | items.push({title: 'runWithPaths() handler called', icon:"FileOperationMove.icns"});
102 | paths.forEach(function(p) {
103 | items.push({title: 'Argument ➔', path:p, label: typeStr(p), icon:"FileOperationMove.icns", children:printItem(p)});
104 | });
105 | return items;
106 | }
107 |
108 | function runWithString(str) {
109 | var items = dumpAction();
110 | items.push({title: 'runWithString() handler called', icon:"EnterText.icns"});
111 | items.push({title: 'Argument ➔', label: typeStr(str), icon:"EnterText.icns", children:printItem(str)});
112 | return items;
113 | }
114 |
115 | function runWithURL(theURL, details) {
116 | var items = dumpAction();
117 | items.push({title: 'runWithURL() handler called', url: theURL, icon:"URL.icns"});
118 | items.push({title: 'URL Argument ➔', label: typeStr(theURL), icon:"URL.icns", url: theURL, children:printItem(theURL)});
119 | items.push({title: 'Details Argument ➔', label: typeStr(details), icon:ICON, children:printItem(details)});
120 | return items;
121 | }
--------------------------------------------------------------------------------
/Expand URL.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.ExpandURL
7 | CFBundleName
8 | Expand URL
9 | CFBundleVersion
10 | 3.5
11 | CFBundleIconFile
12 | URL.icns
13 | LBDebugLogEnabled
14 |
15 | LBTextInputTitle
16 | URL
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | expand.js
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBAcceptedArgumentTypes
28 |
29 | string
30 |
31 | LBReturnsResult
32 |
33 |
34 |
35 | LBDescription
36 |
37 | LBSummary
38 | Expand a short URL. Follows the URL and returns the last Location HTTP header which will point to the real URL. Returns URL back to LaunchBar.
39 | LBRequirements
40 | curl
41 | LBAuthor
42 | Padraic Renaghan
43 | LBEmail
44 | prenagha@renaghan.com
45 | LBWebsiteURL
46 | https://renaghan.com/launchbar/expand-url/
47 | LBTwitter
48 | @prenagha
49 | LBArgument
50 | URL to expand
51 | LBUpdateURL
52 | https://raw.githubusercontent.com/prenagha/launchbar/master/Expand%20URL.lbaction/Contents/Info.plist
53 | LBDownloadURL
54 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Expand%20URL.lbaction&fileName=Expand-URL&rootDirectory=Expand-URL.lbaction
55 | LBChangelog
56 |
57 | 3.5: Improved curl command, thanks Keith Bolland
58 | 3.4: Fix download URL
59 | Support action updates
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Expand URL.lbaction/Contents/Scripts/expand.js:
--------------------------------------------------------------------------------
1 |
2 | function runWithString(string) {
3 | return go(string);
4 | }
5 |
6 | function runWithItem(item) {
7 | if (item && item.url && item.url.length > 0) {
8 | return go(item.url);
9 | }
10 | }
11 |
12 | function runWithURL(url, details) {
13 | return go(url);
14 | }
15 |
16 | function run() {
17 | return go(LaunchBar.getClipboardString());
18 | }
19 |
20 | function go(url) {
21 | if (!url || url == undefined || url.length == 0) {
22 | LaunchBar.alert('No URL found to expand');
23 | return;
24 | }
25 | try {
26 | LaunchBar.debugLog('URL=' + url);
27 | var exp = LaunchBar.execute('/bin/bash', 'resolve.sh', url);
28 | LaunchBar.debugLog('Expanded' + exp);
29 | if (!exp || exp == undefined || exp.length == 0)
30 | exp = url;
31 | return {'title':exp
32 | ,'subtitle':url
33 | ,'url':exp
34 | ,'quickLookURL':exp};
35 | } catch (exception) {
36 | LaunchBar.log('Error ' + exception);
37 | LaunchBar.alert('Error expanding ' + url, exception);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Expand URL.lbaction/Contents/Scripts/resolve.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # original idea from https://gist.github.com/Zettt/88ef3112c04ebecf475b
3 | EXP=`curl -siL -w '%{url_effective}' -o /dev/null "$1"`
4 | if [ -z "$EXP" ]
5 | then
6 | echo "$1"
7 | else
8 | echo "$EXP"
9 | fi
10 | exit 0
11 |
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Forecast
7 | CFBundleName
8 | Forecast
9 | CFBundleVersion
10 | 4.6
11 | CFBundleIconFile
12 | Sun-Low.png
13 | LBAbbreviation
14 | weather temperature precipitation
15 | LBDebugLogEnabled
16 |
17 | LBMinimumLaunchBarVersion
18 | 6101
19 | LBTextInputTitle
20 | Location
21 | LBScripts
22 |
23 | LBDefaultScript
24 |
25 | LBScriptName
26 | forecast-main.js
27 | LBActionURLScript
28 | forecast-main.js
29 | LBRunInBackground
30 |
31 | LBRequiresArgument
32 |
33 | LBAcceptedArgumentTypes
34 |
35 | string
36 |
37 | LBReturnsResult
38 |
39 |
40 |
41 | LBDescription
42 |
43 | LBSummary
44 | Forecast the weather for a location using Forecast.io forecast data
45 | LBRequirements
46 | curl
47 | LBAuthor
48 | Padraic Renaghan
49 | LBEmail
50 | prenagha@renaghan.com
51 | LBWebsiteURL
52 | https://renaghan.com/launchbar/forecast/
53 | LBTwitter
54 | @prenagha
55 | LBArgument
56 | Location
57 | LBUpdateURL
58 | https://raw.githubusercontent.com/prenagha/launchbar/master/Forecast.lbaction/Contents/Info.plist
59 | LBDownloadURL
60 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Forecast.lbaction
61 | LBChangelog
62 |
63 | 4.6: moment.js updates
64 | 4.5: Fix download URL
65 | 4.4: Removed use of Location Helper since Google now requires API key, now using ip-api.com for current location
66 | 4.2: Forecast.io branding change to Dark Sky
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud-Fog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud-Fog.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud-Hail-Alt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud-Hail-Alt.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud-Hail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud-Hail.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud-Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud-Moon.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud-Rain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud-Rain.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud-Sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud-Sun.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Cloud.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Moon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Moon.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Snowflake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Snowflake.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Sun-Low.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Sun-Low.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Sun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Sun.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Sunrise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Sunrise.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Sunset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Sunset.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Thermometer-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Thermometer-25.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Thermometer-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Thermometer-50.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Thermometer-75.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Thermometer-75.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Tornado.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Tornado.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Umbrella.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Umbrella.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/Wind.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/Wind.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/airplane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/airplane.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/bosnia.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/bosnia.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/canada.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/canada.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/en.lproj/default.strings:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/en.lproj/default.strings
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/forecastio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/forecastio.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/france.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/france.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/germany.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/germany.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/indonesia.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/indonesia.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/italy.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/italy.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/netherlands.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/netherlands.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/poland.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/poland.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/portugal.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/portugal.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/russia.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/russia.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/si.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/si.png
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/spain.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/spain.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/uk.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/uk.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Resources/us.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Forecast.lbaction/Contents/Resources/us.icns
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Scripts/forecast-main.js:
--------------------------------------------------------------------------------
1 | String.prototype.localizationTable = 'default';
2 |
3 | include('forecast.js');
4 | include('location.js');
5 | include('search.js');
6 | include('settings.js');
7 | include('moment.js');
8 | include('moment-timezone.js');
9 |
10 | function isDebug() {
11 | return Action.preferences.debug || Action.debugLogEnabled;
12 | }
13 |
14 | function debugLog(l) {
15 | if (isDebug())
16 | LaunchBar.log(l);
17 | }
18 |
19 |
20 | function runWithString(string) {
21 | return locationSearch(string);
22 | }
23 |
24 | function runWithItem(item) {
25 | return locationSearch(item.title);
26 | }
27 |
28 | function runWithURL(url, details) {
29 | if (details && details.queryParameters && details.queryParameters.q)
30 | return locationSearch(details.queryParameters.q);
31 | }
32 |
33 | function run() {
34 | var items = [];
35 | var loc = selectedLoc();
36 | if (loc == null) {
37 | items.push({'title':'locationNotFound'.localize(),'icon':'NotFound.icns'});
38 | } else {
39 | items = items.concat(forecast(loc));
40 | }
41 | items.push({'title':'Locations'.localize()
42 | ,'icon':DEFAULT_ICON
43 | ,'action':'actionLocations'
44 | ,'actionReturnsItems':true});
45 | return items;
46 | }
47 |
48 | function actionLocations(item) {
49 | var items = getLocations();
50 | return items.concat(getSettings());
51 | }
52 |
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Scripts/forecast.js:
--------------------------------------------------------------------------------
1 |
2 | var ALERT_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns';
3 | var imap = {};
4 | imap['clear-day'] = 'Sun.png';
5 | imap['clear-night'] = 'Moon.png';
6 | imap['rain'] = 'Cloud-Rain.png';
7 | imap['snow'] = 'Snowflake.png';
8 | imap['sleet'] = 'Cloud-Hail-Alt.png';
9 | imap['wind'] = 'Wind.png';
10 | imap['fog'] = 'Cloud-Fog.png';
11 | imap['cloudy'] = 'Cloud.png';
12 | imap['partly-cloudy-day'] = 'Cloud-Sun.png';
13 | imap['partly-cloudy-night'] = 'Cloud-Moon.png';
14 | imap['hail'] = 'Cloud-Hail.png';
15 | imap['thunderstorm'] = 'Umbrella.png';
16 | imap['tornado'] = 'Tornado.png';
17 |
18 | var wmap = {};
19 | wmap['us'] = 'mph';
20 | wmap['si'] = 'm/s';
21 | wmap['ca'] = 'km/h';
22 | wmap['uk'] = 'mph';
23 |
24 | function getAPIKey() {
25 | if (Action.preferences.apiKey == undefined) {
26 | Action.preferences.apiKey = '';
27 | }
28 | if (Action.preferences.apiKey.length == 0) {
29 | LaunchBar.alert('addKey'.localize());
30 | LaunchBar.openURL('https://darksky.net/dev/');
31 | var key = LaunchBar.executeAppleScript(
32 | 'return text returned of (display dialog "Dark Sky API Key:" default answer "" giving up after 120 with icon note)');
33 | Action.preferences.apiKey = key && key != undefined ? key.trim() : '';
34 | }
35 | return Action.preferences.apiKey;
36 | }
37 |
38 | function getIcon(i) {
39 | var icon = imap[i];
40 | if (!icon || icon == undefined || icon.length == 0) {
41 | LaunchBar.log('Need weather icon map for ' + i);
42 | return 'NotFound.icns';
43 | }
44 | return icon;
45 | }
46 |
47 | function getLocalTime(offset, t) {
48 | return moment.unix(t).zone(offset).format('HH:mm');
49 | }
50 |
51 | var sparks = '▁▂▃▄▅▆▇█'
52 | function getSparkline(values) {
53 | }
54 |
55 | function getPrecipLevel(much) {
56 | if (!much || much == undefined || much == 0.0)
57 | return '';
58 | if (much >= 0.4)
59 | return 'heavy';
60 | if (much >= 0.1)
61 | return 'moderate';
62 | if (much >= 0.017)
63 | return 'light';
64 | return 'very light';
65 | }
66 |
67 | function getTemps(real, app) {
68 | var d = Math.abs(real - app);
69 | if (d > 5)
70 | return Math.round(real) + '°/' + Math.round(app) + '°';
71 | return Math.round(real) + '°';
72 | }
73 |
74 | function forecast(loc) {
75 | var url = "unset";
76 | var name = loc.name;
77 | var latitude = loc.latitude;
78 | var longitude = loc.longitude;
79 | try {
80 | var items = [];
81 | var apiKey = getAPIKey();
82 | if (apiKey.length == 0)
83 | return;
84 |
85 | url = 'https://api.darksky.net/forecast/' + apiKey + '/' + latitude + ',' + longitude
86 | + '?units=' + Action.preferences.units
87 | + '&lang=' + Action.preferences.lang;
88 | LaunchBar.log('Forecast URL "' + url +'"');
89 |
90 | var furl = 'https://darksky.net/' + latitude + ',' + longitude;
91 | var result = HTTP.getJSON(url, TIMEOUT);
92 | if (result && result.data && result.data.error)
93 | items.push({'title':'Forecast Error: ' + result.data.error
94 | ,'subtitle':url
95 | ,'icon':ALERT_ICON
96 | ,'url':url});
97 | var units = '';
98 | if (result && result.data && result.data.flags && result.data.flags.units)
99 | units = result.data.flags.units;
100 |
101 | if (result && result.data && result.data.timezone ) {
102 | if (result.data.alerts) {
103 | for (var i = 0; i < result.data.alerts.length; i++) {
104 | var a = result.data.alerts[i];
105 | items.push({
106 | 'title':a.title
107 | ,'subtitle': 'Expires ' + moment.unix(a.expires).tz(result.data.timezone).calendar()
108 | ,'icon':ALERT_ICON
109 | ,'url':a.uri
110 | ,'text':a.description
111 | });
112 | }
113 | }
114 |
115 | var hourDetails = [];
116 | if (result.data.hourly) {
117 | for (var i = 1; i < result.data.hourly.data.length; i++) {
118 | if (i >= 12)
119 | break;
120 | var d = result.data.hourly.data[i];
121 | hourDetails.push({
122 | 'title':moment.unix(d.time).tz(result.data.timezone).format('h a')
123 | + (d.precipProbability>0?' ' + Math.round(d.precipProbability*100)+'%':'')
124 | + ' ' + d.summary
125 | + ' ' + getTemps(d.temperature,d.apparentTemperature)
126 | ,'icon':getIcon(d.icon)});
127 | }
128 | } else {
129 | LaunchBar.log('Hourly forecast not available');
130 | }
131 |
132 | var todayDetails = [];
133 | var todaySummary = '';
134 | var week = [];
135 | if (result.data.daily) {
136 | var t = result.data.daily.data[0];
137 | todayDetails = dayDetail(result.data.timezone,units,t);
138 | todaySummary = ', ' + t.summary.substring(0, t.summary.length-1)
139 | + ' ' + getTemps(t.temperatureMax,t.apparentTemperatureMax) ;
140 | for (var i=1; i < result.data.daily.data.length; i++) {
141 | var d = result.data.daily.data[i];
142 | week.push({
143 | 'title':moment.unix(d.time).tz(result.data.timezone).format('ddd') + ' '
144 | + d.summary.substring(0, d.summary.length-1) + ' '
145 | + getTemps(d.temperatureMax,d.apparentTemperatureMax)
146 | ,'icon':getIcon(d.icon)
147 | ,'url':furl
148 | ,'children':dayDetail(result.data.timezone,units,d)
149 | });
150 | }
151 | } else {
152 | LaunchBar.log('Daily forecast not available');
153 | }
154 |
155 | var nowTitle = null;
156 | var nowIcon = null;
157 | var nowTemp = null;
158 | var nowTime = new Date().getTime()/1000;
159 | if (result.data.currently) {
160 | var currently = result.data.currently;
161 | nowTitle = currently.summary;
162 | nowIcon = currently.icon;
163 | nowTemp = getTemps(currently.temperature, currently.apparentTemperature);
164 | nowTime = currently.time;
165 | } else {
166 | LaunchBar.log('Current forecast not available');
167 | }
168 | if (result.data.minutely) {
169 | var minutely = result.data.minutely;
170 | nowTitle = minutely.summary.substring(0, minutely.summary.length-1);
171 | nowIcon = minutely.icon;
172 | } else {
173 | LaunchBar.log('Next hour forecast not available');
174 | }
175 | var nowDetails = todayDetails.concat(hourDetails);
176 | items.push({
177 | 'title':nowTitle + ' ' + nowTemp + todaySummary
178 | ,'icon':getIcon(nowIcon)
179 | ,'url':furl
180 | ,'children':nowDetails
181 | });
182 | items.push({
183 | 'title':name + ' ' + moment().tz(result.data.timezone).format('h:mm a')
184 | ,'icon':loc.icon
185 | ,'url':url
186 | });
187 | items = items.concat(week);
188 | }
189 | if (items.length == 0)
190 | items.push({'title':'Forecast not available','icon':'NotFound.icns','url':url});
191 | if (isDebug()) {
192 | items.push({'title':'Dark Sky API call','url':url,'icon':'forecastio.png'});
193 | }
194 | return items;
195 | } catch (exception) {
196 | LaunchBar.log('Error forecast from "' + url + '" ' + exception);
197 | LaunchBar.alert('Error getting forecast from "' + url + '"', exception);
198 | }
199 | }
200 |
201 | function dayDetail(tz, units, d) {
202 | var details = [];
203 | details.push({'title': 'Low ' + getTemps(d.temperatureMin,d.apparentTemperatureMin)
204 | + ' at ' + moment.unix(d.temperatureMinTime).tz(tz).format('h a')
205 | ,'icon':'Thermometer-25.png'});
206 | details.push({'title':'Sunrise ' + moment.unix(d.sunriseTime).tz(tz).format('h:mm a')
207 | ,'icon':'Sunrise.png'});
208 | if (d.precipProbability > 0.0) {
209 | details.push({'title': Math.round(d.precipProbability * 100) + '% chance '
210 | + getPrecipLevel(d.precipIntensityMax) + ' ' + d.precipType
211 | ,'icon':getIcon(d.precipType)});
212 | }
213 | if (d.windSpeed > 0) {
214 | details.push({'title':'Wind ' + Math.round(d.windSpeed) + ' ' + wmap[units]
215 | ,'icon':'Wind.png'});
216 | }
217 | details.push({'title': 'High ' + getTemps(d.temperatureMax,d.apparentTemperatureMax)
218 | + ' at ' + moment.unix(d.temperatureMaxTime).tz(tz).format('h a')
219 | ,'icon':'Thermometer-75.png'});
220 | details.push({'title':'Sunset ' + moment.unix(d.sunsetTime).tz(tz).format('h:mm a')
221 | ,'icon':'Sunset.png'});
222 | return details;
223 | }
224 |
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Scripts/location.js:
--------------------------------------------------------------------------------
1 | var EXISTS_FILTER = function(x){return (x && x!== undefined && x !== false)};
2 | var LOC_FILE = Action.supportPath + '/locations.json';
3 | var HOME_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/HomeFolderIcon.icns';
4 | var DEFAULT_ICON = 'ABLocation.icns';
5 | var FOLLOW_NBR = 9999;
6 | var FOLLOW_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/MagnifyingGlassIcon.icns';
7 | var FOLLOW_NAME = 'Follow-Me';
8 | var FOLLOW_SUB = 'Dynamic location that follows your current whereabouts';
9 | var PLANE_ICON = 'airplane.png';
10 |
11 | function getLocations() {
12 | var kids = [];
13 | var locs = readLocations();
14 | var followMe = false;
15 | for (var i = 0; i < locs.length; i++) {
16 | var loc = locs[i];
17 | var admin = [];
18 | if (loc.latitude == FOLLOW_NBR)
19 | followMe = true;
20 | admin.push({'title':'Forecast'
21 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
22 | ,'icon':'Sun-Low.png'
23 | ,'actionReturnsItems':true
24 | ,'action':'actionForecast'});
25 | admin.push({'title':'Set as Default Location'
26 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
27 | ,'icon':loc.icon
28 | ,'actionRunsInBackground':true
29 | ,'action':'actionSelect'});
30 | admin.push({'title':'Rename ' + loc.name
31 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
32 | ,'icon':'Text.icns'
33 | ,'actionRunsInBackground':true
34 | ,'action':'actionRename'});
35 | admin.push({'title':'Change Icon'
36 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
37 | ,'icon':'Text.icns'
38 | ,'actionRunsInBackground':true
39 | ,'action':'actionIcon'});
40 | admin.push({'title':'Set Home Icon'
41 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
42 | ,'icon':HOME_ICON
43 | ,'action':'actionHome'});
44 | admin.push({'title':'Set Airplane Icon'
45 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
46 | ,'icon':PLANE_ICON
47 | ,'action':'actionPlane'});
48 | admin.push({'title':'Remove ' + loc.name
49 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
50 | ,'actionRunsInBackground':true
51 | ,'icon':'/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/TrashIcon.icns'
52 | ,'action':'actionRemove'});
53 | kids.push({'title':loc.name
54 | ,'name':loc.name,'latitude':loc.latitude,'longitude':loc.longitude,'ico':loc.icon
55 | ,'subtitle':(loc.latitude == FOLLOW_NBR?FOLLOW_SUB:'Latitude ' + loc.latitude + ' Longitude:' + loc.longitude)
56 | ,'actionRunsInBackground':true
57 | ,'icon':loc.icon
58 | ,'action':'actionSelect'
59 | ,'children':admin});
60 | }
61 |
62 | if (!followMe) {
63 | kids.push({'title':'Add Follow-Me Current Location'
64 | ,'subtitle':FOLLOW_SUB
65 | ,'name':FOLLOW_NAME,'latitude':FOLLOW_NBR,'longitude':FOLLOW_NBR,'ico':FOLLOW_ICON
66 | ,'icon':FOLLOW_ICON
67 | ,'actionRunsInBackground':true
68 | ,'action':'actionSelect'});
69 | }
70 | var curr = getCurrentLocation();
71 | if (curr == null) {
72 | kids.push({'title':'Current Location not available','icon':'NotFound.icns'});
73 | } else {
74 | var f = [];
75 | f.push({'title':'Forecast'
76 | ,'name':curr.name,'latitude':curr.latitude,'longitude':curr.longitude,'ico':curr.icon
77 | ,'icon':'Sun-Low.png'
78 | ,'actionReturnsItems':true
79 | ,'action':'actionForecast'});
80 |
81 | kids.push({'title':'Add ' + curr.name + ' Location'
82 | ,'name':curr.name,'latitude':curr.latitude,'longitude':curr.longitude,'ico':curr.icon
83 | ,'icon':DEFAULT_ICON
84 | ,'actionRunsInBackground':true
85 | ,'children':f
86 | ,'action':'actionSelect'});
87 | }
88 |
89 | if (isDebug()) {
90 | kids.push({'title':'Edit locations.json'
91 | ,'actionRunsInBackground':true
92 | ,'path':LOC_FILE
93 | ,'action':'actionJSON'});
94 | }
95 | return kids;
96 | }
97 |
98 |
99 | function selectedLoc() {
100 | var locs = readLocations();
101 | if (locs && locs != undefined && locs.length > 0) {
102 | var loc = locs[0];
103 | if (loc.latitude == FOLLOW_NBR) {
104 | var curr = getCurrentLocation();
105 | if (curr == null)
106 | return null;
107 | curr.icon = loc.icon;
108 | return curr;
109 | } else {
110 | return loc;
111 | }
112 | }
113 | }
114 |
115 | function actionSelect(item) {
116 | locationAdd(item.name, item.latitude, item.longitude, item.ico);
117 | }
118 |
119 | function actionForecast(item) {
120 | return forecast({'name':item.name, 'latitude':item.latitude, 'longitude':item.longitude, 'icon':item.ico});
121 | }
122 |
123 | function actionRename(item) {
124 | var n = LaunchBar.executeAppleScript(
125 | 'return text returned of (display dialog "Name:" default answer "' + item.name + '" giving up after 15 with icon note)');
126 | if (n && n.length > 0) {
127 | var locs = readLocations();
128 | for (var i=0; i < locs.length; i++) {
129 | var loc = locs[i];
130 | if (loc.latitude == item.latitude && loc.longitude == item.longitude) {
131 | loc.name = n.trim();
132 | writeLocations(locs);
133 | break;
134 | }
135 | }
136 | }
137 | }
138 |
139 | function actionIcon(item) {
140 | var ico = LaunchBar.executeAppleScript(
141 | 'return text returned of (display dialog "Icon:" default answer "' + item.icon + '" giving up after 15 with icon note)');
142 | if (ico && ico.length > 0) {
143 | var locs = readLocations();
144 | for (var i=0; i < locs.length; i++) {
145 | var loc = locs[i];
146 | if (loc.latitude == item.latitude && loc.longitude == item.longitude) {
147 | loc.icon = ico.trim();
148 | writeLocations(locs);
149 | break;
150 | }
151 | }
152 | }
153 | }
154 |
155 | function actionHome(item) {
156 | var locs = readLocations();
157 | for (var i=0; i < locs.length; i++) {
158 | var loc = locs[i];
159 | if (loc.latitude == item.latitude && loc.longitude == item.longitude) {
160 | loc.icon = HOME_ICON;
161 | writeLocations(locs);
162 | break;
163 | }
164 | }
165 | }
166 |
167 | function actionPlane(item) {
168 | var locs = readLocations();
169 | for (var i=0; i < locs.length; i++) {
170 | var loc = locs[i];
171 | if (loc.latitude == item.latitude && loc.longitude == item.longitude) {
172 | loc.icon = PLANE_ICON;
173 | writeLocations(locs);
174 | break;
175 | }
176 | }
177 | }
178 |
179 | function actionRemove(item) {
180 | var locs = readLocations();
181 | for (var i=0; i < locs.length; i++) {
182 | var loc = locs[i];
183 | if (loc.latitude == item.latitude && loc.longitude == item.longitude) {
184 | locs[i] = false;
185 | writeLocations(locs);
186 | break;
187 | }
188 | }
189 | }
190 |
191 | function actionJSON(item) {
192 | LaunchBar.openURL('file:/' + encodeURIComponent(LOC_FILE), 'TextEdit');
193 | }
194 |
195 | // add a new location to the top (selected) position
196 | function locationAdd(name, latitude, longitude, icon) {
197 | var locs = [];
198 | locs.push({'name':name,'latitude':latitude,'longitude':longitude
199 | ,'icon':(icon && icon != undefined && icon.length > 0?icon:DEFAULT_ICON)});
200 | locs = locs.concat(readLocations());
201 | writeLocations(locs);
202 | }
203 |
204 | function readLocations() {
205 | // locations file is a json Array, of object containing name,latitude,longitude
206 | if (File.exists(LOC_FILE)) {
207 | try {
208 | return File.readJSON(LOC_FILE);
209 | } catch (exception) {
210 | LaunchBar.log('Error readLocations ' + exception);
211 | LaunchBar.alert('Error readLocations', exception);
212 | }
213 | } else {
214 | writeLocations([]);
215 | }
216 | return [];
217 | }
218 |
219 | function writeLocations(locations) {
220 | var locs = locations.filter(EXISTS_FILTER);
221 | // remove duplicates
222 | for (var i=0; i < locs.length; i++) {
223 | var loc = locs[i];
224 | for (var j=i+1; j < locs.length; j++) {
225 | var other = locs[j];
226 | if (other !== false && loc.latitude == other.latitude && loc.longitude == other.longitude) {
227 | locs[j] = false;
228 | }
229 | }
230 | }
231 | try {
232 | File.writeJSON(locs.filter(EXISTS_FILTER), LOC_FILE);
233 | } catch (exception) {
234 | LaunchBar.log('Error writeLocations ' + exception);
235 | LaunchBar.alert('Error writeLocations', exception);
236 | }
237 | return loc;
238 | }
239 |
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Scripts/search.js:
--------------------------------------------------------------------------------
1 | var GEOLOCATE = "http://ip-api.com/json/";
2 |
3 | function getCurrentLocation() {
4 | try {
5 | var result = HTTP.getJSON(GEOLOCATE, TIMEOUT);
6 | if (result && result.data
7 | && result.data.city && result.data.regionName && result.data.country) {
8 | var place = result.data.city
9 | + ', ' + result.data.regionName
10 | + ', ' + result.data.country;
11 | return {'name': place
12 | ,'latitude': result.data.lat
13 | ,'longitude': result.data.lon
14 | ,'icon':DEFAULT_ICON};
15 | }
16 | } catch (exception) {
17 | LaunchBar.log('Error getCurrentLocation ' + exception);
18 | LaunchBar.alert('Error getCurrentLocation', exception);
19 | }
20 | return null;
21 | }
22 |
23 | function getNameForGeo(latitude, longitude) {
24 | var url = 'https://nominatim.openstreetmap.org/reverse?format=json&zoom=12&addressdetails=1&lat='
25 | + latitude + '&lon=' + longitude;
26 | try {
27 | var result = HTTP.getJSON(url, TIMEOUT);
28 | if (result && result.data && result.data.address ) {
29 | if (result.data.address.village)
30 | return result.data.address.village + ', ' + result.data.address.state;
31 | if (result.data.address.neighbourhood)
32 | return result.data.address.neighbourhood + ', ' + result.data.address.state;
33 | if (result.data.address.city)
34 | return result.data.address.city + ', ' + result.data.address.state;
35 | }
36 | if (result && result.data && result.data.display_name)
37 | return result.data.display_name;
38 | LaunchBar.log('Cannot find name for geo ' + latitude + ' , ' + longitude
39 | + ' ' + url + ' ' + JSON.stringify(result));
40 | } catch (exception) {
41 | LaunchBar.log('Error getNameForGeo ' + exception);
42 | LaunchBar.alert('Error getNameForGeo', exception);
43 | }
44 | return 'Name not available';
45 | }
46 |
47 | function locationSearch(query) {
48 | var url = 'http://nominatim.openstreetmap.org/search?format=json&addressdetails=1&limit=50'
49 | + '&countrycodes=' + encodeURIComponent(Action.preferences.country)
50 | + '&q=' + encodeURIComponent(query);
51 | try {
52 | var items = [];
53 | var result = HTTP.getJSON(url, TIMEOUT);
54 | if (result && result.data) {
55 | for (var i = 0; i < result.data.length; i++) {
56 | var r = result.data[i];
57 | var n = r.display_name;
58 | if (n.length > 50)
59 | n = r.display_name.substring(0,50);
60 | items.push({'title':n
61 | ,'subtitle':r.display_name
62 | ,'name':n
63 | ,'latitude':r.lat
64 | ,'longitude':r.lon
65 | ,'ico':DEFAULT_ICON
66 | ,'icon':DEFAULT_ICON
67 | ,'actionReturnsItems':true
68 | ,'action':'actionSelectAndForecast'
69 | });
70 | }
71 | }
72 | } catch (exception) {
73 | LaunchBar.log('Error locationSearch ' + exception);
74 | LaunchBar.alert('Error locationSearch', exception);
75 | }
76 | if (items.length == 0) {
77 | items.push({'title':'No locations found','icon':'NotFound.icns'});
78 | }
79 | if (isDebug()) {
80 | items.push({'title':'Search API call','url':url});
81 | }
82 | return items;
83 | }
84 |
85 | function actionSelectAndForecast(item) {
86 | actionSelect(item);
87 | return actionForecast(item);
88 | }
--------------------------------------------------------------------------------
/Forecast.lbaction/Contents/Scripts/settings.js:
--------------------------------------------------------------------------------
1 | var PREF_FILE = Action.supportPath + '/Preferences.plist';
2 | var TIMEOUT = 10.0;
3 |
4 | var langs = {};
5 | langs['en'] = 'English';
6 | langs['de'] = 'German';
7 | langs['es'] = 'Spanish';
8 | langs['fr'] = 'French';
9 | langs['nl'] = 'Dutch';
10 | langs['it'] = 'Italian';
11 | langs['tet'] = 'Tetum';
12 | langs['bs'] = 'Bosnian';
13 | langs['pl'] = 'Polish';
14 | langs['pt'] = 'Portuguese';
15 | langs['ru'] = 'Russian';
16 |
17 | if (Action.preferences.units == undefined) {
18 | Action.preferences.units = 'auto';
19 | }
20 |
21 | if (Action.preferences.lang == undefined) {
22 | Action.preferences.lang = 'en';
23 | var l = langs[LaunchBar.currentLocale];
24 | if (l && l != undefined && l.length > 0)
25 | Action.preferences.lang = LaunchBar.currentLocale;
26 | }
27 |
28 | if (Action.preferences.country == undefined) {
29 | Action.preferences.country = 'US';
30 | }
31 |
32 | if (Action.preferences.debug == undefined) {
33 | Action.preferences.debug = false;
34 | }
35 |
36 | function getSettings() {
37 | return {'title':'Settings'
38 | ,'icon':'com.apple.systempreferences'
39 | ,'action':'actionSettings'
40 | ,'actionReturnsItems':true};
41 | }
42 |
43 | function actionSettings() {
44 | var items = [];
45 |
46 | var units = [];
47 | units.push({'title':'United States','units':'us'
48 | ,'icon':'us.icns','action':'actionUnits'});
49 | units.push({'title':'SI, Standard International, Metric','units':'si'
50 | ,'icon':'si.png','action':'actionUnits'});
51 | units.push({'title':'Canada','units':'ca'
52 | ,'subtitle':'Like SI but windSpeed is in km/h'
53 | ,'icon':'canada.icns','action':'actionUnits'});
54 | units.push({'title':'United Kingdom','units':'uk'
55 | ,'subtitle':'Like SI but windSpeed is in miles per hour'
56 | ,'icon':'uk.icns','action':'actionUnits'});
57 | units.push({'title':'Automatic based on location','units':'auto'
58 | ,'icon':FOLLOW_ICON,'action':'actionUnits'});
59 | items.push({'title':'Forecast Units - ' + Action.preferences.units.toUpperCase()
60 | ,'subtitle':'Set units for Dark Sky data, see https://darksky.net/dev/docs'
61 | ,'url':'https://darksky.net/dev/docs'
62 | ,'icon':'Thermometer-25.png'
63 | ,'children':units});
64 |
65 | var langs = [];
66 | langs.push({'title':'English','lang':'en'
67 | ,'icon':'us.icns','action':'actionLang'});
68 | langs.push({'title':'German','lang':'de'
69 | ,'icon':'germany.icns','action':'actionLang'});
70 | langs.push({'title':'French','lang':'fr'
71 | ,'icon':'france.icns','action':'actionLang'});
72 | langs.push({'title':'Spanish','lang':'sp'
73 | ,'icon':'spain.icns','action':'actionLang'});
74 | langs.push({'title':'Dutch','lang':'nl'
75 | ,'icon':'netherlands.icns','action':'actionLang'});
76 | langs.push({'title':'Italian','lang':'it'
77 | ,'icon':'italy.icns','action':'actionLang'});
78 | langs.push({'title':'Tetum','lang':'tet'
79 | ,'icon':'indonesia.icns','action':'actionLang'});
80 | langs.push({'title':'Bosnian','lang':'bs'
81 | ,'icon':'bosnia.icns','action':'actionLang'});
82 | langs.push({'title':'Polish','lang':'pl'
83 | ,'icon':'poland.icns','action':'actionLang'});
84 | langs.push({'title':'Portuguese','lang':'pt'
85 | ,'icon':'portugal.icns','action':'actionLang'});
86 | langs.push({'title':'Russian','lang':'ru'
87 | ,'icon':'russia.icns','action':'actionLang'});
88 |
89 | langs.push({'title':'Other'
90 | ,'url':'https://github.com/darkskyapp/forecast-io-translations'
91 | ,'icon':'forecastio.png'});
92 | items.push({'title':'Forecast Language - ' + Action.preferences.lang.toUpperCase()
93 | ,'subtitle':'Set language for Dark Sky data, see https://darksky.net/dev/docs'
94 | ,'url':'https://darksky.net/dev/docs'
95 | ,'icon':'Thermometer-25.png'
96 | ,'children':langs});
97 |
98 | var cty = [];
99 | cty.push({'title':'United States','country':'US'
100 | ,'icon':'us.icns','action':'actionCountry'});
101 | cty.push({'title':'Canada','country':'CA'
102 | ,'icon':'canada.icns','action':'actionCountry'});
103 | cty.push({'title':'Germany','country':'DE'
104 | ,'icon':'germany.icns','action':'actionCountry'});
105 | cty.push({'title':'France','country':'FR'
106 | ,'icon':'france.icns','action':'actionCountry'});
107 | cty.push({'title':'Spain','country':'ES'
108 | ,'icon':'spain.icns','action':'actionCountry'});
109 | cty.push({'title':'Other Country'
110 | ,'actionRunsInBackground':true
111 | ,'icon':'Text.icns','action':'actionCountryOther'});
112 | items.push({'title':'Location Search Country - ' + Action.preferences.country
113 | ,'subtitle':'Set ISO country(ies , separated) for location search, see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2'
114 | ,'url':'https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2'
115 | ,'icon':FOLLOW_ICON
116 | ,'children':cty});
117 |
118 | items.push({'title':'Dark Sky API Key'
119 | ,'subtitle':'https://darksky.net/dev/'
120 | ,'url':'https://darksky.net/dev/'
121 | ,'actionRunsInBackground':true
122 | ,'icon':'forecastio.png','action':'actionKey'});
123 |
124 | items.push({'title':'Debug Mode - ' + Action.preferences.debug
125 | ,'subtitle':'Toggle debug mode, adds items that link to API calls'
126 | ,'icon':'com.apple.systempreferences','action':'actionDebug'});
127 |
128 | var body = 'Forecast version: ' + Action.version
129 | + '\nLaunchBar version: ' + LaunchBar.shortVersion + ' (' + LaunchBar.version + ')'
130 | + '\nLocale: ' + LaunchBar.currentLocale
131 | + '\nUnits: ' + Action.preferences.units
132 | + '\nLanguage: ' + Action.preferences.lang
133 | + '\nCountry: ' + Action.preferences.country
134 | + '\n\n...\n';
135 |
136 | items.push({'title':'Send Forecast Feedback'
137 | ,'subtitle':'Comments, Suggestions, Bug Reports always welcome'
138 | ,'icon':'com.apple.Mail',
139 | 'url':'mailto:prenagha@renaghan.com?subject=Forecast%20Feedback&body='
140 | + encodeURIComponent(body)});
141 |
142 | if (isDebug()) {
143 | items.push({'title':'Edit preferences file'.localize()
144 | ,'actionRunsInBackground':true
145 | ,'path':PREF_FILE
146 | ,'icon':'com.apple.systempreferences'
147 | ,'action':'actionPrefs'});
148 | }
149 | return items;
150 | }
151 |
152 | function actionUnits(item) {
153 | Action.preferences.units = item.units;
154 | }
155 |
156 | function actionLang(item) {
157 | Action.preferences.lang = item.lang;
158 | }
159 |
160 | function actionCountry(item) {
161 | Action.preferences.country = item.country;
162 | }
163 |
164 | function actionDebug(item) {
165 | Action.preferences.debug = !Action.preferences.debug;
166 | }
167 |
168 | function actionCountryOther(item) {
169 | var c = LaunchBar.executeAppleScript(
170 | 'return text returned of (display dialog "Country Code:" default answer "'
171 | + Action.preferences.country + '" giving up after 15 with icon note)');
172 | if (c && c.length > 0) {
173 | Action.preferences.country = c.trim();
174 | }
175 | }
176 |
177 | function actionKey(item) {
178 | var k = LaunchBar.executeAppleScript(
179 | 'return text returned of (display dialog "Dark Sky API Key:" default answer "'
180 | + Action.preferences.apiKey + '" giving up after 15 with icon note)');
181 | if (k && k.length > 0) {
182 | Action.preferences.apiKey = k.trim();
183 | }
184 | }
185 |
186 | function actionPrefs(item) {
187 | LaunchBar.openURL('file:/' + encodeURIComponent(PREF_FILE), 'TextEdit');
188 | }
189 |
190 |
--------------------------------------------------------------------------------
/Generate Password.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.GeneratePassword
7 | CFBundleName
8 | Generate Password
9 | CFBundleVersion
10 | 6.2
11 | CFBundleIconFile
12 | font-awesome:fa-lock
13 | LBDebugLogEnabled
14 |
15 | LBScripts
16 |
17 | LBDefaultScript
18 |
19 | LBScriptName
20 | generate.js
21 | LBRunInBackground
22 |
23 | LBRequiresArgument
24 |
25 | LBReturnsResult
26 |
27 |
28 |
29 | LBDescription
30 |
31 | LBSummary
32 | Generate passwords, return results, and copy first to the clipboard. Uses https://github.com/bbusschots/hsxkpasswd for word passwords.
33 | LBAuthor
34 | Padraic Renaghan
35 | LBEmail
36 | prenagha@renaghan.com
37 | LBWebsiteURL
38 | https://renaghan.com/launchbar/generate-password/
39 | LBTwitter
40 | @prenagha
41 | LBUpdateURL
42 | https://raw.githubusercontent.com/prenagha/launchbar/master/Generate%20Password.lbaction/Contents/Info.plist
43 | LBDownloadURL
44 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Generate%20Password.lbaction&fileName=Generate-Password&rootDirectory=Generate-Password.lbaction
45 | LBChangelog
46 |
47 | 6.2: Fix download URL
48 | 6.0: Returns multiple results, defaults to hsxkpasswd word based password
49 | 5.0: Rewritten in Javascript. Now supports preferences.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/Generate Password.lbaction/Contents/Scripts/generate.js:
--------------------------------------------------------------------------------
1 | var ALERT_ICON = 'font-awesome:fa-exclamation-triangle';
2 | var WORD_ICON = 'font-awesome:fa-lock';
3 | var RANDOM_ICON = 'font-awesome:fa-random';
4 | var TYPES = "LUNS";
5 |
6 | function setup() {
7 | // setup default preferences if missing
8 | // standard characters WITHOUT easy to mistake 0Oo, iI1lL
9 | // set a *Min value to -1 to mean NONE of those characters
10 | if (!Action.preferences.lowercase)
11 | Action.preferences.lowercase = 'abcdefghjkmnpqrstuvwxyz';
12 | if (!Action.preferences.uppercase)
13 | Action.preferences.uppercase = 'ABCDEFGHJKMNPQRSTUVWXYZ';
14 | if (!Action.preferences.numbers)
15 | Action.preferences.numbers = '23456789';
16 | if (!Action.preferences.symbols)
17 | Action.preferences.symbols = '@#$%!.=+-_';
18 | if (!Action.preferences.minLength)
19 | Action.preferences.minLength = 15;
20 | if (!Action.preferences.lowercaseMin)
21 | Action.preferences.lowercaseMin = 1;
22 | if (!Action.preferences.uppercaseMin)
23 | Action.preferences.uppercaseMin = 1;
24 | if (!Action.preferences.numberMin)
25 | Action.preferences.numberMin = 1;
26 | if (!Action.preferences.symbolMin)
27 | Action.preferences.symbolMin = 1;
28 | if (!Action.preferences.hsxkpasswdPath)
29 | Action.preferences.hsxkpasswdPath="/usr/local/bin/hsxkpasswd";
30 | if (!Action.preferences.hsxkpasswdConfig)
31 | Action.preferences.hsxkpasswdConfig="hsxkpasswd-config.json";
32 | }
33 |
34 | // return a random from the possible values
35 | function rand(possible) {
36 | return possible.charAt(Math.floor(Math.random() * possible.length))
37 | }
38 |
39 | function randomPass() {
40 | var lowercaseMin = 0;
41 | var uppercaseMin = 0;
42 | var numberMin = 0;
43 | var symbolMin = 0;
44 |
45 | var type = "";
46 | var pwd = "";
47 | // don't start or end on a symbol
48 | while (pwd.length < Action.preferences.minLength
49 | || lowercaseMin < Action.preferences.lowercaseMin
50 | || uppercaseMin < Action.preferences.uppercaseMin
51 | || numberMin < Action.preferences.numberMin
52 | || symbolMin < Action.preferences.symbolMin
53 | || type === "S") {
54 | type = rand(TYPES);
55 | if ("L" === type && Action.preferences.lowercaseMin >= 0) {
56 | pwd += rand(Action.preferences.lowercase);
57 | lowercaseMin++;
58 | } else if ("U" === type && Action.preferences.uppercaseMin >= 0) {
59 | pwd += rand(Action.preferences.uppercase);
60 | uppercaseMin++;
61 | } else if ("N" === type && Action.preferences.numberMin >= 0) {
62 | pwd += rand(Action.preferences.numbers);
63 | numberMin++;
64 | } else if ("S" === type && Action.preferences.symbolMin >= 0 && pwd.length > 0) {
65 | pwd += rand(Action.preferences.symbols);
66 | symbolMin++;
67 | }
68 | }
69 | return pwd;
70 | }
71 |
72 | function run() {
73 | setup();
74 | var output = [];
75 | var firstPass = "";
76 |
77 | if (Action.preferences.hsxkpasswdPath.length > 0) {
78 | if (File.isExecutable(Action.preferences.hsxkpasswdPath)) {
79 | var pwds = LaunchBar.execute(
80 | Action.preferences.hsxkpasswdPath
81 | , '--config-file'
82 | , Action.preferences.hsxkpasswdConfig
83 | , '--warn'
84 | , 'NONE'
85 | , '4');
86 | pwds = pwds.split('\n');
87 | for (i=0; i
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Incognito
7 | CFBundleName
8 | Incognito Browser
9 | LBAbbreviation
10 | incognito private chrome www safari
11 | CFBundleVersion
12 | 3.2
13 | CFBundleIconFile
14 | font-awesome:user-secret
15 | LBARequiredApplication
16 | com.google.Chrome
17 | LBTextInputTitle
18 | URL
19 | LBDebugLogEnabled
20 |
21 | LBMinimumLaunchBarVersion
22 | 6121
23 | LBScripts
24 |
25 | LBDefaultScript
26 |
27 | LBScriptName
28 | incognito.scpt
29 | LBKeepWindowActive
30 |
31 | LBRunInBackground
32 |
33 | LBRequiresArgument
34 |
35 | LBAcceptedArgumentTypes
36 |
37 | string
38 |
39 | LBReturnsResult
40 |
41 |
42 |
43 | LBDescription
44 |
45 | LBSummary
46 | LaunchBar action that opens a Chromium browser window/tab in incognito private browsing mode. Opens to URL from active Safari tab, URL from clipboard, DuckDuckGo Search URL with clipboard term, or blank. If more than one option is available will show LaunchBar items for you to select.
47 | LBRequirements
48 | Chromium Browser https://github.com/Eloston/ungoogled-chromium
49 | LBAuthor
50 | Padraic Renaghan
51 | LBEmail
52 | prenagha@renaghan.com
53 | LBWebsiteURL
54 | https://renaghan.com/launchbar/incognito-browser/
55 | LBTwitter
56 | @prenagha
57 | LBArgument
58 | URL
59 | LBUpdateURL
60 | https://raw.githubusercontent.com/prenagha/launchbar/master/Incognito.lbaction/Contents/Info.plist
61 | LBDownloadURL
62 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Incognito.lbaction
63 | LBChangelog
64 |
65 | 3.2: Fix download URL
66 | 3.0: Change to Chromium browser
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/Incognito.lbaction/Contents/Resources/incognito.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Incognito.lbaction/Contents/Resources/incognito.png
--------------------------------------------------------------------------------
/Incognito.lbaction/Contents/Scripts/incognito.applescript:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Incognito.lbaction/Contents/Scripts/incognito.applescript
--------------------------------------------------------------------------------
/JIRA.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.JIRA
7 | CFBundleName
8 | JIRA Search
9 | CFBundleVersion
10 | 2.1
11 | CFBundleIconFile
12 | jira.png
13 | LBDebugLogEnabled
14 |
15 | LBTextInputTitle
16 | Term
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | jira.js
23 | LBRequiresArgument
24 |
25 | LBAcceptedArgumentTypes
26 |
27 | string
28 |
29 | LBRunInBackground
30 |
31 | LBReturnsResult
32 |
33 |
34 |
35 | LBDescription
36 |
37 | LBSummary
38 | Open JIRA search
39 | LBAuthor
40 | Padraic Renaghan
41 | LBEmail
42 | prenagha@renaghan.com
43 | LBWebsiteURL
44 | https://renaghan.com/launchbar/jira/
45 | LBTwitter
46 | @prenagha
47 | LBUpdateURL
48 | https://raw.githubusercontent.com/prenagha/launchbar/master/JIRA.lbaction/Contents/Info.plist
49 | LBDownloadURL
50 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/JIRA.lbaction
51 | LBChangelog
52 |
53 | 2.1: Fix download URL
54 | 2.0: Configurable default project, query. Removed Crucible. Thanks @alloydwhitlock
55 | 1.0: First release
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/JIRA.lbaction/Contents/Resources/jira.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/JIRA.lbaction/Contents/Resources/jira.png
--------------------------------------------------------------------------------
/JIRA.lbaction/Contents/Scripts/jira.js:
--------------------------------------------------------------------------------
1 |
2 | function run(arg) {
3 | if (!arg || arg === null || typeof(arg) != 'string' || arg.length === 0)
4 | return;
5 |
6 | arg = arg.trim();
7 |
8 | // default jira project
9 | if (!Action.preferences.jiraDefaultProject)
10 | Action.preferences.jiraDefaultProject = "";
11 |
12 | // base URL to jira server
13 | if (!Action.preferences.jiraBase)
14 | Action.preferences.jiraBase = "https://jira.example.com";
15 |
16 | // text search query, replaced with the search term
17 | if (!Action.preferences.jiraQuery)
18 | Action.preferences.jiraQuery = "/issues/?jql=text%20~%20%22%22"
19 | +"%20AND%20status%20!%3D%20Closed%20ORDER%20BY%20key%20DESC";
20 |
21 | var url = "";
22 | // number assumed to be jira issue id in default project
23 | if (Action.preferences.jiraDefaultProject.length > 0 && arg.match(/^\d+$/)) {
24 | url = Action.preferences.jiraBase
25 | + "/browse/"
26 | + Action.preferences.jiraDefaultProject
27 | + "-" + arg;
28 |
29 | // XX-number assumed to be a fully qualified jira issue id
30 | } else if (arg.match(/^[A-Z]{2,}-\d+$/i)) {
31 | url = Action.preferences.jiraBase + "/browse/" + arg;
32 |
33 | // otherwise string assumed to be a search term
34 | } else {
35 | const term = encodeURIComponent(arg);
36 | url = Action.preferences.jiraBase
37 | + Action.preferences.jiraQuery.replaceAll('', term);
38 | }
39 |
40 | LaunchBar.debugLog("URL " + url);
41 | LaunchBar.openURL(url);
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/MailMate.applescript:
--------------------------------------------------------------------------------
1 |
2 | on dlog(myObj)
3 | set txt to quoted form of (myObj as string)
4 | log txt
5 | do shell script "logger -t 'LaunchBar.MailMate' " & txt
6 | end dlog
7 |
8 | on urlencode(theText)
9 | set theTextEnc to ""
10 | repeat with eachChar in characters of theText
11 | set useChar to eachChar
12 | set eachCharNum to ASCII number of eachChar
13 | if eachCharNum = 32 or eachCharNum > 127 then
14 | set useChar to "%20"
15 | else if (eachCharNum is not equal to 42) and (eachCharNum is not equal to 95) and (eachCharNum < 45 or eachCharNum > 46) and (eachCharNum < 48 or eachCharNum > 57) and (eachCharNum < 65 or eachCharNum > 90) and (eachCharNum < 97 or eachCharNum > 122) then
16 | set firstDig to round (eachCharNum / 16) rounding down
17 | set secondDig to eachCharNum mod 16
18 | if firstDig > 9 then
19 | set aNum to firstDig + 55
20 | set firstDig to ASCII character aNum
21 | end if
22 | if secondDig > 9 then
23 | set aNum to secondDig + 55
24 | set secondDig to ASCII character aNum
25 | end if
26 | set numHex to ("%" & (firstDig as string) & (secondDig as string)) as string
27 | set useChar to numHex
28 | end if
29 | set theTextEnc to theTextEnc & useChar as string
30 | end repeat
31 | return theTextEnc
32 | end urlencode
33 |
34 | on makeTo(_emailAddresses)
35 | set _to to ""
36 | repeat with addr in _emailAddresses
37 | set _to to _to & "&to=" & urlencode(addr)
38 | end repeat
39 | return _to
40 | end makeTo
41 |
42 | on basename(thePath) -- Requires POSIX path
43 | set ASTID to AppleScript's text item delimiters
44 | set AppleScript's text item delimiters to "/"
45 | set thePath to text item -1 of thePath
46 | set AppleScript's text item delimiters to ASTID
47 | return thePath
48 | end basename
49 |
50 | on sendFiles(_files, _emailAddresses)
51 | try
52 | set _mailto to "mailto:?send-now=no" & makeTo(_emailAddresses)
53 | set _names to ""
54 | repeat with _file in _files
55 | set _filePath to POSIX path of _file
56 | set _names to _names & " " & basename(_filePath)
57 | set _mailto to _mailto & "&attachment-url=file://" & urlencode(_filePath)
58 | end repeat
59 | set _mailto to _mailto & "&subject=File:" & urlencode(_names) & "&body=" & urlencode("File attached")
60 | tell application "MailMate" to open location _mailto with trust
61 | tell application "MailMate" to activate
62 | on error error_message number error_number
63 | set msg to "LaunchBar.MailMate.sendFiles ERROR: " & error_message & " #" & error_number
64 | dlog(msg)
65 | display dialog msg
66 | end try
67 | end sendFiles
68 |
69 | on sendText(txt, _emailAddresses)
70 | try
71 | if txt contains " http" then
72 | set myName to text 1 thru ((offset of " http" in txt) - 5) of txt
73 | set myURL to text ((offset of " http" in txt) + 1) thru end of txt
74 | set mySubj to "Link: " & myName
75 | set myBody to myName & return & myURL & return & return & "Enjoy"
76 | set _mailto to "mailto:?send-now=yes&subject=" & urlencode(mySubj) & makeTo(_emailAddresses) & "&body=" & urlencode(myBody)
77 | tell application "MailMate" to open location _mailto with trust
78 | else
79 | set _mailto to "mailto:?send-now=no&" & makeTo(_emailAddresses) & "&body=" & urlencode(txt)
80 | tell application "MailMate" to open location _mailto
81 | tell application "MailMate" to activate
82 | end if
83 | on error error_message number error_number
84 | set msg to "LaunchBar.MailMate.sendText ERROR: " & error_message & " #" & error_number
85 | dlog(msg)
86 | display dialog msg
87 | end try
88 | end sendText
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [LaunchBar Actions web site, documentation, screenshots](https://renaghan.com/launchbar/)
2 |
3 | ### NOTE on `.applescript` files
4 |
5 | Text `.applescript` Apple scripts are used here for ease of integration with version control systems, diff tools...
6 |
7 | The LaunchBar action `Info.plist` refers to a *COMPILED* `.scpt` version of the script.
8 |
9 | You can compile these text `.applescript` files into `.scpt` files using command line `/usr/bin/osacompile` or by exporting within Script Editor
10 |
11 | [My build script to compile `.applescript` to `.scpt`](https://gist.github.com/prenagha/404284fee1b8ff86aec5)
12 |
--------------------------------------------------------------------------------
/Read-Later.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.ReadLater
7 | CFBundleName
8 | Read Later
9 | CFBundleVersion
10 | 1.0
11 | CFBundleIconFile
12 | font-awesome:fa-link
13 | LBDebugLogEnabled
14 |
15 | LBScripts
16 |
17 | LBDefaultScript
18 |
19 | LBScriptName
20 | readlater.js
21 | LBRunInBackground
22 |
23 | LBRequiresArgument
24 |
25 | LBReturnsResult
26 |
27 |
28 |
29 | LBDescription
30 |
31 | LBSummary
32 | Manage read later links
33 | LBAuthor
34 | Padraic Renaghan
35 | LBEmail
36 | padraic@renaghan.com
37 | LBWebsiteURL
38 | https://renaghan.com/launchbar/read-later/
39 | LBUpdateURL
40 | https://raw.githubusercontent.com/prenagha/launchbar/master/ReadLater.lbaction/Contents/Info.plist
41 | LBDownloadURL
42 | https://github.com/prenagha/launchbar/tree/main/ReadLater.lbaction
43 | LBChangelog
44 |
45 | 1.0: Initial
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Read-Later.lbaction/Contents/Scripts/readlater.js:
--------------------------------------------------------------------------------
1 |
2 | function run() {
3 | links = load();
4 | refreshCounter();
5 | output = [];
6 | if (links.length == 0) {
7 | output.push({
8 | 'title': 'Zero links',
9 | 'icon': 'font-awesome:fa-circle-o'
10 | });
11 | } else {
12 | showLinks = [];
13 | for (let i = 0; i < links.length; i++) {
14 | link = links[i];
15 | if (link && link.url && !link.linkduplicate)
16 | showLinks.push(link);
17 | }
18 | output.push({
19 | title: 'Read All ' + showLinks.length + ' ↗️',
20 | icon: 'font-awesome:fa-trash',
21 | action: 'readAll',
22 | actionRunsInBackground: true
23 | });
24 | output.push({
25 | title: 'Peek All ' + showLinks.length + ' 👀',
26 | icon: 'font-awesome:fa-eye',
27 | action: 'peekAll',
28 | actionRunsInBackground: true
29 | });
30 | output.push(...showLinks);
31 | output.push({
32 | title: 'Remove',
33 | icon: 'font-awesome:file-o',
34 | action: 'removeList',
35 | actionReturnsItems: true
36 | });
37 | output.push({
38 | title: 'Remove All ' + links.length + ' 👋🏼',
39 | icon: 'font-awesome:files-o',
40 | action: 'removeAll',
41 | actionRunsInBackground: true
42 | });
43 | }
44 | return output;
45 | }
46 |
47 | function readAll() {
48 | links = load();
49 | openAll(links);
50 | deleteAll(links);
51 | refreshCounter();
52 | }
53 |
54 | function peekAll() {
55 | links = load();
56 | openAll(links);
57 | }
58 |
59 | function openAll(links) {
60 | for (let i = 0; i < links.length; i++) {
61 | link = links[i];
62 | if (link && link.url && !link.linkduplicate)
63 | LaunchBar.execute('/usr/bin/open', '--url', link.url);
64 | }
65 | }
66 |
67 | function deleteAll(links) {
68 | for (let i = 0; i < links.length; i++) {
69 | link = links[i];
70 | if (link && link.linkfile)
71 | removeFile(link.linkfile);
72 | }
73 | }
74 |
75 | function removeAll() {
76 | links = load();
77 | deleteAll(links);
78 | refreshCounter();
79 | }
80 |
81 | function removeList() {
82 | output = []
83 | links = load();
84 | for (let i = 0; i < links.length; i++) {
85 | link = links[i];
86 | if (link && link.linkfile && !link.linkduplicate) {
87 | link.icon = 'font-awesome:fa-trash';
88 | link.action = 'removeOne';
89 | link.actionArgument = link.url;
90 | output.push(link);
91 | }
92 | }
93 | return output;
94 | }
95 |
96 | function removeOne(url) {
97 | links = load();
98 | for (let i = 0; i < links.length; i++) {
99 | link = links[i];
100 | if (url && link && link.url && url === link.url)
101 | removeFile(link.linkfile);
102 | }
103 | refreshCounter();
104 | }
105 |
106 | function refreshCounter() {
107 | LaunchBar.execute('/usr/bin/open', '--background', '--url', 'swiftbar://refreshplugin?name=links');
108 | }
109 |
110 | function capDir() {
111 | captureDir = LaunchBar.homeDirectory + '/Library/Mobile Documents/iCloud~is~workflow~my~workflows/Documents/Read-Later/Capture';
112 | if (Action.preferences.captureDir == undefined
113 | || Action.preferences.captureDir.length == 0) {
114 | Action.preferences.captureDir = captureDir;
115 | }
116 | return Action.preferences.captureDir;
117 | }
118 |
119 | function rdDir() {
120 | readDir = LaunchBar.homeDirectory + '/Archive/Links/Read'
121 | if (Action.preferences.readDir == undefined
122 | || Action.preferences.readDir.length == 0) {
123 | Action.preferences.readDir = readDir;
124 | }
125 | return Action.preferences.readDir;
126 | }
127 |
128 | function load() {
129 | var captureDir = capDir();
130 | LaunchBar.debugLog("captureDir " + Action.preferences.captureDir);
131 | if (!File.exists(captureDir) || !File.isDirectory(captureDir))
132 | return [err('Capture dir invalid', captureDir)];
133 |
134 | var urls = new Set();
135 | var files = File.getDirectoryContents(Action.preferences.captureDir);
136 | var links = [];
137 | for (let i = 0; i < files.length; i++) {
138 | file = captureDir + '/' + files[i];
139 | LaunchBar.debugLog('Reading ' + file);
140 | lines = File.readText(file).split('\n');
141 | if (lines && lines.length >= 2) {
142 | url = lines[0];
143 | links.push({
144 | title: lines[1],
145 | subtitle: url,
146 | icon: 'font-awesome:fa-globe',
147 | url: url,
148 | linkfile: file,
149 | linkduplicate: urls.has(url)
150 | });
151 | urls.add(url);
152 | } else {
153 | links.push(err('Invalid file', file));
154 | }
155 | };
156 | links.sort((a,b) => a.title.localeCompare(b.title));
157 | return links;
158 | }
159 |
160 | function removeFile(file) {
161 | if (file) {
162 | dt = new Date();
163 | year = dt.getFullYear();
164 | month = (dt.getMonth() < 10 ? "0" : "") + (dt.getMonth()+1)
165 | day = (dt.getDate() < 10 ? "0" : "") + dt.getDate()
166 | dir = rdDir() + '/' + year + '/' + month + '/' + day;
167 | if (!File.exists(dir))
168 | File.createDirectory(dir);
169 | LaunchBar.log('Move to read ' + file);
170 | LaunchBar.execute('/bin/mv', '-f', file, dir);
171 | }
172 | }
173 |
174 | function err(msg, detail) {
175 | var m = 'ERROR: ' + msg;
176 | LaunchBar.log(m);
177 | return {
178 | title: m,
179 | icon: 'font-awesome:fa-exclamation-triangle',
180 | subtitle : detail,
181 | alwaysShowSubtitle: true
182 | };
183 | }
184 |
--------------------------------------------------------------------------------
/Recents.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Recents
7 | CFBundleName
8 | Recent Files
9 | CFBundleVersion
10 | 1.2
11 | CFBundleIconFile
12 | font-awesome:fa-clock-o
13 | LBDebugLogEnabled
14 |
15 | LBMinimumLaunchBarVersion
16 | 6121
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | recents.js
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBReturnsResult
28 |
29 |
30 |
31 | LBDescription
32 |
33 | LBSummary
34 | LaunchBar action integration to return result of recently used files from Smart Folder/Saved Search
35 | LBRequirements
36 | Smart Folder/Saved Search http://www.macworld.com/article/1154709/business/smartfolders.html
37 | LBAuthor
38 | Padraic Renaghan
39 | LBEmail
40 | prenagha@renaghan.com
41 | LBWebsiteURL
42 | https://renaghan.com/launchbar/recent-files/
43 | LBTwitter
44 | @prenagha
45 | LBUpdateURL
46 | https://raw.githubusercontent.com/prenagha/launchbar/master/Recents.lbaction/Contents/Info.plist
47 | LBDownloadURL
48 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Recents.lbaction
49 | LBChangelog
50 |
51 | 1.2: Fix download URL
52 | 1.0: Initial Build
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Recents.lbaction/Contents/Scripts/recents.js:
--------------------------------------------------------------------------------
1 |
2 | function run() {
3 | var homeDir = LaunchBar.homeDirectory;
4 |
5 | if (!Action.preferences.NotFoundMessage)
6 | Action.preferences.NotFoundMessage = 'No recent files found';
7 |
8 | if (!Action.preferences.FindCommand)
9 | Action.preferences.FindCommand =
10 | '/usr/bin/mdfind,-onlyin,'+homeDir+'/Documents,-onlyin,'+homeDir+'/Desktop,-onlyin,'+homeDir+'/Downloads,-s,Recents';
11 |
12 | var args = Action.preferences.FindCommand.split(',');
13 | var searchLines = LaunchBar.execute(...args);
14 | searchLines = searchLines.split('\n');
15 |
16 | var results = [];
17 |
18 | for (i = 0; i < searchLines.length; i++) {
19 | var path = searchLines[i];
20 | var p = path.substring(homeDir.length+1);
21 |
22 | var parts = p.split('/');
23 | var label = parts[0];
24 | var name = parts[parts.length-1];
25 | var dir = '';
26 | if (parts.length > 2) {
27 | dir = parts.slice(1, parts.length).join('/');
28 | }
29 |
30 | results.push({'title': name, 'path': path, 'label': label, 'subtitle': dir});
31 | }
32 |
33 | if (results.length == 0) {
34 | results.push({'title': Action.preferences.NotFoundMessage,
35 | 'icon': 'font-awesome:fa-clock-o'});
36 | }
37 |
38 | return results;
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/Screenshot.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Screenshot
7 | CFBundleName
8 | Screenshot
9 | CFBundleVersion
10 | 3.7
11 | CFBundleIconFile
12 | font-awesome:fa-camera
13 | LBDebugLogEnabled
14 |
15 | LBScripts
16 |
17 | LBDefaultScript
18 |
19 | LBScriptName
20 | screenshot.js
21 | LBRunInBackground
22 |
23 | LBRequiresArgument
24 |
25 | LBReturnsResult
26 |
27 |
28 |
29 | LBDescription
30 |
31 | LBSummary
32 | Capture a screenshot, save it to ~/Downloads folder as original and
33 | optimized with ImageAlpha, then pass optimized version back to LaunchBar for
34 | sending to another action
35 | LBRequirements
36 | ImageAlpha http://pngmini.com
37 | LBAuthor
38 | Padraic Renaghan
39 | LBEmail
40 | prenagha@renaghan.com
41 | LBWebsiteURL
42 | https://renaghan.com/launchbar/screenshot/
43 | LBTwitter
44 | @prenagha
45 | LBResult
46 | Screenshot file optimized through ImageAlpha
47 | LBUpdateURL
48 | https://raw.githubusercontent.com/prenagha/launchbar/master/Screenshot.lbaction/Contents/Info.plist
49 | LBDownloadURL
50 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Screenshot.lbaction
51 | LBChangelog
52 |
53 | 3.7: Fix download URL
54 | 3.5: Fix trash and img copy code
55 | 3.4: No error on missing screenshot, original moved to trash
56 | 3.3: Changed image copy to swift
57 | 3.2: Automatically copy image to clipboard
58 | Support action updates
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/Screenshot.lbaction/Contents/Scripts/imgcopy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Screenshot.lbaction/Contents/Scripts/imgcopy
--------------------------------------------------------------------------------
/Screenshot.lbaction/Contents/Scripts/screenshot.js:
--------------------------------------------------------------------------------
1 | /* orig idea from https://github.com/hlissner/launchbar6-scripts */
2 |
3 | function run() {
4 | var d = new Date();
5 | var df = "" + d.getFullYear() + (d.getMonth()+1) + d.getDate()
6 | + "_" + d.getHours() + d.getMinutes() + d.getSeconds()
7 | + "_" + d.getMilliseconds();
8 | var path = LaunchBar.homeDirectory + '/Downloads/sc_' + df + '.png';
9 | var opt = '/Applications/ImageAlpha.app/Contents/MacOS/pngquant';
10 | try {
11 | LaunchBar.execute('/usr/sbin/screencapture', '-i', path);
12 | if (!File.exists(path))
13 | return;
14 | if (File.exists(opt)) {
15 | var orig = LaunchBar.homeDirectory
16 | + '/Downloads/sc_' + df + '_orig.png';
17 | LaunchBar.execute('/bin/cp', '-p', path, orig);
18 | LaunchBar.execute(opt, '--force', '--ext', '.png', path);
19 | }
20 |
21 | // copy the optimized image to the clipboard
22 | LaunchBar.executeAppleScript('tell app "Finder" to set the clipboard to POSIX file "' + path + '"');
23 |
24 | // move original image to Trash
25 | LaunchBar.execute(Action.path + '/Contents/Scripts/trash', orig);
26 |
27 | } catch (exception) {
28 | LaunchBar.log('Screenshot Error ' + exception + ' -- ' + path);
29 | LaunchBar.alert('Screenshot Error', exception);
30 | return;
31 | }
32 |
33 | // this action runs in background so screencapture can work
34 | // when it is done tell LB to select the screenshot file which
35 | // will make LB reappear
36 | LaunchBar.openCommandURL('select?file='+encodeURIComponent(path));
37 | return;
38 | }
39 |
--------------------------------------------------------------------------------
/Screenshot.lbaction/Contents/Scripts/trash:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Screenshot.lbaction/Contents/Scripts/trash
--------------------------------------------------------------------------------
/Screenshot.lbaction/Contents/Scripts/trash.swift:
--------------------------------------------------------------------------------
1 | // move file to trash
2 | // idea from https://github.com/reklis/recycle
3 | import Cocoa
4 |
5 | let argCount = CommandLine.arguments.count
6 | if argCount > 1 {
7 | let manager = FileManager.default
8 | for path in CommandLine.arguments[1..
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.ShareSafariLink
7 | CFBundleName
8 | Share Safari Link
9 | CFBundleVersion
10 | 3.4
11 | CFBundleIconFile
12 | com.apple.Safari
13 | LBDebugLogEnabled
14 |
15 | LBRequiredApplication
16 | com.apple.Safari
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | share.scpt
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBReturnsResult
28 |
29 |
30 |
31 | LBDescription
32 |
33 | LBSummary
34 | Grab the URL and Title of the currently selected tab in Safari and return it in text form suitable for sending via email.
35 | LBAuthor
36 | Padraic Renaghan
37 | LBEmail
38 | prenagha@renaghan.com
39 | LBWebsiteURL
40 | https://renaghan.com/launchbar/share-safari-link/
41 | LBTwitter
42 | @prenagha
43 | LBResult
44 | URL and Title of selected tab in Safari
45 | LBUpdateURL
46 | https://raw.githubusercontent.com/prenagha/launchbar/master/Share%20Safari%20Link.lbaction/Contents/Info.plist
47 | LBDownloadURL
48 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Share%20Safari%20Link.lbaction&fileName=Share-Safari-Link&rootDirectory=Share-Safari-Link.lbaction
49 | LBChangelog
50 |
51 | 3.4: Fix download URL
52 | Support action updates
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Share Safari Link.lbaction/Contents/Scripts/share.applescript:
--------------------------------------------------------------------------------
1 |
2 | property SAFARI : "com.apple.Safari"
3 |
4 | -- is application running?
5 | on app_running(bundleId)
6 | tell application "System Events"
7 | return (bundle identifier of processes) contains bundleId
8 | end tell
9 | end app_running
10 |
11 | -- This handler is called when the user runs the action:
12 | on run
13 | if app_running(SAFARI) then
14 | -- Info the current Safari tab back to Launchbar
15 | tell application "Safari"
16 | set theURL to URL of current tab of window 1
17 | set theTitle to name of current tab of window 1
18 | set theLink to theTitle & " " & theURL as string
19 | return [{title:theLink, subtitle:theURL, icon:"URL.icns"}]
20 | end tell
21 | else
22 | return [{title:"Safari not running", icon:"Caution.icns"}]
23 | end if
24 | end run
25 |
--------------------------------------------------------------------------------
/Share.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Share
7 | CFBundleName
8 | Share File
9 | CFBundleVersion
10 | 2.2
11 | CFBundleIconFile
12 | font-awesome:fa-share
13 | LBDebugLogEnabled
14 |
15 | LBScripts
16 |
17 | LBDefaultScript
18 |
19 | LBScriptName
20 | share.js
21 | LBRunInBackground
22 |
23 | LBRequiresArgument
24 |
25 | LBAcceptedArgumentTypes
26 |
27 | string
28 | path
29 |
30 | LBAllowsInstantReuseOfLastTextInputString
31 |
32 | LBReturnsResult
33 |
34 |
35 |
36 | LBDescription
37 |
38 | LBSummary
39 | Share a file via Amazon AWS S3 bucket
40 | LBRequirements
41 | Amazon AWS S3 bucket, properly configured, Amazon AWS command line interface (CLI)
42 | LBAuthor
43 | Padraic Renaghan
44 | LBEmail
45 | prenagha@renaghan.com
46 | LBWebsiteURL
47 | https://renaghan.com/launchbar/share-file/
48 | LBTwitter
49 | @prenagha
50 | LBArgument
51 | Selected file in LaunchBar
52 | LBUpdateURL
53 | https://raw.githubusercontent.com/prenagha/launchbar/master/Share.lbaction/Contents/Info.plist
54 | LBDownloadURL
55 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Share.lbaction
56 | LBChangelog
57 |
58 | 2.2: Fix download URL
59 | 2.0: Use AWS S3 pre-signed URL
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Share.lbaction/Contents/Scripts/share.js:
--------------------------------------------------------------------------------
1 |
2 | var ALERT_ICON = 'font-awesome:fa-exclamation-triangle';
3 |
4 | function runWithString(string) {
5 | return go(string);
6 | }
7 |
8 | function runWithPaths(paths) {
9 | if (paths.length == 1)
10 | return go(paths[0]);
11 |
12 | // zip up the paths and dropshare the zipped file
13 | var tzip = LaunchBar.execute('/bin/bash', 'zip.sh', ...paths);
14 | LaunchBar.debugLog('Temp zip file ' + tzip);
15 | tzip = chomp(tzip);
16 | var rtn = go(tzip);
17 | LaunchBar.execute('/bin/rm', tzip);
18 | return rtn;
19 | }
20 |
21 | function runWithItem(item) {
22 | return go(item.path);
23 | }
24 |
25 | function runWithURL(url, details) {
26 | return go(url);
27 | }
28 |
29 | function run(arg) {
30 | return go(LaunchBar.getClipboardString());
31 | }
32 |
33 | function chomp(inp) {
34 | return inp.replace(/(\n|\r)+$/, '');
35 | }
36 |
37 | function go(file) {
38 | if (!file || file == undefined || file.length == 0) {
39 | return err('Missing file to share', '');
40 | }
41 | LaunchBar.debugLog("Input File='" + file + "'");
42 | if (!File.exists(file)) {
43 | return err('File does not exist', file);
44 | }
45 | if (!File.isReadable(file)) {
46 | return err('File is not readable', file);
47 | }
48 |
49 | if (Action.preferences.bucket == undefined
50 | || Action.preferences.bucket.length == 0) {
51 | Action.preferences.bucket = 'mybucket';
52 | }
53 | LaunchBar.debugLog("Pref bucket='" + Action.preferences.bucket + "'");
54 |
55 | if (Action.preferences.dir == undefined
56 | || Action.preferences.dir.length == 0) {
57 | Action.preferences.dir = 'share';
58 | }
59 | LaunchBar.debugLog("Pref dir='" + Action.preferences.dir + "'");
60 |
61 | if (Action.preferences.profilePut == undefined
62 | || Action.preferences.profilePut.length == 0) {
63 | Action.preferences.profilePut = 'share-file-put';
64 | }
65 | LaunchBar.debugLog("Pref profilePut='" + Action.preferences.profilePut + "'");
66 |
67 | if (Action.preferences.profileGet == undefined
68 | || Action.preferences.profileGet.length == 0) {
69 | Action.preferences.profileGet = 'share-file-get';
70 | }
71 | LaunchBar.debugLog("Pref profileGet='" + Action.preferences.profileGet + "'");
72 |
73 | try {
74 | var sharedURL = LaunchBar.execute('/bin/bash', 'share.sh', Action.preferences.bucket, Action.preferences.dir, Action.preferences.profilePut, Action.preferences.profileGet, file);
75 | LaunchBar.debugLog("Shared URL='" + sharedURL + "'");
76 |
77 | return [{
78 | title: 'Share URL'
79 | ,subtitle: sharedURL
80 | ,badge: 'on clipboard'
81 | ,url: sharedURL
82 | }];
83 |
84 | } catch (exception) {
85 | return err('Error sharing: ' + exception, file);
86 | }
87 | }
88 |
89 | function err(msg, file) {
90 | var m = 'ERROR: ' + msg;
91 | LaunchBar.log(m);
92 | return [{'title': m,
93 | 'icon': ALERT_ICON,
94 | path: file,
95 | subtitle: file}];
96 | }
97 |
--------------------------------------------------------------------------------
/Share.lbaction/Contents/Scripts/share.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | AWS=/usr/local/bin/aws
4 | TEN_DAYS_SECONDS="864000"
5 |
6 | BUCKET=$1
7 | BUCKET_DIR=$2
8 | PROFILE_PUT=$3
9 | PROFILE_GET=$4
10 | FILE=$5
11 |
12 | if [ ! -f "${AWS}" ]
13 | then
14 | echo "AWS CLI not found ${AWS}"
15 | exit 1
16 | fi
17 |
18 | if [ ! -f "${FILE}" ]
19 | then
20 | echo "File to share not found ${FILE}"
21 | exit 1
22 | fi
23 |
24 | NAME=${FILE##*/}
25 | DEST=s3://${BUCKET}/${BUCKET_DIR}/${NAME}
26 |
27 | # copy the file to S3, private, no public access
28 | $AWS --profile ${PROFILE_PUT} s3 cp \
29 | --only-show-errors \
30 | --acl private \
31 | --cache-control no-cache \
32 | --storage-class STANDARD \
33 | --sse AES256 \
34 | ${FILE} ${DEST} 2>&1
35 |
36 | if [ $? -ne 0 ]
37 | then
38 | exit 2
39 | fi
40 |
41 | TMPFILE=`/usr/bin/mktemp -t signedUrl.txt` || exit 1
42 |
43 | # get a signed URL that will work for 10 days
44 | $AWS --profile ${PROFILE_GET} s3 presign \
45 | --expires-in ${TEN_DAYS_SECONDS} \
46 | ${DEST} > ${TMPFILE}
47 |
48 | if [ $? -ne 0 ]
49 | then
50 | /bin/rm ${TMPFILE} 2>/dev/null
51 | exit 3
52 | fi
53 |
54 | # put the signed URL on the clipboard
55 | /bin/cat ${TMPFILE} | /usr/bin/pbcopy
56 |
57 | # return signed URL back to LaunchBar
58 | /bin/cat ${TMPFILE}
59 |
60 | /bin/rm ${TMPFILE} 2>/dev/null
61 |
62 | exit 0
63 |
--------------------------------------------------------------------------------
/Share.lbaction/Contents/Scripts/zip.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | TDIR=`/usr/bin/mktemp -d`
3 | BFIRST=`basename $1`
4 | FIRST="${BFIRST%.*}"
5 | TZIP="${TDIR}/${FIRST}.zip"
6 | /usr/bin/zip -jqo "${TZIP}" $*
7 | ##/bin/cp -a $TZIP ~/Desktop
8 | echo "${TZIP}"
--------------------------------------------------------------------------------
/Switch Audio.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.SwitchAudio
7 | CFBundleName
8 | Switch Audio
9 | LBAbbreviation
10 | sound speaker headphones microphone
11 | CFBundleVersion
12 | 2.7
13 | CFBundleIconFile
14 | font-awesome:fa-headphones
15 | LBDebugLogEnabled
16 |
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | audio.js
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBReturnsResult
28 |
29 |
30 |
31 | LBDescription
32 |
33 | LBSummary
34 | Switch Input and Output Audio devices. Requires switchaudio-osx which is mostly easily installed via homebrew: "brew install switchaudio-osx". When invoked it returns items for each output device (headphones) and each input device (microphone). The currently selected item for each is prefixed with *. Select an item and hit enter to switch to that item, the action will respond by refreshing the items and you should note the * by the item you just selected.
35 | LBRequirements
36 | https://github.com/deweller/switchaudio-osx
37 | LBAuthor
38 | Padraic Renaghan
39 | LBEmail
40 | prenagha@renaghan.com
41 | LBWebsiteURL
42 | https://renaghan.com/launchbar/switch-audio/
43 | LBTwitter
44 | @prenagha
45 | LBUpdateURL
46 | https://raw.githubusercontent.com/prenagha/launchbar/master/Switch%20Audio.lbaction/Contents/Info.plist
47 | LBDownloadURL
48 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Switch%20Audio.lbaction&fileName=Switch-Audio&rootDirectory=Switch-Audio.lbaction
49 | LBChangelog
50 |
51 | 2.7: Another fix for JSON parse error #22
52 | 2.6: Fix JSON parse error #22
53 | 2.5: Fix download URL
54 | 2.4: Adjust homebrew path for Apple Silicon
55 | 2.3: Support space in device names
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/Switch Audio.lbaction/Contents/Resources/equipment.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Switch Audio.lbaction/Contents/Resources/equipment.icns
--------------------------------------------------------------------------------
/Switch Audio.lbaction/Contents/Resources/headphones.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Switch Audio.lbaction/Contents/Resources/headphones.icns
--------------------------------------------------------------------------------
/Switch Audio.lbaction/Contents/Resources/microphone.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Switch Audio.lbaction/Contents/Resources/microphone.icns
--------------------------------------------------------------------------------
/Switch Audio.lbaction/Contents/Scripts/audio.js:
--------------------------------------------------------------------------------
1 |
2 | function run() {
3 | try {
4 | var audioJson = LaunchBar.execute('/bin/bash', 'audio.sh');
5 | LaunchBar.debugLog('Output ' + audioJson);
6 | var audio = JSON.parse(audioJson);
7 |
8 | var items = [];
9 |
10 | if (audio.outputs && audio.outputs.length > 0) {
11 | for (var i = 0; i < audio.outputs.length; i++) {
12 | var output = audio.outputs[i];
13 | items.push({
14 | 'title': (output == audio.currentOutput ? "* " : "") + output,
15 | 'typ': 'output',
16 | 'name': output,
17 | 'action': 'switchto',
18 | 'actionReturnsItems': true,
19 | 'icon':'headphones.icns'});
20 | }
21 | } else {
22 | LaunchBar.log('Audio outputs not available');
23 | }
24 |
25 | if (audio.inputs && audio.inputs.length > 0) {
26 | for (var i = 0; i < audio.inputs.length; i++) {
27 | var input = audio.inputs[i];
28 | items.push({
29 | 'title': (input == audio.currentInput ? "* " : "") + input,
30 | 'typ': 'input',
31 | 'name': input,
32 | 'action': 'switchto',
33 | 'actionReturnsItems': true,
34 | 'icon':'microphone.icns'});
35 | }
36 | } else {
37 | LaunchBar.log('Audio inputs not available');
38 | }
39 |
40 | return items;
41 | } catch (exception) {
42 | LaunchBar.log('Error ' + exception);
43 | LaunchBar.alert('Error', exception);
44 | }
45 | }
46 |
47 | function switchto(item) {
48 | try {
49 | var rtn = LaunchBar.execute('/bin/bash', 'audio.sh', item.name, item.typ);
50 | LaunchBar.debugLog('Output switch ' + rtn);
51 | return run();
52 | } catch (exception) {
53 | LaunchBar.log('Error switching ' + exception);
54 | LaunchBar.alert('Error switching', exception);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Switch Audio.lbaction/Contents/Scripts/audio.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | PATH="${PATH}:/opt/homebrew/bin:/usr/local/bin"
4 | CMD=SwitchAudioSource
5 |
6 | if [ ! -z "$1" ]
7 | then
8 | $CMD -s "$1" -t "$2"
9 | exit $?
10 | fi
11 |
12 | CINPUT=`$CMD -c -t input | tr '\n"' ' '`
13 | COUTPUT=`$CMD -c -t output | tr '\n"' ' '`
14 | INS=
15 | OUTS=
16 |
17 | IFS=
18 | while read -r INPUT
19 | do
20 | if [ ! -z "${INS}" ]
21 | then
22 | INS="${INS}, "
23 | fi
24 | INS="${INS}\"${INPUT}\""
25 | done <<< `$CMD -a -t input | sed 's/ (input)$//' | tr -d '"'`
26 |
27 | while read -r OUTPUT
28 | do
29 | if [ ! -z "${OUTS}" ]
30 | then
31 | OUTS="${OUTS}, "
32 | fi
33 | OUTS="${OUTS}\"${OUTPUT}\""
34 | done <<< "`$CMD -a -t output | sed 's/ (output)$//' | tr -d '"'`"
35 |
36 | cat << EOF
37 | {
38 | "currentInput": "${CINPUT}",
39 | "currentOutput": "${COUTPUT}",
40 | "inputs": [ ${INS} ],
41 | "outputs": [ ${OUTS} ]
42 | }
43 | EOF
44 | exit 0
45 |
--------------------------------------------------------------------------------
/Things.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Things
7 | CFBundleName
8 | Things To Do
9 | LBAbbreviation
10 | things todo task
11 | CFBundleVersion
12 | 3.2
13 | CFBundleIconFile
14 | font-awesome:fa-inbox
15 | LBAssociatedApplication
16 | com.culturedcode.things
17 | LBARequiredApplication
18 | com.culturedcode.things
19 | LBTextInputTitle
20 | New To Do
21 | LBDebugLogEnabled
22 |
23 | LBMinimumLaunchBarVersion
24 | 6121
25 | LBScripts
26 |
27 | LBDefaultScript
28 |
29 | LBScriptName
30 | things.scpt
31 | LBKeepWindowActive
32 |
33 | LBRunInBackground
34 |
35 | LBRequiresArgument
36 |
37 | LBAcceptedArgumentTypes
38 |
39 | string
40 | path
41 |
42 | LBReturnsResult
43 |
44 |
45 |
46 | LBDescription
47 |
48 | LBSummary
49 | LaunchBar action integration with Things To Do Mac application from Cultured Code
50 | LBRequirements
51 | Things Application http://culturedcode.com/things/
52 | LBAuthor
53 | Padraic Renaghan
54 | LBEmail
55 | prenagha@renaghan.com
56 | LBWebsiteURL
57 | https://renaghan.com/launchbar/things-to-do/
58 | LBTwitter
59 | @prenagha
60 | LBArgument
61 | Text of new to do to create
62 | LBUpdateURL
63 | https://raw.githubusercontent.com/prenagha/launchbar/master/Things.lbaction/Contents/Info.plist
64 | LBDownloadURL
65 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Things.lbaction
66 | LBChangelog
67 |
68 | 3.2: Fix download URL
69 | 3.0: Update for Things 3
70 | 2.2: View URL for URL in notes of todo
71 | 2.1: Support action updates
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Things.lbaction/Contents/Scripts/things.applescript:
--------------------------------------------------------------------------------
1 | --
2 | -- launchbar integration for Things
3 | --
4 | -- NOTE: This is the text AppleScript, the LaunchBar action Info.plist refers to
5 | -- a **COMPILED** .scpt version of this script. You can compile this text AppleScript
6 | -- into .scpt using command line osacompile or by exporting/save-as within Script Editor
7 | --
8 |
9 | property SAFARI : "com.apple.Safari"
10 |
11 | -- debug logging to console
12 | on dlog(myObj)
13 | set txt to quoted form of (myObj as string)
14 | log txt
15 | do shell script "logger -t 'LaunchBar.Things' " & txt
16 | end dlog
17 |
18 | -- is application running?
19 | on app_running(bundleId)
20 | tell application "System Events"
21 | return (bundle identifier of processes) contains bundleId
22 | end tell
23 | end app_running
24 |
25 | -- called by launchbar when it has string input
26 | on handle_string(todo)
27 | if todo is not "" then
28 | set newTodo to {}
29 | tell application "Things3"
30 | set newTodo to parse quicksilver input todo
31 | end tell
32 | did_it(newTodo, "Created")
33 | return new_todo_items(newTodo)
34 | end if
35 | end handle_string
36 |
37 | -- adds a URL as a new todo
38 | on addURL(todo, theURL)
39 | if todo is not "" then
40 | set newTodo to {}
41 | tell application "Things3"
42 | set newTodo to parse quicksilver input todo
43 | if theURL starts with "http" then
44 | set notes of newTodo to "[url=" & theURL & "] " & theURL & " [/url]"
45 | end if
46 | end tell
47 | did_it(newTodo, "Created from URL")
48 | return new_todo_items(newTodo)
49 | end if
50 | end addURL
51 |
52 | -- create a new todo from active safari tab is selected
53 | on safari_add(todo)
54 | addURL((subtitle of todo), (|url| of todo))
55 | end safari_add
56 |
57 | -- called by launchbar when it has an item input
58 | on handle_item(todo)
59 | addURL((title of todo), (|url| of todo))
60 | end handle_item
61 |
62 | -- called by launchbar when it has URL input
63 | on handle_URL(theURL, theDetails)
64 | addURL(theURL, theURL)
65 | end handle_URL
66 |
67 | -- called by launchbar when files are passed to the action
68 | on open (thePaths)
69 | repeat with thePath in thePaths
70 | handle_string("File " & POSIX path of thePath)
71 | end repeat
72 | end open
73 |
74 | on new_todo_items(todo)
75 | set actions to {}
76 | set action to {title:"Today", action:"moveToday", actionArgument:id of todo, icon:"font-awesome:fa-star"}
77 | copy action to end of actions
78 |
79 | set action to {title:"Next", action:"moveNext", actionArgument:id of todo, icon:"font-awesome:fa-thumb-tack"}
80 | copy action to end of actions
81 |
82 | set action to {title:"Someday", action:"moveSomeday", actionArgument:id of todo, icon:"font-awesome:fa-archive"}
83 | copy action to end of actions
84 |
85 | set action to {title:"View", action:"viewTodo", actionArgument:id of todo, icon:"font-awesome:fa-list-ul"}
86 | copy action to end of actions
87 | return actions
88 | end new_todo_items
89 |
90 | -- load all things items
91 | on load_all()
92 | tell application "Things3"
93 | set listsOut to {}
94 | repeat with lst in lists
95 | set listId to id of lst
96 | if listId is in {"TMInboxListSource", "TMTodayListSource", "TMNextListSource", "TMSomedayListSource", "TMCalendarListSource"} then
97 | set todosOut to {}
98 |
99 | set todos to to dos in lst
100 | repeat with todo in todos
101 | try
102 | set sts to status of todo
103 | if sts is open then
104 | set actions to {}
105 |
106 | if listId is not "TMCalendarSource" then
107 | set action to {title:"Complete " & name of todo, subtitle:notes of todo, label:tag names of todo, action:"completeTodo", actionArgument:id of todo, icon:"font-awesome:fa-check-square"}
108 | copy action to end of actions
109 | end if
110 |
111 | set u to stringBetween(notes of todo, "[url=", "]") of me
112 | if u is not "" then
113 | set action to {title:"View " & u, |url|:u}
114 | copy action to end of actions
115 | end if
116 |
117 | if listId is not "TMTodayListSource" and listId is not "TMCalendarListSource" then
118 | set action to {title:"Today", action:"moveToday", actionArgument:id of todo, icon:"font-awesome:fa-star"}
119 | copy action to end of actions
120 | end if
121 |
122 | if listId is not "TMNextListSource" and listId is not "TMCalendarListSource" then
123 | set title to "Next"
124 | if listId is "TMTodayListSource" then
125 | set title to "Not Today"
126 | end if
127 | set action to {title:title, action:"moveNext", actionArgument:id of todo, icon:"font-awesome:fa-thumb-tack"}
128 | copy action to end of actions
129 | end if
130 |
131 | if listId is not "TMSomedayListSource" and listId is not "TMCalendarListSource" then
132 | set action to {title:"Someday", action:"moveSomeday", actionArgument:id of todo, icon:"font-awesome:fa-archive"}
133 | copy action to end of actions
134 | end if
135 |
136 | set action to {title:"View", action:"viewTodo", actionArgument:id of todo, icon:"font-awesome:fa-list-ul"}
137 | copy action to end of actions
138 |
139 | if listId is not "TMCalendarListSource" then
140 | set action to {title:"Cancel", action:"cancelTodo", actionArgument:id of todo, icon:"font-awesome:fa-cancel"}
141 | copy action to end of actions
142 | end if
143 |
144 | set action to {title:"Trash", action:"trashTodo", actionArgument:id of todo, icon:"font-awesome:fa-trash"}
145 | copy action to end of actions
146 |
147 | set icon to "font-awesome:fa-square-o"
148 | if listId is "TMCalendarListSource" then
149 | set icon to "font-awesome:fa-calendar"
150 | end if
151 | set subt to notes of todo
152 | if due date of todo is not missing value then
153 | set icon to "font-awesome:fa-calendar"
154 | set subt to (due date of todo as string) & " " & (notes of todo)
155 | end if
156 | set todoOut to {title:name of todo, icon:icon, subtitle:subt, label:tag names of todo, children:actions}
157 | copy todoOut to end of todosOut
158 | end if
159 | end try
160 | end repeat
161 |
162 | if (count of todosOut) is greater than 0 then
163 | if listId is "TMTodayListSource" then
164 | set icon to "font-awesome:fa-star"
165 | else if listId is "TMNextListSource" then
166 | set icon to "font-awesome:fa-thumb-tack"
167 | else if listId is "TMSomedayListSource" then
168 | set icon to "font-awesome:fa-archive"
169 | else if listId is "TMCalendarListSource" then
170 | set icon to "font-awesome:fa-calendar"
171 | else
172 | set icon to "font-awesome:fa-inbox"
173 | end if
174 | set listOut to {title:name of lst, icon:icon, badge:(count of todosOut) as string, children:todosOut}
175 | copy listOut to end of listsOut
176 | end if
177 | end if
178 | end repeat
179 | end tell
180 |
181 | set theURL to ""
182 | if app_running(SAFARI) then
183 | tell application "Safari"
184 | set theName to name of current tab of window 1
185 | set theURL to URL of current tab of window 1
186 | set x to {title:"Add To Do from Safari", icon:SAFARI, subtitle:theName, |url|:theURL, action:"safari_add"}
187 | copy x to end of listsOut
188 | end tell
189 | end if
190 |
191 | set clip to get_clipboard()
192 | if clip is not "" and clip is not theURL then
193 | set x to {title:"Add To Do from Clipboard", subtitle:clip, icon:"ClipObject.icns", action:"handle_string", actionArgument:clip}
194 | copy x to end of listsOut
195 | end if
196 |
197 | return listsOut
198 | end load_all
199 |
200 | -- get the contents of the clipboard (if any) as plain text
201 | on get_clipboard()
202 | if (the clipboard) is not {} then
203 | return the clipboard as text
204 | end if
205 | return ""
206 | end get_clipboard
207 |
208 | -- get a list by id
209 | on get_list(listId)
210 | tell application "Things3"
211 | repeat with lst in lists
212 | if id of lst is listId then
213 | return lst
214 | end if
215 | end repeat
216 | end tell
217 | tell application "LaunchBar" to display in large type "List not found " & listId
218 | end get_list
219 |
220 | -- get a todo by id
221 | on get_todo(todoId)
222 | tell application "Things3"
223 | repeat with lst in lists
224 | set todos to to dos in lst
225 | repeat with todo in todos
226 | if id of todo is todoId then
227 | return todo
228 | end if
229 | end repeat
230 | end repeat
231 | end tell
232 | tell application "LaunchBar" to display in large type "Todo not found " & todoId
233 | end get_todo
234 |
235 | -- handle complete todo action
236 | on completeTodo(todoId)
237 | set todo to get_todo(todoId)
238 | tell application "Things3" to set status of todo to completed
239 | did_it(todo, "Completed")
240 | end completeTodo
241 |
242 | -- handle cancel todo action
243 | on cancelTodo(todoId)
244 | set todo to get_todo(todoId)
245 | tell application "Things3" to set status of todo to canceled
246 | did_it(todo, "Canceled")
247 | end cancelTodo
248 |
249 | -- handle move to today action
250 | on moveToday(todoId)
251 | set todo to get_todo(todoId)
252 | set lst to get_list("TMTodayListSource")
253 | tell application "Things3" to move todo to lst
254 | did_it(todo, "Moved to Today")
255 | end moveToday
256 |
257 | -- handle move to next action
258 | on moveNext(todoId)
259 | set todo to get_todo(todoId)
260 | set lst to get_list("TMNextListSource")
261 | tell application "Things3" to move todo to lst
262 | did_it(todo, "Moved to Next")
263 | end moveNext
264 |
265 | -- handle move to someday action
266 | on moveSomeday(todoId)
267 | set todo to get_todo(todoId)
268 | set lst to get_list("TMSomedayListSource")
269 | tell application "Things3" to move todo to lst
270 | did_it(todo, "Moved to Someday")
271 | end moveSomeday
272 |
273 | -- OSX notification to tell the user what we just did
274 | on did_it(todo, msg)
275 | log "Todo " & msg & " -- " & (name of todo) & " -- " & (id of todo)
276 | tell application "LaunchBar"
277 | display in notification center with title "To Do " & msg subtitle (name of todo)
278 | hide
279 | end tell
280 | end did_it
281 |
282 | -- view todo in Things application
283 | on viewTodo(todoId)
284 | set todo to get_todo(todoId)
285 | tell application "LaunchBar" to hide
286 | tell application "Things3"
287 | activate
288 | show todo
289 | end tell
290 | end viewTodo
291 |
292 | -- handle trash todo action
293 | on trashTodo(todoId)
294 | set todo to get_todo(todoId)
295 | tell application "Things3" to delete todo
296 | did_it(todo, "Deleted")
297 | end trashTodo
298 |
299 | -- http://applescript.bratis-lover.net/library/string/#explode
300 | on explode(delimiter, input)
301 | local delimiter, input, ASTID
302 | set ASTID to AppleScript's text item delimiters
303 | try
304 | set AppleScript's text item delimiters to delimiter
305 | set input to text items of input
306 | set AppleScript's text item delimiters to ASTID
307 | return input --> list
308 | on error eMsg number eNum
309 | set AppleScript's text item delimiters to ASTID
310 | error "Error in explode: " & eMsg number eNum
311 | end try
312 | end explode
313 |
314 | -- http://applescript.bratis-lover.net/library/string/#explode
315 | on stringBetween(str, head, tail)
316 | local str, head, tail
317 | try
318 | if str is {} then return ""
319 | if str is "" then return ""
320 | if str does not contain head then return ""
321 | if str does not contain tail then return ""
322 | set str to item 2 of my explode(head, str)
323 | set str to item 1 of my explode(tail, str)
324 | return str
325 | on error eMsg number eNum
326 | error "Error in stringBetween: " & eMsg number eNum
327 | end try
328 | end stringBetween
329 |
330 |
331 | -- called by launchbar when enter or browse into from top item
332 | on run
333 | load_all()
334 | end run
335 |
--------------------------------------------------------------------------------
/Timer.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Timer
7 | CFBundleName
8 | Set a Timer
9 | CFBundleVersion
10 | 3.3
11 | CFBundleIconFile
12 | font-awesome:fa-clock-o
13 | LBDebugLogEnabled
14 |
15 | LBTextInputTitle
16 | Reminder [when]
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | timer.js
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBAcceptedArgumentTypes
28 |
29 | string
30 |
31 | LBReturnsResult
32 |
33 |
34 |
35 | LBDescription
36 |
37 | LBSummary
38 | Create a timer to remind you of something later. Like "Turn off oven 15m". LaunchBar will create the reminder then after the delay time it will show you the reminder text as a large message with an accompanying sound.
39 | LBAuthor
40 | Padraic Renaghan
41 | LBEmail
42 | prenagha@renaghan.com
43 | LBWebsiteURL
44 | https://renaghan.com/launchbar/set-a-timer/
45 | LBTwitter
46 | @prenagha
47 | LBArgument
48 | Reminder text in the form of "[reminder message] [delay]". Delay a string like 1m, 2m30s, 4h...
49 | LBUpdateURL
50 | https://raw.githubusercontent.com/prenagha/launchbar/master/Timer.lbaction/Contents/Info.plist
51 | LBDownloadURL
52 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Timer.lbaction
53 | LBChangelog
54 |
55 | 3.3: Fix download URL
56 | Support action updates
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Timer.lbaction/Contents/Scripts/timer.js:
--------------------------------------------------------------------------------
1 |
2 | function runWithString(string) {
3 | return go(string);
4 | }
5 |
6 | function runWithItem(item) {
7 | return go(item.title);
8 | }
9 |
10 | function go(str) {
11 | if (!str || str == undefined || str.length == 0) {
12 | return {'title':'Reminder is empty','icon':'NotFound.icns'};
13 | }
14 | try {
15 |
16 | var parts = str.split(' ');
17 | var delay = parts.pop();
18 | var remind = parts.join(' ');
19 | LaunchBar.displayInLargeType({
20 | 'title' : 'Reminder',
21 | 'string' : remind,
22 | 'sound' : 'Tink',
23 | 'delay': delay
24 | });
25 |
26 | return {'title': 'Reminder in ' + delay
27 | ,'subtitle': remind
28 | ,'icon':'clock.pdf'};
29 |
30 | } catch (exception) {
31 | return {'title': 'Timer Error ' + exception};
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Today.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Today
7 | CFBundleName
8 | Today
9 | LBAbbreviation
10 | calendar ical event appointment meeting
11 | CFBundleVersion
12 | 1.5
13 | CFBundleIconFile
14 | font-awesome:fa-calendar
15 | LBDebugLogEnabled
16 |
17 | LBScripts
18 |
19 | LBDefaultScript
20 |
21 | LBScriptName
22 | today.js
23 | LBRunInBackground
24 |
25 | LBRequiresArgument
26 |
27 | LBReturnsResult
28 |
29 |
30 |
31 | LBDescription
32 |
33 | LBSummary
34 | Today view (and optionally beyond) of calendar events. Requires icalBuddy be installed on your system. http://hasseg.org/icalBuddy/. See man page of icalBuddy and customize this actions parameters to it via this action's preferences file.
35 | LBRequiredApplication
36 | com.apple.iCal
37 | LBRequirements
38 | http://hasseg.org/icalBuddy/
39 | LBAuthor
40 | Padraic Renaghan
41 | LBEmail
42 | prenagha@renaghan.com
43 | LBWebsiteURL
44 | https://renaghan.com/launchbar/today/
45 | LBTwitter
46 | @prenagha
47 | LBUpdateURL
48 | https://raw.githubusercontent.com/prenagha/launchbar/master/Today.lbaction/Contents/Info.plist
49 | LBDownloadURL
50 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Today.lbaction
51 | LBChangelog
52 |
53 | 1.5: Fix download URL
54 | 1.3: Improved timing of click to call attempts
55 | 1.2: Ability to click the "Call" confirmation from FaceTime when dialing via phone
56 | 1.1: Ability to set time format as preference
57 | 1.0: Initial release
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Today.lbaction/Contents/Resources/icalb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Today.lbaction/Contents/Resources/icalb.png
--------------------------------------------------------------------------------
/Today.lbaction/Contents/Scripts/call.applescript:
--------------------------------------------------------------------------------
1 |
2 | on dlog(myObj)
3 | set txt to quoted form of (myObj as string)
4 | log txt
5 | do shell script "/usr/bin/logger -t 'LaunchBar.Today' " & txt
6 | end dlog
7 |
8 | -- code snippet from UI Browser http://pfiddlesoft.com/uibrowser/index.html
9 | tell application "System Events" to set GUIScriptingEnabled to UI elements enabled
10 | if not GUIScriptingEnabled then
11 | activate
12 | set scriptRunner to name of current application
13 | my dlog("GUI scripting not enabled")
14 | display alert "GUI Scripting is not enabled for " & scriptRunner & "." message "Open System Preferences, unlock the Security & Privacy preference, select " & scriptRunner & " in the Privacy Pane's Accessibility list, and then run this script again." buttons {"Open System Preferences", "Cancel"} default button "Cancel"
15 | if button returned of result is "Open System Preferences" then
16 | tell application "System Preferences"
17 | tell pane id "com.apple.preference.security" to reveal anchor "Privacy_Accessibility"
18 | activate
19 | end tell
20 | end if
21 | return
22 | end if
23 | my dlog("GUI scripting is enabled")
24 |
25 | -- click the Call button on the facetime call confirm window
26 | repeat 5 times
27 | delay 2
28 | my dlog("Looking for FaceTime call confirm window")
29 | tell application "System Events"
30 | if process "FaceTime" exists then
31 | tell process "FaceTime"
32 | if window 1 exists then
33 | if button "Call" of window 1 exists then
34 | my dlog("Clicking on FaceTime Call button")
35 | click button "Call" of window 1
36 | return
37 | else
38 | my dlog("FaceTime window button does not exist yet")
39 | end if
40 | else
41 | my dlog("FaceTime window does not exist yet")
42 | end if
43 | end tell
44 | else
45 | my dlog("FaceTime process does not exist yet")
46 | end if
47 | end tell
48 | end repeat
49 | my dlog("Unable to click FaceTime Call button")
50 |
--------------------------------------------------------------------------------
/Today.lbaction/Contents/Scripts/today.js:
--------------------------------------------------------------------------------
1 | include('moment-with-locales.js');
2 |
3 | var ALERT_ICON = '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns';
4 | var PREFS = Action.preferences;
5 | var PREFS_RESET = false;
6 | var PLUGIN = false;
7 | //grab first set of numbers, if 10 or 11 long then we have a phone
8 | var PHONE_NBR = /1?\W*([2-9][0-8]\d)\W*([2-9]\d{2})\W*(\d{4})/;
9 | //grab next set of numbers, if any, assume that is PIN
10 | var PHONE_CODE = /\d[\d -]+/;
11 | //remove anything not a number from PIN
12 | var NOT_NBR = /\D/g;
13 |
14 |
15 | function setupPreferences() {
16 | if (!Action.preferences.clickCall)
17 | Action.preferences.clickCall = "true";
18 |
19 | if (PREFS_RESET || !Action.preferences.icalBuddyPath) {
20 | Action.preferences.icalBuddyPath = "/usr/local/bin/icalBuddy";
21 | Action.preferences.icalBuddyCommand = "eventsToday+1";
22 | Action.preferences.icalBuddyPropertySeparator = "^";
23 | var opts = [
24 | "--noCalendarNames"
25 | ,"--noRelativeDates"
26 | ,"--includeOnlyEventsFromNowOn"
27 | ,"--maxNumNoteChars" , "100"
28 | ,"--dateFormat", "%Y-%m-%d"
29 | ,"--timeFormat", "%H:%M:%S %z"
30 | ,"--propertyOrder", "title,datetime,location,url,notes"
31 | ,"--propertySeparators", "a" + Action.preferences.icalBuddyPropertySeparator + "a"
32 | ,"--bullet", ""
33 | //,"--includeCals", "Personal,Work"
34 | ,"--includeEventProps", "title,datetime,location,url,notes"
35 | ,"--notesNewlineReplacement" , " "
36 | ];
37 | Action.preferences.icalBuddyOptions = opts;
38 | Action.preferences.pluginFile = "";
39 | }
40 | if (!Action.preferences.momentJsFormatToday)
41 | Action.preferences.momentJsFormatToday = "h:mm a";
42 | if (!Action.preferences.momentJsFormat)
43 | Action.preferences.momentJsFormat = "ddd h:mm a";
44 | if (!Action.preferences.momentJsFormatAllDay)
45 | Action.preferences.momentJsFormatAllDay = "ddd";
46 |
47 | if (Action.preferences.pluginFile && Action.preferences.pluginFile.length > 5) {
48 | LaunchBar.log("Loading plugin from " + Action.preferences.pluginFile);
49 | include(Action.preferences.pluginFile);
50 | PLUGIN = true;
51 | }
52 | }
53 |
54 | function updatePreferences() {
55 | return[
56 | {title: 'Edit Preferences', icon: "Pref_Advanced.icns", action: "editPreferences"}
57 | ,{title: 'iCalBuddy Website', url: 'http://hasseg.org/icalBuddy/', icon:'icalb.png'}
58 | ,{title: 'iCalBuddy Calendar List', action: 'calendarList', icon:'icalb.png'}
59 | ,{title: 'MomentJs Date Time Formats', url: 'http://momentjs.com/docs/#/displaying/format/'}
60 | ];
61 | }
62 |
63 | function editPreferences() {
64 | LaunchBar.openURL('file://' + encodeURI(Action.supportPath + '/Preferences.plist'));
65 | }
66 |
67 | function calendarList() {
68 | try {
69 | var output = LaunchBar.execute(Action.preferences.icalBuddyPath, 'calendars');
70 | return output;
71 | } catch (exception) {
72 | LaunchBar.log('Error ' + exception);
73 | LaunchBar.alert('Error', exception);
74 | }
75 | }
76 |
77 | function checkBuddy() {
78 | var err = [];
79 | if (!File.exists(PREFS.icalBuddyPath)) {
80 | err.push({title:'icalBuddy not found at ' + Action.preferences.icalBuddyPath
81 | ,icon: ALERT_ICON, url: 'http://hasseg.org/icalBuddy/'});
82 | } else if (!File.isExecutable(PREFS.icalBuddyPath)) {
83 | err.push({title:'icalBuddy not executable', path: Action.preferences.icalBuddyPath, icon: ALERT_ICON});
84 | }
85 | return err.concat(updatePreferences());
86 | }
87 |
88 | function run() {
89 | try {
90 | setupPreferences();
91 | var err = checkBuddy();
92 | if (err.length > 4 || LaunchBar.options.commandKey)
93 | return err;
94 |
95 | moment.locale(LaunchBar.currentLocale);
96 | var now = moment();
97 |
98 | var items = [];
99 | var args = [];
100 | args.push(Action.preferences.icalBuddyPath);
101 | args = args.concat(Action.preferences.icalBuddyOptions);
102 | args.push(Action.preferences.icalBuddyCommand);
103 | LaunchBar.debugLog('Execute ' + args);
104 | var output = LaunchBar.execute.apply(LaunchBar, args);
105 | LaunchBar.debugLog('Output ' + output);
106 | var lines = output.split("\n");
107 | var event = {name:"",location:"",url:"",notes:"",start:"",end:"",phone:"",allDay:false};
108 | for (var i = 0; i < lines.length; i++) {
109 | var fields = lines[i].split(Action.preferences.icalBuddyPropertySeparator);
110 | // extra lines are continuations of the location field
111 | if (fields.length == 1) {
112 | var l = lines[i].trim();
113 | if (l.length > 1)
114 | event.location += ", " + l;
115 | continue;
116 | }
117 |
118 | if (event.name.length > 0) {
119 | items.push({
120 | title: t
121 | ,subtitle: event.phone.length > 0 ? event.phone : event.location
122 | ,icon: icon(now, event)
123 | ,children: eventChildren(event)
124 | });
125 | }
126 |
127 | event = {name:"",location:"",url:"",notes:"",start:"",end:"",phone:"",allDay:false};
128 | for (var f=0; f 0) {
177 | items.push({
178 | title: t
179 | ,subtitle: event.phone.length > 0 ? event.phone : event.location
180 | ,icon: icon(now, event)
181 | ,children: eventChildren(event)
182 | });
183 | }
184 |
185 | if (LaunchBar.options.alternateKey)
186 | return items.concat(updatePreferences());
187 | return items;
188 |
189 | } catch (exception) {
190 | LaunchBar.log('Error ' + exception);
191 | LaunchBar.alert('Error', exception);
192 | }
193 | }
194 |
195 | function icon(now, event) {
196 | if (now.isSame(event.start,'day')) {
197 | return (event.phone.length > 0 ? 'MobilePhone' : 'CalendarRule')
198 | } else {
199 | return "Calendar";
200 | }
201 | }
202 |
203 | //
204 | // parse and filter the event data as you wish
205 | // input is the event object with properties (name, start, end, location, url, notes)
206 | // all properties are not null, default is ""
207 | // method should return same
208 | // tell caller that event is filtered out by returning null
209 | // override with your own parse method by creating plugin.js in Action.supportDir
210 | // with method pluginParse
211 | //
212 | function parse(event) {
213 |
214 | // look for a phone in the location field first
215 | event.phone = parsePhone(event.location);
216 |
217 | // if not look in the name
218 | if (event.phone.length == 0)
219 | event.phone = parsePhone(event.name);
220 |
221 | return event;
222 | }
223 |
224 | function parsePhone(str) {
225 | if (!str || str.length < 10)
226 | return "";
227 |
228 | var match = PHONE_NBR.exec(str);
229 | if (match == null)
230 | return "";
231 | var phone = "(" + match[1] + ") " + match[2] + "-" + match[3];
232 | var remain = str.substring(str.indexOf(match[0]) + match[0].length);
233 | var pmatch = PHONE_CODE.exec(remain);
234 | if (pmatch == null)
235 | return phone;
236 | return phone + " ,, " + pmatch[0].replace(NOT_NBR, "") + " #";
237 | }
238 |
239 | // remove a section of a string
240 | function strDelete(str, start, end) {
241 | }
242 |
243 | function eventChildren(event) {
244 | var items = [];
245 |
246 | if (event.phone.length > 0) {
247 | items.push({title: 'Dial ' + event.phone, subtitle: event.location,
248 | actionArgument: event.phone, action: 'dial', icon: 'MobilePhone'});
249 | items.push({title: 'Big Display',
250 | subtitle: (event.location.length == 0 ? event.phone : event.location),
251 | actionArgument: (event.location.length == 0 ? event.phone : event.location)
252 | , action: 'big', icon: 'MobilePhone'});
253 | }
254 | if (event.phone.length == 0 && event.location.length > 0) {
255 | items.push({title: 'Open Map', subtitle: event.location,
256 | actionArgument: event.location, action: 'maps', icon: 'com.apple.Maps'});
257 | }
258 | if (event.url.length > 0) {
259 | items.push({title: 'Open URL', subtitle: event.url, url: event.url});
260 | }
261 | if (event.notes.length > 0) {
262 | items.push({title: event.notes, subtitle: 'Notes'});
263 | }
264 | return items;
265 | }
266 |
267 | function maps(str) {
268 | LaunchBar.openURL("http://maps.apple.com/?q=" + encodeURIComponent(str), "com.apple.Maps");
269 | }
270 |
271 | function dial(str) {
272 | LaunchBar.performAction("Call With iPhone", str);
273 | big(str);
274 | if (Action.preferences.clickCall === "true")
275 | LaunchBar.executeAppleScriptFile("call.scpt");
276 | }
277 |
278 | function big(str) {
279 | LaunchBar.displayInLargeType({string: str});
280 | }
281 |
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleIdentifier
6 | com.renaghan.launchbar.Updates
7 | CFBundleName
8 | Action Updates
9 | CFBundleVersion
10 | 2.11
11 | CFBundleIconFile
12 | font-awesome:fa-spinner
13 | LBDebugLogEnabled
14 |
15 | LBScripts
16 |
17 | LBDefaultScript
18 |
19 | LBScriptName
20 | updates.js
21 | LBRunInBackground
22 |
23 | LBReturnsResult
24 |
25 |
26 |
27 | LBDescription
28 |
29 | LBSummary
30 | Checks custom actions to check to see if new version is available. Actions should specify a /LBDescription/LBUpdate key in the Info.plist of their action pointing at the URL which returns the Info.plist of the latest version of the action. Additionally they may specify a /LBDescription/LBDownload key specifying the URL to download the latest .lbaction package of the action.
31 | LBAuthor
32 | Padraic Renaghan
33 | LBEmail
34 | prenagha@renaghan.com
35 | LBWebsiteURL
36 | https://renaghan.com/launchbar/action-updates/
37 | LBTwitter
38 | @prenagha
39 | LBUpdateURL
40 | https://raw.githubusercontent.com/prenagha/launchbar/master/Updates.lbaction/Contents/Info.plist
41 | LBDownloadURL
42 | https://minhaskamal.github.io/DownGit/#/home?url=https://github.com/prenagha/launchbar/tree/main/Updates.lbaction
43 | LBChangelog
44 |
45 | 2.11: Fix download URL
46 | 2.10: Fix for new icons @Ptujec
47 | 2.9: New icons @Ptujec
48 | 2.8: Fix LaunchBar version check
49 | 2.6: Additional error handling
50 | 2.5: Added ability to select an action and set it to skip in the preferences file
51 | 2.4: Proper version compare assuming major[.minor][.patch] format
52 | 2.3: Fix if URLs are already encoded
53 | 2.2: replace startsWith and endsWith for better javascript support
54 | 2.1: Support new and old keys
55 | 2.0: Changed LBUpdateURL, LBWebsiteURL, LBDownloadURL keys per Objective Development standard key names
56 | 1.7: Moved skipped items to own group.
57 | 1.6: Standardized error handling.
58 | 1.5: Support for LBChangelog key.
59 | 1.5: Download via explicitly selecting LB items.
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/actionTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/actionTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/alertTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/alertTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/cautionTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/cautionTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/checkTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/checkTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/downloadTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/downloadTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/logTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/logTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/prefTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/prefTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/skipTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/skipTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Resources/urlTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prenagha/launchbar/7a717f16d841e5309b2f637ac8a4013c3e260d4d/Updates.lbaction/Contents/Resources/urlTemplate.png
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Scripts/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Action Updates Action
4 |
5 | Custom actions are great, but difficult to keep up to date. Rather than all of us individually build updating mechanisms, and clutter our actions with update related UI and pinging.
6 |
7 | [Action Updates User Information and Download](https://renaghan.com/launchbar/action-updates/)
8 |
9 | This action checks each custom action a user has installed and figures out if a newer version exists. It reports on results and provides options for user
10 | * visit `/LBDescription/LBWebsiteURL`
11 | * see `/LBDescription/LBChangelog`
12 | * download `/LBDescription/LBDownloadURL` (trigger browser open URL)
13 |
14 | The trick is for all custom actions to include the `/LBDescription/LBUpdateURL` key in their `Info.plist` as a string URL reference to a remote server of the `Info.plist` of most recent version
15 |
16 | Also make sure the `CFBundleVersion` key is specified and uses standard version numbering convention `major[.minor][.patch]` where all 3 are integers.
17 |
18 | *Optional* keys to enable changelog and download link features:
19 | * `/LBDescription/LBChangelog` - string changelog text to display to user
20 | * `/LBDescription/LBDownloadURL` - string URL reference to remote server where most recent action package can be downloaded
21 |
22 | ## Preferences
23 | You may override/specify the Update URL for any action you have installed by setting a preference in this action's local preferences file
24 | `~/Library/Application Support/LaunchBar/Action Support/com.renaghan.launchbar.Updates/Preferences.plist`
25 |
26 | You may also tell this action that a custom action should be skipped by setting the LBUpdate URL to `SKIP` in this action's local preferences file.
27 |
28 | The action will search for custom actions in `~/Application Support/LaunchBar/Actions`. You may override this by setting a local `ActionsDir` preference.
29 |
30 | ## Preferences Example
31 | ```
32 |
33 |
34 |
35 |
36 | ActionsDir
37 | /Users/jsmith/Library/Application Support/LaunchBar/Actions
38 | LBUpdateURL
39 |
40 | com.example.action1
41 | https://example.com/action1.lbaction
42 | com.example.action2
43 | SKIP
44 |
45 |
46 |
47 | ```
48 |
49 | ## Action Info.plist Example
50 | ```
51 |
52 |
53 |
54 |
55 | ...
56 | LBDescription
57 |
58 | ...
59 | LBUpdateURL
60 | https://raw.githubusercontent.com/jsmith/launchbar/master/Checker.lbaction/Contents/Info.plist
61 | LBDownloadURL
62 | https://download.com/lbdist/Checker.lbaction
63 | LBChangelog
64 |
65 | 1.2: Fixed bug when user option clicked on 2nd item.
66 |
67 |
68 |
69 |
70 | ```
71 |
--------------------------------------------------------------------------------
/Updates.lbaction/Contents/Scripts/updates.js:
--------------------------------------------------------------------------------
1 |
2 | var TIMEOUT = {timeout: 10.0};
3 | var ACTION_INFO = 'https://raw.githubusercontent.com/prenagha/launchbar/main/Updates.lbaction/Contents/Info.plist';
4 | var LB_INFO = 'http://sw-update.obdev.at/update-feeds/launchbar-6.plist';
5 | var LB_DOWNLOAD = 'http://www.obdev.at/products/launchbar/download.html';
6 | var ALERT_ICON = 'alertTemplate';
7 | var CAUTION = 'cautionTemplate';
8 | var CHECK = "checkTemplate";
9 | var SKIP = "skipTemplate";
10 |
11 | function setup() {
12 | if (Action.preferences.ActionsDir)
13 | return;
14 | // load up an initial preferences object with defaults if first time run
15 | Action.preferences.ActionsDir = LaunchBar.homeDirectory + "/Library/Application Support/LaunchBar/Actions";
16 | var urls = {"com.example.action1": "https://example.com/action1.lbaction"
17 | , "com.example.action2": "SKIP" };
18 | Action.preferences.LBUpdateURL = urls;
19 | }
20 |
21 | function run(arg) {
22 | setup();
23 | var actionsDir = Action.preferences.ActionsDir;
24 |
25 | var items = [];
26 | var good = [];
27 | var bad = [];
28 | var skip = [];
29 | var error = [];
30 |
31 | skip.push({'title': 'Edit Preferences', icon: "prefTemplate", action: "editPref"});
32 |
33 | loadResult(items, good, bad, skip, error, checkLaunchBar());
34 |
35 | if (File.exists(actionsDir)
36 | && File.isDirectory(actionsDir)
37 | && File.isReadable(actionsDir)) {
38 | LaunchBar.debugLog('Actions dir ' + actionsDir);
39 | var actions = File.getDirectoryContents(actionsDir);
40 | actions.forEach(function(actionPackage) {
41 | loadResult(items, good, bad, skip, error, checkAction(actionsDir, actionPackage));
42 | });
43 | } else {
44 | error.push({'title': 'Actions dir not accessible'
45 | ,'subtitle':actionsDir
46 | ,'alwaysShowsSubtitle': true
47 | ,'icon':ALERT_ICON});
48 | }
49 |
50 | if (error.length > 0)
51 | items.push({'title': 'Error', badge: ""+error.length, icon:ALERT_ICON, children: error});
52 |
53 | items.push({'title': 'Newer versions available', badge: ""+bad.length, icon:CAUTION, children: bad});
54 | items.push({'title': 'Up to date', badge: ""+good.length, icon:CHECK, children: good});
55 |
56 | if (skip.length > 1)
57 | items.push({'title': 'Skipped', badge: ""+(skip.length-1), icon:SKIP, children: skip});
58 |
59 | return items;
60 | }
61 |
62 | function editPref() {
63 | LaunchBar.openURL('file://' + encodeURI(Action.supportPath + '/Preferences.plist'));
64 | }
65 |
66 | function loadResult(items, good, bad, skip, error, item) {
67 | if (!item || !item.title)
68 | return;
69 | if (item.icon && item.icon == CHECK) {
70 | good.push(item);
71 | return;
72 | }
73 | if (item.icon && item.icon == CAUTION) {
74 | bad.push(item);
75 | return;
76 | }
77 | if (item.icon && item.icon == ALERT_ICON) {
78 | error.push(item);
79 | return;
80 | }
81 | if (item.icon && item.icon == SKIP) {
82 | skip.push(item);
83 | return;
84 | }
85 | items.push(item);
86 | }
87 |
88 | function checkAction(actionsDir, actionPackage) {
89 | LaunchBar.debugLog("Checking action " + actionPackage);
90 | var actionFile = actionsDir + "/" + actionPackage;
91 | if (!actionPackage
92 | || typeof(actionPackage) != "string"
93 | || actionPackage.indexOf(".lbaction") < 0)
94 | return;
95 | var plistFile = actionsDir + "/" + actionPackage + "/Contents/Info.plist";
96 | if (!File.exists(plistFile)) {
97 | return {'title': actionPackage + ': Error local Info.plist does not exist ' + plistFile
98 | ,children: getActionChildren(actionFile, null, null)
99 | ,'icon':ALERT_ICON};
100 | }
101 | if (!File.isReadable(plistFile)) {
102 | return {'title': actionPackage + ': Error local Info.plist not readable ' + plistFile
103 | ,children: getActionChildren(actionFile, null, null)
104 | ,'icon':ALERT_ICON};
105 | }
106 |
107 | var plist;
108 | try {
109 | plist = File.readPlist(plistFile);
110 | } catch (exception) {
111 | LaunchBar.log('Error ' + actionPackage + ' reading plist -- ' + exception);
112 | return {'title': actionPackage + ': Error reading plist ' + plistFile
113 | ,children: getActionChildren(actionFile, null, null)
114 | ,'icon':ALERT_ICON};
115 | }
116 |
117 | var updateURL = getUpdateURL(actionPackage, plist);
118 | if (updateURL == "SKIP") {
119 | return {'title': plist.CFBundleName + ': skipped'
120 | ,'icon':SKIP
121 | ,subtitle: 'Skipped via user preferences'
122 | ,children: getActionChildren(actionFile, plist, null)};
123 | }
124 | if (!updateURL || updateURL.indexOf('http') != 0) {
125 | return {'title': plist.CFBundleName + ': updates not supported'
126 | ,subtitle: 'Missing LBUpdateURL key'
127 | ,'icon':SKIP
128 | ,children: getActionChildren(actionFile, plist, null)};
129 | }
130 |
131 | if (updateURL.indexOf(" ") > 0)
132 | updateURL = encodeURI(updateURL);
133 | LaunchBar.debugLog(actionPackage + ' URL ' + updateURL);
134 |
135 | var result = {};
136 | try {
137 | result = HTTP.getPlist(updateURL, TIMEOUT);
138 | } catch (exception) {
139 | LaunchBar.log('Error ' + actionPackage + ' -- ' + exception);
140 | return {'title':plist.CFBundleName + ': HTTP Error remote plist ' + exception + ' -- ' + updateURL
141 | ,'icon':ALERT_ICON
142 | ,children: getActionChildren(actionFile, plist, null)};
143 | }
144 |
145 | if (!result) {
146 | return {'title': plist.CFBundleName + ': Error remote plist empty result -- ' + updateURL
147 | ,'icon':ALERT_ICON
148 | ,children: getActionChildren(actionFile, plist, null)};
149 | }
150 | if (result.error) {
151 | return {'title': plist.CFBundleName + ': Error result remote plist ' + result.error
152 | + (result.response && result.response.status ? " -- " + result.response.status : "")
153 | + (result.response && result.response.localizedStatus ? " -- " + result.response.localizedStatus : "")
154 | ,'icon':ALERT_ICON
155 | ,children: getActionChildren(actionFile, plist, null)};
156 | }
157 | if (!result.data || result.data.length < 1) {
158 | return {'title': plist.CFBundleName + ': Error remote plist empty data ' + updateURL
159 | ,'icon':ALERT_ICON
160 | ,children: getActionChildren(actionFile, plist, null)};
161 | }
162 |
163 | if (upToDate(plist.CFBundleVersion, result.data.CFBundleVersion)) {
164 | return {'title': plist.CFBundleName + ': up to date'
165 | ,'badge' : plist.CFBundleVersion
166 | ,'icon': CHECK
167 | ,children: getActionChildren(actionFile, plist, result.data)};
168 | } else {
169 | return {'title': plist.CFBundleName + ': Newer version available'
170 | + ' ' + plist.CFBundleVersion + ' ➔ ' + result.data.CFBundleVersion
171 | ,'icon':CAUTION
172 | ,children: getActionChildren(actionFile, plist, result.data)};
173 | }
174 |
175 | return [];
176 | }
177 |
178 | function getActionChildren(actionFile, currPlist, plist) {
179 | var items = [];
180 | var w = getWebsite(currPlist);
181 | if (w) {
182 | items.push({'title': 'Open ' + currPlist.CFBundleName + ' web site'
183 | ,'subtitle': w
184 | ,'icon':'urlTemplate'
185 | ,'url': w});
186 | }
187 | if (plist && plist.LBDescription && plist.LBDescription.LBChangelog && plist.LBDescription.LBChangelog.indexOf('http') == 0) {
188 | items.push({'title': 'Open version ' + plist.CFBundleVersion + ' change log'
189 | ,'subtitle':plist.LBDescription.LBChangelog
190 | ,'icon':'logTemplate'
191 | ,'url': plist.LBDescription.LBChangelog});
192 | }
193 | if (plist && plist.LBDescription && plist.LBDescription.LBChangelog && plist.LBDescription.LBChangelog.indexOf('http') != 0) {
194 | var changes = [{title: plist.LBDescription.LBChangelog, icon:'Text.icns'}];
195 | items.push({'title': 'Version ' + plist.CFBundleVersion + ' change log'
196 | ,'icon':'logTemplate'
197 | ,'children': changes});
198 | }
199 | if (plist && plist.LBDescription && plist.LBDescription.LBDownloadURL) {
200 | items.push({'title': 'Download version ' + plist.CFBundleVersion
201 | ,'subtitle':plist.LBDescription.LBDownloadURL
202 | ,'icon':'downloadTemplate'
203 | ,'url': plist.LBDescription.LBDownloadURL});
204 | }
205 | if (plist && plist.LBDescription && plist.LBDescription.LBDownload) {
206 | items.push({'title': 'Download version ' + plist.CFBundleVersion
207 | ,'subtitle':plist.LBDescription.LBDownload
208 | ,'icon':'downloadTemplate'
209 | ,'url': plist.LBDescription.LBDownload});
210 | }
211 | if (currPlist && currPlist.LBDescription && currPlist.LBDescription.LBUpdateURL) {
212 | items.push({'title': 'Open remote Info.plist'
213 | ,'subtitle':currPlist.LBDescription.LBUpdateURL
214 | ,'icon':'urlTemplate'
215 | ,'url': currPlist.LBDescription.LBUpdateURL});
216 | }
217 | if (currPlist && currPlist.LBDescription && currPlist.LBDescription.LBUpdate) {
218 | items.push({'title': 'Open remote Info.plist'
219 | ,'subtitle':currPlist.LBDescription.LBUpdate
220 | ,'icon':'urlTemplate'
221 | ,'url': currPlist.LBDescription.LBUpdate});
222 | }
223 | items.push({'title': 'Installed action version ' + (currPlist ? currPlist.CFBundleVersion : "")
224 | ,'subtitle':actionFile
225 | ,'icon': 'actionTemplate'
226 | ,'path': actionFile});
227 |
228 | if (currPlist) {
229 | if (Action.preferences
230 | && Action.preferences.LBUpdateURL
231 | && Action.preferences.LBUpdateURL[currPlist.CFBundleIdentifier]
232 | && Action.preferences.LBUpdateURL[currPlist.CFBundleIdentifier] == "SKIP") {
233 | items.push({'title': 'Resume checking this action for updates'
234 | ,'subtitle':currPlist.CFBundleIdentifier
235 | ,'icon':SKIP
236 | ,'action':'unskipper'
237 | ,'actionRunsInBackground':true
238 | ,'actionArgument': currPlist.CFBundleIdentifier});
239 | } else {
240 | items.push({'title': 'Skip checking this action for updates'
241 | ,'subtitle':currPlist.CFBundleIdentifier
242 | ,'icon':SKIP
243 | ,'action':'skipper'
244 | ,'actionRunsInBackground':true
245 | ,'actionArgument': currPlist.CFBundleIdentifier});
246 | }
247 | }
248 | return items;
249 | }
250 |
251 | function skipper(bundleId) {
252 | Action.preferences.LBUpdateURL[bundleId] = "SKIP";
253 | }
254 |
255 | function unskipper(bundleId) {
256 | Action.preferences.LBUpdateURL[bundleId] = "";
257 | }
258 |
259 | function getUpdateURL(actionPackage, plist) {
260 | if (Action.preferences
261 | && Action.preferences.LBUpdateURL
262 | && Action.preferences.LBUpdateURL[plist.CFBundleIdentifier]
263 | && Action.preferences.LBUpdateURL[plist.CFBundleIdentifier] == "SKIP")
264 | return "SKIP";
265 |
266 | if (Action.preferences
267 | && Action.preferences.LBUpdateURL
268 | && Action.preferences.LBUpdateURL[plist.CFBundleIdentifier]
269 | && Action.preferences.LBUpdateURL[plist.CFBundleIdentifier].indexOf('http') == 0)
270 | return Action.preferences.LBUpdateURL[plist.CFBundleIdentifier];
271 |
272 | if (plist.LBDescription
273 | && plist.LBDescription.LBUpdateURL
274 | && plist.LBDescription.LBUpdateURL.indexOf('http') == 0)
275 | return plist.LBDescription.LBUpdateURL;
276 |
277 | if (plist.LBDescription
278 | && plist.LBDescription.LBUpdate
279 | && plist.LBDescription.LBUpdate.indexOf('http') == 0)
280 | return plist.LBDescription.LBUpdate;
281 |
282 | return "";
283 | }
284 |
285 | function getWebsite(plist) {
286 | if (!plist || !plist.LBDescription)
287 | return null;
288 | if (plist.LBDescription.LBWebsiteURL)
289 | return plist.LBDescription.LBWebsiteURL;
290 | if (plist.LBDescription.LBWebsite)
291 | return plist.LBDescription.LBWebsite;
292 | return null;
293 | }
294 |
295 | function checkLaunchBar() {
296 | try {
297 | var result = HTTP.getPlist(LB_INFO, TIMEOUT);
298 | if (!result) {
299 | return {'title':'Error checking LaunchBar version - empty result'
300 | ,'subtitle':'Empty Plist result from ' + LB_INFO
301 | ,'alwaysShowsSubtitle': true
302 | ,'icon':ALERT_ICON
303 | ,'url':LB_INFO};
304 | }
305 | if (result.error) {
306 | return {'title':'Error checking LaunchBar version - ' + result.error
307 | ,'subtitle':result.error
308 | ,'alwaysShowsSubtitle': true
309 | ,'icon':ALERT_ICON
310 | ,'url':LB_INFO};
311 | }
312 | if (!result.data || result.data.length < 1) {
313 | return {'title':'Error checking LaunchBar version - empty data'
314 | ,'subtitle':'Empty Plist result data from ' + LB_INFO
315 | ,'alwaysShowsSubtitle': true
316 | ,'icon':ALERT_ICON
317 | ,'url':LB_INFO};
318 | }
319 | if (upToDate(LaunchBar.version, result.data[0].BundleVersion)) {
320 | return {'title':'LaunchBar: up to date'
321 | ,'badge': LaunchBar.shortVersion
322 | ,'icon':CHECK
323 | ,'url':LB_DOWNLOAD};
324 | } else {
325 | return {'title':'LaunchBar: Newer version available '
326 | + LaunchBar.shortVersion + ' ➔ ' + result.data[0].BundleShortVersionString
327 | ,'icon':CAUTION
328 | ,'url':LB_DOWNLOAD};
329 | }
330 | } catch (exception) {
331 | LaunchBar.log('Error checkLaunchBar ' + exception);
332 | return {'title':'HTTP Error checking LaunchBar version'
333 | ,'subtitle':exception
334 | ,'alwaysShowsSubtitle': true
335 | ,'icon':ALERT_ICON
336 | ,'url':LB_INFO};
337 | }
338 | return {};
339 | }
340 |
341 | // compare two versions, return true if local is up to date, false otherwise
342 | // if both versions are in the form of major[.minor][.patch] then the comparison parses and compares as such
343 | // otherwise the versions are treated as strings and normal string compare is done
344 | var VPAT = /^\d+(\.\d+){0,2}$/;
345 |
346 | function upToDate(local, remote) {
347 | if (!local || !remote || local.length === 0 || remote.length === 0)
348 | return false;
349 | if (local == remote)
350 | return true;
351 | if (VPAT.test(local) && VPAT.test(remote)) {
352 | var lparts = local.split('.');
353 | while(lparts.length < 3)
354 | lparts.push("0");
355 | var rparts = remote.split('.');
356 | while (rparts.length < 3)
357 | rparts.push("0");
358 | for (var i=0; i<3; i++) {
359 | var l = parseInt(lparts[i], 10);
360 | var r = parseInt(rparts[i], 10);
361 | if (l === r)
362 | continue;
363 | return l > r;
364 | }
365 | return true;
366 | } else {
367 | return local >= remote;
368 | }
369 | }
370 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 | Redirect to renaghan.com/launchbar/
10 |
11 |
12 | If you are not redirected automatically, follow this
13 | link to renaghan.com/launchbar
14 |
15 |
--------------------------------------------------------------------------------
/docs/updates.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 | Redirect to renaghan.com/launchbar/action-updates
10 |
11 |
12 | If you are not redirected automatically, follow this
13 | link to renaghan.com/launchbar
14 |
15 |
--------------------------------------------------------------------------------