Extra caution is recommended when installing recently uploaded/updated scripts (read more)
Be sure you trust any scripts you install

GMail - GTD Tickler

Very simple tickler for GMail using GCal( Google Calendar )

Updated for New GMail - Tue Nov 13, 2007

GTD Tickler functionality for GMail using GCal( Google Calendar ).

Features:
+ Adds 'Tickle me' link for each message in message view.
+ Custom Popup for Quick Add and link to 'edit event details>>'
+ ';' shortcut key to popup 'Tickle Me' box.
+ Quickly add 'When' you want to be reminded, just like GCal QuickAdd - otherwise -
+ Click 'edit event details' for GCal Template in separate window with link to original message.
+ Link in GCal reminder will open GMail message in a separate window.

Credits: grabbed loads of stuff from so many scripts:)

Have fun! Please send feedback to work out the kinks.

Version History
2.5 Merge bug fixes by cbg3 - bugs were breaking/halting script. Thank cbg3!
2.3 Fixed for New GMail






1 point
login to vote
Derek C user
Posted Sep 25, 2008

It would probably help if I had a better understanding of how Gmail, Gcal, and Google Apps worked. It would also be easier if I knew how to code javascript, but still I've made the most progress towards making the tickler Gapps-friendly.

I think the problem is how the script recognizes times and dates through the Gapps interface in comparison to the Gmail interface. I'm still getting the "tickle me error - invalid date or time" message.

Also, when I click on "Edit Event Details" in the Gmail tickler, it opens up the popup window and fills the title and the description, but the dates range from ??/??/???? to ??/??/????, regardless of what date I entered in the original tickler bubble.

Any ideas?

1 point
login to vote
Derek C user
Posted Sep 25, 2008

Okay, I've made some progress in 5 minutes. At the links for schema.google.com/g, replace the "g" with "apps" and it will give you another error.

Now it's telling me "tickle me error; invalid date or time."

At least it's communicating now. It just can't understand anything. I really want this to work with Google Apps.

1 point
login to vote
Derek C user
Posted Sep 25, 2008

For some reason, I need to have the tickler installed for Gmail Addons to work right. I'm trying to adapt it to Google Apps, but I'm having some difficulties.

Edit Event Details works so far, but Remind Me doesn't work so you always have to do it the long way. Anybody know what schemas.google.com is, and if it differs between regular Google accounts and Google Apps accounts? I think that's the only issue right now.

Here's the semi-fix for Google Apps users:

// ==UserScript==
// @name GMail - GTD Tickler
// @author Martin Ruiz
// @namespace Martin Ruiz
// @description Very Simple Ticker file functionality to GMail/GTDGMail using GCal
// @include http://mail.google.com/*
// @include https://mail.google.com/*
// ==/UserScript==
/*
Author: Martin Ruiz

Credits:

- I adapted code from a lot of other scripts on userscripts... lost track:)

Features:
+ Adds 'Tickle me' link for each message in message view.
+ Custom Popup for Quick Add and link to 'edit event details>>'
+ ';' shortcut key to popup 'Tickle Me' box.
+ Quickly add 'When' you want to be reminded, just like GCal QuickAdd - otherwise -
+ Opens and Fills out GCal Template in separate window with link to original message.
+ Link in GCal reminder will open GMail message in a seperate window.

Tips:
+ Choose the color 'red' or some other bold color so that it clearly stands out.
+ Make your goals calendar public and have your friends track your goals too.

Bonus:
+ Also works with Drafts - In round about way. Draft and email, save, leave draft,
come back to draft, then 'Tickle Me'
- Catch: only works with keyboard shortcut because 'Tickle Me' link does not appear
with draft.

Todo:
+ Add 'Tickle Me' link when new msgs in thread appear.
+ When not in GMail - The highlighted text and current link are included in GCal details

Testing:
+ Works with Firefox 2.0 for PC with Greasemonkey

Version History:
2.5 - 05.05.2008 - merge bug fixes by cbg3
2.4 - 03.26.2007 - Fix date issues
2.3 - 11.12.2007 - Fixed for New GMail
2.2 - 10.29.2007 - Bug fix - default reminders not being set
2.1 - 10.23.2007 - Bug fix - disable shortcut in text areas
2.0 - 09.01.2007 - Rewrite
+ Shortcut
+ Quick Add
*/
(function() {

var gmail;
var DEBUG = false;
var p = null;
var Window;
var Document;

window.addEventListener('load', function() {
if (unsafeWindow.gmonkey) {
unsafeWindow.gmonkey.load('1.0', main);
}
}, true);

function main(g) {

gmail = g;
gmail.registerViewChangeCallback(runGTDTickler);

Document = gmail.getNavPaneElement().ownerDocument;
Window = Document.defaultView;
p = new Prompt();
setupShortcut();
runGTDTickler();
}

function runGTDTickler() {
if (gmail.getActiveViewType() != 'cv') return;
//gmail.getActiveViewElement().addEventListener("DOMNodeInserted", function() { insertDelegateLinks() }, false);
insertDelegateLinks();
}

function setupShortcut() // keyboard shortcut: ";"
{
Window.addEventListener('keyup', function(e) {
if (e.altKey || e.ctrlKey || e.metaKey) {
return false;
}

if (e.keyCode!=59) return;
if (p.isEditing()) return;

var element;
if(e.target) element=e.target;
else if(e.srcElement) element=e.srcElement;
if(element.nodeType==3) element=element.parentNode;

if(element.tagName == 'INPUT' ||
element.tagName == 'TEXTAREA') return;

if (e.keyCode==59) delegate();
}, true);
}

function addReminder(title,when,content) {
when = parseWhen(when);
gcalHttpRequestUnauthorized({
method: 'GET',
url: 'https://www.google.com/calendar/hosted/yourdomain.com/compose?ctext=myevent%20' + when,
onComplete: function(detail) {
var txt = detail.responseText;
txt = txt.replace(/while\(1\)\;/ig,'');
log(txt);
var dates = eval('('+txt+')');
var enddate;
var startdate;
var date;
var from = dates[0][4];
var to = dates[0][5];
if (noDate(from) && noDate(to) ) {
alert('Tickle Me Error. Invalid date or time');
return;
}
// some heuristics...
if ( noDate(to) ) {
date = from;
log("no to date");
} else if ( noDate(from) ) {
date = to;
log("no from date");
} else if ( containsToday(when) ) {
date = from;
log("contains today");
} else if ( containsTomorrow(when) ) {
date = from;
log("contains tomorrow");
} else if ( containsDay(when) && !containsMonth(when) ) {//mon vs month
date = from;
log("contains day but not month");
} else if ( containsNext(when) ) {
date = from;
log("contains next");
} else if ( !isToday(from)) {
date = from;
log(from+' != '+today());
log("from != today");
} else {
date = to;
log("default date = to");
}
if (containsTime(when)) {
startdate = enddate = formatDate(date)+formatTime(date);
} else {
startdate = formatDate(date);
}
log(startdate+' - '+enddate);
addEvent(title, content, startdate, enddate);
},
onError: function(detail) {
alert('Tickle Me Error. Invalid date or time.');
}
});
}

function addEvent(title, content, startdate, enddate) {
var data=
"<entry>"+
" <category>"+
" <title>"+title+"</title>"+
(content?("<content>"+content+"</content>"):"")+
" <gd:transparency>"+
" "+
" <gd:eventstatus>"+
" "+
" <gd:when><gd:reminder>"+
"";
log(data);
gcalHttpRequestAuthorized({
method: 'POST',
url: "https://www.google.com/calendar/hosted/yourdomain.com/feeds/default/private/full",
data: data
});
}

function gcalHttpRequestAuthorized(params) {
gcalHttpRequestUnauthorized({
method: 'POST',
data: '',
url: "http://www.google.com/calendar/hosted/yourdomain.com/render",
onComplete: function (detail) {
var token;
try {
detail.responseHeaders.match(/CAL=([^;]+)/);
token= RegExp.$1;
if (!params.headers) params['headers'] = {};
params['headers']['Authorization'] = "GoogleLogin auth="+token;
gcalHttpRequestUnauthorized(params);
} catch (e) {}
if (!token) { alert('You may not be logged into GCal. Try again later.'); return;}
},
onError: function (detail) {
alert('Tickle Me Error. You may not be logged into GCal.');
}
});
}

function gcalHttpRequestUnauthorized(params) {
log('params\n'+requestToString(params)+'/params');
var r = {headers:{}};

r.method = params.method;

r.headers = (params.headers?params.headers:{});

r.headers["Content-Type"] = "application/atom+xml";
if (params.data) {
r.headers['Content-length'] = params.data.length;
r.data = params.data;
} else {
r.headers['Content-length'] = 0;
}

r.url = params.url;

r.onload = function(detail) {
if (detail.status == 200 || detail.status == 201) {
if(params.onComplete) params.onComplete(detail);
} else {
if (params.onError) params.onError(detail);
error("HTTP request failed2", detail);
}
}

r.onerror = function(detail) {
if (params.onError) params.onError(detail);
error("HTTP request failed", detail);
}

log('req\n'+requestToString(r)+'/req');

GM_xmlhttpRequest(r);
}

function requestToString(r) {
var s='';
for (var i in r) {
s = s + i + ": "+
(i=='headers'?requestToString(r[i]):r[i]) + "\n";
}
return s;
}

function parseWhen(when) {
var w = String(when);

// support 'tomorrow' shortcut 'tom'
w = w.replace(/^tom$/ig,"tomorrow");

if (containsNext(w)) {
if (!containsDay(w)) {
// support 'next' instead of one
w = w.replace(/next/ig,'one');
}
}
return w;
}

function get2Digits(x) {
return (x<10>
}
function today() {
var date = "yyyymmdd";
var d = new Date();

date = date.replace(/yyyy/i, d.getFullYear());
date = date.replace(/mm/i, get2Digits(d.getMonth()+1));
date = date.replace(/dd/i, get2Digits(d.getDate()));
log('today is '+date);
return date;
}
function isToday(d) { return (d==today()); }

function noDate(date) {return (date.search(/^\?/ig) > -1);}
function containsMonth(when) {return (when.search(/month/ig) > -1);}
function containsTomorrow(when) {return (when.search(/tomorrow/ig) > -1);}
function containsToday(when) {return (when.search(/(tod(ay)?)|(now)/ig) > -1);}
function containsNext(when) {return (when.search(/next/ig) > -1);}
function containsTime(when) {return (when.search(/\d{1,2}((pm|am)|(:\d{2}(am|pm)))/ig) > -1);}
function containsDay(when) {return (when.search(/(mon(day)?)|(tue(sday)?)|(wed(nesday)?)|(thu(rsday)?)|(fri(day)?)|(sat(urday)?)|(sun(day)?)/ig) > -1);}
function formatTime(date) {
var s = 'T00:00:00.000';//Z

if (date.length>8) {
s = 'T'+
date.substr(9,2)+':'+
date.substr(11,2)+':'+
date.substr(13,2)+'.000';//Z
}

return s;
}

function formatDate(date) {
var s = date.substr(0,4)+'-'+
date.substr(4,2)+'-'+
date.substr(6,2);

return s;
}

function containsTime(when) {
var t = /\d{1,2}((pm|am)|(:\d{2}(am|pm)))/ig;
return (when.search(t)>-1);
}

function log(x) {
if (!DEBUG) return;
GM_log(x);
return;
if (unsafeWindow.console) {
unsafeWindow.console.log.apply(unsafeWindow.console,
Array.slice(arguments));
}
}

function error(x,d) { log(x+"\n"+d.responseText); };

function askWhen(callback) {

p.show(callback);
return;

var when = prompt("When? eg. tomorrow, next week, friday at 4pm");
if (callback && when != null) callback(when);
}

function editReminderDetails(url) {
var nwin = Window.open(url,"Tickle Me",'height=470,width=600,scrollbars=yes,menubar=no,toolbar=no,status=no');
if (nwin.focus) {nwin.focus();}
}

function processReminder(when, title, gmail_url, event_details_url) {
if (when) {
addReminder(title, when, gmail_url);
return;
}

if (when == null) return; // cancelled --- 'blank' is Okay, open event details

editReminderDetails(event_details_url);
}

function delegate()
{
var url = String(Window.top.location);
var real_id = url.match(/#(.*)?\/(.*)/)[2];
var title = Window.frames.content.document.title; var t=title;
title = escape(t);
//var gmail_url = 'http://mail.google.com/a/yourdomain.com/mail/?source=navclient-ff#inbox/'+real_id;
var gmail_url = "http://mail.google.com/a/yourdomain.commail/?tf=1&fs=1&source=atom&view=cv&search=all&th=" + real_id;
var event_details_url = "http://www.google.com/calendar/hosted/yourdomain.com/event?action=TEMPLATE&text=" + title + "&details=" + escape(gmail_url);

gmail_url = gmail_url.replace(/\&/ig,'&'); // replace ampersands

askWhen(function(when) { processReminder(when, t, gmail_url, event_details_url); });
}

function insertDelegateLinks() {
var msgs = getMessages();

var msg_count = msgs.length;
for(var id=0; id < msg_count; id++) {
var msg = msgs[id];
if(!Document.getElementById("remindme_" + id)) {

var footerLink = document.createElement('div');
footerLink.className = "X5Xvu";
footerLink.setAttribute("style","float: left;");
footerLink.id = "remindme_" + id;
footerLink.addEventListener("click",delegate,false);

footerLink.innerHTML = 'Tickle Me';

msg.appendChild(footerLink);
}
}
}

// customized popup window
function Prompt()
{
var box = Document.getElementById('tickler_prompt');
if (box) { p.show(); return this; }

var self = this;

this.callback = null;

box = Document.createElement('div');
box.setAttribute('id','tickler_prompt');
box.style.zIndex = '101';

var overlay = Document.createElement('div');
box.appendChild(overlay);
overlay.setAttribute('style','display: none;position: fixed;width: 100%;height: 100%;top: 0px;left: 0px;background: #FFFFFF;filter: alpha(opacity=50);-moz-opacity: 0.5;opacity: 0.5;');

var container = Document.createElement('div');
box.appendChild(container);
container.setAttribute('style','display: none;position: fixed;width: 100%;height: 100%;top: 0px;left: 0px;background: transparent;');
var inner = Document.createElement('div');
container.appendChild(inner);
inner.setAttribute('style','display: block;width: 325px;height: 131px;margin: 120px auto 0 auto;background: #FFFFFF;text-align: center;border: 2px solid rgb(171, 171, 171); -moz-border-radius: 25px;');

inner.innerHTML=
'

'+
'
'+
''+
'
'+
'
'+
'
'+
'<table><tbody>'+
'<tr><td>
</td>'+
'<td>When?</td>'+
'<td>'+
'<input />'+
'</td>'+
'<td>
</td>'+
'</tr>'+
'<tr><td></td><td></td>'+
'<td>e.g., tomorrow, next week, friday at 5pm</td>'+
'<td></td></tr>'+
'<tr><td></td><td></td>'+
'<td><input />'+
' '+ //need space
'<label>edit event details »</label>'+
'</td>'+
'<td></td></tr>'+
'</tbody></table>'+
'
';

this.box = box;
this.overlay = overlay;
this.container = container;
this.inner = inner;

this.isEditing = function() { return (this.box.style.display=='');}

this.hide = function() { this.box.style.display = this.overlay.style.display = this.container.style.display= 'none';}
this.show = function(callback) {
this.callback = callback;
var when = Document.getElementById('tickler_when');
this.box.style.display = this.overlay.style.display = this.container.style.display= '';
when.value = '';
when.focus();
}

this.hide();

Document.body.appendChild(box);

// close button
Document.getElementById('tickler_close_btn').addEventListener("mousedown", function() { self.hide();}, false);
// ok button - book reminder in gcal
Document.getElementById('tickler_btn').addEventListener("click",
function() {
self.hide();
var when = Document.getElementById('tickler_when').value;
if(when) self.callback(when);
}, false);
// goto to gcal details
Document.getElementById('tickler_edit_details').addEventListener("mousedown",
function() {
self.hide();
self.callback("");
}, false);
// escape key
//Document.getElementById('tickler_when').addEventListener("blur", function(e) { self.hide(); }, false);

// enter and submit
Document.getElementById('tickler_when').addEventListener("keydown",
function(e) {
if (e.keyCode == 27) self.hide();
}, false);

Document.getElementById('tickler_when').addEventListener("keyup",
function(e) {
if (e.keyCode == 13) {
self.hide();
self.callback((e.target.value.length>0?e.target.value:null));
}
}, false);

}

function getMessages()
{
var msgs = [];
try {
var iterator = gmail.getActiveViewElement().ownerDocument.evaluate(
".//td[contains(concat(' ', @class, ' '), ' bEgJye ')]",
gmail.getActiveViewElement(),
null,
XPathResult.ORDERED_NODE_ITERATOR_TYPE,
null);
} catch(e) { return null; }
for (var msg = iterator.iterateNext(); msg; msg = iterator.iterateNext()) {
msgs.push(msg);
}

return msgs;
}

})();

----------

Comment if you're able to get it to work perfectly, and I'll do the same if I beat you to it.</gd:reminder>

1 point
login to vote
veganjay user
Posted May 11, 2008

Please disregard my previous comment - I did not realize GCal has default reminders!
Google Calendar -> Settings -> Calendars -> Notifications -> Event Reminders -> default
This is working great now! Thanks!

1 point
login to vote
veganjay user
Posted May 11, 2008

Nice add-on! I've been looking for something like this. Would it be possible to add a check-box for "Reminder" on the initial pop-up instead of going to "Edit event details"?
Thanks!

1 point
login to vote
Posted May 8, 2008

And also would be great if we could use this with Google Apps.
Thanks for a great tool.

1 point
login to vote
emptyowe user
Posted May 6, 2008

It would be great if the initial popup allowed one to select which calendar to post into. Or if one could specify the default calendar to post tickles to.

1 point
login to vote
Britonius user
Posted May 5, 2008

Thanks Ruiz. I look forward to your findings on testing FF3b5. I would also love to see this work with the GreaseKit Simbl plugin for Safari on Mac. Know how to do that or know a Mac script dev who could help make it compatible for the Safari browser as well?

1 point
login to vote
ruiz script's author
Posted May 5, 2008

@cbg3 thanks for the bug fixes. I've merged them into the script. We all appreciate you looking at it.

1 point
login to vote
cbg3 user
Posted May 4, 2008

Firebug says that containsMonth is undefined. This seems to prevent the script from adding anything to Google Calendar. But once the containsMonth function is added, the script works. (BTW, the definition of Tuesday in the containsDay function needs to be tweaked from (tue(day)?) to (tue(sday)?)

1 point
login to vote
ruiz script's author
Posted May 3, 2008

I've never tested with FF3b5. I'll try it.

1 point
login to vote
Britonius user
Posted May 1, 2008

I added this to Firefox 3b5 and I am not seeing anything. I also use Folders4Gmail, GmailAgenda & Goolge Account Multi-Login. I have also tried this in Safari via the GreaseKit plugin and still nothing.

1 point
login to vote
Posted Apr 2, 2008

I want to second Nathan Sudds - request - it would be great if this worked with Google Apps email accounts as well.

1 point
login to vote
ruiz script's author
Posted Mar 26, 2008

@MySaturdaySelf just fixed some of the date issues.

1 point
login to vote
Posted Mar 23, 2008

The script does not recognize the date now. I don't know why. Before it works ok, now it only accepts tomorrow as date. Any other dates listed in the Gcal quick add don't work.

1 point
login to vote
ruiz script's author
Posted Mar 17, 2008

@Kate2174 Will try:)

1 point
login to vote
Kate2174 user
Posted Mar 16, 2008

This is EXACTLY the tool I've been searching for, but I'm having the same problem from my private domain Google Apps Gmail as "Master Scott" (see 03/16/08 posting below). Hopefully, this can be resolved soon. Thanks!

1 point
login to vote
Posted Mar 16, 2008

I read the previous post about this, but am unclear as to the solution, or if there is one.

I'm using this in my custom domain Google Apps Gmail account. When I click the 'Tickle Me' link, it works like a charm, but, it defaults back to my @gmail.com calendar, not the @mydomain.com Gmail/cal account that I am sending from. Is this fixable on my end? If not, I'd like to request it. Thanks

FYI: My account does have Gmail 2 new features enabled. I have GTDInbox installed.

1 point
login to vote
ruiz script's author
Posted Feb 15, 2008

@Tim Nikolaev almost got it working with google apps. It needs a few tweaks. Hopefully, I'll have a working version shortly.

1 point
login to vote
Posted Feb 14, 2008

Google Apps is easily upgradable to Gmail 2.0.
It is under Domain Setting - General Tab:
Checkmark this: "Turn on new application features to my domain before they are rolled out to all Google Apps customers"

This is an awesome script and it would be great if it worked with Goggle Apps + Gmail 2.0

1 point
login to vote
ruiz script's author
Posted Jan 31, 2008

@Nathan Sudds - Unfortunately, it appears that Google Apps uses the old version of GMail. The script is incompatible with the old version. I'll have to investigate some more.

1 point
login to vote
Posted Jan 17, 2008

I love this idea, I'm using Google Apps, just wondering how hard it would be to adapt to Google Apps or if it's working already.

I've tried it but doesn't seem to add to my calendar.

Great script though -- oh and for those with Google Apps that mentioned the "new code not updated yet" if you are the administrator or can get them to change the setting in Manage This Domain -- In Domain Settings... Turn on new application features to my domain before they are rolled out to all Google Apps customers."

Here's the link to the instructionss:
http://www.google.com/support/a/bin/answer.py?a...

(sorry I hope this wouldn't be considered clogging up the thread.)

1 point
login to vote
rakess user
Posted Jan 16, 2008

thanks man it is greatly appreciated!!

1 point
login to vote
ruiz script's author
Posted Jan 15, 2008

@rakess No, but I can look into it.

1 point
login to vote
rakess user
Posted Jan 15, 2008

hey ruiz, thanks for th script but got a question my primary email is one gmail account but i have calendar my through google apps for my domain. Can the tickle be pointed to another calendar not attached to my email?

You could comment on this script if you were logged in.