// ==UserScript==
// @name		Fast Look up Alc and Excite
// @namespace	http://mtamaki.com/
// @description	original: http://lowreal.net/blog/2007/09/06/1
// @include 	http://*
// @include 	file:///*
// ==/UserScript==


(function () {
	var popups = [];
	var pre_selection = null;

	function popup_add(area) {
		function on_search(e) {
			search(false)
			e.stopPropagation();
		}
		area.addEventListener('click', on_search, false);
		area.addEventListener('mouseup', on_search, false);
		document.body.appendChild(area);
		popups.push(area);
	}

	function popup_remove(parent) {
		var e;
		while (e = popups.pop()) {
			e.parentNode.removeChild(e);
		}
	}

	function popup_create(child) {
		return $N('div', {style: [
			'position: fixed',
			'bottom: 0',
			'left: 0',
			'right: 0',
			'border: 1px solid #000',
			'background: #fff',
			'color: #000',
			'max-height: 50%',
			'overflow: auto',
			'opacity: 0.9',
			'z-index:100'
		].join(';')}, [child])
	}

	function search(is_closable) {
		var selection = window.getSelection().toString();
		//トリミング
		selection = selection.replace(/^[　\s]+|[　\s]+$/mg, "")
		//前回と同じ文字列なら何もしない。
		if (pre_selection == selection)
			return;
		pre_selection = selection
		//選択がなければクリアしてかえる
		if (!selection.length)
		{
			if( is_closable )
				popup_remove();
			return
		}
		//excite検索
		function search_excite(selection) {
			//excite
			GM_xmlhttpRequest({
				method: 'get',
				url: "http://www.excite.co.jp/world/english/?wb_lp=ENJA&before=" + encodeURIComponent(selection),
				onload : function (req) {
					var result = req.responseText.match(/name="after"[^>]+>([^<]*)/m)
					if (1 < result.length)
						popup_add(popup_create(result[1]))
				},
				overrideMimeType:"text/plain; charset=Shift_JIS"
			});
		}
		//alc検索
		function search_alc(selection) {
			GM_xmlhttpRequest({
				method: 'get',
				url: 'http://eow.alc.co.jp/'+selection+'/',
				onload : function (req) {
					var t = $N('div');
					t.innerHTML = req.responseText;
					var result = $X('.//li[span[@class="midashi"] and (position() = 1)]', t)
					if (result.length) {
						//alc
						result.forEach(function (e) {
							var area = popup_create(e);
							$X('.//span[@class="midashi"]', area)[0].addEventListener('click', function (e) {
								area.style.display = 'none';
							}, false);
							popup_add(area)
						});
					} else
						search_excite(selection)
				}
			});
		}
		//英字と空白で構成されていたらアルクで検索
		if (selection.match(/^[a-z\s]+$/i))
			search_alc(selection)
		//改行を含めて英文ぽかったらexciteで検索
		else if (!selection.match(/[^ -~\s–,'"’“”—…‑]/m))
			search_excite(selection)
	}

	window.addEventListener("mouseup", function (e) { search(true) }, false);
	window.addEventListener("click", function (e) { search(true) }, false);

	function $N(name, attr, childs) {
		var ret = document.createElement(name);
		for (k in attr) {
			if (!attr.hasOwnProperty(k)) continue;
			v = attr[k];
			if (k == "class") {
				ret.className = v;
			} else {
				ret.setAttribute(k, v);
			}
		}
		switch (typeof childs) {
			case "string": {
				ret.appendChild(document.createTextNode(childs));
				break;
			}
			case "object": {
				for (var i = 0, len = childs.length; i < len; i++) {
					var child = childs[i];
					if (typeof child == "string") {
						ret.appendChild(document.createTextNode(child));
					} else {
						ret.appendChild(child);
					}
				}
				break;
			}
		}
		return ret;
	}

	function $X(exp, context) {
		if (!context) context = document;
		var resolver = function (prefix) {
			var o = document.createNSResolver(context)(prefix);
			return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml";
		}
		var exp = document.createExpression(exp, resolver);

		var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
		switch (result.resultType) {
			case XPathResult.STRING_TYPE : return result.stringValue;
			case XPathResult.NUMBER_TYPE : return result.numberValue;
			case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
			case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
				result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
				var ret = [];
				for (var i = 0, len = result.snapshotLength; i < len ; i++) {
					ret.push(result.snapshotItem(i));
				}
				return ret;
			}
		}
		return null;
	}

})();

