NicoDicBBSViewer

ニコニコ大百科のBBSの拡張

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name NicoDicBBSViewer
// @description ニコニコ大百科のBBSの拡張
// @namespace http://threeaster.net
// @include http://dic.nicovideo.jp/a/*
// @include http://dic.nicovideo.jp/b/*
// @include http://dic.nicovideo.jp/l/*
// @include http://dic.nicovideo.jp/v/*
// @include http://dic.nicovideo.jp/i/*
// @include http://dic.nicovideo.jp/u/*
// @include https://dic.nicovideo.jp/a/*
// @include https://dic.nicovideo.jp/b/*
// @include https://dic.nicovideo.jp/l/*
// @include https://dic.nicovideo.jp/v/*
// @include https://dic.nicovideo.jp/i/*
// @include https://dic.nicovideo.jp/u/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js
// @grant GM_getValue
// @grant GM_setValue
// @version 1.2.1
// ==/UserScript==
$.noConflict();
var net_threeaster_NicoDicBBSViewer = {};
(function($){
	//-----UrlAnalyzer-----
	function UrlAnalyzer(){};

	UrlAnalyzer.prototype.getNowUrl = function(){
		return document.URL;
	};

	UrlAnalyzer.prototype.inArticlePage = function(){
		return this.getNowUrl().indexOf("//dic.nicovideo.jp/b/") === -1;
	};

	UrlAnalyzer.prototype.getBBSURLs = function(pager){
		if(pager.size() === 0){
			return [];
		}
		var urls = pager.find("a").not(".navi").map(function(){return this.href}).get();
		var bbsURLs = [];
		if(urls.length){
			var lastURLParts = urls[urls.length - 1].split("/");
			var lastNumber = lastURLParts[lastURLParts.length - 1].replace("-", "");
			if(!this.inArticlePage()){
				var nowURLParts = this.getNowUrl().split("#")[0].split("/");
				var nowNumber = nowURLParts[nowURLParts.length - 1].replace("-", "");
				lastNumber = (lastNumber - 0 >= nowNumber - 0) ? lastNumber : nowNumber;
			}
			lastURLParts.pop();
			var basicURL = lastURLParts.join("/") + "/";
			for(var i = lastNumber; i > 0; i -= 30){
				bbsURLs.unshift(basicURL + i + "-");
			}
		}else{
			var url = this.getNowUrl();
			if(url.indexOf("#") !== -1){
				url = url.substring(0, url.indexOf("#"));
				if(url.indexOf("-") === -1){
					url = url + "-";
				}
			}
			bbsURLs.push(url);
		}
		return bbsURLs;
	};

	UrlAnalyzer.prototype.isPageOf = function(url){
		var nowUrl = this.getNowUrl();
		type = this.getPageType(url);
		nowType = type !== undefined ? this.getPageType(nowUrl): undefined; //idPageOfの仕様のつじつま合わせ
		url = this.getPageName(url);
		nowUrl = this.getPageName(nowUrl);
		return type === nowType && url === nowUrl;
	};

	UrlAnalyzer.prototype.getPageName = function(url){
		var type = this.getPageType(url);
		if(type !== undefined && url.indexOf(type) !== -1){
			url = url.split(type + "/")[1];
		}
		url = url.split("/")[0];
		url = url.split(":")[0];
		url = url.split("#")[0];
		return url;
	}

	UrlAnalyzer.prototype.getNowPageName = function(){
		return this.getPageName(this.getNowUrl());
	}

	UrlAnalyzer.prototype.getPageType = function(url) {
		if(url.indexOf('//dic.nicovideo.jp') !== -1){
			url = url.replace('//dic.nicovideo.jp', '');
		}
		var parts = url.split('/');
		if(parts[1] === 'b'){
			return parts[2];
		}else{
			return parts[1];
		}
	};

	UrlAnalyzer.prototype.getNowPageType = function(){
		return this.getPageType(this.getNowUrl());
	}

	UrlAnalyzer.prototype.changeNumber = function(url){
		if(this.inArticlePage()){
			return url;
		}else{
			var parts = url.split("/");
			var last = parts.pop();
			var lastParts = last.split("-");
			var lastNum = lastParts.shift();
			var lastTail = lastParts.join("-");
			var base = parts.join("/");
			var nowNum = this.getNowUrl().split("/").pop().split("-")[0];
			var newUrl = base + "/" + nowNum + "-" + lastTail;
			return newUrl;
		}
	}

	//-----ResCollection-----
	function ResCollection(ana){
		if(ana === undefined){
			this.urlAnalyzer = new UrlAnalyzer();
		}else{
			this.urlAnalyzer = ana;
		}

	}

	ResCollection.prototype.createResList =  function(dl){
		dl.find("dt").each(function(){
			var self = $(this);
			self.attr("data-number", self.find("a").eq(0).attr("name"));
			self.attr("data-name", self.find(".st-bbs_name").text());
			var id = self.text().split(":");
			id = id[id.length - 1].split("[");
			id = id[0];
			self.attr("data-id", $.trim(id));
		});
		var resheads = dl.find("dt");
		var resbodies = dl.find("dd");
		this.resList = new Array(resheads.size());
		for(var i = 0; i < resheads.size(); i++){
			this.resList[i] = new Res(resheads.eq(i), resbodies.eq(i), this.urlAnalyzer);
		}
	};

	ResCollection.prototype.createResListById = function(){
		this.resListById = {};
		for(var i = 0; i < this.resList.length; i++){
			if(!this.resListById[$(this.resList[i].reshead).attr("data-id")]){
				this.resListById[$(this.resList[i].reshead).attr("data-id")] = [];
			}
			this.resListById[$(this.resList[i].reshead).attr("data-id")].push(this.resList[i]);
		}
	};

	ResCollection.prototype.createResListByNumber = function(){
		this.resListByNumber = [];
		for(var i = 0; i < this.resList.length; i++){
			var res = this.resList[i]
			this.resListByNumber[res.reshead.attr("data-number")] = res;
		}
	};

	ResCollection.prototype.makeTooltips = function(){
		var cannotMakeTooltip = !GM_getValue("tooltipOnDicPage") && this.urlAnalyzer.inArticlePage();
		for(var i = 0; i < this.resList.length; i++){
			this.resList[i].makeIDDiv(this.resListById, !cannotMakeTooltip);
			this.resList[i].makeNumberDiv(this.resList);
			if(cannotMakeTooltip){
				continue;
			}
			if(GM_getValue("showIDTooltip")){
				this.resList[i].makeIDTooltip(this.resListById);
			}
			if(GM_getValue("showResAnchorTooltip")){
				this.resList[i].makeNumTooltip(this.resListByNumber);
			}
			if(GM_getValue("showResNumberTooltip")){
				this.resList[i].makeLinkedNumberTooltip();
			}
			if(GM_getValue("showResHandleTooltip")){
				this.resList[i].makeNumberHandleTooltip(this.resListByNumber);
			}
		}
	};

	ResCollection.prototype.showRes = function(){
		var dl = $(".st-bbs-contents dl");
		for(var i = 0; i < this.resList.length; i++){
			dl.append(this.resList[i].reshead);
			dl.append(this.resList[i].resbody);
		}
	};

	ResCollection.prototype.revivalAllRes = function(){
		for(var i = 0; i < this.resList.length; i++){
			if(this.resList[i].reshead.hasClass("deleted")){
				this.resList[i].reshead.removeClass("deleted").find(".name").html(this.resList[i].trueReshead.attr("data-name"));//ここで.nameと.tripが一緒になる。.tripを個別に処理する場合は修正すること
				this.resList[i].resbody.html("").append(this.resList[i].trueResbody.clone(true).contents()).removeClass("deleted");
			}
		}
	};

	ResCollection.prototype.setContextMenu = function(){
		for(var i = 0; i < this.resList.length; i++){
			this.resList[i].reshead.find(".ID, .IDMulti, .IDMany").unbind("click").click(function(e){
				$(this).parent(".st-bbs_resInfo").append($("#contextMenu").css({left : e.pageX, top : e.pageY}).show());
				e.stopPropagation();
			});
		}
		$("html").unbind("click").click(function(){
			$("#contextMenu").hide();
		});
	};

	//-----Res-----

	function Res(reshead, resbody, ana){
		this.reshead = reshead;
		this.resbody = resbody;
		if(ana === undefined){
			this.urlAnalyzer = new UrlAnalyzer();
		}else{
			this.urlAnalyzer = ana;
		}
	};

	Res.prototype.backupRes = function(){
		this.trueReshead = this.reshead.clone(true);
		this.trueResbody = this.resbody.clone(true);
	}

	Res.prototype.makeIDDiv = function(resListById){
		var reflectSameId = GM_getValue("classificationID") && (GM_getValue("tooltipOnDicPage") || !this.urlAnalyzer.inArticlePage());
		var addOrdinalAndTotal = function(res, sameIDRes){
			if(reflectSameId){
				return "[" + (sameIDRes.indexOf(res) + 1) + "/" + sameIDRes.length + "]"
			}else{
				return "";
			}
		}
        var insertFractionIntoDiv = function(html, fraction){
            return html.replace('</div>', fraction + '</div>')
        }
		var sameIDRes = resListById[this.reshead.attr("data-id")];
		if(reflectSameId){
			var addIDMulti = "IDMulti";
			var addIDMany = "IDMany";
		}else{
			var addIDMulti = "ID";
			var addIDMany = "ID";
		}
		if(this.reshead.find(".ID, .IDMulti, .IDMany").size() === 0){
			var s = this.reshead.html().split(":");
			if(sameIDRes.length == 1){
				s[s.length - 2] = s[s.length - 2].replace("ID", "<div class='ID'>ID</div>");
			}else if(sameIDRes.length < 5){
				s[s.length - 2] = s[s.length - 2].replace("ID", "<div class='" + addIDMulti + "'>ID</div>");
				s[s.length - 1] = insertFractionIntoDiv(s[s.length - 1], addOrdinalAndTotal(this, sameIDRes));
			}else{
				s[s.length - 2] = s[s.length - 2].replace("ID", "<div class='" + addIDMany + "'>ID</div>");
				s[s.length - 1] = insertFractionIntoDiv(s[s.length - 1], addOrdinalAndTotal(this, sameIDRes));
			}
			this.reshead.html(s.join(":"));
		}else if(this.reshead.find(".ID").size() !== 0){
			if(sameIDRes.length == 1){
			}else if(sameIDRes.length < 5){
				this.reshead.find(".ID, .IDMulti, .IDMany").removeClass("ID IDMulti IDMany").addClass(addIDMulti);
				var s = this.reshead.html().split(":");
				s[s.length - 1] = insertFractionIntoDiv(s[s.length - 1], addOrdinalAndTotal(this, sameIDRes));
				this.reshead.html(s.join(":"));
			}else{
				this.reshead.find(".ID, .IDMulti, .IDMany").removeClass("ID IDMulti IDMany").addClass(addIDMany);
				var s = this.reshead.html().split(":");
				s[s.length - 1] = insertFractionIntoDiv(s[s.length - 1], addOrdinalAndTotal(this, sameIDRes));
				this.reshead.html(s.join(":"));
			}
		}else{
			if(sameIDRes.length < 5){
				this.reshead.find(".ID, .IDMulti, .IDMany").removeClass("ID IDMulti IDMany").addClass(addIDMulti);
				var s = this.reshead.html().split("[");
				s[s.length - 1] = addOrdinalAndTotal(this, sameIDRes);
				this.reshead.html(s.join(""));
			}else{
				this.reshead.find(".ID, .IDMulti, .IDMany").removeClass("ID IDMulti IDMany").addClass(addIDMany);
				var s = this.reshead.html().split("[");
				s[s.length - 1] = addOrdinalAndTotal(this, sameIDRes);
				this.reshead.html(s.join(""));
			}
		}
	}

	Res.prototype.makeNumberDiv = function(resList){
		this.linkedResponds = [];
		var myNumber = this.reshead.attr("data-number") - 0;
		for(var i = 0; i < resList.length; i++){
			var numberAnchorsWrapset = resList[i].resbody.find("a.dic");
			var numberAnchors = [];
			if(numberAnchorsWrapset.size() !== 0){
				numberAnchorsWrapset.each(function(){
					numberAnchors.push($(this).html().split("&gt;").join(""));
				});
			}else{
				continue;
			}
			for(var j = 0; j < numberAnchors.length; j++){
				var num = numberAnchors[j];
				if(num.indexOf("-") === -1 && myNumber === num - 0){
					this.linkedResponds.push(resList[i]);
					break;
				}else if(num.indexOf("-") !== -1){
					num = num.split("-");
					if(num[0] <= myNumber && myNumber <= num[1]){
						this.linkedResponds.push(resList[i]);
						break;
					}
				}
			}
		}
		this.reshead.find("div.Number, div.NumberMulti, div.NumberMany").contents().unwrap();
		if(this.linkedResponds.length === 0){
		}else if(!GM_getValue("classificationResNumber") || this.linkedResponds.length === 1){
            this.reshead.find('.st-bbs_resNo').html("<div class='Number'>" + this.reshead.find('.st-bbs_resNo').html() + "</div>")
		}else if(this.linkedResponds.length <= 3){
            this.reshead.find('.st-bbs_resNo').html("<div class='NumberMulti'>" + this.reshead.find('.st-bbs_resNo').html() + "</div>")
		}else{
            this.reshead.find('.st-bbs_resNo').html("<div class='NumberMany'>" + this.reshead.find('.st-bbs_resNo').html() + "</div>")
		}
	}


	Res.prototype.makeIDTooltip = function(resListById){
		var sameIDRes = resListById[this.reshead.attr("data-id")];
		var divID = this.reshead.find("div[class^='ID']");
		var that = this;
		divID.unbind("mouseenter").unbind("mouseleave").hover(function(){
			var tooltip = $("<div></div>").click(function(e){e.stopPropagation();});
			for(var i = 0; i < sameIDRes.length; i++){
				tooltip.append(sameIDRes[i].reshead.clone().find("a").removeAttr("id").end());
				tooltip.append(sameIDRes[i].resbody.clone().find("a").removeAttr("id").end());
			}
			divID.append(tooltip);
			divID.focus();
			that.adjustHeightOfTooltip(tooltip);
		}, function(){
			divID.find("div").remove();
		});
	};

	Res.prototype.makeNumTooltip = function(resListByNumber){
		var that = this;
		this.resbody.find(".numTooltip > a.dic").unwrap();
		this.resbody.find("a.dic").filter(function(){return $(this).html().indexOf("&gt;&gt;") !== -1}).each(function(){
			var self = $(this);
			var num = self.html().split("&gt;").join("").split("-");
			for(var i = 0; i < num.length; i++){
				num[i] = num[i] - 0;
			}
			self.wrap("<span class='numTooltip'></span>").parent().unbind("mouseenter").unbind("mouseleave").hover(function(){
				var self = $(this);
				var tooltip = $("<div></div>");
				if(num.length === 1 || !num[1]){
					var res = resListByNumber[num[0]];
					if(res === undefined){
						return;
					}
					var cloneBody = res.resbody.clone();
					cloneBody.find(".numTooltip > a.dic").unwrap();
					tooltip.append(res.reshead.clone().find("a").removeAttr("id").end());
					tooltip.append(cloneBody.find("a").removeAttr("id").end());
				}else{
					for(var i = num[0]; i <= num[1]; i++){
						var res = resListByNumber[i];
						if(res === undefined){
							continue;
						}
						var cloneBody = res.resbody.clone();
						cloneBody.find(".numTooltip > a.dic").unwrap();
						tooltip.append(res.reshead.clone().find("a").removeAttr("id").end());
						tooltip.append(cloneBody.find("a").removeAttr("id").end());
					}
					if(tooltip.html() === $("<div></div>").html()){
						return;
					}
				}
				self.append(tooltip);
				self.focus();
				that.adjustHeightOfTooltip(tooltip);
			}, function(){
				$(this).find("div").remove();
			});
		});
	};

	Res.prototype.makeLinkedNumberTooltip = function(){
		var divNumber = this.reshead.find("div[class^='Number']");
		var linkedResponds = this.linkedResponds;
		var that = this;
		divNumber.unbind("mouseenter").unbind("mouseleave").hover(function(){
			var tooltip = $("<div></div>").click(function(e){e.stopPropagation();});
			for(var i = 0; i < linkedResponds.length; i++){
				tooltip.append(linkedResponds[i].reshead.clone().find("a").removeAttr("id").end());
				tooltip.append(linkedResponds[i].resbody.clone().find("a").removeAttr("id").end());
			}
			divNumber.append(tooltip);
			divNumber.focus();
			that.adjustHeightOfTooltip(tooltip);
		}, function(){
			divNumber.find("div").remove();
		});
	}

	Res.prototype.makeNumberHandleTooltip = function(resListByNumber){
		var nameSpan = this.reshead.find(".st-bbs_name");
		var name = nameSpan.html();
		var transformedName = name.replace(/[0123456789]/g, function(c){return "0123456789".indexOf(c);});
		var that = this;
		if(/^[0-9]+$/.test(transformedName)){
			nameSpan.wrap("<span class='NumberHandle'></span>").parent().unbind("mouseenter").unbind("mouseleave").hover(function(){
				var self = $(this);
				var tooltip = $("<div></div>");
				var res = resListByNumber[transformedName];
				if(res === undefined){
					return;
				}
				tooltip.append(res.reshead.clone().find("a").removeAttr("id").end());
				tooltip.append(res.resbody.clone().find("a").removeAttr("id").end());
				self.append(tooltip);
				self.focus();
				that.adjustHeightOfTooltip(tooltip);
			}, function(){
				$(this).find("div").remove();
			});
		}
	}

	Res.prototype.adjustHeightOfTooltip = function(tooltip){
		var a = $("html").scrollTop() + $("#topline").height();
		var b = tooltip.offset().top;
		var c = $(window).height() - $("#topline").height();
		var d = tooltip.height();
		if(a < b && b < a + c && a < b + d && b + d < a + c){
		}else if(d < c){
			if(b > a){
				tooltip.offset({top : (a + c - d) });
			}else{
				tooltip.offset({top : a});
			}
		}else{
			tooltip.offset({top : a});
			tooltip.height(c - $("#topline").height());
		}
	};

	//-----NgOperator-----
	function NgOperator(ana){
		this.ngList = {};
		this.ngList.ngid = [];
		this.ngList.ngname = [];
		this.ngList.ngword = [];
		this.ngList.ngres = [];
		if(ana === undefined){
			this.urlAnalyzer = new UrlAnalyzer();
		}else{
			this.urlAnalyzer = ana;
		}
	}
	NgOperator.prototype.initNg = function(){
		this.ngList = {};
		this.ngList.ngidText = removeUselessLines(GM_getValue("ngid"));
		if(this.ngList.ngidText){
			this.ngList.ngid = this.ngList.ngidText.split("\n");
			for(var i = 0; i < this.ngList.ngid.length; i++){
				this.ngList.ngid[i] = $.trim(this.ngList.ngid[i]);
			}
		}else{
			this.ngList.ngid = [];
		}
		this.ngList.ngnameText = removeUselessLines(GM_getValue("ngname"));
		if(this.ngList.ngnameText){
			this.ngList.ngname = this.ngList.ngnameText.split("\n");
			for(var i = 0; i < this.ngList.ngname.length; i++){
				this.ngList.ngname[i] = $.trim(this.ngList.ngname[i]);
			}
		}else{
			this.ngList.ngname = [];
		}
		this.ngList.ngwordText = removeUselessLines(GM_getValue("ngword"));
		if(this.ngList.ngwordText){
			this.ngList.ngword = this.ngList.ngwordText.split("\n");
			for(var i = 0; i < this.ngList.ngword.length; i++){
				this.ngList.ngword[i] = $.trim(this.ngList.ngword[i]);
			}
		}else{
			this.ngList.ngword = [];
		}
		this.ngList.ngresText = removeUselessLines(GM_getValue("ngres"));
		if(this.ngList.ngresText){
			this.ngList.ngres = this.ngList.ngresText.split("\n");
			for(var i = 0; i < this.ngList.ngres.length; i++){
				this.ngList.ngres[i] = $.trim(this.ngList.ngres[i]);
			}
		}else{
			this.ngList.ngres = [];
		}
	};

	NgOperator.prototype.applyNg = function(resList){
		for(var i = 0; i < resList.length; i++){
			var r = resList[i];
			var applied = false;
			if(GM_getValue("useNG")){
				var id = r.trueReshead.attr("data-id");
				var name = r.trueReshead.attr("data-name");
				for(var j = 0; !applied && j < this.ngList.ngid.length; j++){
					if(this.ngList.ngid[j] === id){
						applied = true;
					}
				}
				for(var j = 0; !applied && j < this.ngList.ngname.length; j++){
					if(name.indexOf(this.ngList.ngname[j]) !== -1){
						applied = true;
					}
				}
				for(var j = 0; !applied && j < this.ngList.ngword.length; j++){
					if(r.trueResbody.text().indexOf(this.ngList.ngword[j]) !== -1){
						applied = true;
					}
				}
				for(var j = 0; !applied && j < this.ngList.ngres.length; j++){
					var ngres = this.ngList.ngres[j].split(":");
					var number = ngres.pop();
					var URL = ngres.join(":");
					if(this.urlAnalyzer.isPageOf(URL) && r.reshead.attr("data-number") == number){
						applied = true;
					}
				}
			}

			if(applied){
				$("#contextMenu").insertAfter("#ng");
				r.reshead.find(".name").html("削除しました");
				r.reshead.find(".trip").remove();
				r.reshead.addClass("deleted");
				r.resbody.html("削除しました").addClass("deleted");
			}else if(r.reshead.hasClass("deleted")){
				r.reshead.removeClass("deleted").find(".name").html(r.trueReshead.attr("data-name"));//ここで.nameと.tripが一緒になる。.tripを個別に処理する場合は修正すること
				r.resbody.html("").append(r.trueResbody.clone(true).contents()).removeClass("deleted");
			}

			var css = $("#nicoDicBBSViewerCSS");
			if(GM_getValue("seethroughNG")){
				if(css.html().indexOf("deleted") === -1){
					css.html(css.html() + ".deleted{display:none;}");
				}
			}else{
				if(css.html().indexOf("deleted") !== -1){
					css.html(css.html().replace(".deleted{display:none;}", ""));
				}
			}
		}
	};
	//-----MenuOperator-----

	function MenuOperator(resCollection, ngOperator){
		this.resCollection = resCollection;
		this.ngOperator = ngOperator;
		this.urlAnalyzer = new UrlAnalyzer();
		this.bbsScroll = 0;
	};


	MenuOperator.prototype.bindContextMenu = function(){
		var self = this;
		$("#ngidMenu").click(function(){
			$("#contextMenu").hide();
			if($(this).parents(".st-bbs_reshead").hasClass("deleted")){
				return false;
			}
			var id = $(this).parents(".st-bbs_reshead").attr("data-id");
			var gm_ngid = GM_getValue("ngid") ? GM_getValue("ngid") : "";
			var ngidText = gm_ngid + "\n" + id;
			ngidText = removeUselessLines(ngidText);
			$("#ngidTextarea").val(ngidText);
			GM_setValue("ngid", ngidText);
			self.ngOperator.initNg();
			self.ngOperator.applyNg(self.resCollection.resList);
		});

		$("#ngnameMenu").click(function(){
			$("#contextMenu").hide();
			if($(this).parents(".st-bbs_reshead").hasClass("deleted")){
				return false;
			}
			var name = $(this).parents(".st-bbs_reshead").attr("data-name");
			var gm_ngname = GM_getValue("ngname") ? GM_getValue("ngname") : "";
			var ngnameText = gm_ngname + "\n" + name;
			ngnameText = removeUselessLines(ngnameText);
			$("#ngnameTextarea").val(ngnameText);
			GM_setValue("ngname", ngnameText);
			self.ngOperator.initNg();
			self.ngOperator.applyNg(self.resCollection.resList);
		});

		$("#ngresMenu").click(function(){
			$("#contextMenu").hide();
			if($(this).parents(".st-bbs_reshead").hasClass("deleted")){
				return false;
			}
			var number = $(this).parents(".st-bbs_reshead").attr("data-number");
			var gm_ngresText = GM_getValue("ngres") ? GM_getValue("ngres") : "";
			var pageName = self.urlAnalyzer.getNowPageName();
			var ngresText = gm_ngresText + "\n" + pageName + ":" + number;
			ngresText = removeUselessLines(ngresText);
			$("#ngresTextarea").val(ngresText);
			GM_setValue("ngres", ngresText);
			self.ngOperator.initNg();
			self.ngOperator.applyNg(self.resCollection.resList);
		});
	};


	MenuOperator.prototype.insertConfigHtml = function(){
		var self = this;
		var appendNgTextarea = function(labelcore, idcore){
			var text = "";
			text = text + '<div style="float:left; width:24%"><p>改行で区切って' + labelcore + 'を入力or削除してください。</p>';
			text = text + '<textarea id="' + idcore + 'Textarea" cols="20" rows="10" placeholder="' + labelcore + 'を改行で区切って入力してください。">';
			text = text + (GM_getValue(idcore) ? GM_getValue(idcore) : "");
			text = text + '</textarea></div>';
			$("#ng").append(text);
		}
		var appendConfigLi = function(parent, id, label){
			var text = "";
			text = text + '<li><input id="' + id + 'Checkbox" type="checkbox" ' + (GM_getValue(id) ? "checked = 'checked'" : "") + '/>' + label + '</li>';
			parent.append(text);
		}
		var appendSubList = function(parent, list, label){
			var li = $("<li>" + label + "</li>");
			li.append(list);
			parent.append(li);
		}
		var getSubUl = function(){
			return $('<ul style="list-style-type: none; margin-left:5px;"></ul>');
		}
		$("#topbarLogoutMenu").after('<li>NicoDicBBSViewer</li><li id="bbsLi" class="selected"><a href="#">掲示板を表示する</a></li><li id="ngLi"><a href="#">設定画面を表示する</a></li>');
		$(".st-bbs-contents").after('<div id="ng"></div>');
		appendNgTextarea("NGID", "ngid");
		appendNgTextarea("NGName", "ngname");
		appendNgTextarea("NGワード", "ngword");
		appendNgTextarea("NGレスを(BBSのURL:レス番号)の書式で", "ngres");

		$("#ng").append('<div style="clear:left;"><form><ul style="list-style-type: none;"></ul></form><div>');
		var parentUl = $("#ng form ul");
		//appendConfigLi(parentUl, "addToOnePage", '一つのページに継ぎ足す(更新時有効)');
		//appendConfigLi(parentUl, "autoLoad", "下までスクロールした時に次のページを読み込む");

		var ngUl = getSubUl();
		appendConfigLi(ngUl, "useNG", "NG機能を使用する");
		appendConfigLi(ngUl, "seethroughNG", "NGが適用されたレスを表示しない");
		appendSubList(parentUl, ngUl, "NG機能");

		appendConfigLi(parentUl, "tooltipOnDicPage", "記事ページでもID、番号の色分けやツールチップを表示する");

		var tooltipUl = getSubUl();
		appendConfigLi(tooltipUl, "showIDTooltip", 'ID(<span style="text-decoration:underline;">ID</span>)ツールチップを表示する');
		appendConfigLi(tooltipUl, "showResAnchorTooltip", 'レスアンカー(<span style="color: rgb(0, 102, 204);">>>1</span>)ツールチップを表示する');
		appendConfigLi(tooltipUl, "showResNumberTooltip", 'レス番(<span style="text-decoration:underline;">1</span>)ツールチップを表示する');
		appendConfigLi(tooltipUl, "showResHandleTooltip", 'レス番ハンドル(<span style="color: rgb(0, 136, 0); font-weight: bold;">1</span>)ツールチップを表示する');
		appendSubList(parentUl, tooltipUl, "ツールチップ(更新時有効)");

		var colorUl = getSubUl();
		appendConfigLi(colorUl, "classificationID", "IDを色分けし、そのIDのレスの回数を表示する");
		appendConfigLi(colorUl, "classificationResNumber", "参照されているレス番を色分けする");
		appendSubList(parentUl, colorUl, "色分け(更新時有効)");

		$("#ng").append('<button id="decideNG">保存</button> <button id="cancelNG">キャンセル</button> <button id="backToBbsButton">掲示板に戻る</button></div>' +
			' <ul id="contextMenu"><li id="ngidMenu">NGIDに追加</li><li id="ngnameMenu">NGNameに追加</li><li id="ngresMenu">このレスを削除</li></ul>');
	};

	MenuOperator.prototype.bindMenu = function(){
		var self = this;
		var contents = $(".st-bbs-contents, #ng");
		var backBBS = function(){
			if($(".selected").attr("id") === "bbsLi"){
				self.bbsScroll = $("html").scrollTop();
			}
			$(".selected").removeClass("selected");
			$("#bbsLi").addClass("selected");
			contents.not(".st-bbs-contents").css("display", "none");
			$(".st-bbs-contents").css("display", "block");
			$("html").scrollTop(self.bbsScroll);
			return false;
		};
		$("#bbsLi").click(backBBS);
		$("#backToBbsButton").click(backBBS);

		$("#ngLi").click(function(){
			if($(".selected").attr("id") === "bbsLi"){
				self.bbsScroll = $("html").scrollTop();
			}
			$(".selected").removeClass("selected");
			$(this).addClass("selected");
			contents.not("#ng").css("display", "none");
			$("#ng").css("display", "block");
			$("html").scrollTop($("#ng").offset().top - $("#topline").height());
			return false;
		});

		var setcbConfig = function(id){
			GM_setValue(id, $("#" + id + "Checkbox").is(":checked"));
		}

		var checkcbConfig = function(id){
			if(GM_getValue(id)){
				$("#" + id + "Checkbox").attr("checked", true);
			}else{
				$("#" + id + "Checkbox").attr("checked", false);
			}
		}

		$("#decideNG").click(function(){
			GM_setValue("ngid", $("#ngidTextarea").val());
			GM_setValue("ngname", $("#ngnameTextarea").val());
			GM_setValue("ngword", $("#ngwordTextarea").val());
			GM_setValue("ngres", $("#ngresTextarea").val());
			setcbConfig("seethroughNG");
			setcbConfig("loadAll");
			setcbConfig("addToOnePage");
			setcbConfig("autoLoad");
			setcbConfig("useNG");
			setcbConfig("tooltipOnDicPage");
			setcbConfig("showIDTooltip");
			setcbConfig("showResAnchorTooltip");
			setcbConfig("showResNumberTooltip");
			setcbConfig("showResHandleTooltip");
			setcbConfig("classificationID");
			setcbConfig("classificationResNumber");
			setcbConfig("switcherInTopMenu");
			self.ngOperator.initNg();
			self.ngOperator.applyNg(self.resCollection.resList);
		});

		$("#cancelNG").click(function(){
			$("#ngidTextarea").val(GM_getValue("ngid") ? GM_getValue("ngid") : "");
			$("#ngnameTextarea").val(GM_getValue("ngname") ? GM_getValue("ngname") : "");
			$("#ngwordTextarea").val(GM_getValue("ngword") ? GM_getValue("ngword") : "");
			$("#ngresTextarea").val(GM_getValue("ngres") ? GM_getValue("ngres") : "");
			checkcbConfig("seethroughNG");
			checkcbConfig("loadAll");
			checkcbConfig("addToOnePage");
			checkcbConfig("autoLoad");
			checkcbConfig("useNG");
			checkcbConfig("tooltipOnDicPage");
			checkcbConfig("showIDTooltip");
			checkcbConfig("showResAnchorTooltip");
			checkcbConfig("showResNumberTooltip");
			checkcbConfig("showResHandleTooltip");
			checkcbConfig("classificationID");
			checkcbConfig("classificationResNumber");
			checkcbConfig("switcherInTopMenu");
		});
	};

	//-----ManegerToReadBbs-----

	function ManagerToReadBbs(urls, ana){
		if(ana === undefined){
			ana = new UrlAnalyzer();
		}
		this.urlAnalyzer = ana;
		this.bbsUrls = urls;
		var nowUrl = ana.getNowUrl();
		if(!ana.inArticlePage()){
			if(nowUrl.indexOf("#") === -1){
				this.startIndex = urls.indexOf(nowUrl);
			}else{
				var mainurl = nowUrl.substring(0, nowUrl.indexOf("#"));
				if(mainurl.indexOf("-") === -1){
					mainurl = mainurl + "-";
				}
				this.startIndex = urls.indexOf(mainurl);
			}
		}
		this.endIndex = this.startIndex;
		this.isNowLoading = false;
		this.resCollection = new ResCollection(ana);
		this.ngOperator = new NgOperator(ana);
		this.menuOperator = new MenuOperator(this.resCollection, this.ngOperator);
	};

	ManagerToReadBbs.prototype.readPreviousBbs = function(){
		if(this.isNowLoading || this.startIndex <= 0){
			return;
		}
		$("#bbsmain").prepend("<p id='loading'>now loading...</p>");
		this.isNowLoading = true;
		this.startIndex--;
		var self = this;
		$.get(this.bbsUrls[this.startIndex], function(r){
			self.prependBbs($(r).find("dl"));
		});
		if(this.startIndex === 0){
			$("#loadPreviousPageLinks").remove();
		}
	};

	ManagerToReadBbs.prototype.prependBbs = function(dl){
		this.resCollection.revivalAllRes();
		$(".st-bbs-contents dl").prepend(dl.contents());
		this.createAndSetResList();
		$("#loading").remove();
		this.isNowLoading = false;
	};

	ManagerToReadBbs.prototype.readNextBbs = function(){
		if(this.isNowLoading || this.endIndex >= this.bbsUrls.length - 1){
			return;
		}
		$("#bbsmain").append("<p id='loading'>now loading...</p>");
		this.isNowLoading = true;
		this.endIndex++;
		var self = this;
		$.get(this.bbsUrls[this.endIndex], function(r){
			self.appendBbs($(r).find("dl"));
		});
		if(this.endIndex === this.bbsUrls.length - 1){
			$("#loadNextPageLinks").remove();
		}
	};

	ManagerToReadBbs.prototype.appendBbs = function(dl){
		this.resCollection.revivalAllRes();
		$(".st-bbs-contents dl").append(dl.contents());
		this.createAndSetResList();
		$("#loading").remove();
		this.isNowLoading = false;
	};

	ManagerToReadBbs.prototype.initSmallBbs = function(){
		this.initPager();
		this.ngOperator.initNg();
		this.createAndSetResList();
		this.menuOperator.insertConfigHtml();
		this.menuOperator.bindMenu();
		this.menuOperator.bindContextMenu();
	};

	ManagerToReadBbs.prototype.initPager = function(){
        return
		if(!GM_getValue("addToOnePage")){
			return;
		}
		var pager = $(".st-bbs-contents .pager");
		var self = this;
		if(this.urlAnalyzer.inArticlePage()){
			pager.find(".navi").remove();
		}else{
			pager.eq(0).find("a:not(:first), .current, span").remove();
			if(this.startIndex > 0){
				pager.eq(0).append("<a id='loadPreviousPageLinks' href='#'>前へ</a>");
				pager.find("#loadPreviousPageLinks").click(function(){self.readPreviousBbs(); return false;});
			}
			pager.eq(1).find("a:not(:first), .current, span").remove();
			if(this.endIndex < this.bbsUrls.length - 1){
				pager.eq(1).append("<a id='loadNextPageLinks' href='#'>次へ</a>");
				pager.find("#loadNextPageLinks").click(function(){self.readNextBbs(); return false;});
			}
		}
	};

	ManagerToReadBbs.prototype.scrollLoader = function(){
		this.reserved = false;
		var self = this;
		setInterval(function(){
			if(self.reserved){
				self.reserved = false;
				self.readNextBbs();
			}
		}, 1000);
		$(window).scroll(function(){
			if($(".selected").attr("id") === "bbsLi" && GM_getValue("autoLoad") && $("html").scrollTop() + $(window).height() > $("#bbsmain").position().top + $("#bbsmain").height()){
				self.reserved = true;
			}
		});
	};

	ManagerToReadBbs.prototype.createAndSetResList = function(){
		this.resCollection.createResList($(".st-bbs-contents dl"));
		this.resCollection.createResListById();
		this.resCollection.createResListByNumber();
		this.resCollection.makeTooltips();
		this.resCollection.setContextMenu();
		var resList = this.resCollection.resList;
		for(var i = 0; i < resList.length; i++){
			resList[i].backupRes();
		}
		this.ngOperator.applyNg(resList);
	};

	//-----単体の関数-----

	var removeUselessLines = function(s){
		if(!s){
			return;
		}
		var lines = s.split("\n");
		var storage = {};
		for(var i = 0; i < lines.length;){
			if(!lines[i] || lines[i] in storage){
				lines.splice(i, 1);
			}else{
				storage[lines[i]] = 0;
				i++;
			}
		}
		return lines.join("\n");
	};

	var initConfig = function(ids){
		for(var i = 0; i < ids.length; i++){
			if(GM_getValue(ids[i]) === undefined){
				GM_setValue(ids[i], true);
			}
		}
	}

	var insertStyle = function(){
		var idStyle = ".ID{text-decoration:underline; color:black; display:inline;} .IDMulti{text-decoration:underline; color:blue; display:inline;}" +
					".IDMany{text-decoration:underline; color:red; display:inline;}";
		var numberStyle = ".Number{text-decoration: underline; display:inline;} .NumberMulti{text-decoration: underline; display:inline; color:blue;}" +
					".NumberMany{text-decoration: underline; display:inline; color:red;}";
		var insideTooltipStyle = ".dic{display:inline;}";
		var onMouseIdStyle = ".ID:hover, .IDMulti:hover, .IDMany:hover, .dic:hover{text-decoration:none;}";
		var defaultTooltipStyle = ".ID>div, .IDMulti>div, .IDMany>div, .dic>div, .Number>div, .NumberMulti>div, .NumberMany>div, .NumberHandle>div{display:none;}";
		var onMouseTooltipStyle = ".ID:hover>div, .IDMulti:hover>div, .IDMany:hover>div, .numTooltip:hover>div," +
								" .Number:hover>div, .NumberMulti:hover>div, .NumberMany:hover>div, .NumberHandle:hover>div" +
								"{color:black; display:inline; position:absolute; background:#f5f5b5; border:solid black 1px; padding;5px; font-size:8pt; overflow:auto;" +
								" box-shadow:1px 1px; z-index:10000;font-weight:normal;}";
		var leftboxStyle = "div.left-box{border: groove 1px gray; border-radius: 5px; background-image:none;}";
		var ngStyle = "#ng{display:none;}";
		var hideMenu = "#topbarRightMenu #bbsLi.selected,#topbarRightMenu #ngLi.selected{display:none;}";
		var sidemenu = "ul#sidemenu li{border:solid 1px; width:100px;} ul#sidemenu li.selected{color:red;}";
		var contextMenuStyle = "#contextMenu{background : #d4d0c8;color : #000000;display : none;position : absolute;list-style : none;	padding-left : 0px;box-shadow : 1px 1px;}";
		var contextItemStyle = "#contextMenu li{padding : 3px;}#contextMenu li:hover{background : #0a246a;color : #ffffff;}";

		var styleTag = "<style id='nicoDicBBSViewerCSS' type='text/css'>" + idStyle + numberStyle + insideTooltipStyle + onMouseIdStyle + defaultTooltipStyle +
			onMouseTooltipStyle + leftboxStyle + ngStyle + hideMenu + sidemenu + contextMenuStyle + contextItemStyle + "</style>";

		$("link").last().after($(styleTag));
	};

	var counterAutopagerize = function(){
		$(document).bind("AutoPagerize_DOMNodeInserted", function(){
			$("[class^='autopagerize'] , dl:not(#bbsmain) , #autopagerize_message_bar").remove();
		});
	};

//以下main
	var main = function(ana){
		initConfig(["useNG", "tooltipOnDicPage", "showIDTooltip", "showResAnchorTooltip", "showResNumberTooltip", "showResHandleTooltip",
					"classificationID", "classificationResNumber"]);
		insertStyle();
		$(".st-bbs-contents dl").attr("id", "bbsmain");
		$(".border").remove();
		if(ana === undefined){
			var urlAnalyzer = new UrlAnalyzer();
		}else{
			var urlAnalyzer = ana;
		}

		var manager = new ManagerToReadBbs(urlAnalyzer.getBBSURLs($(".st-bbs-contents .pager").eq(0)), urlAnalyzer);
		manager.initSmallBbs();
		counterAutopagerize();
		//if(!urlAnalyzer.inArticlePage()){
		//	manager.scrollLoader();
		//}
	};
	//-----test用-----
	var c = net_threeaster_NicoDicBBSViewer;
	c.removeUselessLines = removeUselessLines;
	c.Res = Res;
	c.ManagerToReadBbs = ManagerToReadBbs;
	c.initConfig = initConfig;
	c.insertStyle = insertStyle;
	c.UrlAnalyzer = UrlAnalyzer;
	c.ResCollection = ResCollection;
	c.NgOperator = NgOperator;
	c.MenuOperator = MenuOperator;
	c.main = main;

	//-----main実行/テスト時には途中で止まる-----
	if(typeof GM_getValue === 'function'){
		main();
	}
})(jQuery);