growl.js 6.9 KB
Newer Older
Rosanny Sihombing's avatar
Rosanny Sihombing committed
1
2
3
4
5
6
// Growl - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)

/**
 * Module dependencies.
 */

7
8
9
10
11
12
13
var exec = require('child_process').exec
  , fs = require('fs')
  , path = require('path')
  , exists = fs.existsSync || path.existsSync
  , os = require('os')
  , quote = JSON.stringify
  , cmd;
Rosanny Sihombing's avatar
Rosanny Sihombing committed
14
15

function which(name) {
16
17
  var paths = process.env.PATH.split(':');
  var loc;
Rosanny Sihombing's avatar
Rosanny Sihombing committed
18

19
  for (var i = 0, len = paths.length; i < len; ++i) {
Rosanny Sihombing's avatar
Rosanny Sihombing committed
20
21
22
23
24
    loc = path.join(paths[i], name);
    if (exists(loc)) return loc;
  }
}

25
26
27
switch(os.type()) {
  case 'Darwin':
    if (which('terminal-notifier')) {
Rosanny Sihombing's avatar
Rosanny Sihombing committed
28
      cmd = {
29
30
31
32
33
34
35
36
37
38
39
40
          type: "Darwin-NotificationCenter"
        , pkg: "terminal-notifier"
        , msg: '-message'
        , title: '-title'
        , subtitle: '-subtitle'
        , icon: '-appIcon'
        , sound:  '-sound'
        , url: '-open'
        , priority: {
              cmd: '-execute'
            , range: []
          }
Rosanny Sihombing's avatar
Rosanny Sihombing committed
41
      };
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    } else {
      cmd = {
          type: "Darwin-Growl"
        , pkg: "growlnotify"
        , msg: '-m'
        , sticky: '--sticky'
        , priority: {
              cmd: '--priority'
            , range: [
                -2
              , -1
              , 0
              , 1
              , 2
              , "Very Low"
              , "Moderate"
              , "Normal"
              , "High"
              , "Emergency"
            ]
          }
      };
    }
    break;
  case 'Linux':
    if (which('growl')) {
      cmd = {
          type: "Linux-Growl"
        , pkg: "growl"
        , msg: '-m'
        , title: '-title'
        , subtitle: '-subtitle'
        , host: {
            cmd: '-H'
          , hostname: '192.168.33.1'
        }
      };
    } else {
      cmd = {
          type: "Linux"
        , pkg: "notify-send"
        , msg: ''
        , sticky: '-t 0'
        , icon: '-i'
        , priority: {
            cmd: '-u'
          , range: [
              "low"
            , "normal"
            , "critical"
          ]
        }
      };
    }
    break;
  case 'Windows_NT':
    cmd = {
        type: "Windows"
      , pkg: "growlnotify"
      , msg: ''
      , sticky: '/s:true'
      , title: '/t:'
      , icon: '/i:'
      , url: '/cu:'
      , priority: {
            cmd: '/p:'
          , range: [
              -2
            , -1
            , 0
            , 1
            , 2
          ]
        }
    };
    break;
Rosanny Sihombing's avatar
Rosanny Sihombing committed
118
119
}

120
121
122
123
124
125
126
127
128
129
130
/**
 * Expose `growl`.
 */

exports = module.exports = growl;

/**
 * Node-growl version.
 */

exports.version = '1.4.1'
Rosanny Sihombing's avatar
Rosanny Sihombing committed
131
132
133
134
135
136
137
138
139
140

/**
 * Send growl notification _msg_ with _options_.
 *
 * Options:
 *
 *  - title   Notification title
 *  - sticky  Make the notification stick (defaults to false)
 *  - priority  Specify an int or named key (default is 0)
 *  - name    Application name (defaults to growlnotify)
141
 *  - sound   Sound efect ( in OSx defined in preferences -> sound -> effects) * works only in OSX > 10.8x
Rosanny Sihombing's avatar
Rosanny Sihombing committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
 *  - image
 *    - path to an icon sets --iconpath
 *    - path to an image sets --image
 *    - capitalized word sets --appIcon
 *    - filename uses extname as --icon
 *    - otherwise treated as --icon
 *
 * Examples:
 *
 *   growl('New email')
 *   growl('5 new emails', { title: 'Thunderbird' })
 *   growl('5 new emails', { title: 'Thunderbird', sound: 'Purr' })
 *   growl('Email sent', function(){
 *     // ... notification sent
 *   })
 *
 * @param {string} msg
159
160
 * @param {object} options
 * @param {function} fn
Rosanny Sihombing's avatar
Rosanny Sihombing committed
161
162
163
 * @api public
 */

164
165
166
167
168
function growl(msg, options, fn) {
  var image
    , args
    , options = options || {}
    , fn = fn || function(){};
Rosanny Sihombing's avatar
Rosanny Sihombing committed
169
170
171

  if (options.exec) {
    cmd = {
172
173
174
        type: "Custom"
      , pkg: options.exec
      , range: []
Rosanny Sihombing's avatar
Rosanny Sihombing committed
175
176
177
178
    };
  }

  // noop
179
180
  if (!cmd) return fn(new Error('growl not supported on this platform'));
  args = [cmd.pkg];
Rosanny Sihombing's avatar
Rosanny Sihombing committed
181
182

  // image
183
184
185
186
187
188
189
190
191
192
  if (image = options.image) {
    switch(cmd.type) {
      case 'Darwin-Growl':
        var flag, ext = path.extname(image).substr(1)
        flag = flag || ext == 'icns' && 'iconpath'
        flag = flag || /^[A-Z]/.test(image) && 'appIcon'
        flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image'
        flag = flag || ext && (image = ext) && 'icon'
        flag = flag || 'icon'
        args.push('--' + flag, quote(image))
Rosanny Sihombing's avatar
Rosanny Sihombing committed
193
194
        break;
      case 'Darwin-NotificationCenter':
195
        args.push(cmd.icon, quote(image));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
196
197
        break;
      case 'Linux':
198
        args.push(cmd.icon, quote(image));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
199
200
201
202
        // libnotify defaults to sticky, set a hint for transient notifications
        if (!options.sticky) args.push('--hint=int:transient:1');
        break;
      case 'Windows':
203
        args.push(cmd.icon + quote(image));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
204
205
206
207
208
209
210
211
212
        break;
    }
  }

  // sticky
  if (options.sticky) args.push(cmd.sticky);

  // priority
  if (options.priority) {
213
214
215
    var priority = options.priority + '';
    var checkindexOf = cmd.priority.range.indexOf(priority);
    if (~cmd.priority.range.indexOf(priority)) {
Rosanny Sihombing's avatar
Rosanny Sihombing committed
216
217
218
219
      args.push(cmd.priority, options.priority);
    }
  }

220
221
222
  //sound
  if(options.sound && cmd.type === 'Darwin-NotificationCenter'){
    args.push(cmd.sound, options.sound)
Rosanny Sihombing's avatar
Rosanny Sihombing committed
223
224
225
  }

  // name
226
  if (options.name && cmd.type === "Darwin-Growl") {
Rosanny Sihombing's avatar
Rosanny Sihombing committed
227
228
229
    args.push('--name', options.name);
  }

230
  switch(cmd.type) {
Rosanny Sihombing's avatar
Rosanny Sihombing committed
231
232
    case 'Darwin-Growl':
      args.push(cmd.msg);
233
234
      args.push(quote(msg).replace(/\\n/g, '\n'));
      if (options.title) args.push(quote(options.title));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
235
      break;
236
    case 'Darwin-NotificationCenter':
Rosanny Sihombing's avatar
Rosanny Sihombing committed
237
      args.push(cmd.msg);
238
239
      var stringifiedMsg = quote(msg);
      var escapedMsg = stringifiedMsg.replace(/\\n/g, '\n');
Rosanny Sihombing's avatar
Rosanny Sihombing committed
240
241
242
      args.push(escapedMsg);
      if (options.title) {
        args.push(cmd.title);
243
        args.push(quote(options.title));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
244
245
246
      }
      if (options.subtitle) {
        args.push(cmd.subtitle);
247
        args.push(quote(options.subtitle));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
248
249
250
      }
      if (options.url) {
        args.push(cmd.url);
251
        args.push(quote(options.url));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
252
253
254
255
      }
      break;
    case 'Linux-Growl':
      args.push(cmd.msg);
256
257
      args.push(quote(msg).replace(/\\n/g, '\n'));
      if (options.title) args.push(quote(options.title));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
258
      if (cmd.host) {
259
        args.push(cmd.host.cmd, cmd.host.hostname)
Rosanny Sihombing's avatar
Rosanny Sihombing committed
260
261
262
263
      }
      break;
    case 'Linux':
      if (options.title) {
264
        args.push(quote(options.title));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
265
        args.push(cmd.msg);
266
        args.push(quote(msg).replace(/\\n/g, '\n'));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
267
      } else {
268
        args.push(quote(msg).replace(/\\n/g, '\n'));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
269
270
271
      }
      break;
    case 'Windows':
272
273
274
      args.push(quote(msg).replace(/\\n/g, '\n'));
      if (options.title) args.push(cmd.title + quote(options.title));
      if (options.url) args.push(cmd.url + quote(options.url));
Rosanny Sihombing's avatar
Rosanny Sihombing committed
275
      break;
276
277
278
279
280
281
282
283
284
    case 'Custom':
      args[0] = (function(origCommand) {
        var message = options.title
          ? options.title + ': ' + msg
          : msg;
        var command = origCommand.replace(/(^|[^%])%s/g, '$1' + quote(message));
        if (command === origCommand) args.push(quote(message));
        return command;
      })(args[0]);
Rosanny Sihombing's avatar
Rosanny Sihombing committed
285
286
287
      break;
  }

288
289
290
  // execute
  exec(args.join(' '), fn);
};