growl.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // Growl - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
  2. /**
  3. * Module dependencies.
  4. */
  5. var exec = require('child_process').exec
  6. , fs = require('fs')
  7. , path = require('path')
  8. , exists = fs.existsSync || path.existsSync
  9. , os = require('os')
  10. , quote = JSON.stringify
  11. , cmd;
  12. function which(name) {
  13. var paths = process.env.PATH.split(':');
  14. var loc;
  15. for (var i = 0, len = paths.length; i < len; ++i) {
  16. loc = path.join(paths[i], name);
  17. if (exists(loc)) return loc;
  18. }
  19. }
  20. switch(os.type()) {
  21. case 'Darwin':
  22. if (which('terminal-notifier')) {
  23. cmd = {
  24. type: "Darwin-NotificationCenter"
  25. , pkg: "terminal-notifier"
  26. , msg: '-message'
  27. , title: '-title'
  28. , subtitle: '-subtitle'
  29. , icon: '-appIcon'
  30. , sound: '-sound'
  31. , url: '-open'
  32. , priority: {
  33. cmd: '-execute'
  34. , range: []
  35. }
  36. };
  37. } else {
  38. cmd = {
  39. type: "Darwin-Growl"
  40. , pkg: "growlnotify"
  41. , msg: '-m'
  42. , sticky: '--sticky'
  43. , priority: {
  44. cmd: '--priority'
  45. , range: [
  46. -2
  47. , -1
  48. , 0
  49. , 1
  50. , 2
  51. , "Very Low"
  52. , "Moderate"
  53. , "Normal"
  54. , "High"
  55. , "Emergency"
  56. ]
  57. }
  58. };
  59. }
  60. break;
  61. case 'Linux':
  62. if (which('growl')) {
  63. cmd = {
  64. type: "Linux-Growl"
  65. , pkg: "growl"
  66. , msg: '-m'
  67. , title: '-title'
  68. , subtitle: '-subtitle'
  69. , host: {
  70. cmd: '-H'
  71. , hostname: '192.168.33.1'
  72. }
  73. };
  74. } else {
  75. cmd = {
  76. type: "Linux"
  77. , pkg: "notify-send"
  78. , msg: ''
  79. , sticky: '-t 0'
  80. , icon: '-i'
  81. , priority: {
  82. cmd: '-u'
  83. , range: [
  84. "low"
  85. , "normal"
  86. , "critical"
  87. ]
  88. }
  89. };
  90. }
  91. break;
  92. case 'Windows_NT':
  93. cmd = {
  94. type: "Windows"
  95. , pkg: "growlnotify"
  96. , msg: ''
  97. , sticky: '/s:true'
  98. , title: '/t:'
  99. , icon: '/i:'
  100. , url: '/cu:'
  101. , priority: {
  102. cmd: '/p:'
  103. , range: [
  104. -2
  105. , -1
  106. , 0
  107. , 1
  108. , 2
  109. ]
  110. }
  111. };
  112. break;
  113. }
  114. /**
  115. * Expose `growl`.
  116. */
  117. exports = module.exports = growl;
  118. /**
  119. * Node-growl version.
  120. */
  121. exports.version = '1.4.1'
  122. /**
  123. * Send growl notification _msg_ with _options_.
  124. *
  125. * Options:
  126. *
  127. * - title Notification title
  128. * - sticky Make the notification stick (defaults to false)
  129. * - priority Specify an int or named key (default is 0)
  130. * - name Application name (defaults to growlnotify)
  131. * - sound Sound efect ( in OSx defined in preferences -> sound -> effects) * works only in OSX > 10.8x
  132. * - image
  133. * - path to an icon sets --iconpath
  134. * - path to an image sets --image
  135. * - capitalized word sets --appIcon
  136. * - filename uses extname as --icon
  137. * - otherwise treated as --icon
  138. *
  139. * Examples:
  140. *
  141. * growl('New email')
  142. * growl('5 new emails', { title: 'Thunderbird' })
  143. * growl('5 new emails', { title: 'Thunderbird', sound: 'Purr' })
  144. * growl('Email sent', function(){
  145. * // ... notification sent
  146. * })
  147. *
  148. * @param {string} msg
  149. * @param {object} options
  150. * @param {function} fn
  151. * @api public
  152. */
  153. function growl(msg, options, fn) {
  154. var image
  155. , args
  156. , options = options || {}
  157. , fn = fn || function(){};
  158. if (options.exec) {
  159. cmd = {
  160. type: "Custom"
  161. , pkg: options.exec
  162. , range: []
  163. };
  164. }
  165. // noop
  166. if (!cmd) return fn(new Error('growl not supported on this platform'));
  167. args = [cmd.pkg];
  168. // image
  169. if (image = options.image) {
  170. switch(cmd.type) {
  171. case 'Darwin-Growl':
  172. var flag, ext = path.extname(image).substr(1)
  173. flag = flag || ext == 'icns' && 'iconpath'
  174. flag = flag || /^[A-Z]/.test(image) && 'appIcon'
  175. flag = flag || /^png|gif|jpe?g$/.test(ext) && 'image'
  176. flag = flag || ext && (image = ext) && 'icon'
  177. flag = flag || 'icon'
  178. args.push('--' + flag, quote(image))
  179. break;
  180. case 'Darwin-NotificationCenter':
  181. args.push(cmd.icon, quote(image));
  182. break;
  183. case 'Linux':
  184. args.push(cmd.icon, quote(image));
  185. // libnotify defaults to sticky, set a hint for transient notifications
  186. if (!options.sticky) args.push('--hint=int:transient:1');
  187. break;
  188. case 'Windows':
  189. args.push(cmd.icon + quote(image));
  190. break;
  191. }
  192. }
  193. // sticky
  194. if (options.sticky) args.push(cmd.sticky);
  195. // priority
  196. if (options.priority) {
  197. var priority = options.priority + '';
  198. var checkindexOf = cmd.priority.range.indexOf(priority);
  199. if (~cmd.priority.range.indexOf(priority)) {
  200. args.push(cmd.priority, options.priority);
  201. }
  202. }
  203. //sound
  204. if(options.sound && cmd.type === 'Darwin-NotificationCenter'){
  205. args.push(cmd.sound, options.sound)
  206. }
  207. // name
  208. if (options.name && cmd.type === "Darwin-Growl") {
  209. args.push('--name', options.name);
  210. }
  211. switch(cmd.type) {
  212. case 'Darwin-Growl':
  213. args.push(cmd.msg);
  214. args.push(quote(msg).replace(/\\n/g, '\n'));
  215. if (options.title) args.push(quote(options.title));
  216. break;
  217. case 'Darwin-NotificationCenter':
  218. args.push(cmd.msg);
  219. var stringifiedMsg = quote(msg);
  220. var escapedMsg = stringifiedMsg.replace(/\\n/g, '\n');
  221. args.push(escapedMsg);
  222. if (options.title) {
  223. args.push(cmd.title);
  224. args.push(quote(options.title));
  225. }
  226. if (options.subtitle) {
  227. args.push(cmd.subtitle);
  228. args.push(quote(options.subtitle));
  229. }
  230. if (options.url) {
  231. args.push(cmd.url);
  232. args.push(quote(options.url));
  233. }
  234. break;
  235. case 'Linux-Growl':
  236. args.push(cmd.msg);
  237. args.push(quote(msg).replace(/\\n/g, '\n'));
  238. if (options.title) args.push(quote(options.title));
  239. if (cmd.host) {
  240. args.push(cmd.host.cmd, cmd.host.hostname)
  241. }
  242. break;
  243. case 'Linux':
  244. if (options.title) {
  245. args.push(quote(options.title));
  246. args.push(cmd.msg);
  247. args.push(quote(msg).replace(/\\n/g, '\n'));
  248. } else {
  249. args.push(quote(msg).replace(/\\n/g, '\n'));
  250. }
  251. break;
  252. case 'Windows':
  253. args.push(quote(msg).replace(/\\n/g, '\n'));
  254. if (options.title) args.push(cmd.title + quote(options.title));
  255. if (options.url) args.push(cmd.url + quote(options.url));
  256. break;
  257. case 'Custom':
  258. args[0] = (function(origCommand) {
  259. var message = options.title
  260. ? options.title + ': ' + msg
  261. : msg;
  262. var command = origCommand.replace(/(^|[^%])%s/g, '$1' + quote(message));
  263. if (command === origCommand) args.push(quote(message));
  264. return command;
  265. })(args[0]);
  266. break;
  267. }
  268. // execute
  269. exec(args.join(' '), fn);
  270. };