RE:RE:Googleリーダー内で、はてなブックマークのコメントを読むためのGreasemonkeyを書いてみた。

http://d.hatena.ne.jp/kennak/20091118/1258536113
なんかうまくはてなスターが動かん。

// ==UserScript==
// @name           GoogleReaderHatebuComments
// @namespace      http://blog.ne2ma2.com/
// @include        https://www.google.tld/reader/*
// @include        http://www.google.tld/reader/*
// @description loading Hatena Bookmark Comments on GoogleReader
// @version     0.0.1
// thanks:
// * google reader full feed changer http://blog.fkoji.com/ 
// * LDR Hatebu Comments http://www.milk1000.cc/
// ==/UserScript==
(function() {
	var timeID = null;
	const AUTO_LOAD = false;
	const FLASH_MESSAGE = false;

	var style = {
			container: [
				'color: #444',
				'margin: 0 10px'
			].join(';'),

			caption: [
				'border-top: 1px dotted #cbcbcb',
				'font-size: 90%'
			].join(';'),

			comments: [
				'list-style-type: none',
				'padding: 5px;',
				'margin: 0px',
				'list-style-type: circle',
				'list-style-position: inside',
				'font-size: 90%',
				'line-height: 150%'
			].join(';'),

			image: [
				'border: 0px',
				'vertical-align: middle',
				'margin-right: 2px',
				'width: 16px',
				'height: 16px'
			].join(';'),

			icon: [
				'border: 0px',
				'vertical-align: middle',
				'padding:1px 12px 1px 16px'
//				'margin-right: 2px',
			].join(';')
		};


	document.addEventListener(
		'keydown',
		function(event) {
			var key = String.fromCharCode(event.keyCode);
			//ブックマークの表示非表示の切り替え
			if (key.toLowerCase() == 'd') {
				if(checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]')){
					setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]','value','false');
					getCurrentEntry();
				}else{
					removeElement('//*[@id="current-entry"]//div[@class="gr-hatebu-comment"]');
					setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="false"]','value','true');
				}
			}
		},
		false
	);

	//ブックマークの表示非表示の切り替え
	function createViewBookmarkIcon() {
		var a = document.createElement('a');
		a.setAttribute('href', '#');
		a.addEventListener("click", function(event){
			if(checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]')){
				setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]','value','false');
				getCurrentEntry();
			}else{
				removeElement('//*[@id="current-entry"]//div[@class="gr-hatebu-comment"]');
				setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="false"]','value','true');
			}
		}, false);

		var img = document.createElement('img');
		img.setAttribute('src', 'http://r.hatena.ne.jp/images/popup.gif');
		img.setAttribute('alt', 'view bookmark');
		img.setAttribute('title', 'view bookmark');
		img.setAttribute('style', style.icon);

		a.appendChild(img);

		var node = document.createElement('span');
		node.setAttribute('class', 'hatena-bcomment-view-icon');
		node.setAttribute('value', 'true');
		node.appendChild(a);

		return node;
	}

	function createAddBookmarkIcon() {

		var url = getFocusedLink();
		var title = getFocusedTitle();

		var a = document.createElement('a');
		a.setAttribute('href', 'http://b.hatena.ne.jp/add?mode=confirm&is_bm=1&title=' + escape(title) + '&url=' + escape(url));

		a.setAttribute('target', '_blank');

		var img = document.createElement('img');
		img.setAttribute('src', 'http://b.hatena.ne.jp/images/append.gif');
		img.setAttribute('alt', 'add bookmark');
		img.setAttribute('title', 'add bookmark');
		img.setAttribute('style', style.icon);

		a.appendChild(img);

		var node = document.createElement('span');
		node.setAttribute('class', 'hatena-bookmark-icon');
		node.appendChild(a);

		return node;
	}

	//ブックマーク関連のアイコンの追加
	function addViewBookmarkButton(){
		if(!checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon"]')){
			appendElement('//div[@id="current-entry"]//div[@class="entry-actions"]',createViewBookmarkIcon());
		}
		if(!checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bookmark-icon"]')){
			appendElement('//div[@id="current-entry"]//div[@class="entry-actions"]',createAddBookmarkIcon());
		}
		addHatenaStar();
	}

	//リクエスト
	function getCurrentEntry() {
		var link = getFocusedLink();

		if(!link ||
			checkXPath('//*[@id="current-entry"]//div[@class="gr-hatebu-comment"]') ||
			checkXPath('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]')){
			return;
		};

		container = document.createElement('div');
		container.setAttribute('style', style.container);
		container.setAttribute('class', 'gr-hatebu-comment');
		appendElement('//div[@id="current-entry"]//div[@class="entry-comments"]',container);

		GM_xmlhttpRequest({
			url:'http://b.hatena.ne.jp/entry/jsonlite/?url=' + encodeURIComponent(link),
		  method:"GET",
		  onload:function(xhr){
			var obj = eval("(" + xhr.responseText + ")");
			var element =createComments(obj);
			setAttribute('//div[@id="current-entry"]//div[@class="entry-actions"]//span[@class="hatena-bcomment-view-icon" and @value="true"]','value','false');
			}
		});
	}

	function appendElement(xpath,element){
		var currentEntry = document.evaluate(xpath,document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
		var currentItem = currentEntry.snapshotItem(0);
		if(currentItem){
			currentItem.appendChild(element);
		}
	};

	function removeElement(xpath){
		var currentEntry = document.evaluate(xpath,document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );

        for (var i = 0; i < currentEntry.snapshotLength; i++) {
			currentEntry.snapshotItem(i).parentNode.removeChild(currentEntry.snapshotItem(i));
        }

	};

	function setAttribute(xpath,attr,value){
		var currentEntry = document.evaluate(xpath,document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
		var currentItem = currentEntry.snapshotItem(0);
		if(currentItem){
			currentItem.setAttribute(attr,value);
		}
	};

	//コメント表示
	function createComments(json){

		if(!json || !json.count){return;}

		message('Loading Hatebu Comment...');

		var caption, comments;
		container = document.createElement('div');

		caption = document.createElement('div');
		caption.setAttribute('style', style.caption);
		caption.innerHTML = '\u3053\u306e\u30a8\u30f3\u30c8\u30ea\u30fc\u3092\u30d6\u30c3\u30af\u30de\u30fc\u30af\u3057\u3066\u3044\u308b\u30e6\u30fc\u30b6\u30fc (' + json.count + ')';
		container.appendChild(caption);

		comments = document.createElement('ul');
		comments.setAttribute('style', style.comments);

		json.bookmarks.forEach(function(bookmark) {

			if (bookmark.comment.length != 0){

				var comment, image;
				var date = new Date(bookmark.timestamp);

				comment = document.createElement('li');
				comment.innerHTML = date.getFullYear() + '\u5e74' + (date.getMonth() + 1) + '\u6708' + date.getDate() + '\u65e5 ';

				image = document.createElement('img');
				image.setAttribute('style', style.image);
				image.src = 'http://www.hatena.ne.jp/users/' + bookmark.user.slice(0, 2) + '/' + bookmark.user + '/profile_s.gif';
				comment.appendChild(image);
				comment.innerHTML += '<a href="http://b.hatena.ne.jp/' + bookmark.user + '">' + bookmark.user + '</a>\u300e';
				bookmark.tags.forEach(function(tag) {
					comment.innerHTML += '[<a href="http://b.hatena.ne.jp/' + bookmark.user + '/' + tag + '">' + tag + '</a>]';
				});
				comment.innerHTML += bookmark.comment + '\u300f';

				comments.appendChild(comment);
			}
		});
		container.appendChild(comments);
		appendElement('//div[@id="current-entry"]//div[@class="gr-hatebu-comment"]',container);
		message('Loading Hatebu Comment... Done');
	};

	//フォーカスの当たっているリンクを取得
	function getFocusedLink() {
		return getStringByXPath('//div[@id="current-entry"]//a[@class="entry-title-link"]/@href');
	};

	//フォーカスの当たっているタイトルを取得
	function getFocusedTitle() {
		return getStringByXPath('//div[@id="current-entry"]//h2[@class="entry-title"]');
	};

	function getStringByXPath(xpath, node) {
		var node = node || document
		var doc = node.ownerDocument ? node.ownerDocument : node
		var str = doc.evaluate(xpath, node, null, XPathResult.STRING_TYPE, null);
		return (str.stringValue) ? str.stringValue : ''
	};

	function checkXPath(xpath){
		var item = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
		return (item.snapshotLength) ? true : false;
	}

	function checkXPathLength(xpath){
		var item = document.evaluate(xpath, document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
		return item.snapshotLength;
	}

	// copied from FLASH KEY (c) id:brazil
	// http://userscripts.org/scripts/show/11996
	// slightly modified.
	var FlashMessage = new function() {
  		addStyle(<><![CDATA[
		#FLASH_MESSAGE {
		  position : fixed;
		  font-size : 200%;
		  z-index : 10000;

		  padding : 20px 50px 20px 50px;
		  left : 1em;
		  top : 1em;

		  background-color : #444;
		  color : #FFF;
		  -moz-border-radius: 0.3em;
		  min-width : 1em;
		  text-align : center;
		}
	  ]]></>.toString()
			.replace(/^\ {4}/gm, ""));
	  var opacity = 0.9;
	  var flash = document.createElement('div');
	  flash.id = 'FLASH_MESSAGE';
	  hide(flash);
	  document.documentElement.appendChild(flash);
	  var canceler;
	  this.showFlashMessageWindow = function(string, duration) {
		duration = duration || 400;
		canceler && canceler();
		flash.innerHTML = string;
		flash.style.MozOpacity = opacity;
		show(flash);

		canceler = callLater(function() {
		  canceler = tween(function(value) {
			flash.style.MozOpacity = opacity * (1 - value);
		  }, 100, 5);
		}, duration);
	  };

	  // ----[Utility]-------------------------------------------------
	  function callLater(callback, interval) {
		var timeoutId = setTimeout(callback, interval);
		return function() {
		  clearTimeout(timeoutId);
		};
	  }

	  function tween(callback, span, count) {
		count = count || 20;
		var interval = span / count;
		var value = 0;
		var calls = 0;
		var intervalId = setInterval(function() {
		  callback(calls / count);

		  if (count == calls) {
			canceler();
			return;
		  }
		  calls++;
		}, interval);
		var canceler = function() {
		  clearInterval(intervalId);
		  hide(flash);
		};
		return canceler;
	  }

	  function hide(target) {
		target.style.display = 'none';
	  }

	  function show(target, style) {
		target.style.display = style || '';
	  }
	};

	function message(mes) {
	  //w.message(""); w.message(mes);
	  //console.log(mes);
	  if(FLASH_MESSAGE){
	    FlashMessage.showFlashMessageWindow("");
	    FlashMessage.showFlashMessageWindow(mes, 1000);
	  }
	}

	// copied from LDRize (c) id:snj14
	function addStyle(css, id) { // GM_addStyle is slow
	  var link = document.createElement('link');
	  link.rel = 'stylesheet';
	  link.href = 'data:text/css,' + escape(css);
	  document.documentElement.appendChild(link);
	}

	function addHatenaStar(){
		//alert(checkXPathLength('//*[@class="entry-container"]'));
		//alert(checkXPathLength('//*[@class="entry-container"]//span[@class="hatena-star-star-container"]'));
		var entry_count = 0;
		var star_count = 0;

		if(w.Hatena){
			//enttry_count = checkXPathLength('//*[@class="entry-container"]');
			star_count = checkXPathLength('//*[@class="entry-container"]//span[@class="hatena-star-star-container"]');
			//var node = document.evaluate('//div[@id="current-entry"]', document, null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
			//alert(checkXPathLength('//div[@id="current-entry"]'));
			//w.Hatena.Star.EntryLoader.loadNewEntries(node);
			//alert(enttry_count);
			//alert(star_count);
			if(0 == star_count){
				w.Hatena.Star.EntryLoader.loadNewEntries();
			}
			//if(enttry_count != star_count){
				//alert("addentryloader");
				//w.Hatena.Star.EntryLoader();
				//w.Hatena.Star.EntryLoader.loadNewEntries(node);
			//}
			//alert(entry_count - star_count);
		}
	}

	if(AUTO_LOAD){
		setInterval(getCurrentEntry, 3000);
	}
	setInterval(addViewBookmarkButton, 3000);

    var w = typeof unsafeWindow != "undefined" ? unsafeWindow : window;
    var initialized = false;
    var s = document.createElement('script');
    s.src = 'http://s.hatena.ne.jp/js/HatenaStar.js';
    s.charset = 'utf-8';

    document.body.appendChild(s);
    var t = setInterval(function(){
        if(w.Hatena){
			w.Hatena.Star.SiteConfig = {
			  entryNodes: {
			    'div.entry-container': {
			      uri: 'h2 a',
			      title: 'h2',
			      container: 'h2'
			    }
			  }
			};
			//addHatenaStar();
            clearInterval(t);
        }
    }, 3000);
})();