Skip to content

Instantly share code, notes, and snippets.

@ELLIOTTCABLE
Created July 21, 2010 07:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ELLIOTTCABLE/484206 to your computer and use it in GitHub Desktop.
Save ELLIOTTCABLE/484206 to your computer and use it in GitHub Desktop.
Google Wave userscript for Fluid.app
// ==UserScript==
// @name Google Wave
// @namespace http://ell.io
// @description Fluid.app: Growl notifications and Dock badges for Google’s Wave web-client (includes support for
// both Google Wave *and* GAFYD Wave installations). Also provides seperate notifications for
// unread waves, and unread “pings.”
// @include https://wave.google.com/wave/*
// @include https://wave.google.com/a/*
// @author elliottcable <http://elliottcable.name>
// @copyright Copyright ©2010 elliottcable, all rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the “Software”), to deal in the Software without restriction, including without
// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is furnished to do so, subject to the following
// conditions:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the
// following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution, and in the
// same place and form as other copyright, license and disclaimer information.
//
// 3. The end-user documentation included with the redistribution, if any, must include the following
// acknowledgment: “This product includes software developed by elliottcable <http://elliottcable.name>”, in
// the same place and form as other third-party acknowledgments. Alternately, this acknowledgment may appear
// in the software itself, in the same form and location as other such third-party acknowledgments.
//
// 4. Except as contained in this notice, the name “elliottcable” shall not be used in advertising or otherwise
// to promote the sale, use or other dealings in this Software without prior written authorization from
// elliottcable.
//
// THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// ELLIOTTCABLE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ==/UserScript==
;(function(){ if (!window.fluid) return
var
// === Configuration
// Interval: Change this (number of seconds) to change how often this UserScript attempts to update the dock
// badge and/or post Growl notifications.
Interval = 2.5
// Whether or not “You’ve new unread waves” notifications should be ‘sticky’
, StickyUnread = false
// Whether or not “You’ve been pinged” notifications should be ‘sticky’
, StickyPings = true
;(function(){
var titleRegex = /(?:\(([0-9]+)\+?\) - )?Google Wave/
FluidWave = {
// `FluidWave.unreadCount` returns either the number of unread messages (`0` if there are none), or `null`
// if the titlebar is currently displaying something else (the number of unread messages is inaccessible.)
get unreadCount(){ var match
return( (match = titleRegex.exec(document.title)) ? (match[1] ? match[1] : 0) : null )}
// `FluidWave.pings` is an `Array` of currently-unread “pings.” Unfortunately, we’re effectively limited to
// retreiving the unread pings one at a time, which means this method caches the results; it may take a
// little while for a full cycle to occur, which allows this to provide an updated `Array`.
, pings: new(Array)() }
var seenPing, seenPings, invalidationTimer
// This fires when the `document.title` hasn’t flashed to a ping notification for a sufficient period of
// time. It ensures that we don’t carry around an invalid ping cache when there are no active pings (this
// solves the edge case where you read a ping from a given user, and then get another ping from that same
// user immediately afterwards.)
, invalidatePings = function(){ seenPings = FluidWave.pings = new(Array)() }
// I’m going to be absolutely honest here: I have not the most remote idea, whatsoever, why
// `seenPings.length > 2` works here. I originally wrote it with `1` instead of `2`, and that *should* have
// worked… but it didn’t. It’s sheer luck that I tried 2, just to see what would happen… and it worked.
//
// I’m going to chalk this one down as a win, blame it on black magic, and then avoid thinking about it.
// For the rest of my life.
, pingCycle = function(){ var match
if (!titleRegex.test(document.title) && !seenPing) { var ping, index; seenPing = true
if ((index = seenPings.indexOf(ping = document.title.substring(0, document.title.length - 1))) !== -1 &&
!(seenPings.length > 2 && index === seenPings.length - 1))
(FluidWave.pings = seenPings) && (seenPings = new(Array)())
if (!(seenPings.length > 2 && index === seenPings.length - 1)) {
seenPings.push(ping)
if (invalidationTimer) clearTimeout(invalidationTimer)
invalidationTimer = setTimeout(invalidatePings, 6 * 1000) }}
else seenPing = false }
setInterval(pingCycle, 1000) && invalidatePings() && pingCycle() })()
// We set this interval to actually *update the badge* and *dispatch Growl notifications*.
var cachedCount, cachedPings = new(Array)()
, ocassionally = function(){ var count = 0, diff = 0, pings, newPings
if ((count = FluidWave.unreadCount) !== null) { window.fluid.dockBadge = count || null
if (typeof(cachedCount)!=="undefined" && (diff = count - cachedCount) > 0)
fluid.showGrowlNotification({ title: "You’ve new blips in "+diff+" wave"+(diff > 1 ? "s" : "")
, description: "("+count+" total unread waves)"
, priority: 0, sticky: StickyUnread })
cachedCount = count }
pings = FluidWave.pings.slice(0)
if (cachedPings && pings.length > 0) {
newPings = pings.filter(function (ping) { return cachedPings.indexOf(ping) === -1 })
if (newPings.length > 0)
fluid.showGrowlNotification({
title: (newPings.length > 1
? newPings.slice(0, -1).join(", ")+" and "+newPings[newPings.length - 1]+" have"
: newPings[newPings.length - 1]+" has"
)+" pinged you"
, description: (pings.length > 1
? pings.slice(0, -1).join(", ")+" and "+pings[pings.length - 1]
: pings[pings.length - 1]
)+" require"+(pings.length > 1 ? "" : "s")+" your attention"
, priority: 0, sticky: StickyPings
}) }
cachedPings = pings.slice(0) }
setInterval(ocassionally, Interval * 1000) && ocassionally() })();
@ELLIOTTCABLE
Copy link
Author

There’s also a wave about this script: http://wave.google.com/wave/waveref/ell.io/w+JbVCRR2fA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment