Growl for Windows - Implementing Website Notifications with Greasemonkey

Introduction

Since Growl for Windows is built on the GNTP protocol, there are numerous libraries available fors sending notifications. For developers that want to send notifications from their sites, the Javascript library provides the best option for doing so. But what if you want to get notifications from an existing site that doesn't integrate with Growl? By leveraging the power of Greasemonkey scripts, it is easy to add notifications to almost any site.

Requirements

There are a few pieces that need to be in place in order to receive notifications from external websites using this method.

  1. You must be using Firefox as your browser
  2. You need to install the Greasemonkey extension
  3. You need to install the Growl/GNTP extension

Once those pieces are in place, you can easily start developing your own Greasemonkey/Growl scripts.

Growl-Greasemonkey bridge: GrowlMonkey

Greasemonkey-based Growl notifications are achieved using the following process:

  1. Greasemonkey javascript is used to hook into website events (new mail received, song changed, etc)
  2. Greasemonkey javascript passes the notification information (title, text, icon, etc) into the Growl/GNTP extension for handling
  3. The Growl/GNTP extension constructs the GNTP request and sends it to Growl for Windows (using a TCP socket)

It is up to you to provide the necessary javascript to accomplish step 1 above. Step 3 is handled automatically for you, so that only leaves step 2 - the Growl-Greasemonkey bridge: GrowlMonkey

In your Greasemonkey userscript, you can paste the following code to handle the communication between your script and the Growl/GNTP extension. (It also includes a JSON encoder for passing Javascript objects to the extension).

// -- GrowlMonkey stuff below here - do not edit
GrowlMonkey = function(){
    function fireGrowlEvent(type, data){
        var element = document.createElement("GrowlEventElement");
        element.setAttribute("data", JSON.stringify(data));
        document.documentElement.appendChild(element);

        var evt = document.createEvent("Events");
        evt.initEvent(type, true, false);
        element.dispatchEvent(evt);
    }
    
    return {
        register : function(appName, icon, notificationTypes){
            var r = {};
            r.appName = appName;
            r.icon = icon;
            r.notificationTypes = notificationTypes;
            fireGrowlEvent("GrowlRegister", r);
        },
        
        notify : function(appName, notificationType, title, text, icon){
            var n = {};
            n.appName = appName;
            n.type = notificationType;
            n.title = title;
            n.text = text;
            n.icon = icon;
            fireGrowlEvent("GrowlNotify", n);
        }
    }
}();

/* json2.js 
 * 2008-01-17
 * Public Domain
 * No warranty expressed or implied. Use at your own risk.
 * See http://www.JSON.org/js.html
*/
if(!this.JSON){JSON=function(){function f(n){return n<10?'0'+n:n;}
Date.prototype.toJSON=function(){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function stringify(value,whitelist){var a,i,k,l,r=/["\\\x00-\x1f\x7f-\x9f]/g,v;switch(typeof value){case'string':return r.test(value)?'"'+value.replace(r,function(a){var c=m[a];if(c){return c;}
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+
(c%16).toString(16);})+'"':'"'+value+'"';case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
if(typeof value.toJSON==='function'){return stringify(value.toJSON());}
a=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){l=value.length;for(i=0;i

Usage

Growl requires that any applications that want to send notifications must register themselves first. By registering, the app lets Growl know what types of notifications it intends to send. To register, the code would resemble:

register :  function(){
    var ntNewVoicemail = {};
    ntNewVoicemail.name = "newvoicemail";
    ntNewVoicemail.displayName = "New Voicemail";
    ntNewVoicemail.enabled = true;

    var ntNewSMS = {};
    ntNewSMS.name = "newsms";
    ntNewSMS.displayName = "New SMS";
    ntNewSMS.enabled = true;

    var ntNewRecording = {};
    ntNewRecording.name = "newrecording";
    ntNewRecording.displayName = "New Call Recording";
    ntNewRecording.enabled = true;

    var ntNewInboxItem = {};
    ntNewInboxItem.name = "newinboxitem";
    ntNewInboxItem.displayName = "New Inbox Item";
    ntNewInboxItem.enabled = true;

    var types = [ntNewVoicemail, ntNewSMS, ntNewRecording, ntNewInboxItem];

    GrowlMonkey.register("APPLICATION NAME", "http://path/to/your/application/icon.png", types);
}

You can add as many notification types as you like, but you must register at least one notification type in order to be able to communicate with Growl.

The best time to register is once when the page first loads, so adding a listener for the 'load' event is usually a good idea:

window.addEventListener("load", function(e) {
     register();
}, false);

When the website performs an action or has new information to alert you about, you should call the notify() method:

GrowlMonkey.notify("APPLICATION NAME", "NOTIFICATION TYPE", "TITLE", "TEXT");

 - or -
 
GrowlMonkey.notify("APPLICATION NAME", "NOTIFICATION TYPE", "TITLE", "TEXT", "ICON URL");

The value for 'APPLICATION NAME' must match the value that you used when registering your application. The value for 'NOTIFICATION TYPE' must match the name of one of the notification types that you registered as well. If you specify 'ICON URL', it will be used, otherwise if this value is omitted, the notification type's default icon or the application's default icon will be used instead.