// ==UserScript==
// @name 真白萌新站阅读插件
// @namespace mashiro_me
// @version 0.6.7
// @description 去除字体样式,添加功能按钮等。
// @author MikaRyu
// @match https://masiro.me/admin/novel*
// @license BSD
// @icon https://www.google.com/s2/favicons?domain=masiro.me
// @grant none
// ==/UserScript==
(function() {
'use strict';
//最大连续换行数
window.AddIn_MaxBreakLines = 4;
//纯文本模式Flag(改为true使用纯文本模式)
window.AddIn_TextModFlag = false;
//当前地址
var curHref = window.location.href;
//小说阅读页面
if ( /\/novelReading\?cid=[\d]+$/.test(curHref) ){
InitReadingPage();
return;
}
//小说目录页面
if ( /\/novelView\?novel_id=[\d]+$/.test(curHref) ){
InitViewPage();
return;
}
})();
function InitViewPage(){
//目录按钮组
var baseBox = document.getElementsByClassName("btn-box")[0];
//全部购买按钮
var payButton = document.createElement("span");
payButton.setAttribute("class", "n-btn btn-read btn-font");
payButton.appendChild(document.createTextNode("购买全部章节"));
payButton.onclick = function(){
var chapters = document.getElementsByClassName("chapter-ul")[0].querySelectorAll("a");
window.AddIn_PayList = [];
window.AddIn_TotalPay = 0;
for (var i = 0; i < chapters.length; i++){
if ((chapters[i].getAttribute("data-cost") > 0) &&
(chapters[i].getAttribute("data-payed") == "0")){
window.AddIn_PayList[window.AddIn_PayList.length] = chapters[i];
window.AddIn_TotalPay += Number(chapters[i].getAttribute("data-cost"));
}
}
//以下部分依赖于站点对于layui和jQuery的原始引用
layui.use(['layer'], function () {
let layer = layui.layer;
if (window.AddIn_PayList.length == 0){
layer.msg("本书暂无未购买章节", {icon: 2});
return;
}
layer.confirm(
"确定要支付"+ window.AddIn_TotalPay + "金币吗?" +
"(共" + window.AddIn_PayList.length + "章未购买)",
{icon: 3, title:"积分支付"},
function(index){
window.AddIn_PayListIndex = 0;
window.AddIn_PayInterval = setInterval(BuyAllChapters(),200);
});
});
}
//按钮追加
baseBox.appendChild(payButton);
}
function BuyAllChapters(){
return function(){
layui.use(['layer'], function () {
let layer = layui.layer;
window.AddIn_ToPay = window.AddIn_PayList[window.AddIn_PayListIndex];
$.ajax({
type:"post",
url:"/admin/pay",
dataType:"json",
data:{"type":2,
"object_id":window.AddIn_ToPay.getAttribute("data-id"),
"cost":window.AddIn_ToPay.getAttribute("data-cost")
},
success:function(data){
if (data.code == 1){
window.AddIn_ToPay.setAttribute("data-payed", "1");
}else{
window.AddIn_PayError = data;
}
},
error:function(data){
window.AddIn_PayError = data;
}
});
if (!!window.AddIn_PayError){
clearInterval(window.AddIn_PayInterval);
if (window.AddIn_PayError.hasOwnProperty('msg')){
layer.msg("网络错误,稍后重试!", {icon: 2});
}else{
layer.msg(window.AddIn_PayError.msg, {icon: 2});
}
window.location.href = window.location.href;
window.AddIn_PayError = null;
return;
}
window.AddIn_PayListIndex += 1;
if (window.AddIn_PayListIndex >= window.AddIn_PayList.length){
clearInterval(window.AddIn_PayInterval);
layer.open({
title: "积分支付",
content: "共" + window.AddIn_PayList.length + "章节, 支付成功",
yes: function(index, layero){
window.location.href = window.location.href;
layer.close(index);
}
});
}
});
}
}
function InitReadingPage(){
//小说内容Box
var textBox;
var baseBox = document.getElementsByClassName("box-body nvl-content")[0];
//替换翻页动作(全屏适配)
PrelacePageAction();
//样式跟随系统主题
FollowSystemTheme();
//追加插件按钮组
AddButtonGroup();
//允许点击任意区域隐藏目录
LetAnyClickHideChapter();
if (window.AddIn_TextModFlag){
//复制纯文本用Box
textBox = baseBox.parentNode.insertBefore(baseBox.cloneNode(false), baseBox);
//文本内容复制
FormartNodesAsText(textBox, baseBox);
}else{
//原内容Box复制
textBox = baseBox.parentNode.insertBefore(baseBox.cloneNode(true), baseBox);
//删除空行
DeleteEmptyRows(textBox);
//删除字体、字体大小、字体颜色
DeleteFontStyles(textBox);
}
//删除【maxBreakLines】个以上的换行
DeleteMultiBrs(textBox, window.AddIn_MaxBreakLines);
//原内容Box隐藏
textBox.style.display = "block";
baseBox.style.display = "none";
}
function PrelacePageAction(){
//【上一话】跳转动作替换
var lastPage = document.querySelector(".last").firstElementChild;
window.AddIn_LastPage = lastPage.getAttribute("href");
lastPage.removeAttribute("href");
lastPage.setAttribute("style", "cursor: pointer;");
lastPage.onclick = function(){ RequestInPage(window.AddIn_LastPage); };
//【下一话】跳转动作替换
var nextPage = document.querySelector(".next").firstElementChild;
window.AddIn_NextPage = nextPage.getAttribute("href");
nextPage.removeAttribute("href");
nextPage.setAttribute("style", "cursor: pointer;");
nextPage.onclick = function(){ RequestInPage(window.AddIn_NextPage); };
}
function RequestInPage(requestUrl){
if (!!window.AddIn_IsFull){
//全屏状态下通过保留document维持全屏状态
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if ((req.readyState == 4) && (req.status == 200)) {
const parser = new DOMParser();
let htmlData = parser.parseFromString(req.responseText,"text/html");
ReplaceDocuments(document, htmlData, requestUrl);
}
}
req.open('GET', requestUrl, true);
req.send(null);
}else{
window.location = requestUrl;
}
}
function ReplaceDocuments(documentData, htmlData, currentUrl){
//标题替换
document.head.querySelector("title").innerHTML =
htmlData.head.querySelector("title").innerHTML;
//主要显示区域替换
var baseObject = documentData.querySelector("#pjax-container");
var newApp = htmlData.querySelector("#app").cloneNode(true);
baseObject.appendChild(newApp);
baseObject.replaceChild(newApp, documentData.querySelector("#app"));
//URL更新
history.pushState(null, null, window.location.origin + currentUrl);
//layui组件刷新
layui.use(["element"], function(){ layui.element.init(); });
//滚动条复位
documentData.documentElement.scrollTop = newApp.offsetTop;
//插件刷新
InitReadingPage();
}
function FollowSystemTheme(){
let displaymode = document.documentElement.getAttribute("data-theme");
let styleTag = document.createElement("style");
styleTag.setAttribute("type", "text/css");
//解除box内联样式
let displayBoxs = document.querySelectorAll(".box");
if (displayBoxs.length > 0) {
styleTag.innerHTML =
".box { "+
" color: " + displayBoxs[0].style.color +";" +
" background-color: " + displayBoxs[0].style.backgroundColor +";" +
"}"
document.head.appendChild(styleTag);
styleTag = document.createElement("style");
styleTag.setAttribute("type", "text/css");
for(let i = 0; i < displayBoxs.length; i++){
displayBoxs[0].style.color = null;
displayBoxs[0].style.backgroundColor = null;
}
}
//跟随系统模式
if (displaymode == "0"){
styleTag.innerHTML =
"@media (prefers-color-scheme: light) {"+
" .addin-f-button { background-color: white; }"+
"}"+
"@media (prefers-color-scheme: dark) {"+
" .box { "+
" color: white;" +
" background-color: var(--secondary-background);"+
" }"+
"}"
document.head.appendChild(styleTag);
//明亮模式
}else if(displaymode == "1"){
styleTag.innerHTML =
".addin-f-button { background-color: white; }"
document.head.appendChild(styleTag);
//黑暗模式
}else if(displaymode == "2"){
styleTag.innerHTML =
".box { "+
" color: white;" +
" background-color: var(--secondary-background);"+
"}"
document.head.appendChild(styleTag);
}
}
function AddButtonGroup(){
//按钮组父对象
var parent = document.getElementById("app");
//移除默认回到顶部按钮
var originToTop = document.getElementById("totop");
if (!!originToTop){
document.body.removeChild(originToTop);
}
//插件按钮组
window.Addin_ButtonBox = document.createElement("div");
var styleList =
"position: fixed; bottom: 50px;" +
"width: 36px; height: 280px;";
window.Addin_ButtonBox.setAttribute("style", styleList);
window.Addin_ButtonBox.setAttribute("id", "AddIn_ButtonGroup");
//原内容/编辑后内容切换按钮
AddFunctionButton(
window.Addin_ButtonBox, "fa-refresh", "bottom: 240px; left: 0px; position: absolute;",
function() {
var contents = document.getElementsByClassName("box-body nvl-content");
if (contents.length < 2) { return; }
var toHide, toShow, toHideBlockHeight, originPosition, positionCoeff;
if (contents[0].style.display == "block"){
toHide = contents[0];
toShow = contents[1];
}else{
toHide = contents[1];
toShow = contents[0];
}
toHideBlockHeight = toHide.offsetHeight;
originPosition = document.documentElement.scrollTop;
positionCoeff = (originPosition - toHide.offsetTop) /
toHideBlockHeight;
toHide.style.display = "none";
toShow.style.display = "block";
if ( positionCoeff > 1 ){
document.documentElement.scrollTop = originPosition +
( toShow.offsetHeight - toHideBlockHeight );
}
else if ( positionCoeff > 0 ){
document.documentElement.scrollTop = ( positionCoeff * toShow.offsetHeight ) +
toShow.offsetTop;
}
}
);
//目录按钮
AddFunctionButton(
window.Addin_ButtonBox, "fa-list", "bottom: 200px; left: 0px; position: absolute;",
function() {
let chapter = window.AddIn_Chapter;
if (chapter.offsetWidth == 0){
var baseWidth = document.body.clientWidth;
var titleBox = chapter.children[1];
var bookMark = chapter.querySelector(".marked");
if (baseWidth > 1000) {
titleBox.style.width = ((baseWidth * 0.3) - 5) + "px";
chapter.style.width = "30%";
}else{
titleBox.style.width = ((baseWidth * 0.6) - 5) + "px";
chapter.style.width = "60%";
}
chapter.scrollTop = bookMark.offsetTop - bookMark.offsetHeight;
ResetTitleBoxWidth();
}
}
);
//全屏按钮
var iconClass = "fa-expand";
if (IsFullScreen()){ iconClass = "fa-compress"; }
window.AddIn_FSIcon = AddFunctionButton(
window.Addin_ButtonBox, iconClass, "bottom: 160px; left: 0px; position: absolute;",
function() {
if (IsFullScreen()){
document.exitFullscreen();
}else{
document.documentElement.requestFullscreen();
}
}
).childNodes[0];
//直达评论区按钮
window.AddIn_OP1 = -1;
AddFunctionButton(
window.Addin_ButtonBox, "fa-comments-o", "bottom: 120px; left: 0px; position: absolute;",
function() {
if (window.AddIn_OP1 < 0) {
window.AddIn_OP1 = document.documentElement.scrollTop;
document.documentElement.scrollTop =
document.getElementsByClassName("col-md-12")[1].offsetTop;
}else{
document.documentElement.scrollTop = window.AddIn_OP1;
window.AddIn_OP1 = -1;
}
}
);
//返回顶部按钮
window.AddIn_OP2 = -1;
AddFunctionButton(
window.Addin_ButtonBox, "fa-chevron-up", "bottom: 0px; left: 0px; position: absolute;",
function() {
if (window.AddIn_OP2 < 0) {
window.AddIn_OP2 = document.documentElement.scrollTop;
document.documentElement.scrollTop = 0;
}else{
document.documentElement.scrollTop = window.AddIn_OP2;
window.AddIn_OP2 = -1;
}
}
);
//追加按钮组,重设位置
parent.appendChild(window.Addin_ButtonBox);
KeepButtonPosition();
window.addEventListener("resize", function(){ KeepButtonPosition(); });
}
function IsFullScreen(){
//显示区域与屏幕区域比较
window.AddIn_IsFull =
(document.documentElement.clientHeight == window.screen.height);
return window.AddIn_IsFull;
}
function ChangFullScreenIcon(){
if (IsFullScreen()){
ReplaceClass(window.AddIn_FSIcon, "fa-expand", "fa-compress");
}else{
ReplaceClass(window.AddIn_FSIcon, "fa-compress", "fa-expand");
}
}
function AddFunctionButton(buttonBox, iconClass, positionInfo, buttonAction){
var icon, button, styleList;
//按钮图标设置
icon = document.createElement("i");
icon.setAttribute("class", "fa " + iconClass);
icon.setAttribute("style", "margin-top: 11px;margin-left: 11px;");
//按钮设置
button = document.createElement("div");
button.appendChild(icon);
styleList = positionInfo +
"width: 36px; height: 36px;"+
"border-radius: 3px; border: 1px solid; border-color: #E6E6E6;"+
"cursor: pointer;";
button.setAttribute("class", "addin-f-button");
button.setAttribute("style", styleList);
button.onclick = buttonAction;
buttonBox.appendChild(button);
return button;
}
function LetAnyClickHideChapter(){
//鼠标在目录中时设定为利用中
window.AddIn_Chapter = document.querySelector(".chapter-nav");
let chapter = window.AddIn_Chapter;
chapter.addEventListener("mouseenter", function(event){
AddClass(window.AddIn_Chapter, "AddIn_Using");
});
chapter.addEventListener("mouseleave", function(event){
RemoveClass(window.AddIn_Chapter, "AddIn_Using");
});
//文档全体添加隐藏目录事件
document.addEventListener("click", function(event){
let chapter = window.AddIn_Chapter;
if ((! /(?<=^| )AddIn_Using(?= |$)/.test(chapter.className)) &&
(chapter.offsetWidth > 0)){
var baseWidth = document.body.clientWidth;
var titleBox = chapter.children[1];
if (baseWidth > 1000) {
titleBox.style.width = ((baseWidth * 0.3) - 5) + "px";
}else{
titleBox.style.width = ((baseWidth * 0.6) - 5) + "px";
}
chapter.style.width = "0px";
ResetTitleBoxWidth();
}
});
}
function ResetTitleBoxWidth(){
window.AddIn_Reset && clearTimeout(window.AddIn_Reset);
window.AddIn_Reset =
setTimeout(function(){
window.AddIn_Chapter.firstElementChild.style.width = "100%"
window.AddIn_Reset = null;
}, 600);
}
function AddClass(item, className){
var reg = new RegExp("(?<=^| )" + className + "(?= |$)");
var allClasses = item.className;
if (! reg.test(allClasses)){
item.setAttribute("class", allClasses + " " + className);
}
}
function RemoveClass(item, className){
var reg = new RegExp("(?<=^| )" + className + "(?= |$)");
var allClasses = item.className;
if (reg.test(allClasses)){
item.setAttribute("class", allClasses.replace(reg, ""));
}
}
function ReplaceClass(item, className, classNameNew){
var reg = new RegExp("(?<=^| )" + className + "(?= |$)");
var allClasses = item.className;
if (reg.test(allClasses)){
item.setAttribute("class", allClasses.replace(reg, classNameNew));
}
}
function KeepButtonPosition(){
let button = window.Addin_ButtonBox;
var marginWidth = document.querySelector(".content").offsetLeft -
document.querySelector("#app").offsetLeft;
if ( marginWidth > 136 ){
button.style.right = (marginWidth - 86) + "px";
if(button.style.opacity != 1){
button.style.opacity = 1;
}
}else{
if (button.style.right != "50px"){
button.style.right = "50px";
}
if (marginWidth < 71){
if(button.style.opacity != 0.4){
button.style.opacity = 0.4;
}
}else{
if(button.style.opacity != 1){
button.style.opacity = 1;
}
}
}
//同步全屏图标
ChangFullScreenIcon();
}
function FormartNodesAsText(base, parent){
var childs = parent.childNodes;
for(var i = 0; i < childs.length; i++){
if (childs[i].nodeType == 3){
var innerText = childs[i].data;
if (/^( |\s)*$/.test(innerText)){
continue;
}
base.appendChild(childs[i].cloneNode(false));
}else{
var tagName = childs[i].localName;
if (tagName == "br" || tagName == "img" || tagName == "ruby"){
base.appendChild(childs[i].cloneNode(true));
}else{
if (( tagName == "p" ) &&
( typeof(childs[i].style) != "undefined" ) &&
( typeof(childs[i].style.textIndent) != "undefined" )){
base.appendChild(document.createElement("br"));
}
if (childs[i].hasChildNodes()){
FormartNodesAsText(base, childs[i]);
}
if (tagName != "span"){
base.appendChild(document.createElement("br"));
}
}
}
}
}
function DeleteFontStyles(parent){
var childs = parent.children;
for(var i = 0; i < childs.length; i++){
if (typeof(childs[i].style) != "undefined"){
childs[i].style.fontFamily = null;
childs[i].style.fontSize = null;
childs[i].style.color = null;
childs[i].style.backgroundColor = null;
}
if (childs[i].hasChildNodes()){
DeleteFontStyles(childs[i]);
}
}
}
function DeleteEmptyRows(parent){
var childs = parent.childNodes;
var tagName, innerText;
for(var i = 0; i < childs.length; i++){
tagName = childs[i].localName;
if (tagName == "br" ||
tagName == "img"){
continue;
}
innerText = childs[i].innerHTML;
if (typeof(innerText) == "undefined"){
innerText = childs[i].data;
}
if ((/^( |\s)*$/g.test(innerText)) ||
((! childs[i].hasChildNodes()) && typeof(tagName) != "undefined")){
parent.removeChild(childs[i]);
i -= 1;
continue;
}
DeleteEmptyRows(childs[i]);
}
}
function DeleteMultiBrs(parent, num){
var j = 0;
var childs = parent.childNodes;
var tagName;
for(var i = 0; i < childs.length; i++){
tagName = childs[i].localName;
if (tagName == "br"){
if( j == num ){
parent.removeChild(childs[i]);
i -= 1;
}else{
j += 1;
}
continue;
}
j = 0;
if (childs[i].hasChildNodes()){
DeleteMultiBrs(childs[i], num);
}
}
}