Outfoxing Gmail with Greasemonkey
NOTE: The code in this post is out-of-date and does not work with recent versions of Outfox. See http://mindtrove.info/outfox-in-greasemonkey-revisited/ for a simpler, more compatible example. If you do update the GMail announcer code so it works with Outfox again, drop me a line and I'll link to your script.
Can you remember a time when the title of this blog post might have landed me in a straight jacket? Can you believe that was just a few short years ago? Yea, I can't either.
Anyway, Gary's post Outfox: speech, sound, and more for Firefox talks about a new Firefox extension. He's using it to create cross-platform, self-voicing Web apps for kids with disabilities using a pure JS API. He hopes to extend his work to support alternative input devices such as game pads and switches as the Outfox extension matures and grows more flexible.
One of the other potential uses listed on the Outfox homepage is Adding new I/O to web sites with Greasemonkey. Interesting. It's one thing to include Outfox explicitly in a page, but can it possibly work when injected by GM? What about for a complex app like Gmail with multiple iframes, dynamic changes, refreshing, etc.?
To learn about Outfox (and for fun), I decided to write a quick GM script for Gmail that announces the senders and times of new messages (bold items) in the inbox. (I would have done subject and summary too, but Outfox 0.1.0 appears to have some unicode issues and balked at some of the Gmail separator characters. Less is more at this point.) The script makes the announcement when the Gmail interface first loads, any time Gmail automatically refreshes its inbox view, or when the user clicks the refresh link to check for new mail. It is smart enough to announce a given message only once, however, so you don't hear the same message over and over again on each refresh.
Yes. It does actually work.
To try this script, make sure you have the Greasemonkey 0.8 and Outfox 0.1 extensions installed on Firefox 3. (Or use the latest available version of each.) Then visit the following link to have GM install the script: gmail_announcer.user.js.
For reference, the entire script is listed below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | // ==UserScript== // @name Gmail Announcer // @namespace http://www.mindtrove.info/ // @description Speaks new Gmail inbox messages using Outfox // @include https://mail.google.com/mail/* // @include http://mail.google.com/mail/* // @require http://outfox.googlecode.com/svn/trunk/js/outfox.js // ==/UserScript== var need_say = null; var ids = {}; function sayMessages(msgs) { if(!outfox.defaults.config) { // outfox really needs a better way to detect ready ... need_say = msgs; return false; } var header = 'New messages'; for(var id in msgs) { // say all messages var msg = msgs[id]; var segs = msg.split('»'); var sender = segs[0]; var time = segs[1].slice(segs[1].search('…')+2); if(header) { outfox.say(header); header = null; } outfox.say(sender + ' at ' + time); } return true; } function onOutfoxReady() { if(need_say) { // say anything already queued sayMessages(need_say); ids = need_say; need_say = null; } } function onTableChange(event) { var div = event.target; var trs = div.getElementsByTagName('tr'); var count = 0; var new_ids = {}; var curr_ids = {}; for(var i=0; i < trs.length; i++) { var tr = trs[i]; if(tr.innerHTML.search('<b>') != -1) { // marked as a new message if(ids[tr.id] == undefined) { // never announced new_ids[tr.id] = tr.textContent; ++count; } // curr is announced + new curr_ids[tr.id] = tr.textContent; } } // report if we can if(sayMessages(new_ids)) { ids = curr_ids; } } function onDocumentChange(event) { if(event.target.tagName == 'DIV') { var div = event.target; var tables = div.getElementsByTagName('table'); for(var i in tables) { var table = tables[i]; if(table.id != '' && !table.getAttribute('role')) { // watch just table changes from now on var div = table.parentNode.parentNode; div.addEventListener('DOMNodeInserted', onTableChange, false); document.removeEventListener('DOMNodeInserted', onDocumentChange, false); // start outfox var div = document.createElement('div'); document.body.appendChild(div); outfox.init(div, onOutfoxReady); // kick off initial read manually onTableChange({'target' : table.parentNode}); } } } } document.addEventListener('DOMNodeInserted', onDocumentChange, false); |
Accessibility Daily Moved
If you were tracking the Accessibility Daily feed I set up on Google Reader, you'll want to update your reader to point to the new feed aliased by http://a11y.mindtrove.info. I've switched over to using a clone of Eitan's Yahoo! Pipes aggregator to get more control over the output (and because I forgot the credentials for the Reader account! Doh!)
If you're interested, the raw URL is http://pipes.yahoo.com/pipes/pipe.run?_id=6280947538e79c29bdef3017f1f6846e&_render=rss.
Maze Day 2008
About once a year, K-12 kids with disabilities from all over North Carolina (and beyond) travel to UNC-Chapel Hill to take part in Maze Day. Throughout the day, the kids, their teachers, and their parents wander Sitterson Hall to try out the numerous games, applications, activities, and demos designed to help them learn and have fun at the same time.
The kids always have a blast, and this year was no exception. Some of the 21 projects demoed this year included the following:
- Carolina Rocker: A rocking-horse game that combines exercise and acoustic localization.
- Carolina Beat: An accessible version of Dance Dance Revolution for a good exercise workout to music.
- Braille Twister: A twister game using a DDR pad to form Braille letters and get exercise.
- Sweet Beat: A vision-based, edible (yes, edible) music tracker.
- Deep View: Auditory display of complex diagrams.
I believe the estimated attendance this year was something like 80 kids and 90 adults. About 30 or so stopped by to try my demo of Clique. While many of the kids were young and not yet familiar with using desktop applications, they still enjoyed figuring out Clique's commands, typing messages, and hearing Clique's output. And when they got bored, they jumped to the ever popular Nanomanipulator demo in the same room to touch carbon nanotubes using a PHANTOM and Atomic Force Microscope data.
While the day is intended to give these kids a trip to remember, I think I have just as much fun watching their reactions to the various projects. I find it equally wonderful to see the CS department staff, faculty, and students band together to create a memorable experience for all of the visitors.