// ==UserScript==
// @name 起点听书/www.qidian.com
// @namespace yoursatan
// @version 0.2
// @description 阅读界面左侧功能栏增加“听书”按钮,点击“听书”开始朗读:Esc-结束朗读;空格-暂定/继续(360安全浏览器急速模式(已测试),Chrome浏览器);后台静默复制文章内容到剪贴板。
// @author yorusatan
// @include https://read.qidian.com/chapter/*
// @grant none
// @require https://code.jquery.com/jquery-2.1.4.min.js
// @license MIT License
// ==/UserScript==
// v0.2 修复一些使用中发现的bug。
// v0.1 在阅读界面左侧功能栏添加“听书”按钮,点击“听书”开始朗读:Esc-结束朗读;空格-暂定/继续(360安全浏览器急速模式(已测试),Chrome浏览器);后台静默复制文章内容到剪贴板。
(function() {
"use strict";
// 用于获取story内文本的父元素全体
var storyAll = "";
// 用于获取story文本全体
var story = "";
// 用于存储格式化后story文本全体
var newStory = "";
// 侧边栏添加 听书 按钮
$(".left-bar-list dl").append(
'<dd style="background:#e60022;"><a href="javascript:"><i><em class="iconfont"></em><span id ="btnSmartRead">听书</span></i></a><div class="guide-box"><cite> </cite></div></dd>'
);
// 获取文章内容
story = $(".read-content")
.html()
.replace(/<\/?p data-type="2">/gi, "\n")
.replace(/<\/p>/gi, "\n")
.replace(/<span class="content-wrap">/g, "\n")
.replace(/<i><cite><\/cite><\/i>/g, "\n")
.replace(/<span class="review-count.*data-segid=\"\d*\">\d*/g, "\n")
.replace(/<\/span>/g, "\n");
$(".read-content").empty();
$(".read-content").append("<div id='storycontent'></div>");
// hover 事件
$("#btnSmartRead").hover(
function() {
$("#btnSmartRead").css({ color: "white" });
},
function() {
$("#btnSmartRead").css({ color: "black" });
}
);
// 将原文本进行格式化,分割存储;
var storyArr = story
.replace(/<div class="hc">[\s\S].*<\/div>/gi, "")
.replace(/<\/?p>/gi, "\n")
.replace(/<\/?div>/gi, "\n")
.replace(/<br\s*\/?>/gi, "\n")
.replace(/\n(\n)*( )*(\n)*\n/g, "\n")
.replace(/\ /g, "")
.split(/\n/);
// 用于存储格式化后,并移除空行文本数组
var newStoryArr = [];
// 移除数组空项(文本空行)
const countPara = storyArr.length;
for (var i = 0; i < countPara; i++) {
storyArr[i] = storyArr[i].replace(/\s+/g, " ").trim();
if (storyArr[i] != "") {
newStory += "<p>" + storyArr[i] + "</p>";
newStoryArr.push(storyArr[i]);
}
}
// 重新加载文章内容,并更改默认样式
$("#storycontent").append(newStory);
$("#storycontent").css({
"font-size": "1em",
"line-height": "1.5em",
"font-family": "sans-serif",
"font-weight": 300
});
const newCountPara = newStoryArr.length;
// 用于逐段朗读
var flag = 0;
// 朗读
var speaker = new window.SpeechSynthesisUtterance();
speaker.rate = 1.24;
speaker.lang = "en-US";
speaker.voiceURI = "Microsoft Zira Desktop - English (United States)";
speaker.lang = "zh-CN";
speaker.voiceURI = "Microsoft Huihui Desktop - Chinese (Simplified)";
$("#storycontent").css("font-family", "PingFangSC-Regular");
// 多次尝试再for循环中无法循环朗读,故添加flag步进;利用setInterval进行循环。
$("#btnSmartRead").click(function() {
// 复制文章内容到剪贴板
if (
// https://read.qidian.com/chapter/ 网站支持
window.location.href.indexOf("https://read.qidian.com/chapter/") >
-1
) {
// 功能参考插件Enable Copy:https://bitbucket.org/keakon/enable-copy/src/default/enable.js
// 非常感谢!Sun Jing。Thanks very much.
var doc = document;
var body = doc.body;
var html = doc.documentElement;
function allowUserSelect(element) {
element.setAttribute(
"style",
"-webkit-user-select: text !important"
);
element.setAttribute("style", "user-select: text !important");
return element;
}
function allowUserSelectById(element_id) {
return allowUserSelect(doc.getElementById(element_id));
}
function allowUserSelectByClassName(element_class) {
var elements = doc.getElementsByClassName(element_class);
var len = elements.length;
for (var i = 0; i < len; ++i) {
allowUserSelect(elements[i]);
}
return elements;
}
function clearHandlers() {
// html.onselectstart = html.oncopy = html.oncut = html.onpaste = html.onkeydown = html.oncontextmenu = html.onmousemove = body.oncopy = body.oncut = body.onpaste = body.onkeydown = body.oncontextmenu = body.onmousedown = body.onmousemove = body.onselectstart = body.ondragstart = doc.onselectstart = doc.oncopy = doc.oncut = doc.onpaste = doc.onkeydown = doc.oncontextmenu = doc.onmousedown = doc.onmouseup = window.onkeyup = window.onkeydown = null;
// 起点网下列三个设置足以
html.oncopy = body.oncopy = doc.oncopy = null;
allowUserSelect(html);
allowUserSelect(body);
}
clearHandlers();
function defaultHandler(event) {
event.returnValue = true;
}
for (var event_type in ["copy", "cut", "paste"]) {
// 起点网,以上三个足以;
// var event_type in ['selectstart', 'copy', 'cut', 'paste', 'keydown', 'contextmenu', 'dragstart']
html.addEventListener(event_type, defaultHandler);
body.addEventListener(event_type, defaultHandler);
doc.addEventListener(event_type, defaultHandler);
}
function removeEventAttributes(element) {
/*
element.removeAttribute('oncontextmenu');
element.removeAttribute('ondragstart');
element.removeAttribute('onselectstart');
element.removeAttribute('onselect');
element.removeAttribute('oncopy');
element.removeAttribute('onbeforecopy');
element.removeAttribute('oncut');
element.removeAttribute('onpaste');
element.removeAttribute('onclick');
element.removeAttribute('onmousedown');
element.removeAttribute('onmouseup');
*/
// 起点网设置这一个足以
element.removeAttribute("oncopy");
}
var jQuery = window.jQuery;
var $Fn = window.$Fn;
if ($Fn) {
try {
$Fn.freeElement(doc);
$Fn.freeElement(body);
} catch (e) {}
}
/* // 不需要
var jindo = window.jindo;
if (jindo) {
jindo.$A = null;
}
*/
function replaceElementEventsWithClone(element) {
var clone = element.cloneNode();
while (element.firstChild) {
clone.appendChild(element.firstChild);
}
element.parentNode.replaceChild(clone, element);
}
function replaceElementsEventsWithClone(elements) {
var length = elements.length;
for (var i = 0; i < length; ++i) {
replaceElementEventsWithClone(elements[i]);
}
}
var url = doc.URL;
var domain_pattern = /^https?:\/\/([^\/]+)/;
var result = domain_pattern.exec(url);
if (result) {
var domain = result[1];
if (
domain.length > 11 &&
domain.substr(-11, 11) == ".lofter.com"
) {
replaceElementsEventsWithClone(jQuery(".pic>a"));
return;
}
switch (domain) {
case "wenku.baidu.com":
jQuery(".doc-reader")
.off("copy")
.removeAttr("oncopy");
jQuery("#reader-container-1").off("copy");
break;
case "www.qidian.com":
case "read.qidian.com":
case "vipreader.qidian.com":
case "big5.qidian.com":
case "www.qdmm.com":
var element = doc.getElementById("bigcontbox");
if (element) {
element.onmousedown = null;
}
//jQuery(body).off('contextmenu copy cut');
// 可使用复制功能
jQuery(body).off("copy");
break;
}
/*
// 百度文库不能覆盖这些事件
// 起点会造成无限递归 bug
if (jQuery) {
var $doc = jQuery(doc);
var $body = jQuery(body);
if ($doc.off) {
$doc.off();
$body.off();
jQuery(window).off();
} else {
$doc.unbind();
$body.unbind();
jQuery(window).unbind();
}
}
*/
}
}
// 完成后台复制
var copyStory = document.createElement("textarea"); //创建textarea对象
copyStory.id = "copyArea";
$("#storycontent").prepend(copyStory); //添加元素
var storyTitle = $("head title").html();
copyStory.value = storyTitle + "\n" + newStoryArr.join("\n"); // 组合文章标题
copyStory.focus();
if (copyStory.setSelectionRange) {
copyStory.setSelectionRange(0, copyStory.value.length); //获取光标起始位置到结束位置
} else {
copyStory.select();
}
document.execCommand("Copy", "false", null); //执行复制
if (document.execCommand("Copy", "false", null)) {
console.log(
"已复制文章到剪贴板!Success,The story has been copied to clipboard!--yoursatan"
);
}
$("#copyArea").remove(); //删除元素
// 朗读文字数组
var storyAllRead = newStoryArr;
// 用于文字选中效果
var range = document.createRange();
var selection = window.getSelection();
// 朗读
var readStory = function() {
var reading = setInterval(function() {
if (!window.speechSynthesis.speaking && flag < newCountPara) {
speaker.text = storyAllRead[flag];
window.speechSynthesis.speak(speaker);
flag += 1;
// 朗读段落文字选中效果
var referenceNode = document
.getElementById("storycontent")
.childNodes.item(flag - 1);
// 起点网朗读效果,当前朗读段落文字变红
$("#storycontent p")
.eq(flag - 1)
.css("color", "red");
$("html,body").animate(
{
scrollTop:
$("#storycontent p")
.eq(flag - 1)
.offset().top -
document.documentElement.clientHeight * 0.382
},
500 /*scroll实现定位滚动*/
); //代码参考,感谢:https://blog.csdn.net/qq_30109365/article/details/86592336
if (flag - 1) {
$("#storycontent p")
.eq(flag - 2)
.css("color", "black");
}
} else if (flag >= newCountPara) {
// 朗读结束
// window.speechSynthesis.cancel();
clearInterval(reading);
selection.removeAllRanges();
$("#storycontent p")
.eq(flag - 1)
.css("color", "black");
flag = 0;
alert("The story is finished");
}
}, 500);
// 监听键盘:Esc/F5
$(document).keyup(function(event) {
if (event.keyCode == 27 || event.keyCode == 116) {
window.speechSynthesis.cancel();
clearInterval(reading);
selection.removeAllRanges();
if (
// https://read.qidian.com/chapter/ 网站支持
window.location.href.indexOf(
"https://read.qidian.com/chapter/"
) > -1
) {
$("#storycontent p")
.eq(flag - 1)
.css("color", "black");
}
flag = 0;
}
});
// 监听键盘:空格键
$(document).keypress(function(event) {
if (event.keyCode == 32) {
if (window.speechSynthesis.speaking) {
window.speechSynthesis.pause();
}
if (window.speechSynthesis.paused) {
window.speechSynthesis.resume();
}
}
});
// 监听标签关闭事件
window.onbeforeunload = function(e) {
clearInterval(reading);
window.speechSynthesis.cancel();
}
};
readStory();
});
})();