Score stars on Google Reader

By X4lldux Last update Jun 20, 2008 — Installed 446 times.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==

// @name          Score stars on Google Reader

// @description   Allows to score from 1-5 starred items
// @author        X4lldux
// @version       0.2

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

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

// ==/UserScript==

var activeStar = 'data:image/png;base64,'+
	'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ'+
	'bWFnZVJlYWR5ccllPAAAAGBQTFRFXFu65dsFl5AkycAhfoHmLi1b7uQJX2HhbGeOvsPwTk2t8+gH'+
	'ko6WvrYErbHt29NSc24wnZdeUk+HdHGvys/yFhWdmp7qLS7S//sWVFBTjZHo/vgvGRnGQUPW/vUP'+
	'////og4vjQAAACB0Uk5T/////////////////////////////////////////wBcXBvtAAAAmUlE'+
	'QVR42lTP2RKCMAwF0LR0AWRVuyft//+lhcqg9yWZ83AzgXJGoG5LgTbchH9gIT3FL+jJ73SBjTG6'+
	'1XcoYrQViADglXzuRxiRCmCfanzOPiUVsIDF0OUzag4m1o43mVMeXLqhXaHeHxDcdZZ2z5hSHL8w'+
	'uJXxwFlnbAMxss04s81BN9DyaLcoJTVYHC11Gwjj/e2djwADAOwCF+1QCTaiAAAAAElFTkSuQmCC';

var inactiveStar = 'data:image/png;base64,'+
	'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAANbY1E9YMgAAABl0RVh0'+
	'U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIiSURBVHjaYvz//z8DNsDIyMgBpED4B1DN'+
	'DwYcACCAGPEYoAekpID4GxAfAar7h00dQABhNQBquwOS0Amgug/YDAAIICYY49u3b3AMBGogYu3a'+
	'tQKqqqogphySHAoACCAGqAuYoP7lUFNTE+bg4ADZbvvs2bN/e/fuPQNkewCxAEwNSD1IHwgDBBAL'+
	'1LmmQMytrKzMY2FhoWhnZ8cmLS39hJ+fn1FKSkqEnZ2d6efPn5YwO4H4K1DfaVDgAgQQI9Rky9jY'+
	'WOnS0tIgYWFhMyCfVUAAJMzAB8QvgGA3MzOz5J07d3a6uLicAopxwcIFIIBYQNEExE9//Pih+vHj'+
	'R22gzSAvIPtSAghigfL/gBo4gfwrQPwMqo8BIIBgYcDBzc1t4Obmlrp8+fKDnz59+vn169f/SPiu'+
	'v79/FDQsQNHLAQsDgABigDGAihg4OTllgOEQ+ujRo1NA/l8g/gfSDDRwG9BV9qCARdYMwgABxITs'+
	'1u/fvws9fPjwFdAgjn///jG+f//+FZAWAAaY48yZMz2gLkZJlQABBDcJGpV2O3bs2A6Mvj9bt259'+
	'q6WlFZ6Xl1dw8+bNh9evX78K9QKKCwACCNkAUMh5AA34BUxAX+Tl5d2BfGmQoba2tgkrVqw4CksP'+
	'yAYABBA8KQOdCXKBNTD6BD58+PAQyL4F9P8PYOByQFMmKF+Aon0ncr4ACCCUvIArByKJM6DnCYAA'+
	'wpkbiQUAAQYAomop3wXyJg8AAAAASUVORK5CYII=';

function addGlobalStyle(css) {
	var head, style;
	head = document.getElementsByTagName('head')[0];
	if (!head) { return; }
	style = document.createElement('style');
	style.type = 'text/css';
	style.innerHTML = css;
	head.appendChild(style);
};

function getAbsolutePosition(element) {
	var r = { x: element.offsetLeft, y: element.offsetTop };
	if (element.offsetParent) {
		var tmp = getAbsolutePosition(element.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}
	return r;
};

var ScoreTable = function () {
	var scoreTable = null;
	var currDefNStars = 3;
	var onStarClick = function(e, n){};
	var onClearClick = function(e){};

	addGlobalStyle(".c>ul {list-style-type: none; display: table;} .c>ul>li {display: table-cell;}");
	addGlobalStyle(".starOn  {height:15px; width:15px; cursor:pointer; background: transparent url($ActiveStar$) no-repeat;}".replace("$ActiveStar$", activeStar, "g"));
	addGlobalStyle(".starOff {height:15px; width:15px; cursor:pointer; background: transparent url($InactiveStar$) no-repeat;}".replace("$InactiveStar$", inactiveStar, "g"));
	
	function create(star) {
		var table=document.createElement('table');
		table.id = "stars-container-template";
		table.className = "round-box tags-container-box tags-container";
		table.border = "0";
		table.cellSpacing="0";
		table.cellPadding="0";
	
		table.style.width = "89px";
		table.style.height = "12px";
		table.style.display = "none";

		table.innerHTML = '<tbody><tr><td class="s tl"/><td class="s"/><td class="s tr"/></tr><tr><td class="s"/><td class="c"><ul id="starList" style="margin-left: 0px;">'+
		'<li><div id="clearStars" style="height: 15px; width: 6px; cursor:pointer; background: transparent"/> </li>'+
		'<li><div id="star1" class="starOff" /></li> <li><div id="star2" class="starOff" /></li> <li><div id="star3" class="starOff" /></li> <li><div id="star4" class="starOff" /></li> <li><div id="star5" class="starOff" /><li>'+
		'</ul></td><td class="s"/></tr><tr><td class="s bl"/><td class="s"/><td class="s br"/></tr></tbody></table>'
		document.getElementById("entries").appendChild(table);


		var e=document.getElementById("clearStars");
		e.addEventListener("mouseover", function(e) {
			setStarsNumber(0);
		}, true);
		e.addEventListener("mouseout", function(ev) {
			setStarsNumber(currDefNStars);
		}, true);
		e.addEventListener("click", function(ev) {
			ScoreTable.onClearClick(ev);
		}, true);
		
		var i;
		for(i=1; i<6; i++) {
			e=document.getElementById("star"+i);
			e.setAttribute("nStar", i);			
			e.addEventListener("mouseover", function(ev) {
				var n = ev.target.getAttribute("nStar");
				setStarsNumber(n);
			}, true);
			e.addEventListener("mouseout", function(ev) {
				setStarsNumber(currDefNStars);
			}, true);
			e.addEventListener("click", function(ev) {
				var n = ev.target.getAttribute("nStar");
				ScoreTable.onStarClick(ev, n);
			}, true);
		}
	
		return table;
	};
	
	function moveToStar(star) {
		var starPos = getAbsolutePosition(star);
		var entriesPos = getAbsolutePosition(document.getElementById('entries'));
		var pos = {x: starPos.x-entriesPos.x, y:starPos.y-entriesPos.y };
		scoreTable.style.left=pos.x+19+"px";
		scoreTable.style.top=pos.y-6+"px";
	};

	function show() {
		scoreTable.style.display = null;
	};

	function hide(e) {
		if(e && e.target.className.indexOf("item-star-active")>=0)
			return;
		scoreTable.style.display = "none";
		document.body.removeEventListener('click', hide, true);

		scoreTable.parentNode.removeChild(scoreTable);
	};
	
	function isVisible() {
		if(scoreTable == null || scoreTable.style.display == "none")
			return false;
		return true;
	};

	function activateTable(star, nStars) {
		scoreTable=create(star);

		moveToStar(star);
		document.body.addEventListener('click', hide, true);
		currDefNStars = nStars;
		setStarsNumber(nStars);
		show();
	};
	
	
	function setStarsNumber(n) {
		var i;
		for(i=1; i<=n; i++) {
			var el = document.getElementById("star"+i);
			el.className="starOn";
		}
		for(; i<6; i++) {
			var el = document.getElementById("star"+i);
			el.className="starOff";
		}
	};
	
	
	ScoreTable = {
		scoreTable: scoreTable,
		activateTable: activateTable,
		show: show,
		hide: hide,
		isVisible: isVisible,
		onStarClick: onStarClick,
		onClearClick: onClearClick,
	};
}

var starNavMenuAvaible = false;
function StarNavMenu () {
	// run only once!
	if(starNavMenuAvaible == true)
		return;
	starNavMenuAvaible = true;

	var starSelector = document.getElementById("star-selector");
	var title = starSelector.firstChild.textContent;

	addGlobalStyle('#selectors-box #star-tree-container {margin-left:3px; padding:0pt; }');
	addGlobalStyle('#star-tree {overflow:visible; width:243px; }');
	addGlobalStyle('#star-tree .toggle-d-0 {left:-14px; top:0pt; }');
	addGlobalStyle('#star-tree-item-0-icon {display:none; }');
	addGlobalStyle('#star-tree #star-tree-item-0-name {font-size:105%; }');
	addGlobalStyle('#star-tree-item-0-main ul {clear:both; overflow-x:hidden; overflow-y:auto; position:relative; }');
	addGlobalStyle('#star-tree-item-0-main .name .icon {margin-right: -5px; margin-bottom: -3px; background: transparent; }');

	var newStar = document.createElement("li");
	newStar.className = "selector scroll-tree-container";
	newStar.id = "star-tree-container";
	var newStarsHtml = ''+
'       <ul class="scroll-tree" id="star-tree">'+
'               <li id="star-tree-item-0-main" class="folder unselectable expanded">'+
'                       <div class="toggle toggle-d-0" /></div>'+
'                       <a id="star-item-0-link" class="link" href="/reader/view/user/-/state/com.google/starred">'+
'                               <span id="star-tree-item-0-name" class="name name-d-0" style="text-decoration: none;">'+
'                                       <span id="star-tree-item-0-name-text" style="text-decoration: underline;" class="name-text name-text-d-0">X4_STARRED_NAME</span>'+
'                                       <span class="icon-container">'+
'                                               <img width="16" style="display: inline; margin-left: -4px;" height="16" src="$ActiveStar$" class="selector-icon selector-icon-selected" alt=""/>'+
'                                       </span>'+
'                               </span>'+
'                       </a>'+
''+
'                       <ul>'+
'                               <li id="star-tree-item-1-main" class="star unselectable expanded">'+
'                                       <a id="star-tree-item-1-link" href="/reader/view/user/-/label/★" class="link">'+
'                                               <span title="★" id="star-tree-item-1-name" class="name name-d-1">'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <span class="name-text name-text-d-1 hidden">★</span>'+
'                                                       <span id="star-tree-item-1-unread-count" class="unread-count unread-count-d-1" />'+
'                                               </span>'+
'                                       </a>'+
'                               </li>'+
'                               <li id="star-tree-item-2-main" class="star unselectable expanded">'+
'                                       <a id="star-tree-item-2-link" href="/reader/view/user/-/label/★★" class="link">'+
'                                               <span title="★★" id="star-tree-item-2-name" class="name name-d-1">'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <span class="name-text name-text-d-1 hidden">★★</span>'+
'                                                       <span id="star-tree-item-2-unread-count" class="unread-count unread-count-d-1" />'+
'                                               </span>'+
'                                       </a>'+
'                               </li>'+
'                               <li id="star-tree-item-3-main" class="star unselectable expanded">'+
'                                       <a id="star-tree-item-3-link" href="/reader/view/user/-/label/★★★" class="link">'+
'                                               <span title="★★★" id="star-tree-item-3-name" class="name name-d-1">'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <span class="name-text name-text-d-1 hidden">★★★</span>'+
'                                                       <span id="star-tree-item-3-unread-count" class="unread-count unread-count-d-1" />'+
'                                               </span>'+
'                                       </a>'+
'                               </li>'+
'                               <li id="star-tree-item-4-main" class="star unselectable expanded">'+
'                                       <a id="star-tree-item-4-link" href="/reader/view/user/-/label/★★★★" class="link">'+
'                                               <span title="★★★★" id="star-tree-item-4-name" class="name name-d-1">'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$InactiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <span class="name-text name-text-d-1 hidden">★★★★</span>'+
'                                                       <span id="star-tree-item-4-unread-count" class="unread-count unread-count-d-1" />'+
'                                               </span>'+
'                                       </a>'+
'                               </li>'+
'                               <li id="star-tree-item-5-main" class="star unselectable expanded">'+
'                                       <a id="star-tree-item-5-link" href="/reader/view/user/-/label/★★★★★" class="link">'+
'                                               <span title="★★★★★" id="star-tree-item-5-name" class="name name-d-1">'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <img src="$ActiveStar$" width="15" height="15" class="icon icon-d-1"/>'+
'                                                       <span class="name-text name-text-d-1 hidden">★★★★★</span>'+
'                                                       <span id="star-tree-item-5-unread-count" class="unread-count unread-count-d-1" />'+
'                                               </span>'+
'                                       </a>'+
'                               </li>'+
'                       </ul>'+
'               </li>'+
'       </ul>';
	newStar.innerHTML=newStarsHtml.replace("$ActiveStar$", activeStar, "g").replace("$InactiveStar$", inactiveStar, "g");

	starSelector.parentNode.replaceChild(newStar, starSelector);

	// set last expand/collapse state
	var starTreeExpanded = GM_getValue('starTreeExpanded', null);
	if(starTreeExpanded == null)
		starTreeExpanded = true;
	var el = document.getElementById("star-tree-item-0-main");
	if(starTreeExpanded == false)
		el.className=el.className.replace("expanded", "collapsed");

	// setting old title to the new star tree
	el = document.getElementById("star-tree-item-0-name-text");
	el.textContent = title;
	
	// making new star tree collapsable
	el = document.getElementById("star-tree-item-0-main").childNodes[1];
	el.addEventListener('click', function() {
		var starTreeExpanded = GM_getValue('starTreeExpanded', null);
		if(starTreeExpanded == null || starTreeExpanded == true)
			starTreeExpanded = false;
		else
			starTreeExpanded = true;

	    GM_setValue('starTreeExpanded', starTreeExpanded);


		var el = document.getElementById("star-tree-item-0-main");
		if(starTreeExpanded == false)
			el.className=el.className.replace("expanded", "collapsed");
		else
			el.className=el.className.replace("collapsed", "expanded");	
	}, true);
}


String.prototype.trim = function() {
	var str = this.replace(/^\s+/, '');
	for (var i = str.length - 1; i >= 0; i--) {
		if (/\S/.test(str.charAt(i))) {
			str = str.substring(0, i + 1);
			break;
		}
	}
	return str;
}


var StarScorer = function() {
	var items = null,
		currentItem = null
		currentStar = null,
		isClearClicked = false;

	var skrypt=document.createElement('script');
	skrypt.type="text/javascript";
	skrypt.innerHTML = ''+
'var X4_OnLoad = function() {};'+
'       function newOpen(method, url, async, user, password) {'+
'               if(url.indexOf("/reader/api/0/stream/contents")>=0 || url.indexOf("/reader/api/0/stream/items/contents")>=0) {'+
'                       this.addEventListener("load", X4_OnLoad, false);'+
'               }'+
''+
'               return this.X4_originalOpen(method, url, async, user, password);'+
'       };'+
''+
'       var XHR = window.XMLHttpRequest;'+
'       XHR.prototype.X4_originalOpen = XHR.prototype.open;'+
'       XHR.prototype.open = newOpen;';

	var head = document.getElementsByTagName("head");
	head[0].appendChild(skrypt);


	unsafeWindow.X4_OnLoad = addNewItems;

	// wait for entries to be fully loaded
	var timer = window.setInterval(function() {
		StarNavMenu();
		
		var entries = document.getElementById("entries");
		if(entries.className == "list" || entries.className == "cards" || entries.className == "search") {
			entries.addEventListener("DOMAttrModified", viewChanged, true);

			ScoreTable();
			ScoreTable.onStarClick = onScoreClick;
			ScoreTable.onClearClick = onClearClick;
					
			window.clearInterval(timer);
		}
	}, 100);

	function viewChanged(e) {
		var entries = e.target;
		if(e.target.id == "entries" && e.attrName.trim()=="class") {
			var newClass = e.newValue;
			if(newClass.indexOf("cards")>=0 || newClass.indexOf("list")>=0 || newClass.indexOf("search")>=0) {
				entries.removeEventListener("DOMAttrModified", viewChanged, true);
				findNewStars();
				entries.addEventListener("DOMAttrModified", viewChanged, true);
			}
		}
	}

	function addNewItems() {
		var resp = eval('(' + this.responseText + ')');
		if(items)
			items.items=items.items.concat(resp.items);
		else
			items=resp;
		
		findNewStars();
	};
	
	function findNewStars() {
		var stars = document.evaluate("//div[contains(@class, 'star link') and not(contains(@class, 'owned'))]", document, null, 6, null);
		var star, i=0;
		while (star = stars.snapshotItem(i++)) {
			star.className += " owned";
			star.parentNode.addEventListener("click", onStarClicked, true);
		}
	}

	function checkScore(item) {
		var labelStars=/.*\/label\/(★{1,5})$/;
		
		for(var i=0; i<item.categories.length; i++) {
			var score = labelStars.exec(item.categories[i]);
			if(score) {
				return score[1].length;
			}
		}
		
		return 0;
	};

	function createTagsRemoveUri() {
		var l5s=/.*\/label\/★★★★★$/;
		var l4s=/.*\/label\/★★★★$/;
		var l3s=/.*\/label\/★★★$/;
		var l2s=/.*\/label\/★★$/;
		var l1s=/.*\/label\/★$/;
		
		var tags="";
		for(var i = currentItem.categories.length-1; i >=0 ; i--) {
			if (l5s.test(currentItem.categories[i]) || l4s.test(currentItem.categories[i]) || l3s.test(currentItem.categories[i]) || l2s.test(currentItem.categories[i]) ||l1s.test(currentItem.categories[i])) {
				var tag = currentItem.categories.splice(i, 1)[0];
				tags+="&r="+escape(tag.replace("★", "★", "g"));
			}
		}

		return tags;
	}
	
	function removeScore() {
		var xhr = new XMLHttpRequest();
		xhr.open('POST', '../api/0/edit-tag?client=scroll', true);
		var params = "T="+escape(unsafeWindow._COMMAND_TOKEN);
		params+="&ac=edit-tags&async=true";
		params+="&s="+escape(currentItem.origin.streamId);
		params+="&i="+escape(currentItem.id);

		var rTags = createTagsRemoveUri();
		if(rTags.length>0) {
			params+=rTags;
			xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			xhr.setRequestHeader("Connection", "keep-alive");
			xhr.send(params);
		}
	};
	
	function setScore(n) {
		var tag = "user/"+unsafeWindow._USER_ID+"/label/";
		for(var i=0; i<n; i++)
			tag+='★';

		var xhr = new XMLHttpRequest();
		xhr.open('POST', '../api/0/edit-tag?client=scroll', true);
		var params = "s="+escape(currentItem.origin.streamId);
		params+="&i="+escape(currentItem.id);
		params+="&ac=edit-tags";
		params+="&async=true";
		params+="&a="+escape(tag.replace('★', '★', "ig"));
		params+=createTagsRemoveUri();
		params+="&T="+escape(unsafeWindow._COMMAND_TOKEN);

		//Send the proper header information along with the request
		xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		xhr.setRequestHeader("Connection", "keep-alive");
		xhr.send(params);

		currentItem.categories.push(tag);

	};
	
	function onScoreClick(e, n) {
		setScore(n);
	};
	
	function onClearClick(e) {
		removeScore();		
		isClearClicked = true;
		var evt = document.createEvent("MouseEvents");
		evt.initMouseEvent("click", true, true, window,
			0, 0, 0, 0, 0, false, false, false, false, 0, null);
		currentStar.dispatchEvent(evt);
	};


	function onStarClicked(e) {	
		if(isClearClicked) {
			isClearClicked = false;
			return;
		}

		// clicked on the star to unstar and clear rating
		if(e.target.className.indexOf("item-star-active")>=0 && ScoreTable.isVisible()) {
			currentStar = null;
			removeScore();
			ScoreTable.hide();

		}  else {
			currentStar = e.target;
			// check do we just show the ScoraTable and prevent unstarring item
			if(currentStar.className.indexOf("item-star-active")>=0 && !ScoreTable.isVisible()) {
				e.stopPropagation();
			}
			
			var entries=document.getElementById("entries");
			var kids=entries.childNodes;
			var currentStarEntry;
			if(entries.className.indexOf("cards")>=0)
				currentStarEntry=currentStar.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
			else // list view or search
				currentStarEntry=currentStar.parentNode.parentNode.parentNode;
			
			for(var i=0; i<kids.length; i++) {
				if(kids[i]==currentStarEntry) {
					currentItem = items.items[i];

					var n=checkScore(currentItem);
					if(n==0) {
						setScore(3);
						ScoreTable.activateTable(this, 3); // the default score
					} else {
						ScoreTable.activateTable(this, n);
					}

					break;
				}
			}
		}
	};
}


window.addEventListener('load', StarScorer, true);