By ruiz
Has 7 other scripts.
/*
Google Calendar Productivity Chain + ToDo list
Based on:
version 0.4.1 - last updated 2007-06-06
http://barklund.org/examples/userscripts/googlecalendartodolist.user.js
Which is Based on:
version 0.2 - last updated 2006-04-16
matiaspelenur@gmail.com
version 0.3 - last updated 2006-11-08
http://gimite.ddo.jp/
Version history since:
version 2.0 - productivity chains
*/
//
// HOW TO USE:
// Click '+' to add todo
// Simply click on an entry to edit it, Enter to save.
// Check to complete normal tasks
// Enter '!chain' in title to make task a productivity chain - inspired by todoist
// Enter '!done' to complete a productivity chain
// Check to complete current day on the productivity chain
// Flush completes when necessary
// Click 'SetCal' to select calendar for Productivity Chains
//
// TO DO: from barklund script
// - refactor to use mochikit in stead of homebrewed stuff
// - refactor to use category in stead of time
// - dockable sidebar
// - settings
// - groupable elements
// - colored elements
// - set dates for expiry and show up in calendar too
//
// ==UserScript==
// @name GCal Productivity Chain + ToDo List
// @namespace Martin Ruiz
// @description PRODUCTIVITY CHAINS using ToDo list sidebar for Google Calendar
// @include http://www.google.com/calendar/*
// @include https://www.google.com/calendar/*
// ==/UserScript==
(function() { // begin closure
setupShortcut(); // 'r' = 'refresh'
//////////////////////////////////////////////////////////////////////
// Utility functions
//////////////////////////////////////////////////////////////////////
//
// Memory-Leak-free event handling
//
// Current Browser/Javascript implementations contain memory leaks in their
// native DOM addEventListener methods. Use these functions instead.
//
var registeredEventListeners = new Array();
//
// Equivalent to target.addEventListener(event, listener, capture)
// Returns nothing.
//
function addEventListener(target, event, listener, capture)
{
registeredEventListeners.push( [target, event, listener, capture] );
target.addEventListener(event, listener, capture);
}
//
// Removes all event listeners from the page when it unloads.
//
function unregisterEventListeners(event)
{
while (registeredEventListeners.length > 0) {
var rel = registeredEventListeners.pop();
rel[0].removeEventListener(rel[1], rel[2], rel[3]);
}
window.removeEventListener('unload', unregisterEventListeners, false);
}
// And add the unload event handler that will unload all the registered
// handlers, including itself.
addEventListener(window, 'unload', unregisterEventListeners, false);
function xpath(query) {
return document.evaluate(query, document, null,
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}
function single_xpath(query) {
var eltList = xpath(query)
return eltList.snapshotItem(0);
}
function add(siblingNode,node) {
siblingNode.parentNode.insertBefore(node, siblingNode.nextSibling);
}
function remove(node) {
node.parentNode.removeChild(node);
}
function hide(node) {
node.style.display = 'none';
}
function show(node) {
node.style.display = 'block';
}
function swap(something, other) {
hide(something);
show(other);
}
//////////////////////////////////////////////////////////////////////
// Todo Chain functions
//////////////////////////////////////////////////////////////////////
var CalMenu = new SetCalMenu();
function Chain(isChain,lastupdate,count,id,editUrl,prevupdate)
{
this.count = (count?count:0);
this.lastupdate = (lastupdate?lastupdate:"");
this.isChain = (isChain?true:false);
this.prevupdate = (prevupdate?prevupdate:"");
this.lastid = (id?id:"");
this.lastEditUrl = (editUrl?editUrl:"");
this.increment = function() {
if (this.lastupdate!=""&&this.lastupdate!=this.today())
{
this.prevupdate = this.lastupdate;
}
this.lastupdate = this.today();
this.count = this.count+1;
return this;
}
this.decrement = function() {
this.lastupdate = this.prevupdate;
this.count = this.count-1;
return this;
}
this.reset = function() {
this.lastupdate = "";
this.count = 0;
return this;
}
this.toString = function() {
var s = 'new Chain('+
(this.isChain?'true':'false')+','+
'"'+this.lastupdate+'",'+
this.count+','+
'"'+this.lastid+'",'+
'"'+this.lastEditUrl+'",'+
'"'+this.prevupdate+'")';
return s;
}
this.today = function() {
var d = new Date();
var s = d.getFullYear()+'-'+
(d.getMonth()<9?'0'+(d.getMonth()+1):d.getMonth()+1)+'-'+
(d.getDate()<10?'0'+d.getDate():d.getDate());
return s;
}
this.yesterday = function() {//buggy
var d = new Date();
d.setDate(d.getDate()-1);
var s = d.getFullYear()+'-'+
(d.getMonth()<9?'0'+(d.getMonth()+1):d.getMonth()+1)+'-'+
(d.getDate()<10?'0'+d.getDate():d.getDate());
return s;
}
this.isBroken = function() {
return (this.isChain)&&(this.prevupdate!="")&&(this.prevupdate!=this.yesterday())&&(this.lastupdate!="")&&(this.lastupdate!=this.yesterday());
}
this.doneToday = function() {
return (this.isChain)&&(this.lastupdate==this.today());
}
this.setFlagFromTitle = function(s) {
var re = /(\!chain)|(!link)|(!links)/i;
this.isChain = re.test(s);
return this;
}
this.createLink = function(title, callback, date)
{
var t = '['+(this.count+1)+(this.count==0?' day] ':' days] ')+title;
date = (date!=null&&date!="")?date:this.today();
var status = EVENT_OPEN;
var req_data=
"<entry xmlns='http://www.w3.org/2005/Atom'"+
" xmlns:gd='http://schemas.google.com/g/2005'>"+
" <category scheme='http://schemas.google.com/g/2005#kind'"+
" term='http://schemas.google.com/g/2005#event'></category>"+
" <title type='text'>"+t+"</title>"+
" <gd:transparency"+
" value='http://schemas.google.com/g/2005#event.opaque'>"+
" </gd:transparency>"+
" <gd:eventStatus"+
" value='"+status+"'>"+
" </gd:eventStatus>"+
" <gd:when startTime='"+date+"'></gd:when>"+
"</entry>";
var self = this;
gcalHttpRequest({
method: "POST",
url: CalMenu.cal().link, //"https://www.google.com/calendar/feeds/default/private/full",
data: req_data,
onComplete: function(detail){
var elem= responseXml(detail);
self.lastid = getTagText(elem, "id");
var links = elem.getElementsByTagName("link");
for (var j= 0; j<links.length; ++j){
if (links[j].getAttribute("rel") == "edit"){
self.lastEditUrl = links[j].getAttribute("href");
}
}
self.increment();
callback();
}
})
}
this.deleteLink = function(title, callback) //function updateTodo(todo, title, status, callback, content){
{
var date = this.lastupdate;
var status = EVENT_CANCELED;
var req_data=
"<entry xmlns='http://www.w3.org/2005/Atom'"+
" xmlns:gd='http://schemas.google.com/g/2005'>"+
" <category scheme='http://schemas.google.com/g/2005#kind'"+
" term='http://schemas.google.com/g/2005#event'></category>"+
" <title type='text'>"+title+"</title>"+
" <gd:transparency"+
" value='http://schemas.google.com/g/2005#event.opaque'>"+
" </gd:transparency>"+
" <gd:eventStatus"+
" value='"+status+"'>"+
" </gd:eventStatus>"+
" <gd:when startTime='"+date+"'></gd:when>"+
"</entry>";
var self = this;
gcalHttpRequest({
method: "PUT",
url: self.lastEditUrl,
data: req_data,
onComplete: function(detail) {
self.decrement();
callback();
}
})
}
return this;
}
function DeleteChain(s)
{
var re = /\!done/i;
return re.test(s);
}
function StringToChain(s)
{
try
{
if (s==null||s=="") return new Chain();
return eval('('+s+')');
} catch(e) { return new Chain(); } //some error occured
}
function SetCalMenu()
{
this.initialized = false;
this.cals = [];
this.initialize = function()
{
var url = 'http://www.google.com/calendar/feeds/default/allcalendars/full';
var self = this;
gcalHttpRequest({
method: "GET",
url: url,
onComplete: function(detail){
self.cals = [];
self.cals.push({id:"https://www.google.com/calendar/feeds/default/private/full",
title:"Default",
color:"",
link:"https://www.google.com/calendar/feeds/default/private/full"});
var entries= responseXml(detail).getElementsByTagName("entry");
for (var i=0;i<entries.length;i++)
{
var cal = {};
cal.id = getTagText(entries[i],'id');
cal.title = getTagText(entries[i], 'title');
cal.color = getTagText(entries[i], 'color'); //doesn't work
var links = entries[i].getElementsByTagName("link");
for (var j= 0; j<links.length; ++j){
if (links[j].getAttribute("rel") == "alternate")
{
cal.link = links[j].getAttribute("href");
break;
}
}
self.cals.push(cal);
}
self.initialized=true;
}
})
}
this.initialize();
this.getCal = function() { return GM_getValue("Calendar","Default"); }
this.setCal = function(s) { return GM_setValue("Calendar", s); }
this.cal = function()
{
var n = this.getCal();
for (var i=0;i<this.cals.length;i++)
{
if (this.cals[i].title==n) return this.cals[i];
}
return this.cals[0]; //default;
}
this.setBold = function()
{
var n = this.cal();
for (var i=0;i<this.cals.length;i++)
{
if (n.title==this.cals[i].title)
{
this.cals[i].menu.style.fontWeight='bold';
}
else
{
this.cals[i].menu.style.fontWeight='';
}
}
}
this.show = function()
{
if (!this.initialized) return;
var setcal = document.getElementById('todo_setcal');
var menu_div = document.getElementById('todo_setcal_menu');
if (!menu_div)
{
var self = this;
menu_div = document.createElement('div');
menu_div.setAttribute('id','todo_setcal_menu');
menu_div.setAttribute('style',"display: none; position: absolute; z-index: 100");
var menu = document.createElement('div');
menu_div.appendChild(menu);
menu.setAttribute('id','addP');
for (var i=0;i<this.cals.length;i++)
{
var menuItem = document.createElement('div');
menuItem.className = 'addmenu';
menuItem.setAttribute('onmouseout','this.style.textDecoration="none"');
menuItem.setAttribute('onmouseover','this.style.textDecoration="underline"');
menuItem.setAttribute('style','text-decoration: none;');
//menuItem.style.backgroundColor = this.cals[i].color;
menuItem.innerHTML = this.cals[i].title;
addEventListener(menuItem, 'mousedown', function(o) { self.setCal(o.target.innerHTML);self.hide();});
menu.appendChild(menuItem);
this.cals[i].menu = menuItem;
}
setcal.appendChild(menu_div);
}
menu_div.style.left = (setcal.offsetLeft+15)+'px';
menu_div.style.top = (setcal.offsetTop+setcal.offsetHeight)+'px';
this.setBold();
menu_div.style.display = '';
}
this.hide = function()
{
var menu_div = document.getElementById('todo_setcal_menu');
if (menu_div)
{
menu_div.style.display='none';
}
}
}
function ShowSetCal()
{
var menu_div = document.getElementById('todo_setcal_menu');
if (!menu_div||menu_div.style.display=='none')
{
CalMenu.show();
}
else
{
CalMenu.hide();
}
}
function setupShortcut() // keyboard shortcut: "r"
{
window.addEventListener('keydown', function(e) {
if (e.altKey || e.ctrlKey || e.metaKey) {
return false;
}
if (e.keyCode==82) unsafeWindow._Ping(); //gcal internal function;;
}, true);
}
//////////////////////////////////////////////////////////////////////
// Gcal todo functions
//////////////////////////////////////////////////////////////////////
// constants for eventStatus - http://code.google.com/apis/gdata/elements.html#gdEventStatus
var EVENT_CANCELED = "http://schemas.google.com/g/2005#event.canceled";
var EVENT_CONFIRMED = "http://schemas.google.com/g/2005#event.confirmed";
var EVENT_TENTATIVE = "http://schemas.google.com/g/2005#event.tentative";
var EVENT_CLOSED = EVENT_TENTATIVE;
var EVENT_OPEN = EVENT_CONFIRMED;
function getTodos(callback){
gcalHttpRequest({
method: "GET",
url: "https://www.google.com/calendar/feeds/default/private/full?"+
"start-min=1970-01-01T00:00:00.000Z&start-max=1970-01-01T01:00:00.000Z",
onComplete: function(detail){
var entries= responseXml(detail).getElementsByTagName("entry");
var events= [];
for (var i= 0; i<entries.length; ++i){
events.push(elementToTodo(entries[i]));
}
callback(events);
}
});
}
function addTodo(title, status, callback, content){
var req_data=
"<entry xmlns='http://www.w3.org/2005/Atom'"+
" xmlns:gd='http://schemas.google.com/g/2005'>"+
" <category scheme='http://schemas.google.com/g/2005#kind'"+
" term='http://schemas.google.com/g/2005#event'></category>"+
" <title type='text'>"+title+"</title>"+
(content?("<content type='text'>"+content+"</content>"):"")+
" <gd:transparency"+
" value='http://schemas.google.com/g/2005#event.opaque'>"+
" </gd:transparency>"+
" <gd:eventStatus"+
" value='"+status+"'>"+
" </gd:eventStatus>"+
" <gd:when startTime='1970-01-01T00:00:00.000Z'"+
" endTime='1970-01-01T01:00:00.000Z'></gd:when>"+
"</entry>";
gcalHttpRequest({
method: "POST",
url: "https://www.google.com/calendar/feeds/default/private/full",
data: req_data,
onComplete: function(detail){
var event= elementToTodo(responseXml(detail));
callback(event);
}
})
}
function updateTodo(todo, title, status, callback, content){
var req_data=
"<entry xmlns='http://www.w3.org/2005/Atom'"+
" xmlns:gd='http://schemas.google.com/g/2005'>"+
" <category scheme='http://schemas.google.com/g/2005#kind'"+
" term='http://schemas.google.com/g/2005#event'></category>"+
" <title type='text'>"+title+"</title>"+
(content?("<content type='text'>"+content+"</content>"):"")+
" <gd:transparency"+
" value='http://schemas.google.com/g/2005#event.opaque'>"+
" </gd:transparency>"+
" <gd:eventStatus"+
" value='"+status+"'>"+
" </gd:eventStatus>"+
" <gd:when startTime='1970-01-01T00:00:00.000Z'"+
" endTime='1970-01-01T01:00:00.000Z'></gd:when>"+
"</entry>";
gcalHttpRequest({
method: "PUT",
url: todo.editUrl,
data: req_data,
onComplete: function(detail) {
var event = elementToTodo(responseXml(detail));
callback(event);
}
})
}
function elementToTodo(elem){
var event= {};
event.id = getTagText(elem, "id");
event.title = getTagText(elem, "title");
event.content = getTagText(elem, "content");
event.status = elem.getElementsByTagName("eventStatus")[0].attributes[0].value;
event.chain = StringToChain(event.content); //alert('c: '+event.chain.toString());
var links = elem.getElementsByTagName("link");
for (var j= 0; j<links.length; ++j){
if (links[j].getAttribute("rel") == "edit"){
event.editUrl = links[j].getAttribute("href");
}
}
event.toString = eventToString;
return event;
}
function eventToString(){
return this.title+": "+(this.status == EVENT_OPEN ? "OPEN" : "CLOSED");
}
function gcalHttpRequest(params){
document.cookie.match(/CAL=([^;]+)/);
var token= RegExp.$1;
GM_xmlhttpRequest({
method: params.method,
url: params.url,
headers: {"Content-Type": "application/atom+xml", "Authorization": "GoogleLogin auth="+token},
data: params.data,
onload: function(detail){
if (detail.status==200 || detail.status==201){
params.onComplete(detail);
}else{
error(["HTTP request failed", detail]);
}
},
onerror: function(detail){
error(["HTTP request failed", detail]);
}
});
}
function responseXml(detail){
var domParser= new DOMParser();
return domParser.parseFromString(detail.responseText, "application/xml");
}
function getTagText(elem, tagName){ // Tenuki.
var textNode= elem.getElementsByTagName(tagName)[0].childNodes[0];
return textNode ? textNode.nodeValue : "";
}
function error(obj){
if (unsafeWindow.console){
unsafeWindow.console.log(obj);
}else{
GM_log(obj);
}
}
//////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////
var todo_status_div;
var todo_status_span;
// begin closure
(function() {
var calendar_div = document.getElementById('nb_0');
if (calendar_div) {
todo_div = document.createElement('div');
todo_div.id = 'nb_todo';
todo_div.innerHTML = new String(calendar_div.innerHTML);
todo_div.style.paddingTop = '8px';
todo_div.style.paddingRight = '6px';
// first we hide this new div and add it to the document, so that we can
// perform xpath queries on it
todo_div.style.display = 'none';
//calendar_div.parentNode.insertBefore(todo_div, calendar_div.nextSibling);
calendar_div.parentNode.insertBefore(todo_div, calendar_div);
// now modify the html we copied: remove the calendars div, change the name, etc
remove(single_xpath("//div[@id='nb_todo']//div[@id='calendars']"));
todo_title_div = single_xpath("//div[@id='nb_todo']//div[@id='nt_0']");
todo_title_div.id = 'nt_todo';
todo_title_div.innerHTML =
'<div style="float: left; background-image: url(images/card_button_a.gif); height: 21px; width: 3px;"></div>'+
'<div style="margin: 0.3em 0pt 0pt 0.3em; float: left;">'+
"<img width='11' height='11' src='images/opentriangle.gif' id='todo_tri_fav' onClick=\"_SwapDisplay('todo_ext_fav', 'todo_tri_fav');\"></img>"+
' ToDo '+
'<a class="lk" id="todo_add"><img src="http://www.google.com/calendar/images/btn_add.gif"></img></a>'+
'</div>'+
'<div style="float: right; cursor: pointer;" id="todo_setcal">'+
'<div id="todoMenu" style="float: left; background-image:url(images/card_button_a.gif); background-position: -3px 50%; height: 21px; width: 4px;"></div>'+
'<div style="float: left; background-image: url(images/card_button_m2.gif); height: 21px;">'+
'<div style="margin: 0.3em 0.3em 0pt;">SetCal</div>'+
'</div>'+
'<div style="float: left; background-image: url(images/card_button_a.gif); height: 21px; width: 23px;background-position: -7px 50%;"></div>'+
'</div>';
todo_title_div.setAttribute('onclick',''); // to get rid of copied SwapDisplay call from calendar div
addEventListener(document.getElementById("todo_add"), 'click', function() { createTodoEntry({}, true); }, false);
addEventListener(document.getElementById("todo_setcal"), 'click', function() { ShowSetCal(); }, false);
todo_content_div = document.createElement('div');
todo_content_div.setAttribute("class", "nb");
todo_content_div.id = 'todo_ext_fav';
todo_content_div.style.backgroundColor = 'white';
// add(todo_title_div, todo_content_div);
todo_status_div = single_xpath("//div[@id='nb_todo']//div[@id='calendarsBottomChrome']");
todo_status_div.id = 'todoBottomChrome';
todo_status_div.style.fontSize = "80%";
todo_status_div.style.textAlign = "right";
todo_status_div.style.paddingRight = "1px";
todo_status_div.parentNode.insertBefore(todo_content_div,todo_status_div);
var todo_current_span = todo_status_div.getElementsByTagName("span")[0];
todo_status_span = document.createElement('span');
add(todo_current_span, todo_status_span);
remove(todo_current_span);
todo_status_span.setAttribute("class","lk");
todo_status_span.appendChild(document.createTextNode("Flush closed"));
load(function(){
// finally show the todo box
show(todo_div);
sortTodos();
setInterval(load, 5*60*1000); // Auto reload
});
}
function load(callback) {
getTodos(function(todos){
if (!editing()){
todo_content_div.innerHTML= "";
for (var i= 0; i<todos.length; ++i){
createTodoEntry(todos[i]);
}
}
if (typeof(callback)=="function") callback();
});
}
function editing() {
var todo_divs= todo_content_div.getElementsByTagName("div");
for (var i=0; i<todo_divs.length; i++) {
if (todo_divs[i].getAttribute("editing")) return true;
}
return false;
}
function sortTodos() {
var todo_divs = todo_content_div.getElementsByTagName("div");
var todo_arr = new Array();
for (var i = 0; i < todo_divs.length; i++)
todo_arr.push(todo_divs[i]);
todo_arr.sort(compareTodos);
for (var i = 0; i < todo_arr.length; i++) {
remove(todo_arr[i]);
todo_content_div.appendChild(todo_arr[i]);
}
}
// link sort to flush after 2 seconds
addEventListener(todo_status_span, 'click', function(event) {
window.setTimeout(sortTodos, 2*1000);
}, false);
function getInputByType(inputs, type) {
for (var i = inputs.length; i--; )
if (inputs[i].type == type) return inputs[i];
return null;
}
function compareTodos(t1, t2) {
// order by checked if not equal
var t1s = t1.getElementsByTagName("input");
var t1_checked = getInputByType(t1s, "checkbox").checked;
var t2s = t2.getElementsByTagName("input");
var t2_checked = getInputByType(t2s, "checkbox").checked;
if (t1_checked && !t2_checked) {
return 1;
} else if (t2_checked && !t1_checked) {
return -1;
} else {
// if equal order lexicographically
var t1_title = getInputByType(t1s, "text").value;
var t2_title = getInputByType(t2s, "text").value;
if (t1_title > t2_title) {
return 1;
} else if (t1_title < t2_title) {
return -1;
} else {
return 0;
}
}
}
function createTodoEntry(todo, focus) {
var i = (todo_content_div.hasChildNodes() ? todo_content_div.childNodes.length : 0);
var todo_div = document.createElement('div');
var chk = document.createElement('input');
chk.type = 'checkbox';
chk.name = chk.id = 'todo_chk_' + i;
chk.checked = (todo.status == EVENT_CLOSED)||(todo.chain?todo.chain.doneToday():false);
addEventListener(chk, 'click', function() {
todo_edit.style.backgroundColor = 'yellow';
setEditing(true);
updateStatus(chk.checked ? EVENT_CLOSED : EVENT_OPEN, function(){ disableEdit(false); });
}, false);
todo_div.appendChild(chk);
var todo_edit = document.createElement('input');
todo_edit.id = 'todo_edit_' + i;
todo_edit.style.fontSize = 'small';
todo_edit.type = 'text';
todo_edit.value = todo.title || "";
todo_edit.size = 15;
todo_edit.style.textDecoration = (chk.checked) ? "line-through" : "none";
function setEditing(flag) {
todo_div.setAttribute("editing", flag ? "on" : "");
}
function enableEdit() {
if (!todo_edit.readOnly) return;
setEditing(true);
todo_edit.readOnly = false;
todo_edit.style.borderTop = '1px solid gray';
todo_edit.style.borderLeft = '1px solid gray';
todo_edit.style.borderBottom = '1px solid silver';
todo_edit.style.borderRight = '1px solid silver';
todo_edit.style.backgroundColor = 'lightyellow';
todo_edit.style.color='darkblue';
todo_edit.selectionStart = 0;
todo_edit.selectionEnd = todo_edit.value.length;
}
function disableEdit(saving) {
todo_edit.readOnly = true;
todo_edit.style.border = 'none';
todo_edit.style.backgroundColor = saving ? 'yellow' : 'white';
todo_edit.style.color='black';
if (!saving) setEditing(false);
unsafeWindow._Ping(); //gcal internal function;
}
function updateTitle(title, callback) {
if (title == todo.title) {
callback();
} else if(todo.id) {
todo.chain.setFlagFromTitle(title);
if (DeleteChain(title))
{
todo.status = EVENT_CANCELED;
}
updateTodo(todo, title, todo.status, function(t) { updateEntry(t); callback(); }, todo.chain.toString());
} else {
var c = new Chain();
var content = c.setFlagFromTitle(title).toString();
addTodo(title, EVENT_OPEN, function(t) { updateEntry(t); callback(); }, content);
}
}
function updateStatus(status, callback) {
// if (status == todo.status){
// callback();
// }else{
if (todo.chain.isChain)
{
switch (status) {
case EVENT_CLOSED:
status=EVENT_OPEN;
if (todo.chain.isBroken()) todo.chain.reset();
todo.chain.createLink(todo.title, function() {updateTodo(todo, todo.title, status, function(t) {
updateEntry(t); callback(); }, todo.chain.toString());});
break;
case EVENT_OPEN:
todo.chain.deleteLink(todo.title, function() {updateTodo(todo, todo.title, status, function(t) {
updateEntry(t); callback(); }, todo.chain.toString());});
break;
}
return;
}
updateTodo(todo, todo.title, status, function(t) { updateEntry(t); callback(); }, todo.chain.toString());
// }
}
function updateEntry(t) {
switch (t.status) {
case EVENT_OPEN:
case EVENT_CLOSED:
todo = t;
todo_edit.value = todo.title || "";
chk.checked = (t.status == EVENT_CLOSED)||todo.chain.doneToday();
todo_edit.style.textDecoration = (chk.checked) ? "line-through" : "none";
sortTodos();
break;
case EVENT_CANCELED:
destroy();
break;
}
}
function destroy() {
// clean up listeners?
todo_content_div.removeChild(todo_div);
}
addEventListener(todo_status_span, 'click', function(event) {
if (todo.status == EVENT_CLOSED) {
updateStatus(EVENT_CANCELED, function(){});
}
}, false);
disableEdit();
addEventListener(todo_edit, 'click', function(event) {
enableEdit();
}, false);
addEventListener(todo_edit, 'hover', function(event) {
this.style.border = '1px solid gray';
}, false);
addEventListener(todo_edit, 'blur', function(event) {
disableEdit(true);
updateTitle(this.value, function(){ disableEdit(false); });
}, false);
addEventListener(todo_edit, 'keydown', function(event) {
if (!this.readOnly && event.keyCode == 13) { //Enter
this.blur();
}else if (!this.readOnly && event.keyCode == 27) { //Escape
this.value= todo.title;
this.blur();
}
}, true);
todo_div.appendChild(todo_edit);
todo_div.setAttribute('todo_content',todo.title);
todo_content_div.appendChild(todo_div);
if (focus) {
enableEdit();
todo_edit.focus();
}
}
})(); // end closure
})(); // end closure