• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<html>
2<head>
3<script>
4var animationFrames = 36;
5var animationSpeed = 10; // ms
6var canvas;
7var canvasContext;
8var loggedInImage;
9var pollIntervalMin = 1000 * 60;  // 1 minute
10var pollIntervalMax = 1000 * 60 * 60;  // 1 hour
11var requestFailureCount = 0;  // used for exponential backoff
12var requestTimeout = 1000 * 2;  // 5 seconds
13var rotation = 0;
14var unreadCount = -1;
15var loadingAnimation = new LoadingAnimation();
16
17function getGmailUrl() {
18  var url = "https://mail.google.com/";
19  if (localStorage.customDomain)
20    url += localStorage.customDomain + "/";
21  else
22    url += "mail/"
23  return url;
24}
25
26function getFeedUrl() {
27  return getGmailUrl() + "feed/atom";
28}
29
30function isGmailUrl(url) {
31  // This is the Gmail we're looking for if:
32  // - starts with the correct gmail url
33  // - doesn't contain any other path chars
34  var gmail = getGmailUrl();
35  if (url.indexOf(gmail) != 0)
36    return false;
37
38  return url.length == gmail.length || url[gmail.length] == '?' ||
39                       url[gmail.length] == '#';
40}
41
42// A "loading" animation displayed while we wait for the first response from
43// Gmail. This animates the badge text with a dot that cycles from left to
44// right.
45function LoadingAnimation() {
46  this.timerId_ = 0;
47  this.maxCount_ = 8;  // Total number of states in animation
48  this.current_ = 0;  // Current state
49  this.maxDot_ = 4;  // Max number of dots in animation
50}
51
52LoadingAnimation.prototype.paintFrame = function() {
53  var text = "";
54  for (var i = 0; i < this.maxDot_; i++) {
55    text += (i == this.current_) ? "." : " ";
56  }
57  if (this.current_ >= this.maxDot_)
58    text += "";
59
60  chrome.browserAction.setBadgeText({text:text});
61  this.current_++;
62  if (this.current_ == this.maxCount_)
63    this.current_ = 0;
64}
65
66LoadingAnimation.prototype.start = function() {
67  if (this.timerId_)
68    return;
69
70  var self = this;
71  this.timerId_ = window.setInterval(function() {
72    self.paintFrame();
73  }, 100);
74}
75
76LoadingAnimation.prototype.stop = function() {
77  if (!this.timerId_)
78    return;
79
80  window.clearInterval(this.timerId_);
81  this.timerId_ = 0;
82}
83
84
85chrome.tabs.onUpdated.addListener(function(tabId, changeInfo) {
86  if (changeInfo.url && isGmailUrl(changeInfo.url)) {
87    getInboxCount(function(count) {
88      updateUnreadCount(count);
89    });
90  }
91});
92
93
94function init() {
95  canvas = document.getElementById('canvas');
96  loggedInImage = document.getElementById('logged_in');
97  canvasContext = canvas.getContext('2d');
98
99  chrome.browserAction.setBadgeBackgroundColor({color:[208, 0, 24, 255]});
100  chrome.browserAction.setIcon({path: "gmail_logged_in.png"});
101  loadingAnimation.start();
102
103  startRequest();
104}
105
106function scheduleRequest() {
107  var randomness = Math.random() * 2;
108  var exponent = Math.pow(2, requestFailureCount);
109  var delay = Math.min(randomness * pollIntervalMin * exponent,
110                       pollIntervalMax);
111  delay = Math.round(delay);
112
113  window.setTimeout(startRequest, delay);
114}
115
116// ajax stuff
117function startRequest() {
118  getInboxCount(
119    function(count) {
120      loadingAnimation.stop();
121      updateUnreadCount(count);
122      scheduleRequest();
123    },
124    function() {
125      loadingAnimation.stop();
126      showLoggedOut();
127      scheduleRequest();
128    }
129  );
130}
131
132function getInboxCount(onSuccess, onError) {
133  var xhr = new XMLHttpRequest();
134  var abortTimerId = window.setTimeout(function() {
135    xhr.abort();  // synchronously calls onreadystatechange
136  }, requestTimeout);
137
138  function handleSuccess(count) {
139    requestFailureCount = 0;
140    window.clearTimeout(abortTimerId);
141    if (onSuccess)
142      onSuccess(count);
143  }
144
145  function handleError() {
146    ++requestFailureCount;
147    window.clearTimeout(abortTimerId);
148    if (onError)
149      onError();
150  }
151
152  try {
153    xhr.onreadystatechange = function(){
154      if (xhr.readyState != 4)
155        return;
156
157      if (xhr.responseXML) {
158        var xmlDoc = xhr.responseXML;
159        var fullCountSet = xmlDoc.evaluate("/gmail:feed/gmail:fullcount",
160            xmlDoc, gmailNSResolver, XPathResult.ANY_TYPE, null);
161        var fullCountNode = fullCountSet.iterateNext();
162        if (fullCountNode) {
163          handleSuccess(fullCountNode.textContent);
164          return;
165        } else {
166          console.error(chrome.i18n.getMessage("gmailcheck_node_error"));
167        }
168      }
169
170      handleError();
171    }
172
173    xhr.onerror = function(error) {
174      handleError();
175    }
176
177    xhr.open("GET", getFeedUrl(), true);
178    xhr.send(null);
179  } catch(e) {
180    console.error(chrome.i18n.getMessage("gmailcheck_exception", e));
181    handleError();
182  }
183}
184
185function gmailNSResolver(prefix) {
186  if(prefix == 'gmail') {
187    return 'http://purl.org/atom/ns#';
188  }
189}
190
191function updateUnreadCount(count) {
192  if (unreadCount != count) {
193    unreadCount = count;
194    animateFlip();
195  }
196}
197
198
199function ease(x) {
200  return (1-Math.sin(Math.PI/2+x*Math.PI))/2;
201}
202
203function animateFlip() {
204  rotation += 1/animationFrames;
205  drawIconAtRotation();
206
207  if (rotation <= 1) {
208    setTimeout("animateFlip()", animationSpeed);
209  } else {
210    rotation = 0;
211    drawIconAtRotation();
212    chrome.browserAction.setBadgeText({
213      text: unreadCount != "0" ? unreadCount : ""
214    });
215    chrome.browserAction.setBadgeBackgroundColor({color:[208, 0, 24, 255]});
216  }
217}
218
219function showLoggedOut() {
220  unreadCount = -1;
221  chrome.browserAction.setIcon({path:"gmail_not_logged_in.png"});
222  chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
223  chrome.browserAction.setBadgeText({text:"?"});
224}
225
226function drawIconAtRotation() {
227  canvasContext.save();
228  canvasContext.clearRect(0, 0, canvas.width, canvas.height);
229  canvasContext.translate(
230      Math.ceil(canvas.width/2),
231      Math.ceil(canvas.height/2));
232  canvasContext.rotate(2*Math.PI*ease(rotation));
233  canvasContext.drawImage(loggedInImage,
234      -Math.ceil(canvas.width/2),
235      -Math.ceil(canvas.height/2));
236  canvasContext.restore();
237
238  chrome.browserAction.setIcon({imageData:canvasContext.getImageData(0, 0,
239      canvas.width,canvas.height)});
240}
241
242function goToInbox() {
243  chrome.tabs.getAllInWindow(undefined, function(tabs) {
244    for (var i = 0, tab; tab = tabs[i]; i++) {
245      if (tab.url && isGmailUrl(tab.url)) {
246        chrome.tabs.update(tab.id, {selected: true});
247        return;
248      }
249    }
250    chrome.tabs.create({url: getGmailUrl()});
251  });
252}
253
254// Called when the user clicks on the browser action.
255chrome.browserAction.onClicked.addListener(function(tab) {
256  goToInbox();
257});
258
259</script>
260</head>
261<body onload="init()">
262<img id="logged_in" src="gmail_logged_in.png">
263<canvas id="canvas" width="19" height="19">
264</body>
265</html>
266
267