LushStories.com & LitErotica.com-去除文章选择、复制限制,后台静默复制文章到剪贴板;添加朗读/听书按钮

LushStories.com & LitErotica.com:默认去除文章选择、复制限制;添加 Smart Read 按钮,实现语音朗读/听书功能,Esc-结束朗读;空格-暂定/继续(360安全浏览器急速模式(已测试),Chrome浏览器);www.qidian.com:左侧功能栏增加“听书”按钮,点击“听书”开始朗读。后台静默复制文章内容到剪贴板。

// ==UserScript==
// @name         LushStories.com & LitErotica.com-去除文章选择、复制限制,后台静默复制文章到剪贴板;添加朗读/听书按钮
// @name:en      LushStories.com & LitErotica.com-Enable Copy & Smart Read
// @namespace    yoursatan
// @version      0.1.3
// @description  LushStories.com & LitErotica.com:默认去除文章选择、复制限制;添加 Smart Read 按钮,实现语音朗读/听书功能,Esc-结束朗读;空格-暂定/继续(360安全浏览器急速模式(已测试),Chrome浏览器);www.qidian.com:左侧功能栏增加“听书”按钮,点击“听书”开始朗读。后台静默复制文章内容到剪贴板。
// @description:en   LushStories.com & LitErotica.com:Default to remove the copy restriction of the site:www.lushStories.com & www.literotica.com;Add a Smart Read button to listen stories(Chrome:Esc to cancel & Space to psuse/resume).Silently copy the content of the article to the clipboard in the background.
// @author       yorusatan
// @include      https://www.lushstories.com/stories/*
// @include      https://www.literotica.com/s/*
// @include      https://read.qidian.com/chapter/*
// @grant        none
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @license      MIT License
// ==/UserScript==
// v0.1.3 修复一些使用中发现的bug。
// v0.1.2 添加朗读时scroll效果 ——2020-3-13
// v0.1.1 1,起点网听书按钮样式更新;2,实现后台静默复制文章到剪贴板(目前三个网站同时支持),感谢Sun Jing和他的 Enable Copy 代码 ——2020-3-11
// v0.1.0 增加 www.qidian.com 支持(在左侧侧边栏增加“听书”按钮,复制功能暂未实现o(╥﹏╥)o) ——2020-3-11
// v0.0.9 增加 www.LitErotica.com 支持 ——2020-3-10
// v0.0.8 修复一个格式化bug ——2020-3-9
// v0.0.7 1,移除 Enable Copy 按钮,“选择、复制,可划词以配合有道翻译”改为默认执行 ——2020-3-9
// v0.0.7 2,实现朗读时当前段落处于选中状态(蓝底白字) ——2020-3-9
// v0.0.6 完善 Smart Read 功能:Esc-结束朗读;空格-暂定/继续 ——2020-3-8
// v0.0.5 添加 Smart Read 按钮,实现语音朗读/听书功能 ——2020-3-8
// v0.0.4 修改介绍文字 -2020-3-7
// v0.0.3 更新按钮样式,统一按钮风格 ——2020-3-7
// v0.0.2 实现基本功能,点击可选,可复制,可划词使用有道翻译 ——2020-3-4
// v0.0.1 添加 Enable Copy 按钮,更改文章样式 ——2020-3-3
(function() {
    "use strict";
    // 用于获取story内文本的父元素全体
    var storyAll = "";
    // 用于获取story文本全体
    var story = "";
    // 用于存储格式化后story文本全体
    var newStory = "";

    if (
        // https://www.lushstories.com/stories/ 网站支持
        window.location.href.indexOf("https://www.lushstories.com/stories/") >
        -1
    ) {
        // 更改网站CSS样式
        $(".blockselect").css({
            "-moz-user-select": "-moz-text",
            "-khtml-user-select": "text",
            "-webkit-user-select": "text",
            "user-select": "text"
        });
        $("#printer").css({
            "-moz-user-select": "-moz-text",
            "-khtml-user-select": "text",
            "-webkit-user-select": "text",
            "user-select": "text"
        });
        $(".onoffswitch").css({
            "-moz-user-select": "-moz-text",
            "-khtml-user-select": "text",
            "-webkit-user-select": "text",
            "user-select": "text"
        });

        // 打包story
        $(".story").wrap("<div id='newCont'></div>");

        // 打包storycontent
        $(".storycontent").wrap("<div id='storycontent'></div>");

        // 添加 Smart Read 按钮
        $("#breadcrumb").append(
            '<a id="btnSmartRead" type = "button" href = "#" title = "Smart Read"  style="color:black;background:#e60022; width:150px;display: run-in ;margin:0 5px; padding: 0 5px;text-align:center ;font: 150% Trebuchet MS; border: 1px solid #e60022;-moz-border-radius: 3px;-webkit-border-radius: 3px;border-radius: 3px; height: 20px;">Smart Read</a>'
        );

        // 获取story文本并格式化;
        // 原.story文本
        storyAll = $(".story").html();

        // 原.storycontent文本
        story = $(".storycontent").html();

        // 解除选择、复制限制,更改默认样式
        $("#sticky-col").remove();
        $(".story").remove();
        $("#newCont").append(storyAll);
        $(".storycontent").remove();
        $("#newCont").css("width", "125%");
    }

    if (
        // https://www.literotica.com/s/ 网站支持
        window.location.href.indexOf("https://www.literotica.com/s/") > -1
    ) {
        // 添加 Smart Read 按钮
        $(".b-breadcrumbs").append(
            '<a id="btnSmartRead" type = "button" href = "#" title = "Smart Read"  style="color:black;background:#e60022; width:150px;display: run-in ;margin:0 5px; padding: 0 5px;text-align:center ;font: 150% Trebuchet MS; border: 1px solid #e60022;-moz-border-radius: 3px;-webkit-border-radius: 3px;border-radius: 3px; height: 20px;">Smart Read</a>'
        );

        // 打包.b-story-body-x div
        $(".b-story-body-x div").wrap("<div id='storycontent'></div>");

        // 获取story文本并格式化;
        // 原#content文本
        storyAll = $("#content").html();

        // 原.b-story-body-x文本
        story = $("#storycontent").html();

        $("#storycontent").empty();

        // 更改默认样式
        $("#w").css({
            width: "70%",
            margin: "0 auto",
            "max-width": "1033px",
            "min-width": "789px"
        });
        $("#b-footer-body").css({
            margin: "0 auto",
            "max-width": "1033px",
            "min-width": "789px",
            width: "70%"
        });
    }

    if (
        // https://read.qidian.com/chapter/ 网站支持
        window.location.href.indexOf("https://read.qidian.com/chapter/") > -1
    ) {
        // 侧边栏添加 听书 按钮
        $(".left-bar-list dl").append(
            '<dd style="background:#e60022;"><a href="javascript:"><i><em class="iconfont">&#xe60f;</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(/\&nbsp;/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);
    if (
        // https://read.qidian.com/chapter/ 网站支持
        window.location.href.indexOf("https://read.qidian.com/chapter/") > -1
    ) {
        $("#storycontent").css({
            "font-size": "1em",
            "line-height": "1.5em"
        });
    } else {
        $("#storycontent").css({
            "font-size": "1.5em",
            "line-height": "1.8em"
        });
    }

    $("#storycontent").css({
        "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)";

    if (
        // https://read.qidian.com/chapter/ 网站支持
        window.location.href.indexOf("https://read.qidian.com/chapter/") > -1
    ) {
        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);

                    if (
                        // https://read.qidian.com/chapter/ 网站支持
                        window.location.href.indexOf(
                            "https://read.qidian.com/chapter/"
                        ) > -1
                    ) {
                        // 不需要了,但是remove的用法可以留下来
                        // $("#storycontent p").removeAttr("data-type");
                        // $("span.review-count").remove();
                        // $("span.content-wrap").removeClass("content-wrap");

                        // 起点网朗读效果,当前朗读段落文字变红
                        $("#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");
                        }

                        // 不需要了,但是方法可以留下来
                        // referenceNode = document
                        //   .getElementById("storycontent")
                        //   .childNodes.item(flag - 1).childNodes.item(1);
                    } else {
                        range.selectNodeContents(referenceNode);
                        selection.removeAllRanges();
                        selection.addRange(range);
                        $("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
                    }
                } else if (flag >= newCountPara) {
                    // 朗读结束
                    // 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;
                    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();
    });
})();