Ultimate GReader favicons

By X4lldux Last update Jun 3, 2008 — Installed 1,382 times. Daily Installs: 2, 2, 2, 3, 3, 2, 2, 3, 2, 2, 2, 3, 3, 2, 4, 8, 7, 5, 3, 3, 6, 3, 2, 4, 2, 4, 2, 2, 5, 4, 5, 7
// ==UserScript==

// @name          Ultimate GReader favicons 

// @description   Adds favicons to feeds and entries
// @author        X4lldux
// @version       0.2

// @include       http://*google.tld/reader/view*

// @include       https://*google.tld/reader/view*

// ==/UserScript==

const ALL_FEED_ICON_XPATH = '//img[contains(@class, "icon icon-d-2")]',
		UNFIXED_FEED_ICON_XPATH = '//img[contains(@src, "-tree-view-subscription.gif")]',

		EXPORT_URL = '/reader/subscriptions/export',

		FAVICON_URL = ['http://', '/favicon.ico'],
		SEPERATOR = '|#$|';



var feedDomains = {}

var fixAll=1;


function setFavicon(title) {
    var faviconsList = GM_getValue('faviconsList', null);
    var url="";
    
    var favicons = {};
    if (faviconsList !="" && faviconsList != null) {

		favicons = faviconsList.split(SEPERATOR);

		for (var i=0; i<favicons.length; i++, i++) {
			if(favicons[i] == title) {
				url=favicons[i+1];
				break;
			}
		}
		var newUrl = prompt("Enter favicon url for "+title, url);
		favicons[i]=title;
		favicons[i+1]=newUrl;
		if(newUrl=="" || newUrl==null)
			favicons.splice(i, 2);
	} else {
		url = prompt("Enter favicon url for"+title, url);
		if(url!="" && url!=null)
			favicons=new Array(title, url);
	}

	faviconsList = favicons.join(SEPERATOR);
	GM_setValue('faviconsList', faviconsList);

	refreshDomains();
}




function refreshDomains(all) {
	var xhr = new XMLHttpRequest();

	xhr.open('get', EXPORT_URL, true);

	xhr.onload = function(){

		setFeedDomains(xhr.responseXML);

	};

	xhr.send('');

}




function setFeedDomains(opml) {

	feedDomains = {}

	var faviconsList = GM_getValue('faviconsList', null);
	if (faviconsList != null) {

		var favicons = faviconsList.split(SEPERATOR);

		for (var i=0; i<favicons.length; i++, i++) {
			feedDomains[favicons[i]]=favicons[i+1];
		}
	}

	Array.forEach(opml.getElementsByTagName('outline'), function(outline){

		if (! outline.hasAttribute('htmlUrl')) return;

		var title = outline.getAttribute('title');
		var domain = outline.getAttribute('htmlUrl').split(/\/|\?/)[2];

		var tmp;
		if(feedDomains[title] == null)

			tmp = FAVICON_URL.join(domain);
		else
			tmp = feedDomains[title];
		feedDomains[title] = tmp;


		if (title.length > 24)

			feedDomains[title.substr(0, 21) + '...'] = tmp;

	});
	
	fixAll=2;
	fixIcons();

}




function fixIcons() {

	var icon, i = 0, label;


	var uncorrectedIcons;
	if(fixAll>0) {

		uncorrectedIcons = document.evaluate(ALL_FEED_ICON_XPATH, document, null, 6, null);
		fixAll--;
	} else
		uncorrectedIcons = document.evaluate(UNFIXED_FEED_ICON_XPATH, document, null, 6, null);


	// feed favicons
	while (icon = uncorrectedIcons.snapshotItem(i++)) {
		if(fixAll==0 && icon.className.indexOf(" reverted_favicon")>0)
			continue;

		icon.width = "16";

		var titleElement = icon.nextSibling;
		titleElement.removeEventListener('click', setPromptForUrl, true);
		titleElement.addEventListener('click', setPromptForUrl, true);


		label = titleElement.firstChild.textContent;
	
		icon.removeEventListener('error', revertIcon, false);
		icon.addEventListener('error', revertIcon, false);	
			

		if (feedDomains.hasOwnProperty(label)) {
			icon.src = feedDomains[label];
		}

	}
  
  
	// entries favicons
	var entries = document.getElementById('entries');

	for (var i=0; i< entries.childNodes.length; i++) {

		var entry = entries.childNodes[i];

		var collapsedTitle = findNode(entry, './/div[@class="collapsed"]//h2');

		var entryContainerTitle = findNode(entry, './/div[@class="entry-container"]//h2');



		var entryLink, collapsedLink;

		if (entryContainerTitle != null) {

			entryLink = entryContainerTitle.firstChild;

		}
		if (collapsedTitle != null)
			collapsedLink = collapsedTitle.parentNode.parentNode.firstChild;


		if (entryContainerTitle != null && getFaviconImg(collapsedTitle) == null) {
			addFaviconToNode(collapsedLink.nextSibling.textContent, entryLink.firstChild);
			

		} else if (collapsedTitle != null && getFaviconImg(collapsedTitle) == null) {

			addFaviconToNode(collapsedLink.nextSibling.textContent, collapsedTitle);

		}

	}

}


function revertIcon(event) {
	this.className += " reverted_favicon";
	this.src = "http://www.google.com/reader/ui/4183653108-tree-view-subscription.gif";
}

function setPromptForUrl(event) {
	if(event.altKey == false && event.ctrlKey == true && event.shiftKey == true) {
		event.cancelBubble=true;
		var unreadCount = this.firstChild.nextSibling.textContent;
		var title = this.title.replace(unreadCount, "")
		setFavicon(title);
	}
}


function findNode(root, xpath) {

	var result = document.evaluate(xpath, root, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

	if (! result.snapshotLength) return null;

	return result.snapshotItem(0);

}




function addFaviconToNode(feedTitle,node) {
	var favicon = findNode(node.parentNode, ".//img[contains(@class, 'entry-favicon')]");
	if (favicon!=null)
		return;
	
	favicon = document.createElement('img');

	favicon.setAttribute('class', 'entry-favicon');

	favicon.removeEventListener('error', revertIcon, false);
	favicon.addEventListener('error', revertIcon, false);

	var faviconUrl=feedDomains[feedTitle];


	favicon.src = faviconUrl;
	favicon.width = 16;

	favicon.alt   ="";

	favicon.style.border = "0";
	

	

	favicon.style.marginRight = "1ex";

	node.parentNode.insertBefore(favicon,node);

}




function getFaviconImg(node){

	return findNode(node,'.//img[contains(@class, "entry-favicon")]');

}

	



refreshDomains();

setInterval(fixIcons, 5000);