Greasy Fork is available in English.
改自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/[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(r###lt) { let targetElement = document.getElementsByClassName('table-default-right')[0] if (targetElement) { return modifyDom(targetElement, r###lt) } 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, r###lt) 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 r###lt = response.data.r###lts.browse if (r###lt) findTarget(r###lt) 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.r###lts.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() }