拷贝漫画PC显示评论

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

// ==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/element-ui@2.15.0/lib/theme-chalk/index.css
// @resource     animate_css https://unpkg.com/animate.css@4.1.1/animate.min.css
// @require      https://unpkg.com/vue@2.6.12/dist/vue.min.js
// @require      https://unpkg.com/element-ui@2.15.0/lib/index.js
// @require      https://unpkg.com/axios@0.27.2/dist/axios.min.js
// @require      https://unpkg.com/store.js@1.0.4/store.js
// @require      https://unpkg.com/jquery@3.5.1/dist/jquery.min.js
// @require      https://unpkg.com/jszip@3.1.5/dist/jszip.min.js
// @require      https://unpkg.com/file-saver@2.0.5/dist/FileSaver.min.js
// @require      https://unpkg.com/crypto-js@4.1.1/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/element-ui@2.15.0/lib/theme-chalk/index.css')
        .then(function (response) {
            element_css = response.data;
        })
        await axios.get('https://unpkg.com/animate.css@4.1.1/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()
}