Greasy Fork

Greasy Fork is available in English.

拷贝漫画PC显示评论

改自Byaidu的拷贝漫画增强插件,只保留评论的加载和发送功能,并重做了ui

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         拷贝漫画PC显示评论
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  改自Byaidu的拷贝漫画增强插件,只保留评论的加载和发送功能,并重做了ui
// @author       ljw2487
// @match        *://*.copymanga.com/*
// @match        *://*.copymanga.org/*
// @match        *://*.copymanga.net/*
// @match        *://*.copymanga.info/*
// @match        *://*.copymanga.site/*
// @match        *://*.copymanga.tv/*
// @match        *://copymanga.com/*
// @match        *://copymanga.org/*
// @match        *://copymanga.net/*
// @match        *://copymanga.info/*
// @match        *://copymanga.site/*
// @match        *://copymanga.tv/*
// @license      GNU General Public License v3.0 or later
// @resource     element_css https://unpkg.com/[email protected]/lib/theme-chalk/index.css
// @resource     animate_css https://unpkg.com/[email protected]/animate.min.css
// @require      https://unpkg.com/[email protected]/dist/vue.min.js
// @require      https://unpkg.com/[email protected]/lib/index.js
// @require      https://unpkg.com/[email protected]/dist/axios.min.js
// @require      https://unpkg.com/[email protected]/store.js
// @require      https://unpkg.com/[email protected]/dist/jquery.min.js
// @require      https://unpkg.com/[email protected]/dist/jszip.min.js
// @require      https://unpkg.com/[email protected]/dist/FileSaver.min.js
// @require      https://unpkg.com/[email protected]/crypto-js.js
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-body
// ==/UserScript==

// 更新了在漫画详情页显示已读章节的字样

// retry 拦截器
axios.interceptors.response.use(undefined, (err) => {
  return new Promise((resolve)=>{setTimeout(()=>{resolve()},1000)}).then(() => axios(err.config));
});

function route() {
    console.log('LOAD SUCCESSED', window.document.title)
    // /comic/gengjuesezhuanshengtaiguotoule/chapter/bf86c68c-195a-11ec-943d-00163e0ca5bd
    if (/^\/comic\/.*\/.*$/.test(location.pathname)) comicPage(1)
    else if (/^\/comic\/[^\/]*$/.test(location.pathname)) tablePage(1);
    else if (/^\/h5\/comicContent\/.*\/.*$/.test(location.pathname)) comicPage(0)
    else if (/^\/h5\/details\/comic\/[^\/]*$/.test(location.pathname)) tablePage(0);
}
route()
///////////////////////////////////////////////////////////////////////
async function loadCSS(){
    var element_css, animate_css;
    if (typeof(GM_getResourceText)=='undefined'){
        await axios.get('https://unpkg.com/[email protected]/lib/theme-chalk/index.css')
        .then(function (response) {
            element_css = response.data;
        })
        await axios.get('https://unpkg.com/[email protected]/animate.min.css')
        .then(function (response) {
            animate_css = response.data;
        })
    }else{
        element_css = GM_getResourceText("element_css");
        animate_css = GM_getResourceText("animate_css");
    }
    GM_addStyle(element_css);
    GM_addStyle(animate_css);
}
/////////////////////////////////////////////////////////////////////
async function tablePage(isPC) {
    // loadCSS()
    let comicName
    if (isPC) {
        comicName = window.location.pathname.split('/')[2]
        console.log("isPC=", isPC, comicName)
    }
    else {
        comicName = window.location.pathname.split('/')[4]
        return console.log("isPC=", isPC, comicName, "H5模式自带历史功能所以终止函数")
    }
    let token = await cookieStore.get('token');
    if (!token) return console.log("未登录 -> 所以访问不到历史记录")

    let findTarget = function(result) {
        let targetElement = document.getElementsByClassName('table-default-right')[0]
        if (targetElement) {
            return modifyDom(targetElement, result)
        }
        else {
            console.log('targetElement Not Found')
            // 若未找到target元素的话则进行监听,但按理说请求队列到这一步时target元素已经加载了
            // 创建一个 MutationObserver 实例,监听目标节点的子节点变化
            var observer = new MutationObserver(function(mutationsList) {
                for (var mutation of mutationsList) {
                    if (mutation.type === 'childList' && mutation.addedNodes.length > 0 && mutation.target.className === "upLoop") {
                        // 检查是否有新添加的节点是具有类名为 'table-default-right' 的元素
                        // var addedNodes = Array.from(mutation.addedNodes);
                        // var tableDefaultRightElements = addedNodes.filter(function(node) {
                        //     return node.classList && node.classList.contains('table-default-right');
                        // });
                        // if (tableDefaultRightElements.length > 0) {
                        //     console.log(2, tableDefaultRightElements)
                        // 停止监听
                        //     observer.disconnect();
                        // }
                        // 而当upLoop找到时,target元素相当于是肯定能找到了,所以不需要上面的方法了
                        targetElement = document.getElementsByClassName('table-default-right')[0]
                    }
                }
                modifyDom(targetElement, result)
                observer.disconnect();
            });
            // 选择需要观察变化的节点
            let targetNode = document.getElementsByTagName('main')[0]
            // 配置观察器的选项
            let config = { childList: true, subtree: true };
            // 开始观察
            observer.observe(targetNode, config);
        }
    }

    let modifyDom = function (element, res) {
        let span = document.createElement('span')
        let a = document.createElement('a')
        span.innerText = "已阅读到:"
        if (res) {
            a.href = `/comic/${res.path_word}/chapter/${res.chapter_id}`
            a.target = "_blank"
            a.innerText = res.chapter_name
        }
        else {
            a.href = '#'
            a.innerText = '未阅读'
            a.style.cssText = 'pointer-events: none; color: gray; text-decoration: none;'
        }
        element.insertBefore(a, element.firstChild);
        element.insertBefore(span, element.firstChild);
    }

    await axios.get('https://api.mangacopy.com/api/v3/comic2/' + comicName + '/query?platform=1&_update=true',{
        headers: {'authorization': 'Token ' + token.value}
    }).then(function (response) {
        console.log('res', response)
        let result = response.data.results.browse
        if (result) findTarget(result)
        else findTarget(false)
    }).catch(function (err) {
        console.log('err', err)
    });
}



async function comicPage(isPC) {
    loadCSS()
    var comic, chapter, htmlBody, newDiv, newStyle
    if (isPC) comic = window.location.pathname.split('/')[2]
    else comic = window.location.pathname.split('/')[3]
    chapter = window.location.pathname.split('/')[4]
    console.log('Comic:', comic,'|| Chapter:', chapter)
    //
    htmlBody = document.getElementsByTagName('body')[0]
    console.log(htmlBody)
    newDiv = document.createElement('div')
    newDiv.innerHTML = `
    <div id="app" class="sideComment">
      <el-button
        @click="switchCommentButton"
        class="showComment"
        :style=" showComment ? 'background-color: rgba(0, 0, 0, 0.8) !important;' : ''"
      >
        查看评论({{comment_count}})
      </el-button>
      <transition name="slide">
        <div v-if="showComment" :key="elementKey">
          <el-input
            v-model="comment_input"
            placeholder="吐槽"
            style="width: 300px; margin: 20px"
            class="commentCreate"
            @keyup.enter.native="send_comment"
            @focus="is_input=1"
            @blur="is_input=0"
          >
            <el-button slot="append" type="primary" @click="send_comment" class="commentSend">
              发表
            </el-button>
          </el-input>
          <ul style="margin-top:0;" class="commentList">
            <template v-for="(item, index) in comment_data">
              <li style="display:inline-block;">
                <span class="comment" v-bind:index="index">{{item.user_name}} : {{item.comment}}</span>
              </li>
            </template>
          <ul>
        </div>
      </transition>
    </div>`
    newStyle = document.createElement('style')
    newStyle.innerHTML = `
    <style>
      button { border: none; }
      button:active { border: none; }
      button:focus { outline: none; }
      .el-button { border: none; }

      .slide-enter-active,
      .slide-leave-active {
        transition: transform 0.5s;
      }

      .slide-enter {
        transform: translateX(100%);
      }

      .slide-leave-to {
        transform: translateX(100%);
      }
      .sideComment {
        position: fixed;
        right: 15px;
        top: 10%;
        bottom: 10%;
        text-align: right;
        color: white;
        z-index:100;
      }
      .showComment {
        position: relative;
        right: 0px;
        border-radius: 999px;
        color: #777777 !important;
        background-color: rgba(0, 0, 0, 0.3) !important;
        outline: none;
      }
      .showComment:hover {
        color: #b3b3b3 !important;
        background-color: rgba(0, 0, 0, 0.7) !important;
      }
      .commentCreate {
        position: relative !important;
        margin: 15px 0 !important;
        border-radius: 999px !important;
      }
      .el-input__inner::placeholder {
        color: #777777 !important;
      }
      .el-input__inner {
        padding-right: 70px;
        color: #b3b3b3 !important;
        border-radius: 999px !important;
        border: none !important;
        background-color: rgba(0, 0, 0, 0.3) !important;
      }
      .el-input__inner:focus {
        border: none !important;
        color: #b3b3b3 !important;
        background-color: rgba(0, 0, 0, 0.8) !important;
      }
      .el-input-group__append {
        position: absolute !important;
        top: 10px;
        right: 30px;
        border: none !important;
        background-color: rgba(0, 0, 0, 0) !important;
      }
      .commentSend {
        color: #777777 !important;
        border-radius: 999px !important;
      }
      .commentSend:hover {
        color: #b3b3b3 !important;
      }
      .commentList {
        position: absolute;
        display: flex;
        flex-direction: column;
        top: 112px;
        bottom: 0;
        margin: 0;
        padding: 0;
        overflow: auto;
        list-style: none;
      }
      .commentList::-webkit-scrollbar {
        display: none;
      }
      .commentList:hover > li {
        color: rgba(179, 179, 179, 1);
        background-color: rgba(0, 0, 0, 0.9);
        transition: all 0.3s ease-in-out;
      }
      .commentList > li {
        box-sizing: border-box;
        display: inline-block;
        margin-bottom: 15px;
        width: 300px;
        padding: 12px 20px;
        text-align: left;
        word-wrap: break-word;
        color: rgba(179, 179, 179, 0.4);
        border-radius: 22px;
        border: none;
        background-color: rgba(0, 0, 0, 0.4);
        transition: all 0.3s ease-in-out;
      }
      .new-comic-size-1 {
        max-width: 100% !important;
        min-width: 100% !important;
      }
      .container-fluid {
        padding-right: 0px;
        padding-left: 0px;
      }
      .comicContent-footer-txt {
        width: 140px !important;
      }
    </style>`
    // 添加评论相关元素
    htmlBody.appendChild(newDiv)
    htmlBody.appendChild(newStyle)
    // 调整图片展示宽度
    // let containerBox = document.getElementsByClassName('container-fluid')[0]
    // containerBox.style.cssText = ' padding-right: 0; padding-left: 0; '
    let container = document.getElementsByClassName('container')[0]
    let content = document.getElementsByClassName('comicContent-list')[0]
    if(window.innerWidth < 1240) {
        container.classList.add('new-comic-size-1')
        content.classList.add('new-comic-size-1')
    }
    // 调整高清显示
    // let list = content.getElementsByTagName('li')
    // console.log(list[0])
    // for (let i of list ){
    //     console.log(i);
    // }
    // 初始化数据
    let showComment = store.get('commentButtonState') == true
    // vue
    const app = new Vue({
        el: '#app',
        data: {
            comment_data: [], // 评论数据源
            comment_input: '',
            comment_count: 0,
            showComment: showComment,
            elementKey: 0,
            windowWidth: null
        },
        watch: {
            windowWidth (newVal, oldVal) {
                if(newVal <= 1240 && oldVal > 1240) {
                    container.classList.add('new-comic-size-1')
                    content.classList.add('new-comic-size-1')
                }
                if(newVal > 1240 && oldVal <= 1240) {
                    container.classList.remove('new-comic-size-1')
                    content.classList.remove('new-comic-size-1')
                }
            }
        },
        mounted () {
            const debounce = (fn, delay) => {
                let timer
                return function() {
                    if(timer) {
                        clearTimeout(timer)
                    }
                    timer = setTimeout(() => {
                        fn()
                    }, delay)
                }
            }
            const cancelDebounce = debounce(this.showWindowWidth, 50)
            window.addEventListener('resize', cancelDebounce)
        },
        destoryed() {
            window.removeEventListener('resize', cancelDebounce)
        },
        methods: {
            send_comment: async function () {
                let token = await cookieStore.get('token');
                await axios.post(
                    'https://api.mangacopy.com/api/v3/member/roast',
                    'chapter_id=' + chapter + '&roast=' + this.comment_input + '&_update=true',
                    {
                    headers: {
                        'authorization': 'Token ' + token.value
                    }
                }).then(function (response) {
                    app.comment_input = '';
                    console.log('评论成功:', response.data.message)
                });
                await this.load_comment();
            },
            load_comment: async function () {
                await axios.get('https://api.mangacopy.com/api/v3/roasts?chapter_id=' + chapter + '&limit=100&offset=0&_update=true')
                .then(function (response) {
                    let list = response.data.results.list
                    app.comment_data = list
                    app.comment_count = list.length
                    // 控制台展示评论
                    // console.log('↓↓↓↓评论列表↓↓↓↓')
                    // for (var i = 0; i < list.length; i++) {
                    //     console.log(list[i].user_name, ' : ', list[i].comment)
                    // }
                    // console.log('↑↑↑↑评论列表↑↑↑↑')
                    // console.log('评论请发送:app.send_comment(评论内容)')
                })
            },
            switchCommentButton() {
                let buttonState = !app.showComment
                this.showComment = buttonState
                // GM_setValue("commentButtonState", buttonState)
                store.set('commentButtonState', this.showComment)
            },
            showWindowWidth() {
                let windowWidth = window.innerWidth
                app.windowWidth = windowWidth
            }
        }
    });
    app.load_comment()
}