/**
 * Trac 用のこまごましたものをまとめたもの
 * jQuery, JavaScript-XPath & its jQuery binding に依存
 *
 * share で検索してヒットする部分や、コミッタページとか
 * CodeRepos 用に決めうちされた部分がいくらかある。
 *   → Lazy People 用に決め打ちし始めてる。
 *      足りてないところがあったら、誰か修正お願いします。
 */


$(function () {
	TracUtils.installAll();
});

var TracUtils = {
	SVN_REPOS_BASE : 'http://svn.lazy-people.org',
	TRAC_BASE      : 'http://lazy-people.org',
	AUTHOR_ICONS   : {
		//'tomoyo'       : 'http://www.hatena.ne.jp/users/ma/mayuki/profile_s.gif'
		//'tomoyo'       : 'id:mayuki'
		  '*default*'    : 'http://lazy-people.org/chrome/site/default-author-icon.png'
		, 'mayuki'       : 'id:mayuki'
		, 'yappo'        : 'id:yappo'
		, 'typester'     : 'http://www.gravatar.com/avatar.php?gravatar_id=fbc6511bcc0649366086c0445fb456d3&size=16'//'id:typester'
		, 'kentaro'      : 'id:antipop'
		, 'mizzy'        : 'id:gosukenator'
		, 'charsbar'     : 'id:charsbar'
		, 'secondlife'   : 'id:secondlife'
		, 'kazeburo'     : 'id:kazeburo'
		, 'otsune'       : 'id:otsune'
		, 'hirose31'     : 'id:hirose31'
		, 'nipotan'      : 'id:nipotan'
		, 'itkz'         : 'id:itkz'
		, 'xcezx'        : 'id:xcezx'
		, 'nyarla'       : 'id:nyarla-net'
		, 'tokuhirom'    : 'id:tokuhirom'
		, 'daisuke'      : 'id:lestrrat'
		, 'kana'         : 'id:ka-nacht'
		, 'cho45'        : 'id:cho45'
		, 'kan'          : 'id:kan'
		, 'zigorou'      : 'id:ZIGOROu'
		, 'masaki'       : 'id:ikasam_a'
		, 'holidays-l'   : 'id:holidays-l'
		, 'yoko'         : 'id:yksk'
		, 'mattn'        : 'id:mattn'
		, 'lopnor'       : 'id:lopnor'
		, 'gyuque'       : 'id:gyuque'
		, 'yugui'        : 'id:yugui'
		, 'akihito'      : 'id:t-akihito'
		, 'fujiwara'     : 'id:sfujiwara'
		, 'yusukebe'     : 'id:kamawada'
		, 'miyagawa'     : 'id:miyagawa'
		, 'topia'        : 'id:topia'
		, 'woremacx'     : 'id:woremacx'
		, 'amachang'     : 'id:amachang'
		, 'takesako'     : 'id:TAKESAKO'
		, 'eto'          : 'id:eto'
		, 'dankogai'     : 'id:dankogai'
		, 'jknaoya'      : 'id:jknaoya'
		, 'yasu'         : 'id:hirata_yasuyuki'
		, 'holygrail'    : 'id:HolyGrail'
		, 'elim'         : 'id:elim'
		, 'cheebow'      : 'id:cheebow'
		, 'ariela'       : 'http://www.gravatar.com/avatar.php?gravatar_id=dd5dfb5d738d5b5fb1c9a8b234945ea6&size=16'//'id:transrain'
		, 'tomyhero'     : 'id:tomyhero'
		, 'hetappi'      : 'id:hetappi'
		, 'coji'         : 'id:coji'
		, 'takemaru'     : 'id:teahut'
		, 'akasata'      : 'id:akasata'
		, 'okuryu'       : 'id:okuryu'
		, 'tasuku'       : 'id:tasukuchan'
		, 'snj14'        : 'id:snj14'
		, 'shachi'       : 'id:shachi_kk'
		, 'omoon'        : 'id:omoon'
		, 'aerith'       : 'id:aerith'
		, 'clouder'      : 'id:clouder'
		, 'takkyun'      : 'id:simpleboxes'
		, 'naoya_t'      : 'http://ntzone.info/misc/keroro.gif'
		, 'takefumi'     : 'id:takefumi'
		, 'walf443'      : 'id:walf443'
		, 'psychs'       : 'id:Psychs'
		, 'ewigkeit'     : 'id:Ewigkeit'
		, 'yoshiori'     : 'id:Yoshiori'
		, 'dev0000'      : 'http://www.developer0000.jp/16.jpg'
		, 'machu'        : 'id:kmachu'
		, 'norio_s'      : 'id:norio_s'
		, 'hsbt'         : 'id:h-sbt'
		, 'nori090'      : 'id:nori090'
		, 'lyokato'      : 'id:lyokato'
		, 'drgqst'       : 'id:drgqst'
		, 'mrkn'         : 'id:mrkn'
		, 'tfunato'      : 'id:tfunato'
		, 'tsuka'        : 'id:tsuka05'
		, 'niw'          : 'id:niw'
		, 'tomi-ru'      : 'id:tomi-ru'
		, 'yto'          : 'id:yto'
		, 'motemen'      : 'id:motemen'
		, 'shyouhei'     : 'http://mput.dip.jp/mput/images/icon.jpg'
		, 'm-takagi'     : 'http://www.gravatar.com/avatar.php?gravatar_id=c3f5a6e729b57f99cb9e7b3ca60152aa&size=16'
		, 'takano32'     : 'id:takano32'
		, 'kakutani'     : 'id:kakutani'
		, 'poppen'       : 'id:poppen'
		, 'kotas'        : 'id:kotas'
		, 'nekokak'      : 'id:nekokak'
		, 'tosik'        : 'id:tosik'
		, 'fbis'         : 'id:fbis'
		, 'rsk'          : 'id:rsky'
		, 'mallowlabs'   : 'id:mallowlabs'
		, 'nakamud'      : 'http://s3.amazonaws.com/twitter_production/profile_images/35752262/SP_mu-toko_mini.jpg'
		, 'chiba'        : 'id:nihen'
		, 'funaki'       : 'id:nob_funaki'
		, 'dzfl'         : 'id:dzfl'
		, 'shimooka'     : 'id:shimooka'
//		, 'noriaki'      : 'http://s3.amazonaws.com/twitter_production/profile_images/40454133/2f94929d_mini.jpg' //FIXME: 404
		, 'suztomo'      : 'id:suztomo'
		, 'klm'          : 'id:k1m'
		, 'akio0911'     : 'id:akio0911'
		, 'cactusman'    : 'id:cactusman'
		, 'vkgtaro'      : 'id:vkgtaro'
		, 'todesking'    : 'http://data.tumblr.com/avatar_1fe558be2f23_16.gif'
		, 'sho'          : 'id:sho'
		, 'a2c'          : 'id:a2c'
		, 'hidek'        : 'http://www.gravatar.com/avatar.php?gravatar_id=7d55041891ece2f35375108aa2d27255&size=16'//'id:hide-K''
		, 'yhara'        : 'id:yhara'
		, 'PoohKid'      : 'id:PoohKid'
		, 'samurai20000' : 'id:samurai20000'
		, 'yasuyuki'     : 'id:YaSuYuKi'
		, 'mootoh'       : 'id:moira'
		, 'tokada'       : 'id:tokada'
		, 'llamerada'    : 'id:llamerada'
		, 'shn'          : 'id:shn'
		, 'nejimaki'     : 'id:firewood'
		, 'kan'          : 'id:mikihoshi'
		, 'hideden'      : 'id:hideden'
		, 'niku'         : 'http://niku.name/favicon.ico'
		, 'ohac'         : 'id:ohac'
		, 'hio'          : 'http://www.gravatar.com/avatar.php?gravatar_id=a72d2120c066d47ad3d29c17b2f4db72&size=16'
		, 'os0x'         : 'id:os0x'
		, 'akiyan'       : 'id:akiyan'
		, 'inudog'       : 'http://eringi.com/favicon.ico'
		, 'suzuki'       : 'id:zatsubun'
		, 'haruka'       : 'http://drumsoft.com/favicon.ico'
		, 'syttru'       : 'id:syttru'
		, 'iskwrsk'      : 'id:iskwrsk'
		, 'komatsuna'    : 'http://data.tumblr.com/avatar_82a9ef4053ec_30.gif'
		, 'tarchan'      : 'id:tarchan'
		, 'tyoro'        : 'id:tyoro1210'
		, 'Sore_0'       : 'id:Sore_0'
		, 'teramako'     : 'id:teramako'
		, 'kamipo'       : 'id:kamipo'
		, 'ihag'         : 'id:ihag'
		, 'valda'        : 'http://data.tumblr.com/avatar_a5fb0192ec7d_16.gif'
		, 'gan2'         : 'id:gan2'
		, 'sasezaki'     : 'id:sasezaki'
		, 'kdoya'        : 'id:kdoya'
		, 'lapis25'      : 'id:lapis25'
	}

	, installAll : function () {
		TracUtils.addJumpToReposLink();
		TracUtils.clickableCodeArea();
		TracUtils.addBodyClass();
		TracUtils.addCommitterRecentCommits();
		TracUtils.addRecentChangesForProject();
		TracUtils.addUserscriptsUtils();
		TracUtils.AuthorIcons.showAuthorIcons();
	}

	, isWikiStart : function () {
		var pn = location.pathname
		return (
			pn == "/wiki/WikiStart" ||
			pn == "/wiki/" ||
			pn == "/wiki" ||
			pn == "/" ||
			pn == ""
		);
	}

	, getSourcePath : function (tracPath) {
		return String(tracPath).replace(TracUtils.TRAC_BASE + "/browser", TracUtils.SVN_REPOS_BASE).replace(/\?.*$/, '');
	}

	/**
	 * Browse Source のときに該当する svn web にすぐ翔べるリンクと
	 * ?rev= をはずすリンクを追加
	 */
	, addJumpToReposLink : function () {
		var aboutLink = $("#metanav li.last a");
		if (location.pathname.indexOf("/browser") == 0) {
			var sourcePath = TracUtils.getSourcePath(location);
			aboutLink.replaceWith(
				$("<a title='Jump to Repository'>SVN Web</a>").attr("href", sourcePath)
			);
			$("#ctxtnav li:first").after($("<li/>").append($("<a>SVN Web</a>").attr("href", sourcePath)));
			$("#ctxtnav li:first").after($("<li/>").append($("<a>Latest Revision</a>").attr("href", location.pathname)));
		} else {
			aboutLink.replaceWith(
				$("<a title='Jump to Repository'>SVN Web</a>").attr("href", TracUtils.SVN_REPOS_BASE)
			);
		}
	}

	/**
	 * Wiki に書かれた JS のコードを実行可能に
	 * 冒頭に //Executable と書くと実行できるようになる。
	 */
	, clickableCodeArea : function () {
		var start = "//Executable";
		$("//*[@id='content']//div[@class='code']/pre[contains(.//span[@class='code-comment'],'"+start+"')]").each(function () {
			var e  = $(this);
			var js = e.text();
			var a  = $("<a href='javascript:void(156)'>Click to execute.</a>").click(function () {
				eval(js);
			}).css({
				fontSize   : "80%",
				fontWeight : "bold",
				border     : "1px solid #ccc",
				display    : "block",
				width      : "10em",
				padding    : "0.2em",
				margin     : "0 auto 0.2em 0",
				textAlign  : "center"
			}).insertBefore(e.parent());
		});
	}

	/**
	 * CSS 補佐用 (IE6 で CSS セレクタの対応が貧弱なため)
	 */
	, addBodyClass : function () {
		if (location.pathname.indexOf("/wiki/Committers") == 0) {
			document.body.className = "committer";
		}
		if (TracUtils.isWikiStart()) {
			$("//*[@id='Committers']/following-sibling::ul[1]").addClass("committer-list");
			$("//*[@id='Projects']/following-sibling::ul[1]").addClass("project-list");
		}
	}

	/**
	 * コミッタページにその人の最近のコミットリストを表示する。
	 * Trac の検索機能を使っているので、コミットログに名前をかきまくると
	 * 検索結果の1ページ目 == 10個しかみていないので
	 * その人の最近のコミットがなかったことになるいじめができる。
	 */
	, addCommitterRecentCommits : function () {
		if (!location.search && !/^\/wiki\/Committers\/([^\/]{3,})/.test(location.pathname)) return;
		var author = RegExp.$1;
		$.get(TracUtils.TRAC_BASE+"/search?q="+author+"&noquickjump=1&changeset=on", {}, function (data) {
			$("<div class='recent-commits'><h2>Recent Commits</h2></div>").append(
				$(data).xfind(".//dl")
					.xfind("dd[span[@class='author'] != 'By "+author+"']")
						.xfind("preceding-sibling::dt[1] | preceding-sibling::dd[1]").remove().end()
					.remove().end()
			).appendTo($("#searchable, #content").get(0));
		});
	}

	/**
	 * プロジェクトページへ最近のコミットリストを表示する。
	 * {{{
	 * svn co http://svn.coderepos.org/share/...
	 * }}}
	 * のように co と checkout, export の書いてあるページが対象。
	 */
	, addRecentChangesForProject : function () {
		// IE だとなぜかどうしようもなく遅いので処理しない
		if ($.browser.msie || !TracUtils.isWikiStart() && location.pathname.indexOf("/wiki/") != 0) return;
		var target_re = RegExp("svn +(?:c(?:o|heckout)|export) +"+TracUtils.SVN_REPOS_BASE+"(\\S+)");
		$("#searchable pre").each(function () {
			var m = $(this).text().match(target_re);
			if (!m) return;
			var path = m[1].replace(/\/trunk\/?$/, "");
			var url  = TracUtils.TRAC_BASE + "/log" + path;
			$.get(url, {}, function (data) {
				// 高速化と、jQuery がだす getElementsByTagName がないという
				// エラーを回避するため正規表現
				data = data.match(/<table id="chglist"[\S\s]+<\/table>/)[0];
				$("<div><h2><a href='"+url+"'>Project Recent Changes</a></h2></div>")
					.append(
						$(data)
							.find("tr:gt(10)").remove().end()
							.find("th.diff, td.diff, th.change, td.change, th.rev, td.rev").remove().end()
					)
				.appendTo("#searchable");
			});
			return false;
		});
	}

	/**
	 * Userscripts のユーティリティ
	 */
	, addUserscriptsUtils : function () {
		if (!$.browser.mozilla && !$.browser.safari) return;
		// 尻?
		var isUserJS = function (path) {
			return /\.user\.js$/.test(path);
		};
		// user.js のページでインストール画面がでてうざいのをなんとかする。
		// あんまり広範囲にやりたくないので決め打ち
		if (location.pathname.indexOf("/browser/lang/javascript/userscripts") == 0) {
			$("a").each(function () {
				if (isUserJS(this.href)) this.href += "?";
			});
		}

		// user.js のページにインストール URL を追加する．
		if (isUserJS(location.pathname)) {
			var uri = TracUtils.getSourcePath(location);
			$("<a/>").attr({
				href  : uri,
				title : uri.split("/").slice(-1) + " をインストールする"
			})
			.text("インストール - " + uri)
			.insertAfter($("h1:first"));
		}
	}

	/**
	 * Trac のあらゆるところに設定されたアイコンを表示する。
	 * TracAuthorIcon.js を jQuery/XPath 依存にして簡略化したもの
	 */
	, AuthorIcons : {
		showAuthorIcons : function () {
			var authors = [];
			switch ($("#content").attr("class")) {
				case 'browser':
					$('//*[@id="info"]//th').each(function () {
						authors.push([this, $(this).text().match(/checked in by ([^,]+)/)[1]]);
					});
					$('//*[@id="dirlist"]//span[@class="author"]').each(function () {
						authors.push([this, $(this).text().match(/^([^:]+):/)[1]]);
					});
					break;
				case 'timeline':
					$('//*[@id="content"]//dt/a').each(function () {
						authors.push([this, this.lastChild.nodeValue.match(/by\s+(.+)$/)[1]]);
					});
					break;
				case 'log':
				case 'changeset':
					authors = $('(//*[@id="overview"] | //*[@id="chglist"])//*[@class="author"]');
					break;
				case 'wiki':
					if (/^\/wiki\/Committers\/([^\/]+)/.test(location.pathname)) {
						var author  = RegExp.$1;
						var iconurl = TracUtils.AuthorIcons.getIconByAuthor(author);
						if (!iconurl) break;
						var img = $("<img width='16' height='16' alt='' class='committer-icon'/>").attr("src", iconurl);
						var h1  = $("//*[@id='searchable']/h1[1]");
						if (h1.length) {
							h1.append(img);
						} else {
							$("#content").prepend(img);
						}
					}
					else if (TracUtils.isWikiStart()) {
						// This is for committers list of CodeRepos.
						authors = $("//*[@id='Committers']/following-sibling::ul[1]//a[contains(@href, '/share/wiki/Committers/')]");
					}
					break;
				default: break;
			}

			for (var i = authors.length; i --> 0; TracUtils.AuthorIcons.insertIcon(authors[i]));

			$("#metanav ul li.first").each(function () {
				var uname = $(this).text().match(/logged in as ([^,]+)/);
				if (!uname) return;
				var a = $("<a/>").attr("href", TracUtils.AuthorIcons.getAuthorPage(uname[1])).append(uname[1]);
				a.appendTo(
					$(this).empty().append("logged in as ")
				);
				TracUtils.AuthorIcons.insertIcon(a);
			});
		}

		, getIconByAuthor : function (author) {
			var iconUrl = TracUtils.AUTHOR_ICONS[author] || TracUtils.AUTHOR_ICONS["*default*"];

			// Hatena ID
			iconUrl = iconUrl.replace(/^id:(([a-zA-Z][\w-])[\w-]{0,29}[a-zA-Z\d])/, 'http://www.hatena.ne.jp/users/$2/$1/profile_s.gif');

			return iconUrl;
		}

		, insertIcon : function (authorEOrArray) {
			var authorE, author;
			if (authorEOrArray instanceof Array) {
				authorE = $(authorEOrArray[0]);
				author  =   authorEOrArray[1];
			} else {
				authorE = $(authorEOrArray);
				author  = authorE.text().replace(/^\s+|\??\s*$/g, '');
			}

			var iconurl = TracUtils.AuthorIcons.getIconByAuthor(author);
			if (!iconurl) return;

			var img = $("<img width='16' height='16' alt='' class='committer-icon'/>")
				.attr("src", iconurl)
				.css("margin-right", "0.5em");

			var a = $("<a style='background-image: none ! important; padding-left: 0pt; border: none;'/>")
				.attr("href", TracUtils.AuthorIcons.getAuthorPage(author))
				.append(img);

			authorE.prepend(a);
		}

		, getAuthorPage : function (author) {
		    return 'http://' + author + '.in.lazy-people.org';
		}
	}
};

// vim:noet:
