Gmail Favicon Alerts 3.1a — Chrome Support


/ Published in: JavaScript
Save to your folder(s)

This is an alpha build of Gmail Favicon Alerts with support for Chrome. It's not perfect, but it's working for me, so I'd love it if you'd give it a try and let me know how it goes.


Copy this code and paste it in your HTML
  1. // ==UserScript==
  2. // @name Gmail Favicon Alerts 3
  3. // @description Alerts you to the status of your Gmail Inbox through distinct Favicons.
  4. // @version 3.1.1a
  5. // @date 2010-03-04
  6. // @author Peter Wooley
  7. // @namespace http://peterwooley.com
  8. // @include https://mail.google.com/mail*
  9. // @include http://mail.google.com/mail*
  10. // @include https://mail.google.com/mail*
  11. // @include http://mail.google.com/a*
  12. // @include https://mail.google.com/a*
  13. // ==/UserScript==
  14.  
  15. if(!GM_getValue) {
  16. function GM_getValue(name, fallback) {
  17. return fallback;
  18. }
  19. }
  20.  
  21. // Register GM Commands and Methods
  22. if(!GM_registerMenuCommand) {
  23. GM_registerMenuCommand( "Gmail Favicon Alerts > Chat Alerts On", function() { setChat(true) } );
  24. GM_registerMenuCommand( "Gmail Favicon Alerts > Chat Alerts Off", function() { setChat(false) } );
  25. GM_registerMenuCommand( "Gmail Favicon Alerts > Unread Count On", function() { setUnreadCountDisplay(true) } );
  26. GM_registerMenuCommand( "Gmail Favicon Alerts > Unread Count Off", function() { setUnreadCountDisplay(false) } );
  27. function setChat(val) { GM_setValue('chatEnabled', val) };
  28. function setUnreadCountDisplay(val) { GM_setValue('unreadCountDisplay', val) };
  29. }
  30.  
  31. var gfia_instance;
  32. var gfia_chat = GM_getValue('chatEnabled', true);
  33.  
  34. if(window.frameElement && window.frameElement.id == "canvas_frame") {
  35. new GmailFavIconAlerts();
  36. }
  37.  
  38. function GmailFavIconAlerts() {
  39. var self = this;
  40. this.construct = function() {
  41. this.chat = this.getChat();
  42. this.chatting = false;
  43. this.head = window.frameElement.parentNode.parentNode.getElementsByTagName('head')[0];
  44. this.title = this.head.getElementsByTagName('title')[0];
  45. this.inboxText = 'Inbox';
  46. this.chatText = [
  47. {value:'\u2026', chars: 1},
  48. {value:'...', chars: 3}
  49. ];
  50. this.timer;
  51. this.icons = {
  52. chat: 'data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFxMRnxZSkS5AAAAFgAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAemtkuz4yLLs4KyeMAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFltLRf+3p6D/ZVZR/ywhHrsAAAAfAAAAFgAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAHzQpJbt9bWf/6dnS/+nZ0v9xYVz/Nysn/zQpJbsxJiOMAAAAHwAAAA8AAAAAAAAAAAAAAAAAAAAWPzMujEc5NP+Cc2z/6dnS/+nZ0v/p2dL/6dnS/+bWz/+mlY//eWhj/0c5NP8/My6MAAAAFgAAAAAAAAAHVEZAu5GBev/ZycL/6dnS/+nZ0v/p2dL/6dnS/+nZ0v/p2dL/5dXO/9XEvf/Csar/hnZw/1RFQLsAAAAPSj46VYZ3cf/r3Nb/69zW/+vc1v/r3Nb/69zW/+vc1v/r3Nb/69zW/+vc1v/fz8n/0L+5/9C/uf+RgHr/WkxHjG1dV//BtK7/7+Pd/+/k3//w5eD/8Obh//Hm4v/x5uL/8ebi//Dm4f/w5eD/6d3Y/9HBu//Rwbr/xrav/2xcVv9zZF//9Ozo//bw7f/48/H/+vb0//v49v/8+fj//Pr5//z5+P/7+Pb/+vb0//fy8P/Txb//0sS+/9LDvf9yY13/e25o/9zX1f/9/Pz//v39//7+/v///v7///////////////////7+//7+/v/28/L/1cnE/9XIw//Ty8f/eWpl/4N3cleupqP/////////////////////////////////////////////////7Ofl/9bKxf/18fD/qJ6b/4BzblcAAAAAjIF8sru1sv//////////////////////////////////////+Pb2//f19P/18vH/tq2q/4l9eLIAAAAAAAAAAAAAAACUiYWylImF/8C6uP/08/P//////////////////v79/+7s6v+6sq//kIWA/5CFgLIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcko98nJKP/5ySj/+cko//m5GN/5mPi/+YjYn/mI2JfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAPv/AAD4/wAA+H8AAPAPAADAAwAAgAEAAIAAAAAAAAAAAAAAAAAAAACAAQAAgAEAAMADAAD4HwAA//8AAA==',
  53. read: 'data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAACMuAAAjLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0Wsv8NFrL/gYGu/4GBrv+Bga7/gYGu/4GBrv+Bga7/gYGu/4GBrv+Bga7/gYGu/4GBrv+Bga7/DRay/w0Wsv8QGbX/EBm1/8HB0//g4OD/4ODg/+Dg4P/g4OD/4ODg/+Dg4P/g4OD/4ODg/+Dg4P/g4OD/wcHT/xAZtf8QGbX/FBy5/xQcuf+Jibn/x8fZ/+fn5//n5+f/5+fn/+fn5//n5+f/5+fn/+fn5//n5+f/x8fZ/4mJuf8UHLn/FBy5/xogvv8aIL7/zc3g/42Nvv/NzeD/7u7u/+7u7v9qat3/amrd/+7u7v/u7u7/zc3g/42Nvv/NzeD/GiC+/xogvv8gJcP/ICXD//X19f/T0+f/kZHE/6Sk0v9SUtv/PT3U/z091P9SUtv/pKTS/5GRxP/T0+f/9fX1/yAlw/8gJcP/Ki7K/youyv/7+/v/+/v7/6qq2f9YWOH/Q0Pb/zw82P88PNj/Q0Pb/1hY4f+qqtn/+/v7//v7+/8qLsr/Ki7K/zc60f83OtH///////////9fX+b/R0fd/0RE3P+6uv//urr//0RE3P9KSt//X1/m////////////NzrR/zc60f9GR9f/RkfX//////9mZuf/U1Pg/0xM3v+9vf////////////+9vf//TEze/1NT4P9mZuf//////0ZH1/9GR9f/UlLd/1JS3f9tbej/V1fg/1RU3//AwP///////////////////////8DA//9UVN//YGDk/21t6P9SUt3/UlLd/1tb4f9bW+H/W1vh/1tb4f/Dw///////////////////////////////////w8P//1tb4f9bW+H/W1vh/1tb4f9gYOH/YGDh/2Bg4f+Kitz/np7P/56ez/+ens//np7P/56ez/+ens//np7P/56ez/+Kitz/YGDh/2Bg4f9gYOH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//////////////////w==',
  54. unread: 'data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAACMuAAAjLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALopAP+6KQD/rYh4/62IeP+tiHj/rYh4/62IeP+tiHj/rYh4/62IeP+tiHj/rYh4/62IeP+tiHj/uikA/7opAP+9LAD/vSwA/9LDvf/e3t7/3t7e/97e3v/e3t7/3t7e/97e3v/e3t7/3t7e/97e3v/e3t7/0sO9/70sAP+9LAD/wTEA/8ExAP+5koD/2MnD/+bm5v/m5ub/5ubm/+bm5v/m5ub/5ubm/+bm5v/m5ub/2MnD/7mSgP/BMQD/wTEA/8Y6A//GOgP/4NDI/76Wg//g0Mj/7e3t/+3t7f/jhVr/44Va/+3t7f/t7e3/4NDI/76Wg//g0Mj/xjoD/8Y6A//MQQn/zEEJ//X19f/n1s//xJqH/9OtnP/jc0D/3GIp/9xiKf/jc0D/062c/8Sah//n1s//9fX1/8xBCf/MQQn/0kwU/9JMFP/7+/v/+/v7/9q0o//pekb/5Ggv/+FiJ//hYif/5Ggv/+l6Rv/atKP/+/v7//v7+//STBT/0kwU/9lZI//ZWSP////////////ugE7/5mwz/+VpMP//zrf//863/+VpMP/objf/7oBO////////////2Vkj/9lZI//eaDL/3mgy///////uhVX/6HZA/+ZvOf//0Lr/////////////0Lr/5m85/+h2QP/uhVX//////95oMv/eaDL/5XRA/+V0QP/vjF3/6HlF/+d3Qv//0r3////////////////////////Svf/nd0L/7IBP/++MXf/ldED/5XRA/+l8Sf/pfEn/6XxJ/+l8Sf//1MD//////////////////////////////////9TA/+l8Sf/pfEn/6XxJ/+l8Sf/of0//6H9P/+h/T//gnH7/0KiV/9Colf/QqJX/0KiV/9Colf/QqJX/0KiV/9Colf/gnH7/6H9P/+h/T//of0//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD//////////w==',
  55. };
  56. this.pixelMaps = {
  57. icons: {
  58. 'unread':
  59. [
  60. ['','','','','','','','','','','','','','','',''],
  61. ['','','','','','','','','','','','','','','',''],
  62. ['','','','','','','','','','','','','','','',''],
  63. ['#4f7fe8','#4f7fe8','#4f7fe8','#7e9ce0','#95a8d0','#95a8d0','#95a8d0','#95a8d0','#95a8d0','#95a8d0','#95a8d0','#95a8d0','#7e9ce0','#4f7fe8','#4f7fe8','#4f7fe8'],
  64. ['#497ce9','#497ce9','#497ce9','$497ce9','#c0d4ff','#ffffff','#ffffff','#ffffff','#ffffff','#ffffff','#ffffff','#c0d4ff','#497ce9','#497ce9','#497ce9','$497ce9'],
  65. ['#4074e5','#4074e5','#5d8cef','#4579e8','#4277e7','#bdd2ff','#ffffff','#ffffff','#ffffff','#ffffff','#bdd2ff','#4277e7','#4f80ec','#5d8cef','#4074e5','#4074e5'],
  66. ['#3268de','#3268de','#ffffff','#5585ee','#4076e8','#396fe6','#bad0ff','#ffffff','#ffffff','#bad0ff','#396fe6','#4076e8','#5585ee','#ffffff','#3268de','#3268de'],
  67. ['#2359d9','#2359d9','#ffffff','#ffffff','#4e80ee','#336ce6','#3069e5','#b7ceff','#b7ceff','#3069e5','#376ee8','#4e80ee','#ffffff','#ffffff','#2359d9','#2359d9'],
  68. ['#144cd2','#144cd2','#fbfbfb','#fbfbfb','#a3b4da','#467ae9','#2f68e4','#2762e1','#2762e1','#2f68e4','#467ae9','#a3b4da','#fbfbfb','#fbfbfb','#144cd2','#144cd2'],
  69. ['#0941cc','#0941cc','#f5f5f5','#cfd6e7','#879ac4','#9cadd3','#4073e3','#2962dc','#2962dc','#4073e3','#9cadd3','#879ac4','#cfd6e7','#f5f5f5','#0941cc','#0941cc'],
  70. ['#033ac6','#033ac6','#c8d0e0','#8396be','#c8d0e0','#ededed','#ededed','#5a85e3','#5a85e3','#ededed','#ededed','#c8d0e0','#8396be','#c8d0e0','#033ac6','#033ac6'],
  71. ['#0031c1','#0031c1','#8092b9','#c3c9d8','#e6e6e6','#e6e6e6','#e6e6e6','#e6e6e6','#e6e6e6','#e6e6e6','#e6e6e6','#e6e6e6','#c3c9d8','#8092b9','#0031c1','#0031c1'],
  72. ['#002cbd','#002cbd','#bdc3d2','#dedede','#dedede','#dedede','#dedede','#dedede','#dedede','#dedede','#dedede','#dedede','#dedede','#bdc3d2','#002cbd','#002cbd'],
  73. ['#0029ba','#0029ba','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#7888ad','#0029ba','#0029ba'],
  74. ['','','','','','','','','','','','','','','',''],
  75. ['','','','','','','','','','','','','','','','']
  76. ]
  77. },
  78. numbers: [
  79. [
  80. [0,1,1,0],
  81. [1,0,0,1],
  82. [1,0,0,1],
  83. [1,0,0,1],
  84. [0,1,1,0]
  85. ],
  86. [
  87. [0,1,0],
  88. [1,1,0],
  89. [0,1,0],
  90. [0,1,0],
  91. [1,1,1]
  92. ],
  93. [
  94. [1,1,1,0],
  95. [0,0,0,1],
  96. [0,1,1,0],
  97. [1,0,0,0],
  98. [1,1,1,1]
  99. ],
  100. [
  101. [1,1,1,0],
  102. [0,0,0,1],
  103. [0,1,1,0],
  104. [0,0,0,1],
  105. [1,1,1,0]
  106. ],
  107. [
  108. [0,0,1,0],
  109. [0,1,1,0],
  110. [1,0,1,0],
  111. [1,1,1,1],
  112. [0,0,1,0]
  113. ],
  114. [
  115. [1,1,1,1],
  116. [1,0,0,0],
  117. [1,1,1,0],
  118. [0,0,0,1],
  119. [1,1,1,0]
  120. ],
  121. [
  122. [0,1,1,0],
  123. [1,0,0,0],
  124. [1,1,1,0],
  125. [1,0,0,1],
  126. [0,1,1,0]
  127. ],
  128. [
  129. [1,1,1,1],
  130. [0,0,0,1],
  131. [0,0,1,0],
  132. [0,1,0,0],
  133. [0,1,0,0]
  134. ],
  135. [
  136. [0,1,1,0],
  137. [1,0,0,1],
  138. [0,1,1,0],
  139. [1,0,0,1],
  140. [0,1,1,0]
  141. ],
  142. [
  143. [0,1,1,0],
  144. [1,0,0,1],
  145. [0,1,1,1],
  146. [0,0,0,1],
  147. [0,1,1,0]
  148. ],
  149. ]
  150. };
  151.  
  152. this.timer = setInterval(this.poll, 500);
  153. this.poll();
  154.  
  155. return true;
  156. }
  157.  
  158. this.drawUnreadCount = function(unread) {
  159. if(!self.textedCanvas) {
  160. self.textedCanvas = [];
  161. }
  162.  
  163. if(!self.textedCanvas[unread]) {
  164. var iconCanvas = self.getUnreadCanvas();
  165. var textedCanvas = document.createElement('canvas');
  166. textedCanvas.height = textedCanvas.width = iconCanvas.width;
  167. var ctx = textedCanvas.getContext('2d');
  168. ctx.drawImage(iconCanvas, 0, 0);
  169.  
  170. ctx.fillStyle = "#fef4ac";
  171. ctx.strokeStyle = "#dabc5c";
  172. ctx.strokeWidth = 1;
  173.  
  174. var count = unread.length;
  175. var bgHeight = self.pixelMaps.numbers[0].length;
  176. var bgWidth = 0;
  177. var padding = count > 2 ? 0 : 1;
  178.  
  179. for(var index = 0; index < count; index++) {
  180. bgWidth += self.pixelMaps.numbers[unread[index]][0].length;
  181. if(index < count-1) {
  182. bgWidth += padding;
  183. }
  184. }
  185. bgWidth = bgWidth > textedCanvas.width-4 ? textedCanvas.width-4 : bgWidth;
  186.  
  187. ctx.fillRect(textedCanvas.width-bgWidth-4,2,bgWidth+4,bgHeight+4);
  188.  
  189.  
  190. var digit;
  191. var digitsWidth = bgWidth;
  192. for(var index = 0; index < count; index++) {
  193. digit = unread[index];
  194. if (self.pixelMaps.numbers[digit]) {
  195. var map = self.pixelMaps.numbers[digit];
  196. var height = map.length;
  197. var width = map[0].length;
  198.  
  199.  
  200. ctx.fillStyle = "#2c3323";
  201.  
  202. for (var y = 0; y < height; y++) {
  203. for (var x = 0; x < width; x++) {
  204. if(map[y][x]) {
  205. ctx.fillRect(14- digitsWidth + x, y+4, 1, 1);
  206. }
  207. }
  208. }
  209.  
  210. digitsWidth -= width + padding;
  211. }
  212. }
  213.  
  214. ctx.strokeRect(textedCanvas.width-bgWidth-3.5,2.5,bgWidth+3,bgHeight+3);
  215.  
  216. self.textedCanvas[unread] = textedCanvas;
  217. }
  218.  
  219. return self.textedCanvas[unread];
  220. }
  221. this.getUnreadCanvas = function() {
  222. if(!self.unreadCanvas) {
  223. self.unreadCanvas = document.createElement('canvas');
  224. self.unreadCanvas.height = self.unreadCanvas.width = 16;
  225.  
  226. var ctx = self.unreadCanvas.getContext('2d');
  227.  
  228. for (var y = 0; y < self.unreadCanvas.width; y++) {
  229. for (var x = 0; x < self.unreadCanvas.height; x++) {
  230. if (self.pixelMaps.icons.unread[y][x]) {
  231. ctx.fillStyle = self.pixelMaps.icons.unread[y][x];
  232. ctx.fillRect(x, y, 1, 1);
  233. }
  234. }
  235. }
  236. }
  237.  
  238. return self.unreadCanvas;
  239. }
  240. this.getChat = function() { return false || GM_getValue('chatEnabled', true); }
  241. this.getDebugging = function() { return false || GM_getValue('debuggingEnabled', false); }
  242. this.getSearchElement = function() {
  243. var element;
  244. var nav = document.body.getElementsByClassName('n0');
  245.  
  246. if(nav) {
  247. var potential = nav[0];
  248.  
  249. if(potential.className.indexOf('n0') !== -1) {
  250. element = potential;
  251. }
  252. }
  253.  
  254. return element ? element: null;
  255. }
  256. this.newChat = function() {
  257. var title = self.title.innerHTML;
  258. for(var index in self.chatText) {
  259. var location = title.indexOf(self.chatText[index].value);
  260. if(self.chatText[index].chars + location == title.length) {
  261. return true;
  262. }
  263. }
  264. return false;
  265. }
  266. this.newMail = function() { return self.searchElement.textContent.match(/\((\d*)\)/); }
  267. this.getUnreadCountDisplay = function() { return GM_getValue('unreadCountDisplay', true); }
  268. this.getUnreadCount = function() {
  269. if(this.newMail()) {
  270. matches = self.searchElement.textContent.match(/\((\d*)\)/);
  271. return matches ? matches[1] : false;
  272. }
  273. }
  274. this.getUnreadCountIcon = function() {
  275. var unread = self.getUnreadCount();
  276. if(this.getUnreadCountDisplay()) {
  277. return self.drawUnreadCount(unread).toDataURL('image/png');
  278. } else {
  279. return self.icons.unread;
  280. }
  281. }
  282.  
  283. this.poll = function() {
  284. if(!self.searchElement)
  285. return self.searchElement = self.getSearchElement();
  286.  
  287. if(self.getChat() && self.newChat()) {
  288. return self.setIcon(self.icons.chat);
  289. }
  290.  
  291. if(self.newMail())
  292. self.setIcon(self.getUnreadCountIcon());
  293. else
  294. self.setIcon(self.icons.read);
  295. }
  296.  
  297. this.setIcon = function(icon) {
  298. var links = self.head.getElementsByTagName("link");
  299. for (var i = 0; i < links.length; i++)
  300. if ((links[i].rel == "shortcut icon" || links[i].rel=="icon") &&
  301. links[i].href != icon)
  302. self.head.removeChild(links[i]);
  303. else if(links[i].href == icon)
  304. return;
  305.  
  306. var newIcon = document.createElement("link");
  307. newIcon.type = "image/png";
  308. newIcon.rel = "shortcut icon";
  309. newIcon.href = icon;
  310.  
  311. self.head.appendChild(newIcon);
  312.  
  313. var shim = document.createElement('iframe');
  314. shim.width = shim.height = 0;
  315. document.body.appendChild(shim);
  316. shim.src = "icon";
  317. document.body.removeChild(shim);
  318. }
  319.  
  320. this.toString = function() { return '[object GmailFavIconAlerts]'; }
  321.  
  322. return this.construct();
  323. }

Report this snippet


Comments

RSS Icon Subscribe to comments

You need to login to post a comment.