Greasy Fork

Greasy Fork is available in English.

真白萌新站阅读插件

去除字体样式,添加功能按钮等。

当前为 2022-05-20 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         真白萌新站阅读插件
// @namespace    mashiro_me
// @version      0.6.11
// @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 footer = document.querySelector(".box-footer")

    //【上一话】跳转动作替换
    var lastPage = footer.firstElementChild.firstElementChild;
    if (!!lastPage){
        window.AddIn_LastPage = lastPage.getAttribute("href");
        lastPage.removeAttribute("href");
        lastPage.setAttribute("style", "cursor: pointer;");
        lastPage.onclick = function(){ RequestInPage(window.AddIn_LastPage); };
    }

    //【下一话】跳转动作替换
    //※最后一章的下一页链接的Class会变成last而非next。。。
    var nextPage = footer.lastElementChild.firstElementChild;
    if (!!nextPage){
        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"));

    //日期标签转换
    var time, timeObject = documentData.querySelectorAll("data-time");
    for(let i = 0; i < timeObject.length; i++){
        time = new Date(timeObject[i].getAttribute("time") * 1000);
        timeObject[i].outerHTML =
            "<span title=\"" + GetDateTimeString(time) + "\" class=\"time\">" +
            GetPassTimeString(time) + "</span>";
    }

    //URL更新
    history.pushState(null, null, window.location.origin + currentUrl);
    //layui组件刷新
    layui.use(["element"], function(){ layui.element.init(); });
    //滚动条复位
    documentData.documentElement.scrollTop = newApp.offsetTop;
    //插件刷新
    InitReadingPage();

}

function GetDateTimeString(dateInput){

    let dateNum, result = "";
    result += dateInput.getFullYear() + "-";

    dateNum = dateInput.getMonth() + 1
    result += (dateNum < 10 ? "0" : "") + dateNum + "-";
    dateNum = dateInput.getDate()
    result += (dateNum < 10 ? "0" : "") + dateNum + " ";

    dateNum = dateInput.getHours()
    result += (dateNum < 10 ? "0" : "") + dateNum + ":";
    dateNum = dateInput.getMinutes()
    result += (dateNum < 10 ? "0" : "") + dateNum + ":";
    dateNum = dateInput.getSeconds()
    result += (dateNum < 10 ? "0" : "") + dateNum;

    return result;
}

function GetPassTimeString(dateInput){

    let dateNum = 0;
    let dateNow = new Date(Date.now());

    dateNum = dateNow.getFullYear() - dateInput.getFullYear();
    if(dateNum > 1){
        return dateNum + "年前";

    }else if(dateNum > 0){
        dateNum = dateNow.getMonth() - dateInput.getMonth();
        return (dateNum < 0 ? (dateNum + 12) + "个月前" : "1年前");

    }else{
        dateNum = dateNow.getMonth() - dateInput.getMonth();
        if(dateNum > 0) return dateNum + "个月前";
    }

    dateNum = dateNow.getDate() - dateInput.getDate();
    if(dateNum > 0) return dateNum + "天前";

    dateNum = dateNow.getHours() - dateInput.getHours();
    if(dateNum > 0) return dateNum + "小时前";

    dateNum = dateNow.getMinutes() - dateInput.getMinutes();
    if(dateNum > 0) return dateNum + "分钟前";

    dateNum = dateNow.getSeconds() - dateInput.getSeconds();
    return dateNum + "秒前";

}

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 (/^(&nbsp;|\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 ((/^(&nbsp;|\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);
        }

    }

}