zaobao / txt reader

// ==UserScript==
// @name        txt reader
// @namespace   txt reader
// @include     *.txt
// @include     *.txt#*
// @exclude     http://www.rfc-editor.org/*
// @exclude     *gfwlist.txt*
// @version     0.2a
// @grant       none
// ==/UserScript==

/*
 * 暂只支持简体中文,只在firefox下测试过
 */

//是否是开发模式
 var devmode = false;
 var timestamp;

//--------------------------------整体布局--------------------------------
document.body.style.margin = "0";
document.body.style.padding = "0";
var pageLayout = document.createElement("div");
pageLayout.style.margin = "0 auto";
pageLayout.style.padding = "0";
pageLayout.style.height = "100%";
pageLayout.style.width = "1224px";
pageLayout.style.borderLeft = "1px solid #999";
pageLayout.style.borderRight = "1px solid #999";
document.body.appendChild(pageLayout);

//创建索引显示框,向左浮动
var index_containter = document.createElement("div");
var index_box = document.createElement("div");
index_containter.id = "index_containter";
index_containter.style.height = "100%";
index_containter.style.cssFloat = "left";
index_containter.style.overflow = "auto";
index_containter.style.backgroundColor = "#F3F2EE";
index_containter.style.width = "219px";
index_containter.appendChild(index_box);
index_box.id = "index_box";
index_box.style.margin = "0 10px";
index_box.style.paddingTop = "5px";
pageLayout.appendChild(index_containter);

//分割线
var split_line = document.createElement("div");
split_line.style.width = "1px";
split_line.style.height = "100%";
split_line.style.cssFloat = "left";
split_line.style.backgroundColor = "#999";
pageLayout.appendChild(split_line);

//创建正文显示框,外边距浏览器默认
var text_containter = document.createElement("div");
var text_box = document.createElement("div");
text_containter.id = "text_containter";
text_box.id = "text_box";
text_containter.style.cssFloat = "left";
text_containter.style.width = "1002px";
text_containter.style.height = "100%";
text_containter.style.margin = "0 1px";
text_containter.style.overflowY = "auto";
text_containter.style.overflowX = "hidden";
text_box.style.width = "904px";
text_box.style.padding = "24px 50px 24px 48px";
text_box.style.backgroundColor = "#F3F2EE";
//text_box.style.backgroundImage = "url(file:///home/zbfs/Desktop/bg.png)";
text_containter.appendChild(text_box);
pageLayout.appendChild(text_containter);


//--------------------------------文本获取、分段和清理工作开始----------------------
if (devmode) {
	timestamp = new Date();
}
//获取原始文本
var originalText = document.getElementsByTagName("pre")[0].innerHTML;

//清除页面文本
document.body.removeChild(document.getElementsByTagName("pre")[0]);

//定义段落文本替换规则
var replacerules = {

	// ===特殊字符===
	"[◎♂]": "",

	// ===去网页代码===
	">": ">",
	"&lt;": "<",
	"<[a-z/\u0021\u0025\u002D][\u0000-\u003D\u003F-\uFFFF]{0,200}>": "",
	"^//": "",

	// ===去无关段落===
	".*(求收藏|求推荐|求订阅|ps:|由“.*”提供|最新、最快、最火的连载作品尽在).*": "",

	// ===括号内容过滤===
	"\u0028.*(祝大家|未完待续|百度搜|WW|精品小说|章节|小说更快更好|网网|138看网|本站|即可找到).*\u0029": "",

	// ===大括号内容过滤===
	"\u007B.*(首发全文字网).*\u007D": "",

	// ===去网址===
	"(bxwx.org|Qisuu.сom)": "",

	// ===提示性文字过滤===
	"正文 ": "",

	// 屏蔽字还原,长度优先策略
	// ===四字还原===
	"十之\\*{2}": "十之八九",
	"十有\\*{2}": "十有八九",
	"\\*{2}不离十": "八九不离十",
	"感\\*{2}彩": "感情色彩",
	"赤身\\*{2}": "赤身裸体",
	"dang然无存": "当然无存",

	// ===三字还原===
	"\\*{2}分": "八九分",
	"shou不了": "受不了",
	"笑mimi": "笑眯眯",

	// ===双字还原===
	"仈ji[ǔu]": "八九",
	"z[iì]\\s*y[oó]u": "自由",
	"j[īi]ng神": "精神",
	"软j[iì]n": "软禁",
	"j[ǐi]ng觉": "警觉",
	"j[ǐi]ng告": "警告",
	"j[iì]女": "妓女",
	"j[iì]院": "妓院",
	"j[ǐi]ng惕": "警惕",
	"激ān": "奸",
	"j[īi]ng美": "精美",
	"j[īi]ng力": "精力",
	"z[iì]ji": "自己",
	"yiy[aà]ng": "一样",
	"y[ǐi]j[īi]ng": "已经",
	"专j[īi]ng": "专精",
	"yidi[aǎ]n": "一点",
	"zhid[aà]o": "知道",
	"dif[aā]ng": "地方",
	"sh[ìi]ji[eè]": "世界",
	"jiēchu": "接触",
	"shime": "什么",
	"ruguo": "如果",
	"gu[aā]nxi": "关系",
	"mingb[aá]i": "明白",
	"shihou": "时候",
	"j[īi]ngyàn": "经验",
	"j[īi]liè": "激烈",
	"赤luo": "赤裸",
	"q[īi]ngch[ǔu]": "清楚",
	"tur[aá]n": "突然",
	"qigu[aà]i": "奇怪",
	"残ren": "残忍",
	"不ren": "不忍",
	"xiu炼": "修炼",
	"shiji[aā]n": "时间",
	"rongyi": "容易",
	"t[eè]bi[eé]": "特别",
	"p[aá]ngbi[aā]n": "旁边",
	"w[èe]izh[ìi]": "位置",
	"m[aǎ]ny[ìi]": "满意",
	"hou宫": "后宫",
	"f[eè]nnu": "愤怒",
	"(政|Z|zh[eè]ng)(f[uǔ]|府)": "政府",
	"ji合": "集合",
	"k[àa]ngy[ìi]": "抗议",
	"暴[l1][uù]": "暴露",
	"hún[1l]uan": "混乱",
	"míhuo": "迷惑",
	"动dang": "动荡",
	"疑huo": "疑惑",
	"困huo": "困惑",

	// ===单字还原===
	"c[aā]o": "操",
	"ch[āa]": "插",
	"chou": "抽",
	"dàng": "荡",
	"féi": "肥",
	"g[aāà]n": "干",
	"gu[àa]": "挂",
	"hu[aá]ng": "黄",
	"hu[aā]": "花",
	"ji[aā]n": "奸",
	"ji[āa]o": "交",
	"jīng": "精",
	"jǐng": "警",
	"jì": "妓",
	"mo": "摸",
	"n[ǎa]i": "奶",
	"nong": "弄",
	"pi[aá]o": "嫖",
	"r[ìi]": "日",
	"rou": "肉",
	"s[āa]o": "骚",
	"s[èe]": "色",
	"xi[ǎa]o": "小",
	"x[ìi]ng": "性",
	"y[īi]n": "阴"

};
    
//文本替换函数
function recoverText(text) {
    for (var key in replacerules) {
        text = text.replace(new RegExp(key, "ig"), replacerules[key]);
    }
    return text;
}

//分割段落
var parags = originalText.split(new RegExp("[\n\r]+"));

//去首尾空格和文本替换
for (var i = 0; i < parags.length; i++) {
	parags[i] = recoverText(parags[i].replace(/^\s+|\s+$/g, ""));
}

if (devmode) {
	alert("文本获取、分段和清理工作完成,耗时:" + (new Date().getTime() - timestamp.getTime()));
}
//--------------------------------文本获取、分段和清理工作结束----------------------

//--------------------------------文本排版工作开始---------------------------
if (devmode) {
	timestamp = new Date();
}
//定义前言关键字
var prefaceTags = ["序","序章","序言","前言","引言","摘要","楔子","背景简介"];

//定义章节关键字
var sectionTags = ["章","节","回","卷","折","篇","幕","集"];

//定义后记关键字
var postscriptTags = ["后记","附言","结语"];

//是否为标题
var titleRex = new RegExp("^[<《].+[>》]$");
function isTitle(str) {
	return str.match(titleRex);
}

//是否为前言
var prefaceRex0 = new RegExp("^("+prefaceTags.join("|")+")$");
var prefaceRex1 = new RegExp("^("+prefaceTags.join("|")+")\\s");
function isPrefaceStr(str) {
	if (str.match(prefaceRex0)) {
		return true;
	}
	if (str.match(prefaceRex1)) {
		return true;
	}
	return false;
}

//定义章节匹配正则
var sectionRex0 = new RegExp("^(第\\s*[0-9〇零一二三四五六七八九十百千万亿]+\\s*["+sectionTags.join("")+"]|([0-9〇零一二三四五六七八九十百千万亿]))$");
var sectionRex1 = new RegExp("^第\\s*[0-9〇零一二三四五六七八九十百千万亿]+\\s*["+sectionTags.join("")+"]\\s");
function matchSectionStr(str) {
	var headline0 = str.match(sectionRex0);
	if (headline0 != null) {
		return headline0.toString().substr(-1);
	}
	var headline1 = str.match(sectionRex1);
	if (headline1 != null) {
		return headline1.toString().slice(-2, -1);
	}
	return null;
}

//是否为后记
function isPostscriptStr(str) {
	for (var i = 0; i < postscriptTags.length; i++) {
		if (str == postscriptTags[i]) {
			return true;
		}
	}
	return false;
}

//匹配到的章节关键字列表
var sectionTagStack = [];

//依次匹配章节
for (var i in parags) {
	var sectionTag = matchSectionStr(parags[i]);
	if (sectionTag != null) {
		var tagI = sectionTagStack.join("").indexOf(sectionTag);//查找关键字之前出现层
		if (tagI == -1) { //之前未匹配到过该章节关键字
			sectionTagStack.push(sectionTag);
		}
	}
}

//正文文本样式
var textStyle = {
	textIndent: "2em",
	fontSize: "14pt",
	color: "#000"
}

//文章标题样式
var titleStyle = {
	fontSize: "21pt",
	fontWeight: "bold"
}

//统一章节标题样式
var unifromHeadlineStyle = {
	marginBottom: "0",
	marginTop: "1em",
	verticalAlign: "middle",
	color: "#000"
}

//标题样式,从底层往上倒排,覆盖上面样式
var headlineStyles = [
	{
		fontSize: "14pt",
		fontWeight: "bold"
	},{
		fontSize: "15pt",
		fontWeight: "bold"
	},{
		fontSize: "16pt",
		fontWeight: "bold"
	},{
		fontSize: "17pt",
		fontWeight: "bold"
	},{
		fontSize: "18pt",
		fontWeight: "bold"
	},{
		fontSize: "19pt",
		fontWeight: "bold"
	},{
		fontSize: "20pt",
		fontWeight: "bold"
	},{
		fontSize: "21pt",
		fontWeight: "bold"
	},{
		fontSize: "22pt",
		fontWeight: "bold"
	}
];

//绘制文本
for (var i in parags) {
	if (isTitle(parags[i])) {
		var p = document.createElement("p");
		for (var k in titleStyle) {
			p.style[k] = titleStyle[k];
		}
		p.appendChild(document.createTextNode(parags[i]));
		text_box.appendChild(p);
		continue;
	}
	if (isPrefaceStr(parags[i]) || isPostscriptStr(parags[i])) {//如果前言或结语
		var p = document.createElement("p");
		var iTagI = sectionTagStack.length;
		if (iTagI >= headlineStyles.length) {
			iTagI = headlineStyles.length - 1;
		}
		for (var k in unifromHeadlineStyle) {
			p.style[k] = unifromHeadlineStyle[k];
		}
		for (var k in headlineStyles[iTagI-1]) {
			p.style[k] = headlineStyles[iTagI-1][k];
		}
		p.setAttribute("sectionlevel", "0");
		p.appendChild(document.createTextNode(parags[i]));
		text_box.appendChild(document.createElement("br"));
		text_box.appendChild(p);
		var hr = document.createElement("hr");
		hr.style.margin = "4px -4px";
		text_box.appendChild(hr);
		continue;
	}
	var sectionTag = matchSectionStr(parags[i]);
	if (sectionTag == null) { //不是章节标题
		var p = document.createElement("p");
		for (var k in textStyle) {
			p.style[k] = textStyle[k];
		}
		p.appendChild(document.createTextNode(parags[i]));
		text_box.appendChild(p);
	} else {
		var p = document.createElement("p");
		var tagI = sectionTagStack.join("").indexOf(sectionTag); //查找关键字之前出现层
		var iTagI = sectionTagStack.length - tagI; //获取倒排层
		if (iTagI >= headlineStyles.length) {
			iTagI = headlineStyles.length - 1;
		}
		for (var k in unifromHeadlineStyle) {
			p.style[k] = unifromHeadlineStyle[k];
		}
		for (var k in headlineStyles[iTagI-1]) {
			p.style[k] = headlineStyles[iTagI-1][k];
		}
		p.setAttribute("sectionlevel", tagI);
		p.appendChild(document.createTextNode(parags[i]));
		text_box.appendChild(document.createElement("br"));
		text_box.appendChild(p);
		var hr = document.createElement("hr");
		hr.style.margin = "4px -4px";
		text_box.appendChild(hr);
		text_box.appendChild(document.createElement("br"));
	}
}

if (devmode) {
	alert("文本排版工作完成,耗时:" + (new Date().getTime() - timestamp.getTime()));
}
//--------------------------------文本排版工作结束--------------------------

//--------------------------------索引生成工作开始--------------------------
if (devmode) {
	timestamp = new Date();
}

//索引根节点
var indexRootNode = {
	name: "索引",
	level: -1,
	children: []
};

//索引游标
var cursorIndex

//生成索引树
var pArray = document.getElementsByTagName("p");
var headlines = [];
for (var i = 0; i < pArray.length; i++) {
	if (pArray[i].getAttribute("sectionlevel") != null) {
		headlines.push(pArray[i]);
	}
}
cursorIndex = indexRootNode;
for (var i = 0; i < headlines.length; i++) {
	while (cursorIndex.level >= headlines[i].getAttribute("sectionlevel")) {
		cursorIndex = cursorIndex.parent;
	}
	if (cursorIndex.children.length != 0 && headlines[i].childNodes[0].data == cursorIndex.children[cursorIndex.children.length - 1].name) {
		cursorIndex = cursorIndex.children[cursorIndex.children.length - 1];
	} else {
		var a = document.createElement("a");
		var iNode = cursorIndex;
		while (iNode != indexRootNode) {
			a.name = iNode.dom.childNodes[0].data + " -> " + a.name;
			iNode = iNode.parent;
		}
		a.name = a.name + headlines[i].childNodes[0].data;
		headlines[i].setAttribute("name", a.name);
		headlines[i].appendChild(a);
		var childIndex = {
			name: headlines[i].childNodes[0].data,
			level: headlines[i].getAttribute("sectionlevel"),
			children: [],
			parent: cursorIndex,
			dom: headlines[i]
		}
		cursorIndex.children.push(childIndex);
		cursorIndex = childIndex;
	}
}

//绘制索引节点函数
function drawIndex(indexNode, container) {
	var indexDiv = document.createElement("div");
	var headDiv = document.createElement("div");
	var iconSpan = document.createElement("span");
	var nameSpan = document.createElement("a");
	indexDiv.style.whiteSpace = "nowrap";
	if (indexNode.children.length > 0) {
		iconSpan.appendChild(document.createTextNode("▶ "));
		iconSpan.style.verticalAlign = ".05em";
		iconSpan.style.cursor = "pointer";
		iconSpan.onclick = function () {
			if (childrenDiv.style.display == "none") {
				childrenDiv.style.display = "block";
				iconSpan.childNodes[0].data = "▼ "
			} else {
				childrenDiv.style.display = "none";
				iconSpan.childNodes[0].data = "▶ "
			}
		}
	} else {
		iconSpan.appendChild(document.createTextNode("▶ "));
		iconSpan.style.verticalAlign = ".05em";
		iconSpan.style.visibility = "hidden";
	}
	nameSpan.appendChild(document.createTextNode(indexNode.name));
	nameSpan.style.color = "black";
	nameSpan.style.textDecoration = "none";
	if (indexNode.dom != null) {
		nameSpan.href = "#" + indexNode.dom.getAttribute("name");
	}
	nameSpan.style.whiteSpace = "nowrap";
	nameSpan.style.width = index_box.style.width.slice(0, -2) + "px";
	headDiv.appendChild(iconSpan);
	headDiv.appendChild(nameSpan);
	headDiv.style.textIndent = new Number(indexNode.level) + "em";
	headDiv.style.verticalAlign = "middle";
	indexDiv.appendChild(headDiv);
	var childrenDiv = document.createElement("div");
	childrenDiv.style.display = "none"
	indexDiv.appendChild(childrenDiv);
	container.appendChild(indexDiv);
	for (var i = 0; i < indexNode.children.length; i++) {
		drawIndex(indexNode.children[i], childrenDiv);
	}
}

//绘制索引
for (var i = 0; i < indexRootNode.children.length; i++) {
	drawIndex(indexRootNode.children[i], index_box);
}

//如果没有索引,则隐藏索引框
if (indexRootNode.children.length == 0) {
	index_containter.style.width = "0px";
	split_line.style.width = "0px";
	pageLayout.style.width = "1004px";
}

if (devmode) {
	alert("索引生成工作完成,耗时:" + (new Date().getTime() - timestamp.getTime()));
}
//--------------------------------索引生成工作完成--------------------------

//--------------------------------关闭后自动恢复功能(HTML5)------------------
var filename = window.location.pathname.split('/').pop();
//保存当前位置
text_containter.onscroll = function () {
	localStorage[filename + "-lastTextPosition"] = this.scrollTop;
}
//滚动到上次位置
if (localStorage[filename + "-lastTextPosition"] != null) {
	text_containter.scrollTop = localStorage[filename + "-lastTextPosition"];
}
//--------------------------------自动恢复功能完成(HTML5)--------------------