Google Reader with favicon

By Yamamaya Last update Aug 14, 2009 — Installed 527 times. Daily Installs: 4, 3, 2, 2, 2, 0, 4, 1, 0, 0, 0, 2, 2, 2, 2, 1, 2, 1, 4, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 1, 0, 1

There are 3 previous versions of this script.

// ==UserScript==
// @name          Google Reader with favicon
// @description   Adds favicons to feeds and entries
// @namespace     Yamamaya
// @version       1.0.2
// @include       http://*google.tld/reader/view*
// @include       https://*google.tld/reader/view*
// ==/UserScript==

/*************************************************
 This script is based on 
  Ultimate GReader favicons
   http://userscripts.org/scripts/show/27739
***************************************************/

(function(){
	var GOOGLE_READER_INFO = eval(GM_getValue('googleReaderInformation')) || {};
	var FAVICON = GOOGLE_READER_INFO.favicon || (GOOGLE_READER_INFO.favicon = {});
	var FAVICON_URL = ['http://', '/favicon.ico'];
	var FAVICON_DEFAULT_IMG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABdUExURa2z+H6CrWtwpnN5uWNopejp9Pf3/6+08Kes6G50uePm/qSr+MTG6ZSZzbW4593e93R4qs3R/JOWu5Oa8dbZ/ISJvJ6l9Ovs/sXJ+amszL3C9/P0/7W6+J+izP////fbRfQAAAEbSURBVHjaYpCTFYICOTAACCAGWT5pNml+QQkpPlGwAEAAMcgKCvKLy7FKsXMISAKBHEAAMchKychIyYjLSbAzioiI8EgCBBCDLIO4HJsMg7i0jCgbUKcsQAAxyMpIMAKBAAjIiUjIAgQQUIsoh7i0uAhQpywTm6AsQAABDRWVkhARl5OWYOBmkRORBQggBlkRUQYxMQYZaTkpMRZpVkmAAGLgZRcV45KTlhGTluZg4ZKWBAggBjleUTEZCS45GRk5KRZ+VkmAAGKQkxNlEBYWE5cWE+dnkuKRBAggkICEtLiwhJywCCsT0KUAAQQSEJSTE5YS55bgZwL6BSCAgAKMYlLsYsJAIAsSAAggoIAcMycnMzMQCYB8CxBgAB2IFJeOuyn5AAAAAElFTkSuQmCC';

	var RSS = $X('id("sub-tree-item-0-main")/ul/li/ul/li').length;
	var FOLDER = $X('id("sub-tree-item-0-main")/ul/li').length;
	var RSS_NUMBERS = GOOGLE_READER_INFO.rssNumber || null;
	var FOLDER_NUMBERS = GOOGLE_READER_INFO.folderNumber || null;
			
 
	var googleReader = {
		init: function(){ 
			this.addBaseCss();
			
			(RSS_NUMBERS === null || FOLDER_NUMBERS === null || RSS !== RSS_NUMBERS || FOLDER !== FOLDER_NUMBERS) ?
				this.noCacheAddFavicon(): this.addFavicon();
		 
			GOOGLE_READER_INFO.rssNumber = RSS;
			GOOGLE_READER_INFO.folderNumber = FOLDER;
			setValue();
			
			GM_registerMenuCommand('Google Reader with favicon - clear cache', clearCache); 
		},
		noCacheAddFavicon: function(){
			var xhr = new XMLHttpRequest();
	   
			xhr.open('get', '/reader/subscriptions/export', true);

			xhr.onload = function(){
				var responseXML = xhr.responseXML;
				Array.forEach(responseXML.getElementsByTagName('outline'), function(outline){
					if (!outline.hasAttribute('htmlUrl')) return;
					var title = outline.getAttribute('title');
					var url = outline.getAttribute('htmlUrl');
					var favicon;
					url = url.split(/\/|\?/)[2];
					(title.length > 24) ? FAVICON[title.substr(0, 21) + '...'] = FAVICON_URL.join(url) : FAVICON[title] = FAVICON_URL.join(url);
				});	
					
				googleReader.addFavicon();
			
				setValue();	
			};

			xhr.send(null);		
		},
		addFavicon: function(){
			entryFaviconNoDOMNodeInserted();
			this.entryFavicon();		
			this.sideBarFavicon();
			
			
			function entryFaviconNoDOMNodeInserted(){
				$X('.//span[@class="entry-source-title"] | .//a[@class="entry-source-title"]').forEach(function(title){
					var match = title.textContent;
					if(match.length > 24) match = match.substr(0,21) + '...';
					var favicon = document.createElement('img');
					var s = favicon.style;
					favicon.width = '16';
					favicon.height= '16';
					if(title.parentNode.className === 'entry-source-title-parent'){
						s.marginRight = '10px';
						s.verticalAlign = 'middle';
					} 
					else {
						s.position = 'absolute';
						s.top = '3px';
						s.left = '1.6em';		 	
					}
						
					(FAVICON.hasOwnProperty(match)) ? favicon.src = FAVICON[match] : favicon.src = FAVICON_DEFAULT_IMG;
					title.parentNode.insertBefore(favicon,title);
					favicon.removeEventListener('error',revertFavicon,false);
					favicon.addEventListener('error',revertFavicon,false);
				});			
			};			
		},
		entryFavicon: function(){
			document.addEventListener('DOMNodeInserted', function(e){
				var target = e.target;
				entry(target);
				sideBar(target);
			}, false);		
			
			function entry(target){
				if(/^entry\s/i.test(target.className) && target.tagName === 'DIV'){
					var title;
					var favicon = document.createElement('img');
					var s = favicon.style;		
					s.width = '16px';
					s.height = '16px';
						
					if(target.firstChild.className === 'card card-common'){
						title = target.getElementsByClassName('entry-source-title')[0]; // 前文表示 .//span[@class='entry-source-title-parent']
						s.marginRight = '5px';
						s.verticalAlign = 'middle';
					}
					else{
						if(target.firstChild.className === 'search-result'){ // 検索
							title = target.getElementsByClassName('entry-source-title')[0];
							s.verticalAlign = 'middle';
							s.marginRight = '5px';
						}
						else
						if(target.firstChild.className === 'comment-entry'){ // コメント共有
							title = target.getElementsByClassName('entry-source-title')[0];
							s.marginRight = '5px';
							s.verticalAlign = 'middle';						
							s.border = 'none';
						}
						else{
							title = target.getElementsByTagName('span')[0]; // リスト表示 .//span[contains(@class,'entry-source-title')]
							s.position = 'absolute';	
							s.top = '3px';
							s.left = '1.6em';
						}
					}						
					
					var match = title.textContent;
					if(match.length > 24) match = match.substr(0,21) + '...';			
						
					(FAVICON.hasOwnProperty(match)) ? favicon.src = FAVICON[match] : favicon.src = FAVICON_DEFAULT_IMG;
					title.parentNode.insertBefore(favicon, title);
					favicon.removeEventListener('error', revertFavicon, false);
					favicon.addEventListener('error', revertFavicon, false);			
				}				
			};
				
			function sideBar(target){
				if(target.nodeName.toLowerCase() === 'li' && target.className.match(/^folder/))
					googleReader.sideBarFavicon();						
			};
		
		},
		sideBarFavicon: function(){
			$X('id("sub-tree")//span[contains(@class,"sub-icon")]').forEach(function(e){
				var title = e.nextSibling;
				var match = title.firstChild.textContent;
				if(match.length > 24) match = match.substr(0,21) + '...';				
				if(FAVICON.hasOwnProperty(match)){
					title.style.paddingLeft = '7px';			
					var favicon = document.createElement('img');
					e.parentNode.insertBefore(favicon,e);
					e.parentNode.removeChild(e);
					
					(FAVICON.hasOwnProperty(match)) ? favicon.src = FAVICON[match] : favicon.src = FAVICON_DEFAULT_IMG;
		
					favicon.removeEventListener('error',revertFavicon,false);
					favicon.addEventListener('error',revertFavicon,false);
				}
			});		
		},
		addBaseCss: function(){
			GM_addStyle(<><![CDATA[  
				#entries.list .collapsed .entry-main .entry-source-title {
					left: 3.25em !important; width:9em !important;
				}
				#sub-tree ul ul li a {
					padding-left: 22px !important;
				}
				#sub-tree a img {
					width: 16px; height: 16px; border: none; vertical-align: middle;
				}
				#entries.list .collapsed .entry-secondary {
					margin: 0 8.5em 0 14em !important;
				}
				#entries.single-source .collapsed .entry-source-title {
					display: block !important;
				}
				#entries.list .read .collapsed {
					opacity:0.6;
				}
				#entries.list .entry .collapsed:hover {
					background:#C2CFF1;
				}
				#entries.list .read .collapsed:hover {
					opacity: 1.0; background: #C2CFF1;
				}	
			]]></>);		
		}
	};
 
	googleReader.init();
  

	function revertFavicon(event){
		this.src = FAVICON_DEFAULT_IMG;
	};
 
	function setValue(){
		GM_setValue('googleReaderInformation',GOOGLE_READER_INFO.toSource());
	};
 
	function clearCache(){
		GM_setValue('googleReaderInformation','');
	};
 
	function $X(exp, ctx){
		var xp = (ctx && ctx.ownerDocument || document).evaluate(exp, ctx || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null),
		r = [];
		for (var i = 0;i < xp.snapshotLength;++i) r.push(xp.snapshotItem(i));
		return r;
	};
})();