Skip to content

Instantly share code, notes, and snippets.

@scottferg
Created October 14, 2009 14:36
Show Gist options
  • Save scottferg/210120 to your computer and use it in GitHub Desktop.
Save scottferg/210120 to your computer and use it in GitHub Desktop.
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is
* Chad Smith.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
if(!com)
var com={};
if(!com.chadsmith)
com.chadsmith={};
com.chadsmith.googlewave=function(){
return {
Auth:null,
WAVE:null,
load:function(){
com.chadsmith.googlewave.initChecker()
},
unload:function(){
com.chadsmith.googlewave.delegateNewMaster();
com.chadsmith.googlewave=null
},
httpReq:null,
intervalId:null,
timeoutId:null,
gwStatusBarId:'google-wave-statusbar',
gwStatusBar:null,
tooltipLabels:null,
tooltipLabelCount:0,
tooltipElements:null,
tooltipElementCount:0,
prefBranch:Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefService).getBranch('extensions.google-wave.'),
JSON:Components.classes["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON),
passwordSites:['chrome://google-wave'],
waveLogo:'chrome://google-wave/content/google-wave.png',
inboxURL:'https://wave.google.com/wave/',
loginURL:'https://www.google.com/accounts/ClientLogin',
logoutURL:'https://wave.google.com/wave/logout',
aboutURL:'http://thatsmith.com/2009/10/google-wave-addon-for-firefox',
usernameFormField:'Email',
passwordFormField:'Passwd',
inboxCount:null,
unreadCount:null,
pwdmgr:null,
pwdmgrInternal:null,
oldUsername:null,
defaultFrequency:1000*60*60*2,
defaultHttpTimeout:30000,
lastUpdated:null,
checkingInProgress:false,
finishedChecking:function(){
this.debug('finished checking');
this.checkingInProgress=false
},
initChecker:function(){
this.debug('in initChecker');
try{
this.gwStatusBar=document.getElementById(this.gwStatusBarId);
this.initOption();
var loggedIn=false,
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow);
if(com.chadsmith.googlewave!=null&&com.chadsmith.googlewave.intervalId!=null){
this.debug(win+' intervalId is not null');
loggedIn=false;
if(this.gwStatusBar!=null){
var
myBar=this.gwStatusBar,
oldBar=xulWindow.docShell.contentViewer.DOMDocument.getElementById(this.gwStatusBarId);
this.debug('copying status bar');
myBar.src=oldBar.src;
myBar.setAttribute('label',oldBar.getAttribute('label'));
myBar.setAttribute('height',oldBar.getAttribute('height'));
myBar.setAttribute('style',oldBar.getAttribute('style'));
if(oldBar.getAttribute('tooltiptext')){
this.debug('copying tooltiptext');
myBar.setAttribute('tooltiptext',oldBar.getAttribute('tooltiptext'))
}
else{
this.debug('copying tooltip');
if(myBar.hasAttribute('tooltiptext'))
myBar.removeAttribute('tooltiptext');
var
tooltipId=oldBar.getAttribute('tooltip'),
oldTooltip=xulWindow.docShell.contentViewer.DOMDocument.getElementById(tooltipId),
tooltip=this.newTooltip(document,myBar),
i;
for(i=0;i<oldTooltip.childNodes.length;i++)
tooltip.appendChild(this.cloneTooltipLabel(document,oldTooltip.childNodes.item(i)));
document.getElementById('mainPopupSet').appendChild(tooltip);
myBar.setAttribute('tooltip',tooltip.getAttribute('id'))
}
}
}
}
if(document.getElementById(this.gwStatusBarId)&&!loggedIn)
this.start()
}
catch(e){
this.warn(e)
}
},
delegateNewMaster:function(){
try{
this.debug('trying to delegate new master');
if(this.intervalId!=null){
var
newMasterFound=false,
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()&&!newMasterFound){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow);
if(com.chadsmith.googlewave!=null&&com.chadsmith.googlewave.intervalId==null){
this.debug('found new master '+win);
com.chadsmith.googlewave.scheduleInboxChecking(win);
newMasterFound=true
}
}
}
}
catch(e){
this.warn(e)
}
},
login:function(){
try{
this.debug('trying to login');
var
loginData=this.getLoginData(),
username=loginData.username,
pass=loginData.password;
if(username&&pass){
this.httpReq=new XMLHttpRequest();
this.httpReq.open('POST',this.loginURL,true);
this.httpReq.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
this.setReqTimeout(this.loginURL);
var
module=this;
this.httpReq.onload=function(){
module.parseLogin()
};
this.httpReq.send('accountType=GOOGLE&'+this.usernameFormField+'='+encodeURIComponent(username)+'&'+this.passwordFormField+'='+encodeURIComponent(pass)+'&service=wave&source=chadsmith-GoogleWaveFirefox-0.0.1');
return true
}
else{
this.finishedChecking();
var
missing='';
if(username==null||username.length==0)
missing+='email';
if(pass==null||pass.length==0)
missing+=((missing!='')?' and ':'')+'password';
this.setStatusBarAttribute('label',missing+' missing from settings');
this.setStatusBarAttribute('height',1);
this.setStatusBarAttribute('src',this.waveLogo);
return false
}
}
catch(e){
this.finishedChecking();
this.warn(e);
return false
}
},
parseLogin:function(){
this.debug('parsing login');
try{
this.clearReqTimeout();
var
responseText=this.httpReq.responseText,
error=responseText.match(/Error=([A-z]+)/),
auth=responseText.match(/Auth=([A-z0-9_-]+)/),
wave=responseText.match(/SID=([A-z0-9_-]+)/);
if(error){
this.finishedChecking();
this.setStatusBarAttribute('label',error[1]);
this.setStatusBarAttribute('height',1);
this.setStatusBarAttribute('src',this.waveLogo)
}
else
if(auth&&wave){
com.chadsmith.googlewave.Auth=auth[1];
com.chadsmith.googlewave.WAVE=wave[1];
this.debug('successfully logged in');
this.readInbox()
}
}
catch(e){
this.finishedChecking();
this.warn(e)
}
},
readInbox:function(){
try{
if(!com.chadsmith.googlewave.Auth||!com.chadsmith.googlewave.WAVE)
this.login();
this.debug('retrieving inbox');
this.httpReq=new XMLHttpRequest();
this.httpReq.open('GET',this.inboxURL+'?nouacheck&auth='+encodeURIComponent(com.chadsmith.googlewave.Auth),true);
this.httpReq.setRequestHeader("WAVE",encodeURIComponent(com.chadsmith.googlewave.WAVE));
this.setReqTimeout(this.inboxURL+'?nouacheck&auth='+encodeURIComponent(com.chadsmith.googlewave.Auth));
var
module=this;
this.httpReq.onload=function(){
module.parseInbox()
};
this.httpReq.send(null)
}
catch(e){
this.finishedChecking();
this.warn(e)
}
},
parseInbox:function(evt){
try{
this.debug('parsing inbox');
this.clearReqTimeout();
if(!com.chadsmith.googlewave.Auth||!com.chadsmith.googlewave.WAVE){
this.login();
return
}
this.setLastUpdatedToNow();
this.parseResponseText(this.httpReq.responseText);
this.finishedChecking()
}
catch(e){
this.finishedChecking();
this.warn(e)
}
},
parseResponseText:function(responseText){
var data=responseText.match(/var json = (\{"r":"\^d1".*});/);
if(!data){
this.finishedChecking();
this.setStatusBarAttribute('label','Incompatible browser');
return this.error('Useragent check failed');
}
data=data[1].match(/"7":([0-9]+),/g);
var messages=0;
var unread=0;
var n;
for(n in data){
messages++;
unread+=parseInt(data[n].match(/"7":([0-9]+),/)[1])?1:0
}
this.setStatusBarAttribute('label',unread?unread:'');
this.setStatusBarAttribute('style','');
this.setStatusBarAttribute('height',1);
this.setStatusBarAttribute('src',this.waveLogo);
},
checkWithLogout:function(){
if(this.checkingInProgress){
this.debug('another check is still in progress');
return
}
this.checkingInProgress=true;
try{
this.debug('logging out');
this.httpReq=new XMLHttpRequest();
this.httpReq.open('GET',this.logoutURL+'?auth='+encodeURIComponent(com.chadsmith.googlewave.Auth),true);
this.setReqTimeout(this.logoutURL+'?auth='+encodeURIComponent(com.chadsmith.googlewave.Auth));
var
module=this;
this.httpReq.onload=function(){
module.afterLogout()
};
this.httpReq.send(null)
}
catch(e){
this.finishedChecking();
this.warn(e)
}
},
afterLogout:function(){
this.debug('logging out finished');
this.clearReqTimeout();
this.login()
},
startInboxChecking:function(){
this.debug('starting inbox checking');
var
needToCheck=true;
if(needToCheck)
this.readInbox();
this.scheduleInboxChecking(window);
},
scheduleInboxChecking:function(win){
if(com.chadsmith.googlewave!=null)
com.chadsmith.googlewave.intervalId=win.setInterval('com.chadsmith.googlewave.readInbox()',this.getUpdateFrequency())
},
stopInboxChecking:function(){
this.debug('stopping inbox checking');
var
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow);
if(com.chadsmith.googlewave!=null&&com.chadsmith.googlewave.intervalId!=null){
this.debug('cleaning old scheduled task');
win.clearInterval(com.chadsmith.googlewave.intervalId);
com.chadsmith.googlewave.intervalId=null
}
}
},
isInboxCheckingStarted:function(){
var
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow);
if(com.chadsmith.googlewave!=null&&com.chadsmith.googlewave.intervalId!=null)
return true
}
return false
},
doNothing:function(){
this.setStatusBarAttribute('label','disabled');
this.setStatusBarAttribute('height',1);
this.setStatusBarAttribute('src',this.waveLogo)
},
start:function(){
if(this.isInboxCheckingStarted())
this.stopInboxChecking();
this.startInboxChecking()
},
getWindowManager:function(){
var
mediatorContractid='@mozilla.org/appshell/window-mediator;1',
nsIWindowMediator=Components.interfaces.nsIWindowMediator;
return Components.classes[mediatorContractid].getService(nsIWindowMediator)
},
getDOMWindow:function(xulWindow){
xulWindow=xulWindow.QueryInterface( Components.interfaces.nsIXULWindow);
var
requestor=xulWindow.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
return requestor.getInterface(Components.interfaces.nsIDOMWindow)
},
setStatusBarAttribute:function(attr,value){
var
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext();
xulWindow=xulWindow.QueryInterface(Components.interfaces.nsIXULWindow);
var
myPanel=xulWindow.docShell.contentViewer.DOMDocument.getElementById(this.gwStatusBarId);
if(myPanel!=null)
myPanel.setAttribute(attr,value)
}
},
addTooltipImage:function(bg,ico){
var
vbox=document.createElement('vbox'),
spacer=document.createElement('spacer'),
image=document.createElement('image');
vbox.setAttribute('style','background:url('+bg+') no-repeat center center');
spacer.setAttribute('style','height:8px');
image.setAttribute('src',ico);
image.setAttribute('style','margin:24px 24px 0px 0px');
vbox.appendChild(spacer);
vbox.appendChild(image);
this.tooltipElements[this.tooltipElementCount++]=vbox
},
addTooltipLabel:function(text,styleClass){
var
lbl=document.createElement('label');
lbl.setAttribute('value',text);
if(styleClass!=null)
lbl.setAttribute('style',styleClass);
this.tooltipLabels[this.tooltipLabelCount++]=lbl
},
createTooltipElement:function(labels){
var
vbox=document.createElement('vbox');
for(var i=0;i<labels.length;i++)
vbox.appendChild(labels[i]);
this.tooltipElements[this.tooltipElementCount++]=vbox
},
cloneTooltipLabel:function(doc,label){
var
clone=doc.createElement('vbox');
clone=label.cloneNode(true);
return clone
},
setStatusBarTooltip:function(labels){
var
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow),
doc=xulWindow.docShell.contentViewer.DOMDocument;
if(com.chadsmith.googlewave!=null)
com.chadsmith.googlewave.setTooltip(doc,labels)
}
},
setTooltip:function(doc,labels){
var
tooltip,
parent;
if(this.gwStatusBar==null){
this.debug('null gw status bar. skip tooltips');
return
}
this.debug('setting tooltip');
if(this.gwStatusBar.hasAttribute('tooltiptext'))
this.gwStatusBar.removeAttribute('tooltiptext');
if(this.gwStatusBar.hasAttribute('tooltip'))
tooltip=doc.getElementById(this.gwStatusBar.getAttribute('tooltip'));
if(tooltip==null){
parent=doc.getElementById('mainPopupSet');
tooltip=this.newTooltip(doc,this.gwStatusBar);
try{
parent.appendChild(tooltip)
}
catch(e){
this.debug('got exception appendChild '+e)
}
this.gwStatusBar.setAttribute('tooltip',tooltip.getAttribute('id'))
}
while(tooltip.hasChildNodes())
tooltip.removeChild(tooltip.firstChild);
var
hbox=document.createElement('hbox'),
i;
for(i=0;i<labels.length;i++){
if(labels[i].ownerDocument==doc)
hbox.appendChild(labels[i]);
else
hbox.appendChild(this.cloneTooltipLabel(doc,labels[i]))
}
tooltip.appendChild(hbox)
},
newTooltip:function(doc,gwStatusBar){
var
tooltip=doc.createElement('tooltip');
tooltip.setAttribute('id',gwStatusBar.getAttribute('id')+'-tip');
tooltip.setAttribute('noautohide',true);
tooltip.setAttribute('orient','vertical');
tooltip.setAttribute('crop','none');
return tooltip
},
checkNow:function(){
this.debug('checking inbox now');
var
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow);
if(com.chadsmith.googlewave!=null&&com.chadsmith.googlewave.intervalId!=null){
com.chadsmith.googlewave.setStatusBarAttribute('label','Loading inbox...');
com.chadsmith.googlewave.setStatusBarAttribute('style','');
win.setTimeout('com.chadsmith.googlewave.readInbox()',1000);
return
}
}
},
gotoInbox:function(){
gBrowser.selectedTab=gBrowser.addTab(this.inboxURL)
},
gotoAboutSite:function(){
gBrowser.selectedTab=gBrowser.addTab(this.aboutURL)
},
isenabled:function(){
return this.prefBranch.prefHasUserValue('enabled')?this.prefBranch.getBoolPref('enabled'):true
},
enable:function(value){
this.prefBranch.setBoolPref('enabled',value);
this[!value?'doNothing':'login']()
},
getLastUpdated:function(){
return this.prefBranch.prefHasUserValue('lastUpdated')?new Date(this.toInt(this.prefBranch.getCharPref('lastUpdated'))):null
},
setLastUpdated:function(date){
this.prefBranch.setCharPref('lastUpdated',date.getTime())
},
setLastUpdatedToNow:function(){
this.setLastUpdated(new Date())
},
initOption:function(){
if('@mozilla.org/passwordmanager;1' in Components.classes){
this.pwdmgrInternal=Components.classes['@mozilla.org/passwordmanager;1'].getService(Components.interfaces.nsIPasswordManagerInternal);
this.pwdmgr=Components.classes['@mozilla.org/passwordmanager;1'].getService(Components.interfaces.nsIPasswordManager)
}
else
this.pwdmgr=Components.classes['@mozilla.org/login-manager;1'].getService(Components.interfaces.nsILoginManager)
},
optionEditLoad:function(){
this.debug('loading preferences');
this.initOption();
var loginData=this.getLoginData();
if(loginData.username!='')
document.getElementById('google-wave-username-field').value=loginData.username;
if(loginData.password!='')
document.getElementById('google-wave-password-field').value=loginData.password;
this.oldUsername=loginData.username;
document.getElementById('google-wave-frequency-field').value=this.getUpdateFrequency()/60000;
document.getElementById('google-wave-httptimeout-field').value=this.getHttpTimeout()/1000
},
optionEditSave:function(){
this.debug('saving preferences');
try{
var
username=document.getElementById('google-wave-username-field').value,
password=document.getElementById('google-wave-password-field').value,
frequency=document.getElementById('google-wave-frequency-field').value,
httpTimeout=document.getElementById('google-wave-httptimeout-field').value;
if(username==''){
alert('You must enter an e-mail address');
return false
}
if(frequency==''||isNaN(frequency)){
alert('You must enter a number for update frequency');
return false
}
if(httpTimeout==''||isNaN(httpTimeout)){
alert('You must enter a number for HTTP timeout');
return false
}
if('@mozilla.org/passwordmanager;1' in Components.classes){
try{
for(var i=0;i<this.passwordSites.length;i++)
this.pwdmgr.removeUser(this.passwordSites[i],this.oldUsername)
}
catch(e){
this.debug('exception removing password '+e)
}
for(var i=0;i<this.passwordSites.length;i++)
this.pwdmgrInternal.addUserFull(this.passwordSites[i],username,password,this.usernameFormField,this.passwordFormField)
}
else
for(var i=0;i<this.passwordSites.length;i++){
var
logins=this.pwdmgr.findLogins({},this.passwordSites[i],this.passwordSites[i],null),
nsLoginInfo=new Components.Constructor('@mozilla.org/login-manager/loginInfo;1',Components.interfaces.nsILoginInfo,'init'),
loginInfo=new nsLoginInfo(this.passwordSites[i],this.passwordSites[i],null,username,password,this.usernameFormField,this.passwordFormField),
j;
for(j=0;j<logins.length;j++)
//if(logins[j].username==this.oldUsername){ // removing all old logins for now
this.pwdmgr.removeLogin(logins[j]);
// break
//}
this.pwdmgr.addLogin(loginInfo)
}
var
needToChangeInterval=false,
frequencyValue=this.toInt(frequency);
if(this.getUpdateFrequency()!=frequencyValue){
this.prefBranch.setIntPref('frequency',frequencyValue);
needToChangeInterval=true
}
this.prefBranch.setIntPref('httpTimeout',this.toInt(httpTimeout));
var
windowManager=this.getWindowManager(),
enumerator=windowManager.getXULWindowEnumerator(null);
while(enumerator.hasMoreElements()){
var
xulWindow=enumerator.getNext(),
win=this.getDOMWindow(xulWindow);
if(com.chadsmith.googlewave!=null&&com.chadsmith.googlewave.intervalId!=null){
if(needToChangeInterval){
this.debug('changing interval to '+this.getUpdateFrequency());
win.clearInterval(com.chadsmith.googlewave.intervalId);
com.chadsmith.googlewave.scheduleInboxChecking(win)
}
win.setTimeout('com.chadsmith.googlewave.checkWithLogout()',1000);
break
}
}
return true
}
catch(e){
alert('An unknown error occurred\n'+e)
}
return false
},
getLoginData:function(){
var
data={username:'',password:''};
if('@mozilla.org/passwordmanager;1' in Components.classes){
for(var i=0;i<this.passwordSites.length;i++){
var
host={value:this.passwordSites[i]},
user={value:''},
password={value:''};
try{
this.pwdmgrInternal.findPasswordEntry(host.value,'','',host,user,password);
this.debug('found user/password from the password manager by host '+this.passwordSites[i]);
break
}
catch(e){
this.debug('no user/password retrieved from the password manager by host '+this.passwordSites[i])
}
}
data.username=user.value;
data.password=password.value
}
else
for(var i=0;i<this.passwordSites.length;i++){
var
logins=this.pwdmgr.findLogins({},this.passwordSites[i],this.passwordSites[i],null);
if(logins.length>0){
// return the first one
data.username=logins[0].username;
data.password=logins[0].password;
break
}
}
return data
},
getUpdateFrequency:function(){
try{
return this.prefBranch.prefHasUserValue('frequency')?this.prefBranch.getIntPref('frequency')*60000:this.defaultFrequency
}
catch(e){
this.warn(e);
return this.defaultFrequency
}
},
getHttpTimeout:function(){
try{
return this.prefBranch.prefHasUserValue('httpTimeout')?this.prefBranch.getIntPref('httpTimeout')*1000:this.defaultHttpTimeout
}
catch(e){
this.warn(e);
return this.defaultHttpTimeout
}
},
callInProgress:function(xmlhttp){
this.debug('xmlhttp ready state = '+xmlhttp.readyState);
switch(xmlhttp.readyState){
case 1:
case 2:
case 3:
return true;
break;
default:
return false;
break
}
},
setReqTimeout:function(url){
var
httpTimeout=this.getHttpTimeout();
this.timeoutId=setTimeout('com.chadsmith.googlewave.timeoutFired("'+url+'");',httpTimeout);
this.debug('set timeout '+httpTimeout+'(ms) id '+this.timeoutId+' for '+url)
},
timeoutFired:function(url){
this.debug('timeout fired for '+url);
if(this.callInProgress(this.httpReq)){
this.warn('http request to '+url+' timed out');
this.finishedChecking();
this.httpReq.abort()
}
else
if(this.httpReq.readyState==4)
this.finishedChecking()
},
clearReqTimeout:function(){
window.clearTimeout(this.timeoutId);
this.debug('cleared timeout id '+this.timeoutId)
},
debug:function(msg){
dump(this.getDate()+' DEBUG: '+msg+'\n')
},
warn:function(msg){
dump(this.getDate()+' WARNING: '+msg+'\n');
this.setStatusBarAttribute('tooltiptext',msg)
},
error:function(msg){
dump(this.getDate()+' ERROR: '+msg+'\n');
this.setStatusBarAttribute('tooltiptext',msg);
this.setStatusBarAttribute('style','color:rgb(255,0,0)');
},
getDate:function(){
var
d=new Date(),
h=d.getHours()+'',
m=d.getMinutes()+'',
s=d.getSeconds()+'';
if(h.length==1)
h='0'+h;
if(m.length==1)
m='0'+m;
if(s.length==1)
s='0'+s;
return(d.getMonth()+1)+'/'+d.getDate()+'/'+d.getFullYear()+' '+h+':'+m+':'+s
},
toInt:function(s){
return s[0]=='0'?parseInt(s.substring(1,s.length)):parseInt(s)
}
}
}();
window.addEventListener('load',com.chadsmith.googlewave.load,false);
window.addEventListener('unload',com.chadsmith.googlewave.unload,false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment