├── README.md ├── gruntfile.js ├── package.json └── src ├── desktopNotification.html ├── desktopNotification.js ├── index.html └── package.json /README.md: -------------------------------------------------------------------------------- 1 | node-webkit-desktop-notification 2 | ======= 3 | see [this](https://github.com/edjafarov/node-webkit-desktop-notification/issues/2#issuecomment-56260958) first. 4 | 5 | This is proper desktop notifications for [node-webkit](https://github.com/rogerwang/node-webkit). 6 | 7 | Idea behind is to create drop-in replacement for html5 desktop notifications. Giving though richer and [better](http://screencast.com/t/bUxB6vNvW8BN) experience. 8 | 9 | Use the lib in your app you need to take 2 files: 10 | 11 | ``` 12 | src/desktopNotification.js 13 | src/desktopNotification.html 14 | ``` 15 | 16 | You need to place them in same folder of your app. Load `desktopNotification.js` to your index.html to use the `DesktopNotification` 17 | 18 | ```javascript 19 | var notif = new DesktopNotification('Hello', {body: 'World'}); 20 | notif.show(); 21 | ``` 22 | check other ways to use `DesktopNotification` in [example](https://github.com/edjafarov/node-webkit-desktop-notification/blob/master/src/index.html). 23 | 24 | #### try live 25 | 26 | * Fetch the repo. 27 | * npm install 28 | * npm start 29 | * find an app for your OS in build/node-webkit-desktop-notification 30 | * [play](http://screencast.com/t/bUxB6vNvW8BN) 31 | 32 | ##API 33 | 34 | ### Class: DesktopNotification(title, options) 35 | 36 | Title is the notification's title. Options: body is required. 37 | 38 | #### options: 39 | 40 | * width: 288 - width 41 | * height: 96 - height 42 | * body - text body for notification 43 | * icon - icon of notification 44 | * ease - ease [function]() 45 | * htmlBody - notification body as HTML 46 | * styles - additional styles to add for notification 47 | 48 | ### Instance: DesktopNotification 49 | 50 | #### methods 51 | * show - show notification 52 | * close - close notification 53 | * on - set up event listener on notification 54 | * off - remove event listener from notification 55 | * emit - emit event on notification 56 | 57 | #### properties (html5 Notification compatibility) 58 | * onclick - assign function for click event 59 | * onshow - assign function for show event 60 | * onclose - assign function for close event 61 | * onerror - assign function for error event 62 | 63 | #### events 64 | 65 | * body.click - if someone clicks on notification 66 | * show - when notification is shown 67 | * close - when notification is closed 68 | * showStart - when notification start showing animation 69 | * closeStart - when notification start closing animation 70 | * error - on error 71 | * close.click - when user clicks on x to close notification 72 | 73 | ### Customizing notification 74 | 75 | You can put design your own beautiful notifications by using `htmlBody` and `styles` options. 76 | 77 | Inside in notification window context you have access to `window` object which is EventEmitter. You can emit events inside notification with `window.emit('eventName')` and catch them on `DesktopNotification` instance in your application. 78 | 79 | For more details, check [example](https://github.com/edjafarov/node-webkit-desktop-notification/blob/master/src/index.html). 80 | 81 | 82 | ### Easing functions 83 | 84 | You can use following ease functions like `DesktopNotification.ease.easeInQuad`, just put it as `ease` option in options. 85 | 86 | ``` 87 | easeInQuad 88 | easeOutQuad 89 | easeInOutQuad 90 | easeInCubic 91 | easeOutCubic 92 | easeInOutCubic 93 | easeInQuart 94 | easeOutQuart 95 | easeInOutQuart 96 | easeInQuint 97 | easeOutQuint 98 | easeInOutQuint 99 | easeInSine 100 | easeOutSine 101 | easeInOutSine 102 | easeInExpo 103 | easeOutExpo 104 | easeInOutExpo 105 | easeInCirc 106 | easeOutCirc 107 | easeInOutCirc 108 | easeInElastic 109 | easeOutElastic 110 | easeInOutElastic 111 | easeInBack 112 | easeOutBack 113 | easeInOutBack 114 | easeInBounce 115 | easeOutBounce 116 | ``` 117 | -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | 3 | 4 | module.exports = function(grunt){ 5 | grunt.initConfig({ 6 | nodewebkit: { 7 | options: { 8 | build_dir: './build', 9 | platforms: ['osx', 'win', 'linux32', 'linux64'], 10 | version: '0.10.3', 11 | toolbar: false, 12 | frame: false 13 | }, 14 | src: ['./src/**/*'] // Your node-wekit app 15 | } 16 | }); 17 | grunt.loadNpmTasks('grunt-node-webkit-builder'); 18 | 19 | grunt.registerTask('default', 'nodewebkit'); 20 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-webkit-desktop-notification", 3 | "version": "0.0.1", 4 | "description": "Rich node-webkit desktop notification with html5 like API ", 5 | "scripts": { 6 | "start": "node node_modules/grunt-cli/bin/grunt" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/edjafarov/node-webkit-desktop-notification" 11 | }, 12 | "keywords": [ 13 | "node-webkit", 14 | "desktop", 15 | "notification" 16 | ], 17 | "author": "Eldar Djafarov ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/edjafarov/node-webkit-desktop-notification/issues" 21 | }, 22 | "homepage": "https://github.com/edjafarov/node-webkit-desktop-notification", 23 | "dependencies": { 24 | "grunt": "^0.4.5", 25 | "grunt-cli": "^0.1.13", 26 | "grunt-node-webkit-builder": "^0.2.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/desktopNotification.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 53 | 54 | 55 |
56 |
×
57 |
58 |
59 |

60 |

61 |
62 |
63 | 64 | -------------------------------------------------------------------------------- /src/desktopNotification.js: -------------------------------------------------------------------------------- 1 | /* 2 | DesktopNotification(title, options) 3 | options: 4 | width: 288, // 5 | height: 96, // 6 | body: null, // text body for notification 7 | icon: null, // icon of notification 8 | ease: easeFunctions.easeOutSine, //ease function 9 | htmlBody: null, //notification body as HTML 10 | easeTime: 125 //how fast notification should show 11 | styles: null //additional styles to add for notification 12 | 13 | DesktopNotification instance 14 | methods: 15 | show 16 | close 17 | on 18 | off 19 | emit 20 | properties 21 | onclick 22 | onshow 23 | onclose 24 | onerror 25 | events 26 | body.click 27 | show 28 | close 29 | showStart 30 | closeStart 31 | error 32 | close.click 33 | 34 | 35 | TODO: 36 | iconBase64: //base64 represetation of icon 37 | css: [] 38 | */ 39 | 40 | /* 41 | * wrapper for lib 42 | */ 43 | (function (root, factory) { 44 | if (typeof define === 'function' && define.amd) { 45 | // AMD. Register as an anonymous module. 46 | define(function () { 47 | return (root.DesktopNotification = factory()); 48 | }); 49 | } else if (typeof exports === 'object') { 50 | // Node. Does not work with strict CommonJS, but 51 | // only CommonJS-like enviroments that support module.exports, 52 | // like Node. 53 | module.exports = factory(); 54 | } else { 55 | // Browser globals 56 | root.DesktopNotification = factory(); 57 | } 58 | }(this, function () { 59 | 60 | var gui = global.window.nwDispatcher.requireNwGui(); 61 | //---------------- 62 | /* STATIC stuff */ 63 | //---------------- 64 | 65 | // get urrent script's url, used to find .html file for popups 66 | var scriptSource = (function(scripts) { 67 | var scripts = document.getElementsByTagName('script'), 68 | script = scripts[scripts.length - 1]; 69 | 70 | if (script.getAttribute.length !== undefined) { 71 | return script.src 72 | } 73 | return script.getAttribute('src', -1) 74 | }()); 75 | 76 | 77 | /* easing functions for popup animations */ 78 | var easeFunctions = { 79 | easeInQuad: function (t, b, c, d) { 80 | return c * (t /= d) * t + b; 81 | }, 82 | easeOutQuad: function (t, b, c, d) { 83 | return -c * (t /= d) * (t - 2) + b; 84 | }, 85 | easeInOutQuad: function (t, b, c, d) { 86 | if ((t /= d / 2) < 1) return c / 2 * t * t + b; 87 | return -c / 2 * ((--t) * (t - 2) - 1) + b; 88 | }, 89 | easeInCubic: function (t, b, c, d) { 90 | return c * (t /= d) * t * t + b; 91 | }, 92 | easeOutCubic: function (t, b, c, d) { 93 | return c * ((t = t / d - 1) * t * t + 1) + b; 94 | }, 95 | easeInOutCubic: function (t, b, c, d) { 96 | if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; 97 | return c / 2 * ((t -= 2) * t * t + 2) + b; 98 | }, 99 | easeInQuart: function (t, b, c, d) { 100 | return c * (t /= d) * t * t * t + b; 101 | }, 102 | easeOutQuart: function (t, b, c, d) { 103 | return -c * ((t = t / d - 1) * t * t * t - 1) + b; 104 | }, 105 | easeInOutQuart: function (t, b, c, d) { 106 | if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; 107 | return -c / 2 * ((t -= 2) * t * t * t - 2) + b; 108 | }, 109 | easeInQuint: function (t, b, c, d) { 110 | return c * (t /= d) * t * t * t * t + b; 111 | }, 112 | easeOutQuint: function (t, b, c, d) { 113 | return c * ((t = t / d - 1) * t * t * t * t + 1) + b; 114 | }, 115 | easeInOutQuint: function (t, b, c, d) { 116 | if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; 117 | return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; 118 | }, 119 | easeInSine: function (t, b, c, d) { 120 | return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; 121 | }, 122 | easeOutSine: function (t, b, c, d) { 123 | return c * Math.sin(t / d * (Math.PI / 2)) + b; 124 | }, 125 | easeInOutSine: function (t, b, c, d) { 126 | return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; 127 | }, 128 | easeInExpo: function (t, b, c, d) { 129 | return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; 130 | }, 131 | easeOutExpo: function (t, b, c, d) { 132 | return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; 133 | }, 134 | easeInOutExpo: function (t, b, c, d) { 135 | if (t == 0) return b; 136 | if (t == d) return b + c; 137 | if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; 138 | return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; 139 | }, 140 | easeInCirc: function (t, b, c, d) { 141 | return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; 142 | }, 143 | easeOutCirc: function (t, b, c, d) { 144 | return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; 145 | }, 146 | easeInOutCirc: function (t, b, c, d) { 147 | if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; 148 | return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; 149 | }, 150 | easeInElastic: function (t, b, c, d) { 151 | var s = 1.70158; 152 | var p = 0; 153 | var a = c; 154 | if (t == 0) return b; 155 | if ((t /= d) == 1) return b + c; 156 | if (!p) p = d * .3; 157 | if (a < Math.abs(c)) { 158 | a = c; 159 | var s = p / 4; 160 | } 161 | else var s = p / (2 * Math.PI) * Math.asin(c / a); 162 | return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 163 | }, 164 | easeOutElastic: function (t, b, c, d) { 165 | var s = 1.70158; 166 | var p = 0; 167 | var a = c; 168 | if (t == 0) return b; 169 | if ((t /= d) == 1) return b + c; 170 | if (!p) p = d * .3; 171 | if (a < Math.abs(c)) { 172 | a = c; 173 | var s = p / 4; 174 | } 175 | else var s = p / (2 * Math.PI) * Math.asin(c / a); 176 | return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b; 177 | }, 178 | easeInOutElastic: function (t, b, c, d) { 179 | var s = 1.70158; 180 | var p = 0; 181 | var a = c; 182 | if (t == 0) return b; 183 | if ((t /= d / 2) == 2) return b + c; 184 | if (!p) p = d * (.3 * 1.5); 185 | if (a < Math.abs(c)) { 186 | a = c; 187 | var s = p / 4; 188 | } 189 | else var s = p / (2 * Math.PI) * Math.asin(c / a); 190 | if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 191 | return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b; 192 | }, 193 | easeInBack: function (t, b, c, d, s) { 194 | if (s == undefined) s = 1.70158; 195 | return c * (t /= d) * t * ((s + 1) * t - s) + b; 196 | }, 197 | easeOutBack: function (t, b, c, d, s) { 198 | if (s == undefined) s = 1.70158; 199 | return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; 200 | }, 201 | easeInOutBack: function (t, b, c, d, s) { 202 | if (s == undefined) s = 1.70158; 203 | if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; 204 | return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; 205 | }, 206 | easeInBounce: function (t, b, c, d) { 207 | return c - easing.easeOutBounce(d - t, 0, c, d) + b; 208 | }, 209 | easeOutBounce: function (t, b, c, d) { 210 | if ((t /= d) < (1 / 2.75)) { 211 | return c * (7.5625 * t * t) + b; 212 | } else if (t < (2 / 2.75)) { 213 | return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b; 214 | } else if (t < (2.5 / 2.75)) { 215 | return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b; 216 | } else { 217 | return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b; 218 | } 219 | } 220 | } 221 | 222 | 223 | var defaultOptions = { 224 | width: 288, // max size of html5 notifications 225 | height: 96, // max size of html5 notifications 226 | x: 10000, 227 | y:10000, 228 | body: null, 229 | icon: null, 230 | ease: easeFunctions.easeOutSine, 231 | htmlBody: null, 232 | javascript: [], 233 | css: [], 234 | 235 | frame: false, 236 | toolbar: false, 237 | 'always-on-top': true, 238 | show: true, 239 | resizable: false, 240 | focus: false, 241 | 242 | easeTime: 125 243 | }; 244 | 245 | // smallest time between frames ms 246 | var keyStep = 1000/120; 247 | // margin between popups 248 | var margin = 10; 249 | 250 | //---------------- 251 | /* ENGINE */ 252 | //---------------- 253 | 254 | //current movement tasks 255 | var movements = []; 256 | 257 | var engine = setInterval(function(){ 258 | movements.forEach(function(movement, i){ 259 | if(!movement.finished && !movement.win.isMoving || movement.win.isMoving == movement) movement.makeStep(); 260 | if(movement.finished) movements.splice(i, 1); 261 | }); 262 | }, keyStep); 263 | 264 | 265 | function MovementTask(win, start, diff, steps, ease, done){ 266 | this.step = 0; 267 | this.win = win; 268 | this.start = start || {}; 269 | 270 | this.diff = diff; 271 | this.steps = steps; 272 | this.ease = ease; 273 | this.done = done; 274 | } 275 | 276 | MovementTask.prototype.makeStep = function(){ 277 | if(!this.win.isActive) { 278 | this.finished = true; 279 | delete this.win.isMoving; 280 | if(this.done) this.done.apply(this); 281 | return; 282 | } 283 | if(!this.win.isMoving) { 284 | this.win.isMoving = this; 285 | this.start.x = this.start.x || this.win.x; 286 | this.start.y = this.start.y || this.win.y; 287 | } 288 | 289 | var x = this.diff.x?Math.floor(this.ease(this.step, this.start.x, this.diff.x, this.steps)):this.start.x; 290 | var y = this.diff.y?Math.floor(this.ease(this.step, this.start.y, this.diff.y, this.steps)):this.start.y; 291 | 292 | this.win.moveTo(x, y); 293 | this.step+=1; 294 | if(this.step >= this.steps - 1){ 295 | this.finished = true; 296 | delete this.win.isMoving; 297 | if(this.done)this.done.apply(this); 298 | } 299 | } 300 | //TODO: to save resources and make performance higher we need to create window pool 301 | //every notification before shown will be taken from the pool and used 302 | 303 | var tagged = {}; 304 | var activeNotifications = []; 305 | 306 | //UTILS 307 | function getNextAvailTop(toIth){ 308 | var len = activeNotifications.length; 309 | if(toIth == 0 || toIth > 0) len = toIth; 310 | var summHeight = screen.availTop + 10; 311 | 312 | for(var i=0; i< len; i++){ 313 | summHeight+= activeNotifications[i].options.height + margin; 314 | } 315 | 316 | return summHeight; 317 | } 318 | 319 | function extend(a, byB){ 320 | for(var key in byB){ 321 | if(byB[key]) a[key] = byB[key]; 322 | } 323 | return a; 324 | } 325 | 326 | function clone(obj){ 327 | var newobj = JSON.parse(JSON.stringify(obj)); 328 | for(var key in obj){ 329 | if(typeof(obj[key]) == 'function'){ 330 | newobj[key] = obj[key]; 331 | } 332 | } 333 | return newobj; 334 | } 335 | 336 | function rightBorder(){ 337 | var screen = global.window.screen; 338 | return screen.availLeft + screen.availWidth; 339 | } 340 | 341 | /*-------* 342 | * EMITTER CLASS, to get messaging in place for popups 343 | *--------*/ 344 | /** 345 | * Initialize a new `Emitter`. 346 | * 347 | * @api public 348 | */ 349 | 350 | function Emitter(obj) { 351 | if (obj) return mixin(obj); 352 | }; 353 | 354 | /** 355 | * Mixin the emitter properties. 356 | * 357 | * @param {Object} obj 358 | * @return {Object} 359 | * @api private 360 | */ 361 | 362 | function mixin(obj) { 363 | for (var key in Emitter.prototype) { 364 | obj[key] = Emitter.prototype[key]; 365 | } 366 | return obj; 367 | } 368 | 369 | /** 370 | * Listen on the given `event` with `fn`. 371 | * 372 | * @param {String} event 373 | * @param {Function} fn 374 | * @return {Emitter} 375 | * @api public 376 | */ 377 | 378 | Emitter.prototype.on = 379 | Emitter.prototype.addEventListener = function(event, fn){ 380 | this._callbacks = this._callbacks || {}; 381 | (this._callbacks[event] = this._callbacks[event] || []) 382 | .push(fn); 383 | return this; 384 | }; 385 | 386 | /** 387 | * Adds an `event` listener that will be invoked a single 388 | * time then automatically removed. 389 | * 390 | * @param {String} event 391 | * @param {Function} fn 392 | * @return {Emitter} 393 | * @api public 394 | */ 395 | 396 | Emitter.prototype.once = function(event, fn){ 397 | var self = this; 398 | this._callbacks = this._callbacks || {}; 399 | 400 | function on() { 401 | self.off(event, on); 402 | fn.apply(this, arguments); 403 | } 404 | 405 | on.fn = fn; 406 | this.on(event, on); 407 | return this; 408 | }; 409 | 410 | /** 411 | * Remove the given callback for `event` or all 412 | * registered callbacks. 413 | * 414 | * @param {String} event 415 | * @param {Function} fn 416 | * @return {Emitter} 417 | * @api public 418 | */ 419 | 420 | Emitter.prototype.off = 421 | Emitter.prototype.removeListener = 422 | Emitter.prototype.removeAllListeners = 423 | Emitter.prototype.removeEventListener = function(event, fn){ 424 | this._callbacks = this._callbacks || {}; 425 | 426 | // all 427 | if (0 == arguments.length) { 428 | this._callbacks = {}; 429 | return this; 430 | } 431 | 432 | // specific event 433 | var callbacks = this._callbacks[event]; 434 | if (!callbacks) return this; 435 | 436 | // remove all handlers 437 | if (1 == arguments.length) { 438 | delete this._callbacks[event]; 439 | return this; 440 | } 441 | 442 | // remove specific handler 443 | var cb; 444 | for (var i = 0; i < callbacks.length; i++) { 445 | cb = callbacks[i]; 446 | if (cb === fn || cb.fn === fn) { 447 | callbacks.splice(i, 1); 448 | break; 449 | } 450 | } 451 | return this; 452 | }; 453 | 454 | /** 455 | * Emit `event` with the given args. 456 | * 457 | * @param {String} event 458 | * @param {Mixed} ... 459 | * @return {Emitter} 460 | */ 461 | 462 | Emitter.prototype.emit = function(event){ 463 | this._callbacks = this._callbacks || {}; 464 | var args = [].slice.call(arguments, 1) 465 | , callbacks = this._callbacks[event]; 466 | 467 | if (callbacks) { 468 | callbacks = callbacks.slice(0); 469 | for (var i = 0, len = callbacks.length; i < len; ++i) { 470 | callbacks[i].apply(this, args); 471 | } 472 | } 473 | 474 | return this; 475 | }; 476 | 477 | /** 478 | * Return array of callbacks for `event`. 479 | * 480 | * @param {String} event 481 | * @return {Array} 482 | * @api public 483 | */ 484 | 485 | Emitter.prototype.listeners = function(event){ 486 | this._callbacks = this._callbacks || {}; 487 | return this._callbacks[event] || []; 488 | }; 489 | 490 | /** 491 | * Check if this emitter has `event` handlers. 492 | * 493 | * @param {String} event 494 | * @return {Boolean} 495 | * @api public 496 | */ 497 | 498 | Emitter.prototype.hasListeners = function(event){ 499 | return !! this.listeners(event).length; 500 | }; 501 | 502 | var scriptArr = scriptSource.split("/"); 503 | scriptArr.splice(scriptArr.length - 1, 1); 504 | var desktopNotificationHtml = scriptArr.join("/") + "/desktopNotification.html"; 505 | var winPool = []; 506 | 507 | for(var w= 0;w < 6;w++){ 508 | var winOpen = gui.Window.open(desktopNotificationHtml, defaultOptions); 509 | winOpen.on('loaded', function(){ 510 | Emitter(this.window); 511 | }); 512 | winPool.push(winOpen); 513 | } 514 | 515 | 516 | window.onunload = function(){ 517 | for(var w= 0;w < winPool.length;w++){ 518 | winPool[w].close(true); 519 | } 520 | } 521 | 522 | function getFreeWin(){ 523 | var result; 524 | for(var i = 0; i< winPool.length; i++){ 525 | if(!winPool[i].isBusy) { 526 | result = winPool[i]; 527 | break; 528 | } 529 | } 530 | if(!result){ 531 | result = gui.Window.open( 532 | desktopNotificationHtml, defaultOptions); 533 | winPool.push(result); 534 | } 535 | result.isBusy = true; 536 | result.originalHtml = result.window.document.body.innerHTML; 537 | return result; 538 | } 539 | 540 | function release(wind){ 541 | //still we need Ideally to reload the notification:( 542 | wind.window.off(); 543 | wind.window.document.body.innerHTML = wind.originalHtml; 544 | delete wind.originalHtml; 545 | delete wind.isBusy; 546 | } 547 | /** 548 | * DESKTOP NOTIFICATIONS CLASS 549 | * 550 | * 551 | * title - The title that must be shown within the notification 552 | * options (Optional) - An object that allows to configure the notification. It can have the following properties: 553 | * -dir : The direction of the notification; it can be auto, ltr, or rtl- 554 | * -lang: Specifiy the lang used within the notification. This string must be a valid BCP 47 language tag.- 555 | * body: A string representing an extra content to display within the notification 556 | * tag: An ID for a given notification that allows to retrieve, replace or remove it if necessary 557 | * icon: The URL of an image to be used as an icon by the notification 558 | */ 559 | 560 | function DesktopNotification(title, options){ 561 | this.title = title; 562 | this.options = extend(clone(defaultOptions), options); 563 | 564 | //put tagged into hash 565 | if(this.options.tag) tagged[this.options.tag] = this; 566 | 567 | this.win = getFreeWin(); 568 | 569 | this.win.resizeTo(this.options.width, this.options.height); 570 | 571 | this.win.moveTo(rightBorder() + 10, getNextAvailTop()); 572 | 573 | 574 | // on events implementation 575 | this.on('body.click', function(){ 576 | if(this.onclick) this.onclick.call(this); 577 | }.bind(this)); 578 | this.on('show', function(){ 579 | if(this.onshow) this.onshow.call(this); 580 | }.bind(this)); 581 | this.on('error', function(){ 582 | if(this.onerror) this.onerror.call(this); 583 | }.bind(this)); 584 | this.on('close', function(){ 585 | if(this.onclose) this.onclose.call(this); 586 | }.bind(this)); 587 | } 588 | 589 | DesktopNotification.ease = easeFunctions; 590 | 591 | DesktopNotification.get = function(tag){ 592 | return tagged[tag]; 593 | } 594 | 595 | DesktopNotification.prototype.on = function(){ 596 | var arg = arguments; 597 | this.win.window.on.apply(this.win.window, arg); 598 | }; 599 | 600 | DesktopNotification.prototype.off = function(){ 601 | var arg = arguments; 602 | this.win.window.off.apply(this.win.window, arg); 603 | }; 604 | 605 | DesktopNotification.prototype.emit = function(){ 606 | var arg = arguments; 607 | this.win.window.emit.apply(this.win.window, arg); 608 | }; 609 | 610 | 611 | /** 612 | * show notification 613 | */ 614 | DesktopNotification.prototype.show = function(cb){ 615 | //this.win.setShowInTaskbar(false); 616 | 617 | this.win.moveTo(rightBorder() + 10, getNextAvailTop()); 618 | activeNotifications.push(this); 619 | this.win.isActive = true; 620 | 621 | this.on('close.click', function(){ 622 | this.close(); 623 | }.bind(this)); 624 | 625 | var msgDoc = this.win.window.document; 626 | //if htmlBody is not defined 627 | if(!this.options.htmlBody){ 628 | var image = msgDoc.getElementById('image'); 629 | var message = msgDoc.getElementById('message'); 630 | var notification = msgDoc.getElementById('notification'); 631 | 632 | //if there is no icon 633 | if(!this.options.icon){ 634 | notification.className = "no-icon"; 635 | } else { 636 | image.getElementsByTagName('img')[0].src = this.options.icon; 637 | } 638 | message.getElementsByTagName('h3')[0].innerHTML = this.title; 639 | message.getElementsByTagName('p')[0].innerHTML = this.options.body; 640 | } else { 641 | msgDoc.body.innerHTML = this.options.htmlBody; 642 | } 643 | 644 | if(this.options.styles){ 645 | var node = msgDoc.createElement('style'); 646 | node.innerHTML = this.options.styles; 647 | msgDoc.body.appendChild(node); 648 | } 649 | 650 | this.emit('showStart'); 651 | 652 | movements.push(new MovementTask(this.win, {}, 653 | {x: -this.options.width -20, y: 0}, this.options.easeTime/keyStep, this.options.ease, function(){ 654 | this.emit('show'); 655 | if(cb) cb.call(this); 656 | }.bind(this))); 657 | 658 | 659 | } 660 | 661 | /** 662 | * close notification 663 | */ 664 | DesktopNotification.prototype.close = function(cb){ 665 | 666 | var start = this.win.x; 667 | this.win.moveTo(this.win.x, this.win.y); 668 | 669 | this.emit('closeStart'); 670 | 671 | movements.push(new MovementTask(this.win, {}, 672 | {x: this.options.width + 20, y: 0}, this.options.easeTime/keyStep, this.options.ease, done.bind(this))); 673 | function done(){ 674 | // need to move up all of previous 675 | // all of them should be frozen until end movement 676 | // enigne should handle this 677 | var spliceIth = activeNotifications.indexOf(this); 678 | activeNotifications.splice(spliceIth, 1); 679 | this.win.isActive = false; 680 | 681 | if(activeNotifications[spliceIth]){ 682 | var diffY = getNextAvailTop(spliceIth) - activeNotifications[spliceIth].win.y; 683 | for(var i=spliceIth; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | desktop notification example 10 | 11 | 12 | 13 | 14 | 15 |
16 | 33 |
34 | 35 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-webkit-desktop-notification", 3 | "version": "0.0.1", 4 | "description": "Rich node-webkit desktop notification with html5 like API ", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "main":"app://node-webkit-desktop-notification/index.html", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/edjafarov/node-webkit-desktop-notification" 12 | }, 13 | "keywords": [ 14 | "node-webkit", 15 | "desktop", 16 | "notification" 17 | ], 18 | "author": "Eldar Djafarov ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/edjafarov/node-webkit-desktop-notification/issues" 22 | }, 23 | "homepage": "https://github.com/edjafarov/node-webkit-desktop-notification", 24 | "dependencies": { 25 | "grunt": "^0.4.5", 26 | "grunt-cli": "^0.1.13", 27 | "grunt-node-webkit-builder": "^0.2.3" 28 | } 29 | } 30 | --------------------------------------------------------------------------------