// ==UserScript==
// @name          Hatena Bookmark AND Search Tags v0.1
// @namespace     http://www.towofu.net/gm/
// @description   Hatena Bookmark AND Search Tags v0.1
// @include       http://b.hatena.ne.jp/t/*
// ==/UserScript==

(function() {
	var ENTRY_COUNT = 0;
	var OF = 25;
	var TAG;	//1番目の検索タグ
	var TAGS;	//2番目以降の検索タグ
	var SORT;
	var DISP;
	var TOOLBAR;
	var ENTRY_TEMP = 
		  "<div id='entry($ID)' class='entry' style='margin: .5em .5em 2em .5em;'>"
		+ "<div class='entrytitle'><a href='($URL)'>($TITLE)</a>"
		+ "&nbsp;&nbsp;<a href='http://b.hatena.ne.jp/append?($URL)'><img src='http://b.hatena.ne.jp/images/append.gif' alt='このエントリーをブックマークに追加' title='このエントリーをブックマークに追加' /></a></div>"
		+ "<div class='entrytags' style='color: #008000; font-size: small;'>($TAGS)</div>"
		+ "<div class='entrydescription'>($DESCRIPTION)</div>"
		+ "</div>";
	var PAGE = 0;
	var CACHED_PAGE = 0;
	var CACHE_LIST = new Array();
	var CACHE_TIMER;
	var CACHING = false;
	var DEBUG;

	function xpath(query, node /* = document */) {
		if (node == undefined) {
			node = document;
		}
		return document.evaluate(query, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
	}

	function parseQuery() {
		var url = decodeURI(window.location.href);
		TAGS = url.match(/\/t\/([^?]+)/)[1].split(" ");
		TAG = TAGS.shift();
		SORT = [url, "sort=hot"].join("&").match(/sort=([^&]+)/)[1];
	}

	function createXmlHttp(url, onload) {
		var xmlhttp;
		/*try{
			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
			xmlhttp.onreadystatechange = function() {
				if (xmlhttp.readyState == 4) onload(xmlhttp.responseText);
			}
			xmlhttp.open("GET", url, true);
			xmlhttp.send(null);
		} catch(e)*/ {
			xmlhttp = GM_xmlhttpRequest({
				method: "GET",
				url: url,
				onload: function(response) {
					onload(response.responseText)
				}
			});
		}
	}

	function doRequest() {
		if (!CACHING && (CACHED_PAGE > PAGE)) {
			//DEBUG.innerHTML += "showing cache...<br />";
			var len = CACHE_LIST.length;
			for (var i = 0; i < len; i++) {
				DISP.insertBefore(CACHE_LIST[i], TOOLBAR);
			}
			CACHE_LIST.length = 0;
			PAGE = CACHED_PAGE;
			updateToolbar(2, len);
		} else {
			//DEBUG.innerHTML += "xmlhttp requesting...<br />";
			updateToolbar(CACHING);
			var xmlhttp = createXmlHttp(
				["http://b.hatena.ne.jp/t/", TAG, "?mode=rss", "&of=", OF, "&sort=", SORT].join(""),
				function(text) {
					var parser = new DOMParser("http://purl.org/dc/elements/1.1/");
					var xml = parser.parseFromString(text, "application/xml");
					var entries = xml.getElementsByTagName("item");
					var len = entries.length;
					var title, url, description, tags;
					var entry;
					var newItemCount = 0;
					for (var i = 0; i < len; i++) {
						var subjects = entries[i].getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", "subject");
						var n;
						var tags = new Array();
						for (n = subjects.length-1; n >= 0; n--)
							tags.push(subjects[n].textContent);
						for (n = TAGS.length-1; n >= 0; n--) {
							if (tags.indexOf(TAGS[n]) == -1) {
								break;
							}
						}
						//item matched
						if (n == -1) {
							title = entries[i].getElementsByTagName("title")[0].textContent;
							url = entries[i].getElementsByTagName("link")[0].textContent;
							description = entries[i].getElementsByTagName("description")[0].textContent.substring(0, 99) + "...";
							entry = document.createElement("div");
							entry.innerHTML = ENTRY_TEMP.replace(/\(\$ID\)/g, ENTRY_COUNT).replace(/\(\$URL\)/g, url).replace(/\(\$TITLE\)/g, title).replace(/\(\$DESCRIPTION\)/g, description).replace(/\(\$TAGS\)/g, tags.join("&nbsp;&nbsp;"));
							if (CACHING) {
								CACHE_LIST.push(entry);
							} else {
								DISP.insertBefore(entry, TOOLBAR);
								newItemCount++;
							}
							ENTRY_COUNT++;
						}
					}
					CACHING ? CACHED_PAGE++ : PAGE++;
					//DEBUG.innerHTML += "caching: " + CACHING + "<br />";
					if (PAGE > CACHED_PAGE) CACHED_PAGE = PAGE;
					updateToolbar(2, CACHING ? undefined : newItemCount);
					CACHING = false;
					if ((CACHED_PAGE - PAGE) < 5)
						CACHE_TIMER = setTimeout(function() {
							CACHING = true;
							//DEBUG.innerHTML += "start caching...<br />";
							doRequest();
						}, 5000);
				}
			);
			OF += 25;
		}
	}

	function initHTML() {
		DISP = xpath("//div[@class='optionsub']").snapshotItem(0);
		DISP.innerHTML = "";
		TOOLBAR = document.createElement("div");
		TOOLBAR.id = "toolbar";
		TOOLBAR.innerHTML =  "<a id='newItemCount'></a>"
							+"<a id='loading' style='display: none; color: white; background-color: #883333; padding: .2em .5em .2em .5em;'></a>"
							//+ "<div id='findmore' style='display: block;'><a href='javascript:void(0)' onclick='doRequest()'>もっと探す</a></div>"; // "doRequest is not defined" エラーが出てしまう
							+ "<div id='findmore' style='display: block;'><a href='javascript:void(0)' id='find'>もっと探す</a></div>";
		DISP.appendChild(TOOLBAR);
		DEBUG = document.createElement("div");
		DEBUG.id = "debug";
		DISP.appendChild(DEBUG);
		var a = document.getElementById("find");
		a.addEventListener(
			"click", 
			function() {
				clearTimeout(CACHE_TIMER);
				doRequest();
			},
			true
		);
	}

	/*
		mode0 ローディングメッセージ表示
		mode1 キャッシュ中めーっセージ表示
		mode2 もっと探すボタン表示
	*/
	function updateToolbar(mode, newItemCount) {
		var elm = document.getElementById("newItemCount");
		if (newItemCount != undefined) {
			elm .innerHTML = newItemCount + "アイテム追加しました。";
			elm.style.display = "inline";
		} else {
			elm.style.display = "none";
		}
		elm = document.getElementById("loading");
		elm.style.display = (mode==0||mode==1) ? "inline" : "none";
		elm.innerHTML = mode ? "キャッシュ中..." : "ロード中...";
		document.getElementById("findmore").style.display = (mode==2) ? "block" : "none";
		//DEBUG.innerHTML = ["cached pages: ", CACHED_PAGE, "pages: ", PAGE].join("");
	}

	if (window.location.href.indexOf("%20") != -1) {
		parseQuery();
		initHTML();
		doRequest();
	}

	return;

})();