blob: bce2c3fe26b5c5a44b1d7a5d5ff170d0b9e7b306 [file] [log] [blame]
<html>
<head>
<script>
// Identifier used to debug the possibility of multiple instances of the
// extension making requests on behalf of a single user.
var instanceId = 'gmc' + parseInt(Date.now() * Math.random(), 10);
var animationFrames = 36;
var animationSpeed = 10; // ms
var canvas;
var canvasContext;
var loggedInImage;
var pollIntervalMin = 1000 * 60; // 1 minute
var pollIntervalMax = 1000 * 60 * 60; // 1 hour
var requestFailureCount = 0; // used for exponential backoff
var requestTimeout = 1000 * 2; // 5 seconds
var rotation = 0;
var unreadCount = -1;
var loadingAnimation = new LoadingAnimation();
var requestTimerId;
function getGmailUrl() {
var url = "https://mail.google.com/";
if (localStorage.customDomain)
url += localStorage.customDomain + "/";
else
url += "mail/"
return url;
}
function getFeedUrl() {
// "zx" is a Gmail query parameter that is expected to contain a random
// string and may be ignored/stripped.
return getGmailUrl() + "feed/atom?zx=" + encodeURIComponent(instanceId);
}
function isGmailUrl(url) {
// This is the Gmail we're looking for if:
// - starts with the correct gmail url
// - doesn't contain any other path chars
var gmail = getGmailUrl();
if (url.indexOf(gmail) != 0)
return false;
return url.length == gmail.length || url[gmail.length] == '?' ||
url[gmail.length] == '#';
}
// A "loading" animation displayed while we wait for the first response from
// Gmail. This animates the badge text with a dot that cycles from left to
// right.
function LoadingAnimation() {
this.timerId_ = 0;
this.maxCount_ = 8; // Total number of states in animation
this.current_ = 0; // Current state
this.maxDot_ = 4; // Max number of dots in animation
}
LoadingAnimation.prototype.paintFrame = function() {
var text = "";
for (var i = 0; i < this.maxDot_; i++) {
text += (i == this.current_) ? "." : " ";
}
if (this.current_ >= this.maxDot_)
text += "";
chrome.browserAction.setBadgeText({text:text});
this.current_++;
if (this.current_ == this.maxCount_)
this.current_ = 0;
}
LoadingAnimation.prototype.start = function() {
if (this.timerId_)
return;
var self = this;
this.timerId_ = window.setInterval(function() {
self.paintFrame();
}, 100);
}
LoadingAnimation.prototype.stop = function() {
if (!this.timerId_)
return;
window.clearInterval(this.timerId_);
this.timerId_ = 0;
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
if (changeInfo.url && isGmailUrl(changeInfo.url)) {
getInboxCount(function(count) {
updateUnreadCount(count);
});
}
});
function init() {
canvas = document.getElementById('canvas');
loggedInImage = document.getElementById('logged_in');
canvasContext = canvas.getContext('2d');
chrome.browserAction.setBadgeBackgroundColor({color:[208, 0, 24, 255]});
chrome.browserAction.setIcon({path: "gmail_logged_in.png"});
loadingAnimation.start();
startRequest();
}
function scheduleRequest() {
if (requestTimerId) {
window.clearTimeout(requestTimerId);
}
var randomness = Math.random() * 2;
var exponent = Math.pow(2, requestFailureCount);
var multiplier = Math.max(randomness * exponent, 1);
var delay = Math.min(multiplier * pollIntervalMin, pollIntervalMax);
delay = Math.round(delay);
requestTimerId = window.setTimeout(startRequest, delay);
}
// ajax stuff
function startRequest() {
getInboxCount(
function(count) {
loadingAnimation.stop();
updateUnreadCount(count);
scheduleRequest();
},
function() {
loadingAnimation.stop();
showLoggedOut();
scheduleRequest();
}
);
}
function getInboxCount(onSuccess, onError) {
var xhr = new XMLHttpRequest();
var abortTimerId = window.setTimeout(function() {
xhr.abort(); // synchronously calls onreadystatechange
}, requestTimeout);
function handleSuccess(count) {
requestFailureCount = 0;
window.clearTimeout(abortTimerId);
if (onSuccess)
onSuccess(count);
}
var invokedErrorCallback = false;
function handleError() {
++requestFailureCount;
window.clearTimeout(abortTimerId);
if (onError && !invokedErrorCallback)
onError();
invokedErrorCallback = true;
}
try {
xhr.onreadystatechange = function(){
if (xhr.readyState != 4)
return;
if (xhr.responseXML) {
var xmlDoc = xhr.responseXML;
var fullCountSet = xmlDoc.evaluate("/gmail:feed/gmail:fullcount",
xmlDoc, gmailNSResolver, XPathResult.ANY_TYPE, null);
var fullCountNode = fullCountSet.iterateNext();
if (fullCountNode) {
handleSuccess(fullCountNode.textContent);
return;
} else {
console.error(chrome.i18n.getMessage("gmailcheck_node_error"));
}
}
handleError();
}
xhr.onerror = function(error) {
handleError();
}
xhr.open("GET", getFeedUrl(), true);
xhr.send(null);
} catch(e) {
console.error(chrome.i18n.getMessage("gmailcheck_exception", e));
handleError();
}
}
function gmailNSResolver(prefix) {
if(prefix == 'gmail') {
return 'http://purl.org/atom/ns#';
}
}
function updateUnreadCount(count) {
if (unreadCount != count) {
unreadCount = count;
animateFlip();
}
}
function ease(x) {
return (1-Math.sin(Math.PI/2+x*Math.PI))/2;
}
function animateFlip() {
rotation += 1/animationFrames;
drawIconAtRotation();
if (rotation <= 1) {
setTimeout("animateFlip()", animationSpeed);
} else {
rotation = 0;
drawIconAtRotation();
chrome.browserAction.setBadgeText({
text: unreadCount != "0" ? unreadCount : ""
});
chrome.browserAction.setBadgeBackgroundColor({color:[208, 0, 24, 255]});
}
}
function showLoggedOut() {
unreadCount = -1;
chrome.browserAction.setIcon({path:"gmail_not_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:"?"});
}
function drawIconAtRotation() {
canvasContext.save();
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
canvasContext.translate(
Math.ceil(canvas.width/2),
Math.ceil(canvas.height/2));
canvasContext.rotate(2*Math.PI*ease(rotation));
canvasContext.drawImage(loggedInImage,
-Math.ceil(canvas.width/2),
-Math.ceil(canvas.height/2));
canvasContext.restore();
chrome.browserAction.setIcon({imageData:canvasContext.getImageData(0, 0,
canvas.width,canvas.height)});
}
function goToInbox() {
chrome.tabs.getAllInWindow(undefined, function(tabs) {
for (var i = 0, tab; tab = tabs[i]; i++) {
if (tab.url && isGmailUrl(tab.url)) {
chrome.tabs.update(tab.id, {selected: true});
return;
}
}
chrome.tabs.create({url: getGmailUrl()});
});
}
// Called when the user clicks on the browser action.
chrome.browserAction.onClicked.addListener(function(tab) {
goToInbox();
});
</script>
</head>
<body onload="init()">
<img id="logged_in" src="gmail_logged_in.png">
<canvas id="canvas" width="19" height="19">
</body>
</html>