node-notifier
Advanced tools
Comparing version 3.4.1 to 4.0.0
Changelog | ||
=== | ||
### `v4.0.0` | ||
Major changes and breaking API. | ||
1. require('node-notifier') now returns an instance with fallbackable notifications. | ||
```js | ||
var notifier = require('node-notifier'); | ||
notifier.notify(); | ||
``` | ||
2. Introduced a `wait` property (default `false`), to get user input for | ||
Notification Center, Windows Toaster, Windows Balloons and Growl. Sadly not | ||
for notify-send. | ||
```js | ||
var notifier = require('node-notifier'); | ||
notifier.notify({ wait: true }, function (err, response) { | ||
// response is response after user have interacted | ||
// with the notification or the notification has timed out. | ||
}); | ||
``` | ||
3. All notification instances are now event emitters, emitting events | ||
`click` or `timeout`. This is only applicable if `{ wait: true }`. | ||
```js | ||
var notifier = require('node-notifier'); | ||
notifier.on('click', function (notificationObject, options) { | ||
// options.someArbitraryData === 'foo' | ||
}); | ||
notifier.notify({ wait: true, someArbitraryData: 'foo' }); | ||
``` | ||
4. WindowsToaster and NotificationCenter now can have sounds by doing `{ sound: true }`. | ||
Default NotificationCenter sound is Bottle. Can still use define sound on | ||
Mac: | ||
```js | ||
var notifier = require('node-notifier'); | ||
notifier.notify({ sound: true }); | ||
// For mac (same as sound: true on Windows 8) | ||
notifier.notify({ sound: 'Morse' }); | ||
``` | ||
### `v3.4.0` | ||
@@ -5,0 +41,0 @@ 1. Adds Growl as priority over Balloons |
@@ -1,16 +0,12 @@ | ||
var Notify = require('../'); | ||
var notifier = new Notify(); | ||
var notifier = require('../'); | ||
var nc = new notifier.NotificationCenter(); | ||
notifier.notify({ | ||
"title": "Phil Coulson", | ||
"subtitle": "Agent of S.H.I.E.L.D.", | ||
"message": "If I come out, will you shoot me? 'Cause then I won't come out.", | ||
"sound": "Funk", // case sensitive | ||
"appIcon": __dirname + "/coulson.jpg", | ||
"contentImage": __dirname + "/coulson.jpg", | ||
"open": "file://" + __dirname + "/coulson.jpg" | ||
nc.notify({ | ||
'title': 'Phil Coulson', | ||
'subtitle': 'Agent of S.H.I.E.L.D.', | ||
'message': 'If I come out, will you shoot me? \'Cause then I won\'t come out.', | ||
'sound': 'Funk', // case sensitive | ||
'appIcon': __dirname + '/coulson.jpg', | ||
'contentImage': __dirname + '/coulson.jpg', | ||
'open': 'file://' + __dirname + '/coulson.jpg' | ||
}); | ||
setTimeout(function() { | ||
console.log("Done"); | ||
}, 5000); |
@@ -1,18 +0,14 @@ | ||
var Notify = require("../index"); | ||
var notifier = new Notify(); | ||
var notifier = require('../index'); | ||
notifier.notify({ | ||
"message": "Hello" | ||
} | ||
, function (err, data) { | ||
if (err) { | ||
return console.error("Error: ", err); | ||
} | ||
console.log(data); | ||
}); | ||
setTimeout(function() { | ||
console.log("Done"); | ||
}, 5000); | ||
notifier | ||
.notify({ | ||
'message': 'Hello', | ||
'wait': true | ||
}, function (err, data) { | ||
// Will also wait until notification is closed. | ||
console.log('Waited'); | ||
console.log(err, data); | ||
}) | ||
.on('click', function () { | ||
console.log(arguments); | ||
}); |
40
index.js
var os = require('os'); | ||
var send = require('./lib/notifiers/notify-send'); | ||
var mac = require('./lib/notifiers/terminal-notifier'); | ||
var win8 = require('./lib/notifiers/toaster'); | ||
var growl = require('./lib/notifiers/growl'); | ||
var balloon = require('./lib/notifiers/balloon'); | ||
var utils = require('./lib/utils'); | ||
// All notifiers | ||
var NotifySend = require('./lib/notifiers/notify-send'); | ||
var NotificationCenter = require('./lib/notifiers/terminal-notifier'); | ||
var WindowsToaster = require('./lib/notifiers/toaster'); | ||
var Growl = require('./lib/notifiers/growl'); | ||
var WindowsBalloon = require('./lib/notifiers/balloon'); | ||
var options = { withFallback: true }; | ||
switch(os.type()) { | ||
case 'Linux': | ||
module.exports = send; | ||
module.exports = new NotifySend(options); | ||
module.exports.Notification = NotifySend; | ||
break; | ||
case 'Darwin': | ||
module.exports = mac; | ||
module.exports = new NotificationCenter(options); | ||
module.exports.Notification = NotificationCenter; | ||
break; | ||
case 'Windows_NT': | ||
if (utils.isLessThanWin8()) { | ||
module.exports = balloon; | ||
module.exports = new WindowsBalloon(options); | ||
module.exports.Notification = WindowsBalloon; | ||
} else { | ||
module.exports = win8; | ||
module.exports = new WindowsToaster(options); | ||
module.exports.Notification = WindowsToaster; | ||
} | ||
break; | ||
default: | ||
module.exports = growl; | ||
module.exports = new Growl(options); | ||
module.exports.Notification = Growl; | ||
} | ||
module.exports.NotifySend = send; | ||
module.exports.NotificationCenter = mac; | ||
module.exports.WindowsToaster = win8; | ||
module.exports.WindowsBalloon = balloon; | ||
module.exports.Growl = growl; | ||
// Expose notifiers to give full control. | ||
module.exports.NotifySend = NotifySend; | ||
module.exports.NotificationCenter = NotificationCenter; | ||
module.exports.WindowsToaster = WindowsToaster; | ||
module.exports.WindowsBalloon = WindowsBalloon; | ||
module.exports.Growl = Growl; |
@@ -1,2 +0,2 @@ | ||
var net = require('net') | ||
var net = require('net'); | ||
@@ -12,2 +12,3 @@ var hasGrowl = false; | ||
socket.on('connect', function() { | ||
socket.end(); | ||
cb(true); | ||
@@ -17,4 +18,5 @@ }); | ||
socket.on('error', function() { | ||
socket.end(); | ||
cb(false); | ||
}); | ||
}; | ||
}; |
@@ -18,2 +18,7 @@ /** | ||
// Kill codes: | ||
2 = Timeout | ||
3 = Clicked | ||
*/ | ||
@@ -27,47 +32,47 @@ var path = require('path'), | ||
var EventEmitter = require('events').EventEmitter; | ||
var util = require('util'); | ||
var hasGrowl = void 0; | ||
var Notifier = function (options) { | ||
if (!(this instanceof Notifier)) { | ||
return new Notifier(); | ||
module.exports = WindowsBalloon; | ||
function WindowsBalloon (options) { | ||
options = options || {}; | ||
if (!(this instanceof WindowsBalloon)) { | ||
return new WindowsBalloon(options); | ||
} | ||
this.options = options; | ||
}; | ||
var allowedArguments = ["t", "d", "p", "m", "i", "e", "q", "w", "xp"]; | ||
var doNotification = function (options, callback) { | ||
options = utils.mapToNotifu(options); | ||
options.p = options.p || 'Node Notification:'; | ||
if (!options.m) { | ||
callback(new Error('Message is required.')); | ||
return this; | ||
} | ||
var argsList = utils.constructArgumentList(options, { | ||
wrapper: '', | ||
noEscape: true, | ||
allowedArguments: allowedArguments | ||
}); | ||
utils.windowsCommand(notifier, argsList, callback); | ||
EventEmitter.call(this); | ||
} | ||
util.inherits(WindowsBalloon, EventEmitter); | ||
Notifier.prototype.notify = function (options, callback) { | ||
WindowsBalloon.prototype.notify = function (options, callback) { | ||
var fallback, notifierOptions = this.options; | ||
callback = callback || function (err, data) {}; | ||
callback = callback || function () {}; | ||
var actionJackedCallback = utils.actionJackerDecorator(this, options, callback, function (data) { | ||
var cleaned = data.toLowerCase().trim(); | ||
if (cleaned === 'activate') { | ||
return 'click'; | ||
} | ||
if (cleaned === 'timeout') { | ||
return 'timeout'; | ||
} | ||
return false; | ||
}); | ||
if (utils.isWin8()) { | ||
fallback = new Toaster(notifierOptions); | ||
if (!!this.options.withFallback && utils.isWin8()) { | ||
fallback = fallback || new Toaster(notifierOptions); | ||
return fallback.notify(options, callback); | ||
} | ||
if (!utils.isLessThanWin8() || hasGrowl === true) { | ||
fallback = new Growl(notifierOptions); | ||
if (!!this.options.withFallback && (!utils.isLessThanWin8() || hasGrowl === true)) { | ||
fallback = fallback || new Growl(notifierOptions); | ||
return fallback.notify(options, callback); | ||
} | ||
if (hasGrowl === false) { | ||
doNotification(options, callback); | ||
if (!this.options.withFallback || hasGrowl === false) { | ||
doNotification(options, notifierOptions, actionJackedCallback); | ||
return this; | ||
@@ -80,7 +85,7 @@ } | ||
if (hasGrowl) { | ||
fallback = new Growl(notifierOptions); | ||
fallback = fallback || new Growl(notifierOptions); | ||
return fallback.notify(options, callback); | ||
} | ||
doNotification(options, callback); | ||
doNotification(options, notifierOptions, actionJackedCallback); | ||
}); | ||
@@ -91,2 +96,40 @@ | ||
module.exports = Notifier; | ||
var allowedArguments = ["t", "d", "p", "m", "i", "e", "q", "w", "xp"]; | ||
function doNotification (options, notifierOptions, callback) { | ||
options = options || {}; | ||
options = utils.mapToNotifu(options); | ||
options.p = options.p || 'Node Notification:'; | ||
var localNotifier = notifierOptions.customPath || notifier; | ||
if (!options.m) { | ||
callback(new Error('Message is required.')); | ||
return this; | ||
} | ||
var argsList = utils.constructArgumentList(options, { | ||
wrapper: '', | ||
noEscape: true, | ||
allowedArguments: allowedArguments | ||
}); | ||
if (!!options.wait) { | ||
return utils.fileCommand(localNotifier, argsList, function (error, data) { | ||
var action = fromErrorCodeToAction(error.code); | ||
if (action === 'error') return callback(error, data); | ||
return callback(null, action); | ||
}); | ||
} | ||
utils.immediateFileCommand(localNotifier, argsList, callback); | ||
} | ||
function fromErrorCodeToAction (errorCode) { | ||
if (errorCode === 2) { | ||
return 'timeout'; | ||
} | ||
if (errorCode === 3) { | ||
return 'activate'; | ||
} | ||
return 'error'; | ||
} |
/** | ||
* Wrapper for the growly module | ||
*/ | ||
var os = require('os'), | ||
utils = require('../utils'), | ||
var utils = require('../utils'), | ||
checkGrowl = require('../checkGrowl'); | ||
growly = require('growly'); | ||
var Notifier = function (options) { | ||
var EventEmitter = require('events').EventEmitter; | ||
var util = require('util'); | ||
var errorMessageNotFound = 'Couldn\'t connect to growl (might be used as a fallback). Make sure it is running'; | ||
module.exports = Growl; | ||
var hasGrowl = void 0; | ||
function Growl (options) { | ||
options = options || {}; | ||
if (!(this instanceof Notifier)) { | ||
return new Notifier(options); | ||
if (!(this instanceof Growl)) { | ||
return new Growl(options); | ||
} | ||
this.name = options.name || 'Node'; | ||
this.isRegistered = false; | ||
}; | ||
var doNotification = function (options, callback) { | ||
options.title = options.title || 'Node Notification:'; | ||
growly.notify(options.message, options, callback); | ||
}; | ||
growly.appname = options.name || 'Node'; | ||
Notifier.prototype.notify = function (options, callback) { | ||
var that = this; | ||
EventEmitter.call(this); | ||
} | ||
util.inherits(Growl, EventEmitter); | ||
Growl.prototype.notify = function (options, callback) { | ||
options = options || {}; | ||
callback = (callback || function () {}).bind(this); | ||
callback = utils.actionJackerDecorator(this, options, callback, function (data) { | ||
var cleaned = data.toLowerCase().trim(); | ||
if (cleaned === 'click') { | ||
return 'click'; | ||
} | ||
if (cleaned === 'timedout') { | ||
return 'timeout'; | ||
} | ||
return false; | ||
}); | ||
@@ -34,23 +50,18 @@ options = utils.mapToGrowl(options); | ||
if (this.isRegistered) { | ||
return doNotification(options, callback); | ||
options.title = options.title || 'Node Notification:'; | ||
if (hasGrowl || !!options.wait) { | ||
var localCallback = !!options.wait ? callback : function () {}; | ||
growly.notify(options.message, options, localCallback); | ||
if (!options.wait) callback(); | ||
return this; | ||
} | ||
growly.register(this.name, function(err) { | ||
if (err) { | ||
if (!that.hasError) { | ||
that.hasError = true; | ||
callback(err); | ||
} | ||
return false; | ||
} | ||
that.isRegistered = true; | ||
that.hasError = false; | ||
doNotification(options, callback); | ||
checkGrowl(function (didHaveGrowl) { | ||
hasGrowl = didHaveGrowl; | ||
if (!didHaveGrowl) return callback(new Error(errorMessageNotFound)); | ||
growly.notify(options.message, options); | ||
callback(); | ||
}); | ||
return this; | ||
}; | ||
module.exports = Notifier; |
/** | ||
* Node.js wrapper for "notify-send". | ||
*/ | ||
var path = require('path'), | ||
os = require('os'), | ||
var os = require('os'), | ||
which = require('which'), | ||
utils = require('../utils'); | ||
var notifier = 'notify-send'; | ||
var EventEmitter = require('events').EventEmitter; | ||
var util = require('util'); | ||
var Notifier = function () { | ||
if (!(this instanceof Notifier)) { | ||
return new Notifier(); | ||
} | ||
this.isNotifyChecked = false; | ||
this.hasNotifier = false; | ||
}; | ||
var notifier = 'notify-send', hasNotifier = void 0; | ||
var allowedArguments = [ | ||
"urgency", | ||
"expire-time", | ||
"icon", | ||
"category", | ||
"hint" | ||
]; | ||
module.exports = NotifySend; | ||
var doNotification = function (options, callback) { | ||
var initial, argsList; | ||
function NotifySend (options) { | ||
options = options || {}; | ||
if (!(this instanceof NotifySend)) { | ||
return new NotifySend(options); | ||
} | ||
options = utils.mapToNotifySend(options); | ||
options.title = options.title || 'Node Notification:'; | ||
this.options = options; | ||
initial = [options.title, options.message]; | ||
delete options.title; | ||
delete options.message; | ||
EventEmitter.call(this); | ||
} | ||
util.inherits(NotifySend, EventEmitter); | ||
argsList = utils.constructArgumentList(options, { | ||
initial: initial, | ||
keyExtra: '-', | ||
allowedArguments: allowedArguments | ||
}); | ||
NotifySend.prototype.notify = function (options, callback) { | ||
options = options || {}; | ||
utils.command(notifier, argsList, callback); | ||
return this; | ||
}; | ||
Notifier.prototype.notify = function (options, callback) { | ||
var that = this; | ||
callback = callback || function () {}; | ||
options = options || {}; | ||
if (!options.message) { | ||
@@ -64,9 +40,9 @@ callback(new Error('Message is required.')); | ||
if (this.isNotifyChecked && this.hasNotifier) { | ||
doNotification(options, callback); | ||
if (hasNotifier === false) { | ||
callback(new Error('notify-send must be installed on the system.')); | ||
return this; | ||
} | ||
if (this.isNotifyChecked && !this.hasNotifier) { | ||
callback(new Error('notify-send must be installed on the system.')); | ||
if (hasNotifier || !!this.options.suppressOsdCheck) { | ||
doNotification(options, callback); | ||
return this; | ||
@@ -76,4 +52,3 @@ } | ||
which(notifier, function (err) { | ||
that.isNotifyChecked = true; | ||
that.hasNotifier = !err; | ||
hasNotifier = !err; | ||
@@ -84,3 +59,3 @@ if (err) { | ||
return doNotification(options, callback); | ||
doNotification(options, callback); | ||
}); | ||
@@ -90,2 +65,27 @@ return this; | ||
module.exports = Notifier; | ||
var allowedArguments = [ | ||
"urgency", | ||
"expire-time", | ||
"icon", | ||
"category", | ||
"hint" | ||
]; | ||
function doNotification (options, callback) { | ||
var initial, argsList; | ||
options = utils.mapToNotifySend(options); | ||
options.title = options.title || 'Node Notification:'; | ||
initial = [options.title, options.message]; | ||
delete options.title; | ||
delete options.message; | ||
argsList = utils.constructArgumentList(options, { | ||
initial: initial, | ||
keyExtra: '-', | ||
allowedArguments: allowedArguments | ||
}); | ||
utils.command(notifier, argsList, callback); | ||
} |
/** | ||
* A Node.js wrapper for terminal-notify. | ||
* | ||
* Requirements: | ||
* - Mac OS X 10.8 | ||
* A Node.js wrapper for terminal-notify (with fallback). | ||
*/ | ||
var path = require('path'), | ||
notifier = path.resolve(__dirname, '../../vendor/terminal-notifier.app/Contents/MacOS/terminal-notifier'), | ||
notifier = path.join(__dirname, '../../vendor/terminal-notifier.app/Contents/MacOS/terminal-notifier'), | ||
utils = require('../utils'), | ||
Growl = require('./growl'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var util = require('util'); | ||
var fallbackNotifier = null; | ||
var Notifier = function (options) { | ||
if (!(this instanceof Notifier)) { | ||
return new Notifier(); | ||
var errorMessageOsX = 'You need Mac OS X 10.8 or above to use NotificationCenter,' + | ||
' or use Growl fallback with constructor option {withFallback: true}.'; | ||
module.exports = NotificationCenter; | ||
function NotificationCenter (options) { | ||
options = options || {}; | ||
if (!(this instanceof NotificationCenter)) { | ||
return new NotificationCenter(options); | ||
} | ||
this.options = options; | ||
this.isOSX = false; | ||
fallbackNotifier = null; | ||
}; | ||
Notifier.prototype.notify = function (options, callback) { | ||
EventEmitter.call(this); | ||
} | ||
util.inherits(NotificationCenter, EventEmitter); | ||
NotificationCenter.prototype.notify = function (options, callback) { | ||
var fallbackNotifier = null; | ||
options = options || {}; | ||
callback = callback || function () {}; | ||
var actionJackedCallback = utils.actionJackerDecorator(this, options, callback, function (data) { | ||
var cleaned = data.toLowerCase().trim(); | ||
if (cleaned === 'activate') { | ||
return 'click'; | ||
} | ||
if (cleaned === 'timeout') { | ||
return 'timeout'; | ||
} | ||
return false; | ||
}); | ||
options = utils.mapToMac(options); | ||
if (!!options.wait) { | ||
options.wait = 'YES'; | ||
} | ||
if (!options.message && !options.group && !options.list && !options.remove) { | ||
callback(new Error('Message, group, remove or list property is required.')); | ||
return this; | ||
} | ||
var argsList = utils.constructArgumentList(options); | ||
callback = callback || function (err, data) {}; | ||
if(this.isOSX) { | ||
utils.command(notifier, argsList, callback); | ||
if(utils.isMountainLion()) { | ||
utils.command(this.options.customPath || notifier, argsList, actionJackedCallback); | ||
return this; | ||
} | ||
if(fallbackNotifier) { | ||
fallbackNotifier.notify(options, callback); | ||
return this; | ||
if (fallbackNotifier || !!this.options.withFallback) { | ||
fallbackNotifier = fallbackNotifier || new Growl(this.options); | ||
return fallbackNotifier.notify(options, callback); | ||
} | ||
var self = this; | ||
utils.isMacOSX(function (error, errorMsg) { | ||
if (error && error !== 'old') { | ||
return callback(new Error(errorMsg)); | ||
} | ||
if (error && error === 'old') { | ||
fallbackNotifier = new Growl(self.options); | ||
return fallbackNotifier.notify(options, callback); | ||
} | ||
utils.command(notifier, argsList, callback); | ||
self.isOSX = true; | ||
}); | ||
callback(new Error(errorMessageOsX)); | ||
return this; | ||
}; | ||
module.exports = Notifier; |
@@ -9,14 +9,37 @@ /** | ||
var Notifier = function (options) { | ||
if (!(this instanceof Notifier)) { | ||
return new Notifier(); | ||
var EventEmitter = require('events').EventEmitter; | ||
var util = require('util'); | ||
var fallback = void 0; | ||
module.exports = WindowsToaster; | ||
function WindowsToaster (options) { | ||
options = options || {}; | ||
if (!(this instanceof WindowsToaster)) { | ||
return new WindowsToaster(options); | ||
} | ||
this.options = options; | ||
}; | ||
Notifier.prototype.notify = function (options, callback) { | ||
var fallback; | ||
EventEmitter.call(this); | ||
} | ||
util.inherits(WindowsToaster, EventEmitter); | ||
WindowsToaster.prototype.notify = function (options, callback) { | ||
options = options || {}; | ||
callback = callback || function () {}; | ||
var actionJackedCallback = utils.actionJackerDecorator(this, options, callback, function (data) { | ||
var cleaned = data.toLowerCase().trim(); | ||
if (cleaned === 'activated') { | ||
return 'click'; | ||
} | ||
if (cleaned === 'timeout') { | ||
return 'timeout'; | ||
} | ||
return false; | ||
}); | ||
options.title = options.title || 'Node Notification:'; | ||
callback = callback || function (err, data) {}; | ||
@@ -28,4 +51,4 @@ if (!options.message) { | ||
if (!utils.isWin8()) { | ||
fallback = new Balloon(this.options); | ||
if (!utils.isWin8() && !!this.options.withFallback) { | ||
fallback = fallback || new Balloon(this.options); | ||
return fallback.notify(options, callback); | ||
@@ -39,6 +62,4 @@ } | ||
}); | ||
utils.windowsCommand(notifier, argsList, callback); | ||
utils.fileCommand(this.options.customPath || notifier, argsList, actionJackedCallback); | ||
return this; | ||
}; | ||
module.exports = Notifier; |
137
lib/utils.js
var cp = require('child_process'), | ||
os = require('os'), | ||
fs = require('fs'), | ||
osVersionError = 'Incorrect OS. node-notify requires Mac OS 10.8 or higher', | ||
shellwords = require('shellwords'), | ||
semver = require('semver'); | ||
semver = require('semver'), | ||
clone = require('clone'); | ||
var escapeQuotes = function (str) { | ||
@@ -41,3 +42,4 @@ if (typeof str === 'string') { | ||
module.exports.command = function (notifier, options, cb) { | ||
return cp.exec(shellwords.escape(notifier) + ' ' + options.join(' '), function (error, stdout, stderr) { | ||
var notifier = shellwords.escape(notifier); | ||
return cp.exec(notifier + ' ' + options.join(' '), function (error, stdout, stderr) { | ||
if (error) return cb(error); | ||
@@ -48,5 +50,5 @@ cb(stderr, stdout); | ||
module.exports.windowsCommand = function (notifier, options, cb) { | ||
module.exports.fileCommand = function (notifier, options, cb) { | ||
return cp.execFile(notifier, options, function (error, stdout, stderr) { | ||
if (error && !!stderr) return cb(error); | ||
if (error) return cb(error, stdout); | ||
cb(stderr, stdout); | ||
@@ -56,2 +58,17 @@ }); | ||
module.exports.immediateFileCommand = function (notifier, options, cb) { | ||
notifierExists(notifier, function (exists) { | ||
if (!exists) return cb(new Error('Notifier (' + notifier + ') not found on system.')); | ||
cp.execFile(notifier, options); | ||
cb(); | ||
}); | ||
}; | ||
function notifierExists (notifier, cb) { | ||
return fs.stat(notifier, function (err, stat) { | ||
if (err) return cb(false); | ||
cb(stat.isFile()); | ||
}); | ||
} | ||
var mapAppIcon = function (options) { | ||
@@ -124,5 +141,26 @@ if (options.appIcon) { | ||
if (options.sound === true) { | ||
options.sound = 'Bottle'; | ||
} | ||
if (options.sound === false) { | ||
delete options.sound; | ||
} | ||
return options; | ||
}; | ||
module.exports.actionJackerDecorator = function (emitter, options, fn, mapper) { | ||
options = clone(options); | ||
fn = fn || function (err, data) {}; | ||
return function (err, data) { | ||
fn.apply(emitter, [err, data]); | ||
if (err || !mapper || !data) return; | ||
var key = mapper(data); | ||
if (!key) return; | ||
emitter.emit(key, emitter, options); | ||
}; | ||
}; | ||
module.exports.constructArgumentList = function (options, extra) { | ||
@@ -153,30 +191,2 @@ var args = []; | ||
module.exports.getOSXVersion = function (cb) { | ||
return cp.exec("sw_vers -productVersion", cb); | ||
} | ||
module.exports.isMacOSX = function (cb) { | ||
if (os.type().toLowerCase() != 'darwin') { | ||
return cb(true, "You can't use the terminal-notifier reporter unless you are on a Mac."); | ||
} | ||
return module.exports.getOSXVersion(function (error, stdout, stderr) { | ||
if (error) { | ||
return cb(true, error, stderr); | ||
} | ||
var version = garanteeSemverFormat(stdout.toString().trim()); | ||
if (semver.satisfies(version, '>=10.8')) { | ||
return cb(false); | ||
} | ||
return cb('old', osVersionError); | ||
}); | ||
}; | ||
function garanteeSemverFormat (version) { | ||
if (version.split('.').length === 2) { | ||
version += '.0'; | ||
} | ||
return version; | ||
}; | ||
module.exports.mapToWin8 = function (options){ | ||
@@ -202,2 +212,24 @@ | ||
if (options.quiet || options.silent) { | ||
options.q = options.quiet || options.silent; | ||
delete options.quiet; | ||
delete options.silent; | ||
} | ||
if (options.q !== false) { | ||
options.q = true; | ||
} else { | ||
delete options.q; | ||
} | ||
if (options.sound) { | ||
delete options.q; | ||
delete options.sound; | ||
} | ||
if (options.wait) { | ||
options.w = options.wait; | ||
delete options.wait; | ||
} | ||
return options; | ||
@@ -226,15 +258,46 @@ }; | ||
if (options.time) { | ||
options.t = options.time; | ||
options.d = options.time; | ||
delete options.time; | ||
} | ||
if (options.q !== false) { | ||
options.q = true; | ||
} else { | ||
delete options.q; | ||
} | ||
if (options.sound) { | ||
delete options.q; | ||
delete options.sound; | ||
} | ||
if (options.t) { | ||
options.d = options.t; | ||
delete options.t; | ||
} | ||
return options; | ||
}; | ||
module.exports.isMac = function() { | ||
return os.type() === 'Darwin'; | ||
}; | ||
module.exports.isMountainLion = function() { | ||
return os.type() === 'Darwin' && semver.satisfies(garanteeSemverFormat(os.release()), '>=12.0.0'); | ||
}; | ||
module.exports.isWin8 = function() { | ||
return os.type() === 'Windows_NT' && semver.satisfies(os.release(), '>=6.2.9200'); | ||
return os.type() === 'Windows_NT' && semver.satisfies(garanteeSemverFormat(os.release()), '>=6.2.9200'); | ||
}; | ||
module.exports.isLessThanWin8 = function() { | ||
return os.type() === 'Windows_NT' && semver.satisfies(os.release(), '<6.2.9200'); | ||
}; | ||
return os.type() === 'Windows_NT' && semver.satisfies(garanteeSemverFormat(os.release()), '<6.2.9200'); | ||
}; | ||
function garanteeSemverFormat (version) { | ||
if (version.split('.').length === 2) { | ||
version += '.0'; | ||
} | ||
return version; | ||
} |
{ | ||
"name": "node-notifier", | ||
"version": "3.4.1", | ||
"version": "4.0.0", | ||
"description": "A Node.js module for sending notifications on native Mac, Windows (post and pre 8) and Linux (or Growl as fallback)", | ||
@@ -31,7 +31,8 @@ "main": "index.js", | ||
"dependencies": { | ||
"semver": "^3.0.1", | ||
"semver": "^4.0.3", | ||
"shellwords": "^0.1.0", | ||
"which": "^1.0.5", | ||
"growly": "^1.1.1" | ||
"growly": "^1.1.1", | ||
"clone": "^0.1.18" | ||
} | ||
} |
272
README.md
# node-notifier [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][depstat-image]][depstat-url] | ||
A node module for sending notification using node. Uses terminal-notifier on mac, | ||
notify-send for Linux, toaster for Windows 8, balloons for Windows pre 8 or Growl for others. | ||
A Node.js module for sending cross platform system notification. Using | ||
Notification Center for Mac, notify-osd for Linux, Toasters for | ||
Windows 8, or lovely taskbar Balloons for earlier Windows versions. If none of | ||
these requirements are met, be it older version of Windows or OS X, | ||
Growl is used. | ||
@@ -10,46 +13,59 @@ ![Mac Screenshot](https://github.com/mikaelbr/node-notifier/blob/master/example/mac.png) | ||
## Requirements | ||
- Mac OS X >= 10.8. | ||
- Linux with the notify-osd/notify-send module | ||
- Windows. Using "toasters" for Win8, Balloons for pre Win8 (if Growl isn't found). | ||
- Or if no of the above requirements are met, Growl is used. | ||
## Easy Usage | ||
If using Linux, `notify-osd` must be installed on your system. | ||
For Mac, [terminal-notifier](https://github.com/alloy/terminal-notifier), comes | ||
bundled in the module. So on Mac, not additional installations is necessary. | ||
Show native notifications on Mac, Windows, Linux or using Growl! | ||
This also goes for native Windows (version >=8) as well, where | ||
[toaster.exe](https://github.com/nels-o/toaster) is bundled. Note, for native | ||
Windows notifications [a toast must have a shortcut installed](http://msdn.microsoft.com/en-in/library/windows/apps/hh779727.aspx) | ||
(though not necessarily pinned) to the Start screen or in the Apps view, but | ||
this happens automatically. | ||
```javascript | ||
var notifier = require('node-notifier'); | ||
notifier.notify({ | ||
'title': 'My notification' | ||
'message': 'Hello, there!' | ||
}); | ||
``` | ||
Growl takes precidence over Windows balloons. So if Growl is active, | ||
growl is used, if not, balloons are shown. | ||
## Requirements | ||
- **Mac OS X**: >= 10.8 or Growl if earlier. | ||
- **Linux**: notify-osd installed (Ubuntu should have this by default) | ||
- **Windows**: >= 8, task bar balloon if earlier or Growl if that is installed. | ||
- **General Fallback**: Growl | ||
If no of the other requirements are met, node-notifier will use Growl. | ||
You have to have Growl installed on your system. See | ||
[Growl for Windows](http://www.growlforwindows.com/gfw/) or | ||
[Growl for Mac](http://growl.info/). | ||
Growl takes precedence over Windows balloons. | ||
See [documentation and flow chart for reporters](./DECISION_FLOW.md) | ||
See [documentation and flow chart for reporter choice](./DECISION_FLOW.md) | ||
## Install | ||
``` | ||
$ npm install node-notifier | ||
$ npm install --save node-notifier | ||
``` | ||
## Standard Usage | ||
## Cross-Platform Advanced Usage | ||
Standard usage, with cross-platform fallbacks as defined in the | ||
[reporter flow chart](./DECISION_FLOW.md). All of the options | ||
below will work in a way or another on all platforms. | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var notifier = new Notification(); | ||
var notifier = require('node-notifier'); | ||
notifier.notify({ | ||
title: 'My awesome title', | ||
message: 'Hello from node, Mr. User!' | ||
message: 'Hello from node, Mr. User!', | ||
icon: path.join(__dirname, 'coulson.jpg'), // absolute path (not balloons) | ||
sound: true, // Only Notification Center or Windows Toasters | ||
wait: true // wait with callback until user action is taken on notification | ||
}, function (err, response) { | ||
// response is response from notification | ||
}); | ||
notifier.on('click', function (notifierObject, options) { | ||
// Happens if `wait: true` and user clicks notification | ||
}); | ||
notifier.on('timeout', function (notifierObject, options) { | ||
// Happens if `wait: true` and notification closes | ||
}); | ||
``` | ||
`Notification` also has specifications for all types of notifications, to be used | ||
manually. | ||
You can also specify what reporter you want to use if you | ||
want to customize it or have more specific options per system. | ||
See documentation for each reporter below. | ||
@@ -60,17 +76,20 @@ Example: | ||
new nn.NotificationCenter().notify(); | ||
new nn.NotifySend().notify(); | ||
new nn.WindowsToaster().notify(options); | ||
new nn.WindowsBalloon().notify(options); | ||
new nn.Growl().notify(options); | ||
new nn.NotificationCenter(options).notify(); | ||
new nn.NotifySend(options).notify(); | ||
new nn.WindowsToaster(options).notify(options); | ||
new nn.WindowsBalloon(options).notify(options); | ||
new nn.Growl(options).notify(options); | ||
``` | ||
### Mapping between notifiers | ||
Common options between the modules (i.e. `icon`) is mapped. This means, | ||
if you are using a Mac and someone on your project is using Linux, you | ||
can both see icons. | ||
## Documentation | ||
* [Notification Center documentation](#usage-notificationcenter) | ||
* [Windows Toaster documentation](#usage-windowstoaster) | ||
* [Windows Balloon documentation](#usage-windowsballoon) | ||
* [Growl documentation](#usage-growl) | ||
* [Notify-send documentation](#usage-notifysend) | ||
## Usage NotificationCenter | ||
### Usage NotificationCenter | ||
Same usage and parameter setup as [terminal-notifier](https://github.com/alloy/terminal-notifier). | ||
@@ -80,41 +99,33 @@ | ||
earlier versions, Growl will be the fallback. If Growl isn't installed, an error | ||
will be thrown. | ||
will be returned in the callback. | ||
--- | ||
### Note: Output parsing from Notification Center is deprecated as of `3.0.0`. | ||
#### Example | ||
**Parsing of output given by terminal-notifier is removed as of node-notifier `3.0.0`.** | ||
You can still use both `remove` and `list` but the output given will not be parsed into a object. | ||
It is a wrapper around [terminal-notifier](https://github.com/alloy/terminal-notifier), and you can | ||
do all terminal-notifier can do through properties to the `notify` method. E.g. | ||
if `terminal-notifier` say `-message`, you can do `{message: 'Foo'}`, or | ||
if `terminal-notifier` say `-list ALL` you can do `{list: 'ALL'}`. Notification | ||
is the primary focus for this module, so listing and activating do work, | ||
but isn't documented. | ||
See removed documentation for pre version `3.0.0` in [Deprecated documentation](DEPRECATED.md) | ||
### All notification options with their defaults: | ||
--- | ||
### Example | ||
Where [terminal-notifier](https://github.com/alloy/terminal-notifier) say to use the ```-message``` option, you can do this in node-notifier | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var NotificationCenter = require('node-notifier').NotificationCenter; | ||
var notifier = new Notification(); | ||
notifier.notify({ | ||
message: 'Hello World' | ||
}); | ||
``` | ||
You can specify the second argument as a callback for getting ```error``` and ```response```. | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var notifier = new Notification({ | ||
// Options passed to Growl if fallback | ||
withFallback: false, // use Growl if <= 10.8? | ||
customPath: void 0 // Relative path if you want to use your fork of terminal-notifier | ||
}); | ||
notifier.notify({ | ||
title: 'My application', | ||
message: 'New notification' | ||
'title': void 0, | ||
'subtitle': void 0, | ||
'message': void 0, | ||
'sound': false, // Case Sensitive string of sound file (see below) | ||
'icon': 'Terminal Icon', // Set icon? (Absolute path to image) | ||
'contentImage': void 0, // Attach image? (Absolute path) | ||
'open': void 0, // URL to open on click | ||
'wait': false // if wait for notification to end | ||
}, function(error, response) { | ||
@@ -125,70 +136,86 @@ console.log(response); | ||
As of version `3.0.0`, you can also specify image used as icon or content image. **For Mac OS notifications, requires 10.9.** | ||
**For Mac OS notifications, icon and contentImage requires OS X 10.9.** | ||
Sound can be one of these: `Basso`, `Blow`, `Bottle`, `Frog`, `Funk`, `Glass`, | ||
`Hero`, `Morse`, `Ping`, `Pop`, `Purr`, `Sosumi`, `Submarine`, `Tink`. If | ||
sound is simply `true`, `Bottle` is used. | ||
```javascript | ||
See [specific Notification Center example](./example/advanced.js). | ||
notifier.notify({ | ||
"title": "Phil Coulson", | ||
"subtitle": "Agent of S.H.I.E.L.D.", | ||
"message": "If I come out, will you shoot me? 'Cause then I won't come out.", | ||
"sound": "Funk", // case sensitive | ||
"contentImage": __dirname + "/coulson.jpg", | ||
"appIcon": __dirname + "/coulson.jpg", | ||
"open": "file://" + __dirname + "/coulson.jpg" | ||
}); | ||
### Usage WindowsToaster | ||
``` | ||
**Note:** There are some limitations for images in native Windows 8 notifications: | ||
The image must be a PNG image, and cannot be over 1024x1024 px, or over over 200Kb. | ||
You also need to specify the image by using absolute path. These limitations are | ||
due to the Toast notification system. A good tip is to use something like | ||
`path.join` or `path.delimiter` to have cross-platform pathing. | ||
See [terminal-notifier](https://github.com/alloy/terminal-notifier) for more options. | ||
[toaster](https://github.com/nels-o/toaster) is used to get native Windows Toasts! | ||
## Usage NotifySend | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var WindowsToaster = require('node-notifier').WindowsToaster; | ||
var notifier = new Notification({ | ||
// Options passed to Growl if fallback | ||
var notifier = new WindowsToaster({ | ||
withFallback: false, // Fallback to Growl or Balloons? | ||
customPath: void 0 // Relative path if you want to use your fork of toast.exe | ||
}); | ||
notifier.notify({ | ||
title: 'Foo', | ||
message: 'Hello World', | ||
icon: __dirname + "/coulson.jpg", | ||
// .. and other notify-send flags | ||
title: void 0, | ||
message: void 0, | ||
icon: void 0, // absolute path to an icon | ||
sound: false, // true | false. | ||
wait: false, // if wait for notification to end | ||
}, function(error, response) { | ||
console.log(response); | ||
}); | ||
``` | ||
See flags and options [on the man pages](http://manpages.ubuntu.com/manpages/gutsy/man1/notify-send.1.html) | ||
### Usage Growl | ||
## Usage Native Windows 8 | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var Growl = require('node-notifier').Growl; | ||
var notifier = new Notification({ | ||
// Options passed to Growl if fallback | ||
var notifier = new Growl({ | ||
name: 'Growl Name Used' // Defaults as 'Node' | ||
}); | ||
notifier.notify({ | ||
title: 'Foo', | ||
message: 'Hello World', | ||
icon: __dirname + "/coulson.jpg" | ||
icon: fs.readFileSync(__dirname + "/coulson.jpg"), | ||
wait: false, // if wait for user interaction | ||
// and other growl options like sticky etc. | ||
sticky: false, | ||
label: void 0, | ||
priority: void 0 | ||
}); | ||
``` | ||
## Usage Native Windows Version 7 and below | ||
See more information about using | ||
[growly](https://github.com/theabraham/growly/). | ||
For earlier Windows versions, the taskbar balloons are used. | ||
For balloons a great project called | ||
[notifu](http://www.paralint.com/projects/notifu/) is used. | ||
### Usage WindowsBalloon | ||
For earlier Windows versions, the taskbar balloons are used (unless | ||
fallback is activated and Growl is running). For balloons a great | ||
project called [notifu](http://www.paralint.com/projects/notifu/) is used. | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var WindowsBalloon = require('node-notifier').WindowsBalloon; | ||
var notifier = new Notification({ | ||
// Options passed to Growl if fallback | ||
var notifier = new WindowsBalloon({ | ||
withFallback: false, // Try Windows 8 and Growl first? | ||
customPath: void 0 // Relative path if you want to use your fork of notifu | ||
}); | ||
notifier.notify({ | ||
p: 'Foo', // Title (can use title:) | ||
m: 'Hello World', // Message (can use message:) | ||
d: 1000 // Show for 1000 ms | ||
title: void 0, | ||
message: void 0, | ||
sound: false, // true | false. | ||
time: 5000, // How long to show balloons in ms | ||
wait: false, // if wait for notification to end | ||
}, function(error, response) { | ||
console.log(response); | ||
}); | ||
@@ -200,10 +227,10 @@ ``` | ||
## Usage Growl | ||
### Usage NotifySend | ||
Note: notify-send doesn't support the wait flag. | ||
```javascript | ||
var Notification = require('node-notifier'); | ||
var NotifySend = require('node-notifier').NotifySend; | ||
var notifier = new Notification({ | ||
name: 'Growl Name Used' // Defaults as 'Node' | ||
}); | ||
var notifier = new NotifySend(); | ||
@@ -213,14 +240,25 @@ notifier.notify({ | ||
message: 'Hello World', | ||
icon: fs.readFileSync(__dirname + "/coulson.jpg") | ||
// and other growl options like sticky etc. | ||
icon: __dirname + "/coulson.jpg", | ||
// .. and other notify-send flags: | ||
urgency: void 0, | ||
time: void 0, | ||
category: void 0, | ||
hint: void 0, | ||
}); | ||
``` | ||
See more information for constructor options in | ||
[growler](https://github.com/theabraham/growly/). | ||
See flags and options [on the man pages](http://manpages.ubuntu.com/manpages/gutsy/man1/notify-send.1.html) | ||
## Thanks to OSS | ||
A very special thanks to all the modules `node-notifier` uses. | ||
* [terminal-notifier](https://github.com/alloy/terminal-notifier) | ||
* [toaster](https://github.com/nels-o/toaster) | ||
* [notifu](http://www.paralint.com/projects/notifu/) | ||
* [growly](https://github.com/theabraham/growly/) | ||
[![NPM downloads][npm-downloads]][npm-url] | ||
## License | ||
@@ -227,0 +265,0 @@ |
@@ -30,5 +30,3 @@ var Notify = require('../').NotifySend | ||
var notifier = new Notify(); | ||
notifier.isNotifyChecked = true; | ||
notifier.hasNotifier = true; | ||
var notifier = new Notify({ suppressOsdCheck: true }); | ||
@@ -52,5 +50,3 @@ notifier.notify({ | ||
var notifier = new Notify(); | ||
notifier.isNotifyChecked = true; | ||
notifier.hasNotifier = true; | ||
var notifier = new Notify({ suppressOsdCheck: true }); | ||
@@ -73,5 +69,3 @@ notifier.notify({ | ||
var notifier = new Notify(); | ||
notifier.isNotifyChecked = true; | ||
notifier.hasNotifier = true; | ||
var notifier = new Notify({ suppressOsdCheck: true }); | ||
@@ -94,5 +88,3 @@ notifier.notify({ | ||
var notifier = new Notify(); | ||
notifier.isNotifyChecked = true; | ||
notifier.hasNotifier = true; | ||
var notifier = new Notify({ suppressOsdCheck: true }); | ||
@@ -115,5 +107,3 @@ notifier.notify({ | ||
var notifier = new Notify(); | ||
notifier.isNotifyChecked = true; | ||
notifier.hasNotifier = true; | ||
var notifier = new Notify({ suppressOsdCheck: true }); | ||
@@ -138,5 +128,3 @@ notifier.notify({ | ||
var notifier = new Notify(); | ||
notifier.isNotifyChecked = true; | ||
notifier.hasNotifier = true; | ||
var notifier = new Notify({ suppressOsdCheck: true }); | ||
@@ -143,0 +131,0 @@ notifier.notify({ |
@@ -12,17 +12,23 @@ var nn = require('../') | ||
var originalUtils = utils.command; | ||
var originalMacVersion = utils.getOSXVersion; | ||
var originalMacVersion = utils.isMountainLion; | ||
var originalType = os.type; | ||
describe('Mac fallback', function () { | ||
var original = utils.isMacOSX; | ||
var original = utils.isMountainLion; | ||
var originalMac = utils.isMac; | ||
after(function () { | ||
utils.isMacOSX = original; | ||
utils.isMountainLion = original; | ||
utils.isMac = originalMac; | ||
}) | ||
it('should default to Growl notification if older Mac OSX than 10.8', function(done){ | ||
utils.isMacOSX = function (cb) { | ||
cb('old'); | ||
utils.isMountainLion = function () { | ||
return false; | ||
}; | ||
var n = new NotificationCenter(); | ||
utils.isMac = function () { | ||
return true; | ||
}; | ||
var n = new NotificationCenter({ withFallback: true }); | ||
n.notify({ | ||
@@ -36,2 +42,20 @@ message: "Hello World" | ||
}); | ||
it('should not fallback to Growl notification if withFallback is false', function(done){ | ||
utils.isMountainLion = function () { | ||
return false; | ||
}; | ||
utils.isMac = function () { | ||
return true; | ||
}; | ||
var n = new NotificationCenter(); | ||
n.notify({ | ||
message: "Hello World" | ||
}, function (err, response) { | ||
err.should.be.ok; | ||
(this instanceof Growl).should.be.false; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -46,4 +70,4 @@ | ||
utils.getOSXVersion = function (cb) { | ||
return cb(null, "10.8"); | ||
utils.isMountainLion = function () { | ||
return true; | ||
} | ||
@@ -58,2 +82,3 @@ }); | ||
os.type = originalType; | ||
utils.isMountainLion = originalMacVersion; | ||
}); | ||
@@ -101,3 +126,3 @@ | ||
cb(null, fs.readFileSync(__dirname + '/fixture/listAll.txt').toString()); | ||
} | ||
}; | ||
@@ -104,0 +129,0 @@ notifier.notify({ |
@@ -8,66 +8,4 @@ var should = require('should'), | ||
var originalType = os.type; | ||
var originalVersion = _.getOSXVersion; | ||
describe('utils', function(){ | ||
before(function () { | ||
os.type = function () { | ||
return "Darwin"; | ||
}; | ||
}); | ||
after(function () { | ||
os.type = originalType; | ||
_.getOSXVersion = originalVersion; | ||
}); | ||
it('should support mac 10.8', function (done) { | ||
_.getOSXVersion = function (cb) { | ||
cb(null, "10.8"); | ||
}; | ||
_.isMacOSX(function (error, msg) { | ||
error.should.be.false; | ||
(msg === void 0).should.be.true; | ||
done(); | ||
}); | ||
}); | ||
it('should support not mac 10.7', function (done) { | ||
_.getOSXVersion = function (cb) { | ||
cb(null, "10.7"); | ||
}; | ||
_.isMacOSX(function (error, msg) { | ||
error.should.equal('old'); | ||
(msg === void 0).should.be.false; | ||
done(); | ||
}); | ||
}); | ||
it('should support 10.10', function (done) { | ||
_.getOSXVersion = function (cb) { | ||
cb(null, "10.10"); | ||
}; | ||
_.isMacOSX(function (error, msg) { | ||
error.should.be.false; | ||
(msg === void 0).should.be.true; | ||
done(); | ||
}); | ||
}); | ||
it('should sopport 10.10 with newline', function (done) { | ||
_.getOSXVersion = function (cb) { | ||
cb(null, "10.10\n"); | ||
}; | ||
_.isMacOSX(function (error, msg) { | ||
error.should.be.false; | ||
(msg === void 0).should.be.true; | ||
done(); | ||
}); | ||
}); | ||
describe('mapping', function () { | ||
@@ -74,0 +12,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
10387023
43
1028
271
5
1
+ Addedclone@^0.1.18
+ Addedclone@0.1.19(transitive)
+ Addedsemver@4.3.6(transitive)
- Removedsemver@3.0.1(transitive)
Updatedsemver@^4.0.3