terminal-notifier 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.markdown +1 -1
  3. data/lib/terminal-notifier.rb +5 -4
  4. data/vendor/terminal-notifier +0 -0
  5. metadata +6 -44
  6. data/vendor/terminal-notifier/LICENSE.md +0 -9
  7. data/vendor/terminal-notifier/README.markdown +0 -301
  8. data/vendor/terminal-notifier/Ruby/Gemfile +0 -2
  9. data/vendor/terminal-notifier/Ruby/Gemfile.lock +0 -23
  10. data/vendor/terminal-notifier/Ruby/LICENSE +0 -24
  11. data/vendor/terminal-notifier/Ruby/README.markdown +0 -48
  12. data/vendor/terminal-notifier/Ruby/Rakefile +0 -48
  13. data/vendor/terminal-notifier/Ruby/bin/terminal-notifier +0 -13
  14. data/vendor/terminal-notifier/Ruby/lib/terminal-notifier.rb +0 -92
  15. data/vendor/terminal-notifier/Ruby/spec/terminal-notifier_spec.rb +0 -79
  16. data/vendor/terminal-notifier/Ruby/terminal-notifier.gemspec +0 -26
  17. data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.pbxproj +0 -315
  18. data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  19. data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/project.xcworkspace/xcshareddata/Terminal Notifier.xccheckout +0 -41
  20. data/vendor/terminal-notifier/Terminal Notifier.xcodeproj/xcshareddata/xcschemes/Terminal Notifier.xcscheme +0 -140
  21. data/vendor/terminal-notifier/Terminal Notifier/AppDelegate.h +0 -4
  22. data/vendor/terminal-notifier/Terminal Notifier/AppDelegate.m +0 -594
  23. data/vendor/terminal-notifier/Terminal Notifier/Terminal Notifier-Info.plist +0 -43
  24. data/vendor/terminal-notifier/Terminal Notifier/Terminal Notifier-Prefix.pch +0 -7
  25. data/vendor/terminal-notifier/Terminal Notifier/en.lproj/Credits.rtf +0 -29
  26. data/vendor/terminal-notifier/Terminal Notifier/en.lproj/InfoPlist.strings +0 -2
  27. data/vendor/terminal-notifier/Terminal Notifier/en.lproj/MainMenu.xib +0 -3387
  28. data/vendor/terminal-notifier/Terminal Notifier/main.m +0 -6
  29. data/vendor/terminal-notifier/Terminal.icns +0 -0
  30. data/vendor/terminal-notifier/assets/Example_1.png +0 -0
  31. data/vendor/terminal-notifier/assets/Example_2.png +0 -0
  32. data/vendor/terminal-notifier/assets/Example_3.png +0 -0
  33. data/vendor/terminal-notifier/assets/Example_4.png +0 -0
  34. data/vendor/terminal-notifier/assets/Example_5.png +0 -0
  35. data/vendor/terminal-notifier/assets/System_prefs.png +0 -0
  36. data/vendor/terminal-notifier/terminal-notifier.app/Contents/Info.plist +0 -63
  37. data/vendor/terminal-notifier/terminal-notifier.app/Contents/MacOS/terminal-notifier +0 -0
  38. data/vendor/terminal-notifier/terminal-notifier.app/Contents/PkgInfo +0 -1
  39. data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/Terminal.icns +0 -0
  40. data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/en.lproj/Credits.rtf +0 -29
  41. data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
  42. data/vendor/terminal-notifier/terminal-notifier.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <Workspace
3
- version = "1.0">
4
- <FileRef
5
- location = "self:Terminal Notifier.xcodeproj">
6
- </FileRef>
7
- </Workspace>
@@ -1,41 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>IDESourceControlProjectFavoriteDictionaryKey</key>
6
- <false/>
7
- <key>IDESourceControlProjectIdentifier</key>
8
- <string>A55309C1-2ABD-460A-B080-5A322DCAF4B5</string>
9
- <key>IDESourceControlProjectName</key>
10
- <string>Terminal Notifier</string>
11
- <key>IDESourceControlProjectOriginsDictionary</key>
12
- <dict>
13
- <key>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</key>
14
- <string>github.com:alloy/terminal-notifier.git</string>
15
- </dict>
16
- <key>IDESourceControlProjectPath</key>
17
- <string>Terminal Notifier.xcodeproj</string>
18
- <key>IDESourceControlProjectRelativeInstallPathDictionary</key>
19
- <dict>
20
- <key>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</key>
21
- <string>../..</string>
22
- </dict>
23
- <key>IDESourceControlProjectURL</key>
24
- <string>github.com:alloy/terminal-notifier.git</string>
25
- <key>IDESourceControlProjectVersion</key>
26
- <integer>111</integer>
27
- <key>IDESourceControlProjectWCCIdentifier</key>
28
- <string>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</string>
29
- <key>IDESourceControlProjectWCConfigurations</key>
30
- <array>
31
- <dict>
32
- <key>IDESourceControlRepositoryExtensionIdentifierKey</key>
33
- <string>public.vcs.git</string>
34
- <key>IDESourceControlWCCIdentifierKey</key>
35
- <string>9F67DD19109869B0F5A85172DA9B0D3E8F7EF383</string>
36
- <key>IDESourceControlWCCName</key>
37
- <string>terminal-notifier</string>
38
- </dict>
39
- </array>
40
- </dict>
41
- </plist>
@@ -1,140 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <Scheme
3
- LastUpgradeVersion = "0440"
4
- version = "1.3">
5
- <BuildAction
6
- parallelizeBuildables = "YES"
7
- buildImplicitDependencies = "YES">
8
- <BuildActionEntries>
9
- <BuildActionEntry
10
- buildForTesting = "YES"
11
- buildForRunning = "YES"
12
- buildForProfiling = "YES"
13
- buildForArchiving = "YES"
14
- buildForAnalyzing = "YES">
15
- <BuildableReference
16
- BuildableIdentifier = "primary"
17
- BlueprintIdentifier = "5199791315B1F92B003AFC57"
18
- BuildableName = "terminal-notifier.app"
19
- BlueprintName = "terminal-notifier"
20
- ReferencedContainer = "container:Terminal Notifier.xcodeproj">
21
- </BuildableReference>
22
- </BuildActionEntry>
23
- </BuildActionEntries>
24
- </BuildAction>
25
- <TestAction
26
- selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
27
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
28
- shouldUseLaunchSchemeArgsEnv = "YES"
29
- buildConfiguration = "Debug">
30
- <Testables>
31
- </Testables>
32
- <MacroExpansion>
33
- <BuildableReference
34
- BuildableIdentifier = "primary"
35
- BlueprintIdentifier = "5199791315B1F92B003AFC57"
36
- BuildableName = "terminal-notifier.app"
37
- BlueprintName = "terminal-notifier"
38
- ReferencedContainer = "container:Terminal Notifier.xcodeproj">
39
- </BuildableReference>
40
- </MacroExpansion>
41
- </TestAction>
42
- <LaunchAction
43
- selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
44
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
45
- launchStyle = "0"
46
- useCustomWorkingDirectory = "NO"
47
- buildConfiguration = "Release"
48
- ignoresPersistentStateOnLaunch = "NO"
49
- debugDocumentVersioning = "NO"
50
- allowLocationSimulation = "YES">
51
- <BuildableProductRunnable>
52
- <BuildableReference
53
- BuildableIdentifier = "primary"
54
- BlueprintIdentifier = "5199791315B1F92B003AFC57"
55
- BuildableName = "terminal-notifier.app"
56
- BlueprintName = "terminal-notifier"
57
- ReferencedContainer = "container:Terminal Notifier.xcodeproj">
58
- </BuildableReference>
59
- </BuildableProductRunnable>
60
- <CommandLineArguments>
61
- <CommandLineArgument
62
- argument = "-group abcde"
63
- isEnabled = "NO">
64
- </CommandLineArgument>
65
- <CommandLineArgument
66
- argument = "-help"
67
- isEnabled = "NO">
68
- </CommandLineArgument>
69
- <CommandLineArgument
70
- argument = "-sound default"
71
- isEnabled = "NO">
72
- </CommandLineArgument>
73
- <CommandLineArgument
74
- argument = "-list ALL"
75
- isEnabled = "NO">
76
- </CommandLineArgument>
77
- <CommandLineArgument
78
- argument = "-list abcde"
79
- isEnabled = "NO">
80
- </CommandLineArgument>
81
- <CommandLineArgument
82
- argument = "-remove abcde"
83
- isEnabled = "NO">
84
- </CommandLineArgument>
85
- <CommandLineArgument
86
- argument = "-open &apos;http://google.com/search?q=http status codes&apos;"
87
- isEnabled = "YES">
88
- </CommandLineArgument>
89
- <CommandLineArgument
90
- argument = "-execute &apos;say &quot;OMG&quot;&apos;"
91
- isEnabled = "NO">
92
- </CommandLineArgument>
93
- <CommandLineArgument
94
- argument = "-execute &apos;ls -l&apos;"
95
- isEnabled = "NO">
96
- </CommandLineArgument>
97
- <CommandLineArgument
98
- argument = "-title Kicker"
99
- isEnabled = "NO">
100
- </CommandLineArgument>
101
- <CommandLineArgument
102
- argument = "-message &apos;Execute: rake spec&apos;"
103
- isEnabled = "NO">
104
- </CommandLineArgument>
105
- <CommandLineArgument
106
- argument = "-activate com.apple.Safari"
107
- isEnabled = "NO">
108
- </CommandLineArgument>
109
- <CommandLineArgument
110
- argument = "-sender com.apple.Safari"
111
- isEnabled = "NO">
112
- </CommandLineArgument>
113
- </CommandLineArguments>
114
- <AdditionalOptions>
115
- </AdditionalOptions>
116
- </LaunchAction>
117
- <ProfileAction
118
- shouldUseLaunchSchemeArgsEnv = "YES"
119
- savedToolIdentifier = ""
120
- useCustomWorkingDirectory = "NO"
121
- buildConfiguration = "Release"
122
- debugDocumentVersioning = "YES">
123
- <BuildableProductRunnable>
124
- <BuildableReference
125
- BuildableIdentifier = "primary"
126
- BlueprintIdentifier = "5199791315B1F92B003AFC57"
127
- BuildableName = "terminal-notifier.app"
128
- BlueprintName = "terminal-notifier"
129
- ReferencedContainer = "container:Terminal Notifier.xcodeproj">
130
- </BuildableReference>
131
- </BuildableProductRunnable>
132
- </ProfileAction>
133
- <AnalyzeAction
134
- buildConfiguration = "Debug">
135
- </AnalyzeAction>
136
- <ArchiveAction
137
- buildConfiguration = "Release"
138
- revealArchiveInOrganizer = "YES">
139
- </ArchiveAction>
140
- </Scheme>
@@ -1,4 +0,0 @@
1
- #import <Cocoa/Cocoa.h>
2
-
3
- @interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
4
- @end
@@ -1,594 +0,0 @@
1
- #import "AppDelegate.h"
2
- #import <ScriptingBridge/ScriptingBridge.h>
3
- #import <objc/runtime.h>
4
-
5
- NSString * const TerminalNotifierBundleID = @"nl.superalloy.oss.terminal-notifier";
6
- NSString * const NotificationCenterUIBundleID = @"com.apple.notificationcenterui";
7
-
8
- // Set OS Params
9
- #define NSAppKitVersionNumber10_8 1187
10
- #define NSAppKitVersionNumber10_9 1265
11
-
12
- #define contains(str1, str2) ([str1 rangeOfString: str2 ].location != NSNotFound)
13
-
14
- NSString *_fakeBundleIdentifier = nil;
15
- NSUserNotification *currentNotification = nil;
16
-
17
- @implementation NSBundle (FakeBundleIdentifier)
18
-
19
- // Overriding bundleIdentifier works, but overriding NSUserNotificationAlertStyle does not work.
20
-
21
- - (NSString *)__bundleIdentifier;
22
- {
23
- if (self == [NSBundle mainBundle]) {
24
- return _fakeBundleIdentifier ? _fakeBundleIdentifier : TerminalNotifierBundleID;
25
- } else {
26
- return [self __bundleIdentifier];
27
- }
28
- }
29
-
30
- @end
31
-
32
- static BOOL
33
- InstallFakeBundleIdentifierHook()
34
- {
35
- Class class = objc_getClass("NSBundle");
36
- if (class) {
37
- method_exchangeImplementations(class_getInstanceMethod(class, @selector(bundleIdentifier)),
38
- class_getInstanceMethod(class, @selector(__bundleIdentifier)));
39
- return YES;
40
- }
41
- return NO;
42
- }
43
-
44
- static BOOL
45
- isMavericks()
46
- {
47
- if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_8) {
48
- /* On a 10.8 - 10.8.x system */
49
- return NO;
50
- } else {
51
- /* 10.9 or later system */
52
- return YES;
53
- }
54
- }
55
-
56
- @implementation NSUserDefaults (SubscriptAndUnescape)
57
- - (id)objectForKeyedSubscript:(id)key;
58
- {
59
- id obj = [self objectForKey:key];
60
- if ([obj isKindOfClass:[NSString class]] && [(NSString *)obj hasPrefix:@"\\"]) {
61
- obj = [(NSString *)obj substringFromIndex:1];
62
- }
63
- return obj;
64
- }
65
- @end
66
-
67
-
68
- @implementation AppDelegate
69
-
70
- +(void)initializeUserDefaults
71
- {
72
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
73
-
74
- // initialize the dictionary with default values depending on OS level
75
- NSDictionary *appDefaults;
76
-
77
- if (isMavericks()) {
78
- //10.9
79
- appDefaults = @{@"sender": @"com.apple.Terminal"};
80
- } else {
81
- //10.8
82
- appDefaults = @{@"": @"message"};
83
- }
84
-
85
- // and set them appropriately
86
- [defaults registerDefaults:appDefaults];
87
- }
88
-
89
- - (void)printHelpBanner;
90
- {
91
- const char *appName = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String];
92
- const char *appVersion = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] UTF8String];
93
- printf("%s (%s) is a command-line tool to send OS X User Notifications.\n" \
94
- "\n" \
95
- "Usage: %s -[message|list|remove] [VALUE|ID|ID] [options]\n" \
96
- "\n" \
97
- " Either of these is required (unless message data is piped to the tool):\n" \
98
- "\n" \
99
- " -help Display this help banner.\n" \
100
- " -message VALUE The notification message.\n" \
101
- " -remove ID Removes a notification with the specified ‘group’ ID.\n" \
102
- " -list ID If the specified ‘group’ ID exists show when it was delivered,\n" \
103
- " or use ‘ALL’ as ID to see all notifications.\n" \
104
- " The output is a tab-separated list.\n"
105
- "\n" \
106
- " Reply type notification:\n" \
107
- "\n" \
108
- " -reply VALUE The notification will be displayed as a reply type alert, VALUE used as placeholder.\n" \
109
- "\n" \
110
- " Actions type notification:\n" \
111
- "\n" \
112
- " -actions VALUE1,VALUE2.\n" \
113
- " The notification actions avalaible.\n" \
114
- " When you provide more than one value, a dropdown will be displayed.\n" \
115
- " You can customize this dropdown label with the next option.\n" \
116
- " -dropdownLabel VALUE\n" \
117
- " The notification actions dropdown title (only when multiples actions are provided).\n" \
118
- " Notification style must be set to Alert.\n" \
119
- "\n" \
120
- " Optional:\n" \
121
- "\n" \
122
- " -title VALUE The notification title. Defaults to ‘Terminal’.\n" \
123
- " -subtitle VALUE The notification subtitle.\n" \
124
- " -closeLabel VALUE The notification close button label.\n" \
125
- " -sound NAME The name of a sound to play when the notification appears. The names are listed\n" \
126
- " in Sound Preferences. Use 'default' for the default notification sound.\n" \
127
- " -group ID A string which identifies the group the notifications belong to.\n" \
128
- " Old notifications with the same ID will be removed.\n" \
129
- " -activate ID The bundle identifier of the application to activate when the user clicks the notification.\n" \
130
- " -sender ID The bundle identifier of the application that should be shown as the sender, including its icon.\n" \
131
- " -appIcon URL The URL of a image to display instead of the application icon (Mavericks+ only)\n" \
132
- " -contentImage URL The URL of a image to display attached to the notification (Mavericks+ only)\n" \
133
- " -open URL The URL of a resource to open when the user clicks the notification.\n" \
134
- " -execute COMMAND A shell command to perform when the user clicks the notification.\n" \
135
- " -timeout NUMBER Close the notification after NUMBER seconds.\n" \
136
- " -json Output event or value to stdout as JSON \n" \
137
- "\n" \
138
- "When the user activates a notification, the results are logged to the system logs.\n" \
139
- "Use Console.app to view these logs.\n" \
140
- "\n" \
141
- "Note that in some circumstances the first character of a message has to be escaped in order to be recognized.\n" \
142
- "An example of this is when using an open bracket, which has to be escaped like so: ‘\\[’.\n" \
143
- "\n" \
144
- "For more information see https://github.com/julienXX/terminal-notifier.\n",
145
- appName, appVersion, appName);
146
- }
147
-
148
- - (void)applicationDidFinishLaunching:(NSNotification *)notification;
149
- {
150
- NSUserNotification *userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey];
151
- if (userNotification) {
152
- [self userActivatedNotification:userNotification];
153
-
154
- } else {
155
- if ([[[NSProcessInfo processInfo] arguments] indexOfObject:@"-help"] != NSNotFound) {
156
- [self printHelpBanner];
157
- exit(0);
158
- }
159
-
160
- NSArray *runningProcesses = [[[NSWorkspace sharedWorkspace] runningApplications] valueForKey:@"bundleIdentifier"];
161
- if ([runningProcesses indexOfObject:NotificationCenterUIBundleID] == NSNotFound) {
162
- NSLog(@"[!] Unable to post a notification for the current user (%@), as it has no running NotificationCenter instance.", NSUserName());
163
- exit(1);
164
- }
165
-
166
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
167
-
168
- NSString *subtitle = defaults[@"subtitle"];
169
- NSString *message = defaults[@"message"];
170
- NSString *remove = defaults[@"remove"];
171
- NSString *list = defaults[@"list"];
172
- NSString *sound = defaults[@"sound"];
173
-
174
- // If there is no message and data is piped to the application, use that
175
- // instead.
176
- if (message == nil && !isatty(STDIN_FILENO)) {
177
- NSData *inputData = [NSData dataWithData:[[NSFileHandle fileHandleWithStandardInput] readDataToEndOfFile]];
178
- message = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
179
- }
180
-
181
- if (message == nil && remove == nil && list == nil) {
182
- [self printHelpBanner];
183
- exit(1);
184
- }
185
-
186
- if (list) {
187
- [self listNotificationWithGroupID:list];
188
- exit(0);
189
- }
190
-
191
- // Install the fake bundle ID hook so we can fake the sender. This also
192
- // needs to be done to be able to remove a message.
193
- if (defaults[@"sender"]) {
194
- @autoreleasepool {
195
- if (InstallFakeBundleIdentifierHook()) {
196
- _fakeBundleIdentifier = defaults[@"sender"];
197
- }
198
- }
199
- }
200
-
201
- if (remove) {
202
- [self removeNotificationWithGroupID:remove];
203
- if (message == nil) exit(0);
204
- }
205
-
206
- if (message) {
207
- NSMutableDictionary *options = [NSMutableDictionary dictionary];
208
- if (defaults[@"activate"]) options[@"bundleID"] = defaults[@"activate"];
209
- if (defaults[@"group"]) options[@"groupID"] = defaults[@"group"];
210
- if (defaults[@"execute"]) options[@"command"] = defaults[@"execute"];
211
- if (defaults[@"appIcon"]) options[@"appIcon"] = defaults[@"appIcon"];
212
- if (defaults[@"contentImage"]) options[@"contentImage"] = defaults[@"contentImage"];
213
- if (defaults[@"closeLabel"]) options[@"closeLabel"] = defaults[@"closeLabel"];
214
- if (defaults[@"dropdownLabel"]) options[@"dropdownLabel"] = defaults[@"dropdownLabel"];
215
- if (defaults[@"actions"]) options[@"actions"] = defaults[@"actions"];
216
-
217
- if([[[NSProcessInfo processInfo] arguments] containsObject:@"-reply"] == true) {
218
- options[@"reply"] = @"Reply";
219
- if (defaults[@"reply"]) options[@"reply"] = defaults[@"reply"];
220
- }
221
-
222
- options[@"output"] = @"outputEvent";
223
- if([[[NSProcessInfo processInfo] arguments] containsObject:@"-json"] == true) {
224
- options[@"output"] = @"json";
225
- }
226
-
227
- options[@"uuid"] = [NSString stringWithFormat:@"%ld", self.hash];
228
- options[@"timeout"] = defaults[@"timeout"] ? defaults[@"timeout"] : @"0";
229
-
230
- if (options[@"reply"] || defaults[@"timeout"] || defaults[@"actions"]) options[@"waitForResponse"] = @YES;
231
-
232
- if (defaults[@"open"]) {
233
- /*
234
- * it may be better to use stringByAddingPercentEncodingWithAllowedCharacters instead of stringByAddingPercentEscapesUsingEncoding,
235
- * but stringByAddingPercentEncodingWithAllowedCharacters is only available on OS X 10.9 or higher.
236
- */
237
- NSString *encodedURL = [defaults[@"open"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
238
- NSURL *url = [NSURL URLWithString:defaults[@"open"]];
239
- NSString *fragment = [url fragment];
240
- if (fragment) {
241
- options[@"open"] = [self decodeFragmentInURL:encodedURL fragment:fragment];
242
- } else {
243
- options[@"open"] = encodedURL;
244
- }
245
- }
246
-
247
- options[@"uuid"] = [NSString stringWithFormat:@"%ld", self.hash];
248
-
249
- [self deliverNotificationWithTitle:defaults[@"title"] ?: @"Terminal"
250
- subtitle:subtitle
251
- message:message
252
- options:options
253
- sound:sound];
254
- }
255
- }
256
- }
257
-
258
- - (NSImage*)getImageFromURL:(NSString *) url;
259
- {
260
- NSURL *imageURL = [NSURL URLWithString:url];
261
- if([[imageURL scheme] length] == 0){
262
- // Prefix 'file://' if no scheme
263
- imageURL = [NSURL fileURLWithPath:url];
264
- }
265
- return [[NSImage alloc] initWithContentsOfURL:imageURL];
266
- }
267
-
268
- /**
269
- * Decode fragment identifier
270
- *
271
- * @see http://tools.ietf.org/html/rfc3986#section-2.1
272
- * @see http://en.wikipedia.org/wiki/URI_scheme
273
- */
274
- - (NSString*)decodeFragmentInURL:(NSString *) encodedURL fragment:(NSString *) framgent
275
- {
276
- NSString *beforeStr = [@"%23" stringByAppendingString:framgent];
277
- NSString *afterStr = [@"#" stringByAppendingString:framgent];
278
- NSString *decodedURL = [encodedURL stringByReplacingOccurrencesOfString:beforeStr withString:afterStr];
279
- return decodedURL;
280
- }
281
-
282
- - (void)deliverNotificationWithTitle:(NSString *)title
283
- subtitle:(NSString *)subtitle
284
- message:(NSString *)message
285
- options:(NSDictionary *)options
286
- sound:(NSString *)sound;
287
- {
288
- // First remove earlier notification with the same group ID.
289
- if (options[@"groupID"]) [self removeNotificationWithGroupID:options[@"groupID"]];
290
-
291
- NSUserNotification *userNotification = [NSUserNotification new];
292
- userNotification.title = title;
293
- userNotification.subtitle = subtitle;
294
- userNotification.informativeText = message;
295
- userNotification.userInfo = options;
296
-
297
- if(isMavericks()){
298
- // Mavericks options
299
- if(options[@"appIcon"]){
300
- // replacement app icon
301
- [userNotification setValue:[self getImageFromURL:options[@"appIcon"]] forKey:@"_identityImage"];
302
- [userNotification setValue:@(false) forKey:@"_identityImageHasBorder"];
303
- }
304
- if(options[@"contentImage"]){
305
- // content image
306
- userNotification.contentImage = [self getImageFromURL:options[@"contentImage"]];
307
- }
308
- // Actions
309
- if (options[@"actions"]){
310
- [userNotification setValue:@YES forKey:@"_showsButtons"];
311
- NSArray *myActions = [options[@"actions"] componentsSeparatedByString:@","];
312
- if (myActions.count > 1) {
313
- [userNotification setValue:@YES forKey:@"_alwaysShowAlternateActionMenu"];
314
- [userNotification setValue:myActions forKey:@"_alternateActionButtonTitles"];
315
-
316
- //Main Actions Title
317
- if(options[@"dropdownLabel"]){
318
- userNotification.actionButtonTitle = options[@"dropdownLabel"];
319
- userNotification.hasActionButton = true;
320
- }
321
- }else{
322
- userNotification.actionButtonTitle = options[@"actions"];
323
- }
324
- }else if (options[@"reply"]) {
325
- [userNotification setValue:@YES forKey:@"_showsButtons"];
326
- userNotification.hasReplyButton = 1;
327
- userNotification.responsePlaceholder = options[@"reply"];
328
- }
329
-
330
- // Close button
331
- if(options[@"closeLabel"]){
332
- userNotification.otherButtonTitle = options[@"closeLabel"];
333
- }
334
- }
335
-
336
- if (sound != nil) {
337
- userNotification.soundName = [sound isEqualToString: @"default"] ? NSUserNotificationDefaultSoundName : sound;
338
- }
339
-
340
- NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
341
- center.delegate = self;
342
- [center deliverNotification:userNotification];
343
- }
344
-
345
- - (void)removeNotificationWithGroupID:(NSString *)groupID;
346
- {
347
- NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
348
- for (NSUserNotification *userNotification in center.deliveredNotifications) {
349
- if ([@"ALL" isEqualToString:groupID] || [userNotification.userInfo[@"groupID"] isEqualToString:groupID]) {
350
- [center removeDeliveredNotification:userNotification];
351
- }
352
- }
353
- }
354
-
355
- - (void)userActivatedNotification:(NSUserNotification *)userNotification;
356
- {
357
- [[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification:userNotification];
358
-
359
- NSString *groupID = userNotification.userInfo[@"groupID"];
360
- NSString *bundleID = userNotification.userInfo[@"bundleID"];
361
- NSString *command = userNotification.userInfo[@"command"];
362
- NSString *open = userNotification.userInfo[@"open"];
363
-
364
- if (bundleID) [self activateAppWithBundleID:bundleID];
365
- if (command) [self executeShellCommand:command];
366
- if (open) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:open]];
367
- }
368
-
369
- - (BOOL)activateAppWithBundleID:(NSString *)bundleID;
370
- {
371
- id app = [SBApplication applicationWithBundleIdentifier:bundleID];
372
- if (app) {
373
- [app activate];
374
- return YES;
375
-
376
- } else {
377
- NSLog(@"Unable to find an application with the specified bundle indentifier.");
378
- return NO;
379
- }
380
- }
381
-
382
- - (BOOL)executeShellCommand:(NSString *)command;
383
- {
384
- NSPipe *pipe = [NSPipe pipe];
385
- NSFileHandle *fileHandle = [pipe fileHandleForReading];
386
-
387
- NSTask *task = [NSTask new];
388
- task.launchPath = @"/bin/sh";
389
- task.arguments = @[@"-c", command];
390
- task.standardOutput = pipe;
391
- task.standardError = pipe;
392
- [task launch];
393
-
394
- NSData *data = nil;
395
- NSMutableData *accumulatedData = [NSMutableData data];
396
- while ((data = [fileHandle availableData]) && [data length]) {
397
- [accumulatedData appendData:data];
398
- }
399
-
400
- [task waitUntilExit];
401
- NSLog(@"command output:\n%@", [[NSString alloc] initWithData:accumulatedData encoding:NSUTF8StringEncoding]);
402
- return [task terminationStatus] == 0;
403
- }
404
-
405
- - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
406
- shouldPresentNotification:(NSUserNotification *)notification;
407
- {
408
- return YES;
409
- }
410
-
411
- // Once the notification is delivered we can exit. (Only if no actions or reply)
412
- - (void)userNotificationCenter:(NSUserNotificationCenter *)center
413
- didDeliverNotification:(NSUserNotification *)userNotification;
414
- {
415
- if (!userNotification.userInfo[@"waitForResponse"]) exit(0);
416
-
417
- currentNotification = userNotification;
418
-
419
- dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
420
- ^{
421
- __block BOOL notificationStillPresent;
422
- do {
423
- @autoreleasepool {
424
- notificationStillPresent = NO;
425
- for (NSUserNotification *nox in [[NSUserNotificationCenter defaultUserNotificationCenter] deliveredNotifications]) {
426
- if ([nox.userInfo[@"uuid"] isEqualToString:[NSString stringWithFormat:@"%ld", self.hash] ]) notificationStillPresent = YES;
427
- }
428
- if (notificationStillPresent) [NSThread sleepForTimeInterval:0.20f];
429
- }
430
- } while (notificationStillPresent);
431
-
432
- dispatch_async(dispatch_get_main_queue(), ^{
433
- NSDictionary *udict = @{@"activationType" : @"closed", @"activationValue" : userNotification.otherButtonTitle};
434
- [self Quit:udict notification:userNotification];
435
- exit(0);
436
- });
437
- });
438
-
439
- if ([userNotification.userInfo[@"timeout"] integerValue] > 0){
440
- dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
441
- ^{
442
- [NSThread sleepForTimeInterval:[userNotification.userInfo[@"timeout"] integerValue]];
443
- [center removeDeliveredNotification:currentNotification];
444
- [center removeDeliveredNotification:userNotification];
445
- NSDictionary *udict = @{@"activationType" : @"timeout"};
446
- [self Quit:udict notification:userNotification];
447
- exit(0);
448
- });
449
- }
450
- }
451
-
452
- - (void)userNotificationCenter:(NSUserNotificationCenter *)center
453
- didActivateNotification:(NSUserNotification *)notification {
454
-
455
- if ([notification.userInfo[@"uuid"] isNotEqualTo:[NSString stringWithFormat:@"%ld", self.hash] ]) {
456
- return;
457
- };
458
-
459
- unsigned long long additionalActionIndex = ULLONG_MAX;
460
-
461
- NSString* ActionsClicked = @"";
462
- switch (notification.activationType) {
463
-
464
- case NSUserNotificationActivationTypeAdditionalActionClicked:
465
- case NSUserNotificationActivationTypeActionButtonClicked:
466
- if ([[(NSObject*)notification valueForKey:@"_alternateActionButtonTitles"] count] > 1 ){
467
- NSNumber *alternateActionIndex = [(NSObject*)notification valueForKey:@"_alternateActionIndex"];
468
- additionalActionIndex = [alternateActionIndex unsignedLongLongValue];
469
- ActionsClicked = [(NSObject*)notification valueForKey:@"_alternateActionButtonTitles"][additionalActionIndex];
470
-
471
- NSDictionary *udict = @{@"activationType" : @"actionClicked", @"activationValue" : ActionsClicked, @"activationValueIndex" :[NSString stringWithFormat:@"%llu", additionalActionIndex]};
472
- [self Quit:udict notification:notification];
473
- }else{
474
- NSDictionary *udict = @{@"activationType" : @"actionClicked", @"activationValue" : notification.actionButtonTitle};
475
- [self Quit:udict notification:notification];
476
- }
477
- break;
478
-
479
- case NSUserNotificationActivationTypeContentsClicked:
480
- [self userActivatedNotification:notification];
481
- [self Quit:@{@"activationType" : @"contentsClicked"} notification:notification];
482
- break;
483
-
484
- case NSUserNotificationActivationTypeReplied:
485
- [self Quit:@{@"activationType" : @"replied",@"activationValue":notification.response.string} notification:notification];
486
- break;
487
- case NSUserNotificationActivationTypeNone:
488
- default:
489
- [self Quit:@{@"activationType" : @"none"} notification:notification];
490
- break;
491
- }
492
-
493
- [center removeDeliveredNotification:notification];
494
- [center removeDeliveredNotification:currentNotification];
495
- exit(0);
496
- }
497
-
498
- - (BOOL)Quit:(NSDictionary *)udict notification:(NSUserNotification *)notification;
499
- {
500
- if ([notification.userInfo[@"output"] isEqualToString:@"outputEvent"]) {
501
- if ([udict[@"activationType"] isEqualToString:@"closed"]) {
502
- if ([udict[@"activationValue"] isEqualToString:@""]) {
503
- printf("%s", "@CLOSED" );
504
- }else{
505
- printf("%s", [udict[@"activationValue"] UTF8String] );
506
- }
507
- } else if ([udict[@"activationType"] isEqualToString:@"timeout"]) {
508
- printf("%s", "@TIMEOUT" );
509
- } else if ([udict[@"activationType"] isEqualToString:@"contentsClicked"]) {
510
- printf("%s", "@CONTENTCLICKED" );
511
- } else{
512
- if ([udict[@"activationValue"] isEqualToString:@""]) {
513
- printf("%s", "@ACTIONCLICKED" );
514
- }else{
515
- printf("%s", [udict[@"activationValue"] UTF8String] );
516
- }
517
- }
518
-
519
- return 1;
520
- }
521
-
522
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
523
- dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss Z";
524
-
525
- // Dictionary with several key/value pairs and the above array of arrays
526
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
527
- [dict addEntriesFromDictionary:udict];
528
- [dict setValue:[dateFormatter stringFromDate:notification.actualDeliveryDate] forKey:@"deliveredAt"];
529
- [dict setValue:[dateFormatter stringFromDate:[NSDate new]] forKey:@"activationAt"];
530
-
531
- NSError *error = nil;
532
- NSData *json;
533
-
534
- // Dictionary convertable to JSON ?
535
- if ([NSJSONSerialization isValidJSONObject:dict])
536
- {
537
- // Serialize the dictionary
538
- json = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
539
-
540
- // If no errors, let's view the JSON
541
- if (json != nil && error == nil)
542
- {
543
- NSString *jsonString = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
544
- printf("%s", [jsonString cStringUsingEncoding:NSUTF8StringEncoding]);
545
- }
546
- }
547
-
548
- return 1;
549
- }
550
-
551
- - (void)listNotificationWithGroupID:(NSString *)listGroupID;
552
- {
553
- NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
554
-
555
- NSMutableArray *lines = [NSMutableArray array];
556
- for (NSUserNotification *userNotification in center.deliveredNotifications) {
557
- NSString *deliveredgroupID = userNotification.userInfo[@"groupID"];
558
- NSString *title = userNotification.title;
559
- NSString *subtitle = userNotification.subtitle;
560
- NSString *message = userNotification.informativeText;
561
- NSString *deliveredAt = [userNotification.actualDeliveryDate description];
562
-
563
- if ([@"ALL" isEqualToString:listGroupID] || [deliveredgroupID isEqualToString:listGroupID]) {
564
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
565
- [dict setValue:deliveredgroupID forKey:@"GroupID"];
566
- [dict setValue:title forKey:@"Title"];
567
- [dict setValue:subtitle forKey:@"subtitle"];
568
- [dict setValue:message forKey:@"message"];
569
- [dict setValue:deliveredAt forKey:@"deliveredAt"];
570
- [lines addObject:dict];
571
- }
572
- }
573
-
574
- if (lines.count > 0) {
575
- NSData *json;
576
- NSError *error = nil;
577
- // Dictionary convertable to JSON ?
578
- if ([NSJSONSerialization isValidJSONObject:lines])
579
- {
580
- // Serialize the dictionary
581
- json = [NSJSONSerialization dataWithJSONObject:lines options:NSJSONWritingPrettyPrinted error:&error];
582
-
583
- // If no errors, let's view the JSON
584
- if (json != nil && error == nil)
585
- {
586
- NSString *jsonString = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
587
- printf("%s", [jsonString cStringUsingEncoding:NSUTF8StringEncoding]);
588
- }
589
- }
590
-
591
- }
592
- }
593
-
594
- @end