☄️拷贝漫画增强☄️

拷贝漫画去广告🚫、加速访问🚀、并排布局📖、图片高度自适应↕️、辅助翻页↔️、页码显示⏱、侧边目录栏📑、暗夜模式🌙、章节评论💬

Install this script?
Author's suggested script

You may also like ☄️动漫之家增强☄️.

Install this script
  1. // ==UserScript==
  2. // @name ☄️拷贝漫画增强☄️
  3. // @namespace http://tampermonkey.net/
  4. // @version 11.4
  5. // @description 拷贝漫画去广告🚫、加速访问🚀、并排布局📖、图片高度自适应↕️、辅助翻页↔️、页码显示⏱、侧边目录栏📑、暗夜模式🌙、章节评论💬
  6. // @author Byaidu
  7. // @match *://*.copymanga.com/*
  8. // @match *://*.copymanga.org/*
  9. // @match *://*.copymanga.net/*
  10. // @match *://*.copymanga.info/*
  11. // @match *://*.copymanga.site/*
  12. // @match *://*.copymanga.tv/*
  13. // @match *://*.mangacopy.com/*
  14. // @match *://copymanga.com/*
  15. // @match *://copymanga.org/*
  16. // @match *://copymanga.net/*
  17. // @match *://copymanga.info/*
  18. // @match *://copymanga.site/*
  19. // @match *://copymanga.tv/*
  20. // @match *://mangacopy.com/*
  21. // @license GNU General Public License v3.0 or later
  22. // @resource element_css https://unpkg.com/element-ui@2.15.13/lib/theme-chalk/index.css
  23. // @resource animate_css https://unpkg.com/animate.css@4.1.1/animate.min.css
  24. // @require https://unpkg.com/vue@2.7.14/dist/vue.min.js
  25. // @require https://unpkg.com/element-ui@2.15.13/lib/index.js
  26. // @require https://unpkg.com/axios@1.3.6/dist/axios.min.js
  27. // @require https://unpkg.com/store.js@1.0.4/store.js
  28. // @require https://unpkg.com/jquery@3.6.4/dist/jquery.min.js
  29. // @require https://unpkg.com/jszip@3.1.5/dist/jszip.min.js
  30. // @require https://unpkg.com/file-saver@2.0.5/dist/FileSaver.min.js
  31. // @require https://unpkg.com/crypto-js@4.1.1/crypto-js.js
  32. // @grant GM_addStyle
  33. // @grant GM_getResourceText
  34. // @grant GM_xmlhttpRequest
  35. // @run-at document-start
  36. // ==/UserScript==
  37.  
  38. var largeMode = 1;
  39. var enableDownload = 0;
  40.  
  41. // retry
  42. axios.interceptors.response.use(undefined, (err) => {
  43. return new Promise((resolve) => { setTimeout(() => { resolve() }, 1000) }).then(() => axios(err.config));
  44. });
  45.  
  46. function route() {
  47. if (document.getElementsByClassName('ban').length) banPage();
  48. else if (/^\/comic\/.*\/.*$/.test(location.pathname)) comicPage(1);
  49. else if (/^\/comic\/[^\/]*$/.test(location.pathname)) tablePage(1);
  50. else if (/^\/$/.test(location.pathname)) homePage();
  51. else if (/^\/h5\/details\/comic\/[^\/]*$/.test(location.pathname)) tablePage(0);
  52. else if (/^\/h5\/comicContent\/.*$/.test(location.pathname)) comicPage(0);
  53. }
  54.  
  55. route();
  56.  
  57. if (/^\/h5.*$/.test(location.pathname)) {
  58. let previousUrl = location.href;
  59. const observer = new MutationObserver(function (mutations) {
  60. if (location.href !== previousUrl) {
  61. previousUrl = location.href;
  62. route();
  63. }
  64. });
  65. const config = { subtree: true, childList: true };
  66. observer.observe(document, config);
  67. }
  68.  
  69. async function loadCSS() {
  70. var element_css, animate_css;
  71. if (typeof (GM_getResourceText) == 'undefined') {
  72. await axios.get('https://unpkg.com/element-ui@2.15.0/lib/theme-chalk/index.css')
  73. .then(function (response) {
  74. element_css = response.data;
  75. })
  76. await axios.get('https://unpkg.com/animate.css@4.1.1/animate.min.css')
  77. .then(function (response) {
  78. animate_css = response.data;
  79. })
  80. } else {
  81. element_css = GM_getResourceText("element_css");
  82. animate_css = GM_getResourceText("animate_css");
  83. }
  84. GM_addStyle(element_css);
  85. GM_addStyle(animate_css);
  86. }
  87.  
  88. function banPage() {
  89. window.stop();
  90.  
  91. document.querySelectorAll('main')[0].innerHTML = `
  92. <p class="ban"><img class="banIcon" src="https://hi.est152.com/static/websitefree/jpg/logo.png" alt=""></p>
  93. <p class="textItem">来自 ☄️拷贝漫画增强☄️ 的消息:</p>
  94. <p class="textItem">请安装 <a href="https://chrome.google.com/webstore/detail/user-agent-switcher-and-m/bhchdcejhohfmigjafbampogmaanbfkg">User-Agent Switcher and Manager</a> 插件并切换浏览器 UA</p>
  95. `
  96. }
  97.  
  98. function homePage() {
  99. GM_addStyle('.header-jum {display:none;}');
  100. }
  101.  
  102. function makeRequest(url,isPC) {
  103. if (isPC) {
  104. // axios
  105. return axios.get(url)
  106. } else {
  107. // gm绕过ua
  108. return new Promise((resolve, reject) => {
  109. GM_xmlhttpRequest({
  110. url: url,
  111. headers:{'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0'},
  112. responseType:'json',
  113. onload: function(response) {
  114. resolve({data:response.response});
  115. },
  116. onerror: function(error) {
  117. reject(error);
  118. }
  119. });
  120. });
  121. }
  122. }
  123.  
  124. function apiChapters(comic,isPC) {
  125. return makeRequest('https://www.mangacopy.com/comicdetail/' + comic + '/chapters',isPC)
  126. .then((response) => {
  127. let iv = response.data.results.substring(0, 16),
  128. cipher = response.data.results.substring(16),
  129. result = JSON.parse(CryptoJS.AES.decrypt(
  130. CryptoJS.enc.Base64.stringify(
  131. CryptoJS.enc.Hex.parse(cipher)
  132. ),
  133. CryptoJS.enc.Utf8.parse('xxxmanga.woo.key'),
  134. {
  135. 'iv': CryptoJS.enc.Utf8.parse(iv),
  136. 'mode': CryptoJS.mode.CBC,
  137. 'padding': CryptoJS.pad.Pkcs7
  138. }
  139. ).toString(CryptoJS.enc.Utf8));
  140. let type_map = new Map();
  141. result.build.type.forEach((v, index) => {
  142. type_map.set(v.id, v.name);
  143. });
  144. result.groups.default.chapters.forEach((v, index) => {
  145. v.index = index;
  146. let type_name = type_map.get(v.type);
  147. v.name = "【" + type_name + "】" + v.name;
  148. v.type_name = type_name;
  149. });
  150. return result;
  151. })
  152. }
  153.  
  154. function tablePage(isPC) {
  155. loadCSS();
  156. var collect, save,
  157. comic,
  158. content_comic = [],
  159. app;
  160. if (isPC)
  161. comic = window.location.pathname.split('/')[2];
  162. else
  163. comic = window.location.pathname.split('/')[4];
  164. $(() => {
  165. if (enableDownload) {
  166. GM_addStyle('.comicParticulars-botton:nth-of-type(4) {background: lightskyblue;}');
  167. if (isPC)
  168. collect = document.getElementsByClassName('collect')[0];
  169. else
  170. collect = document.getElementsByTagName('button')[2];
  171. save = collect.cloneNode();
  172. save.innerHTML = '批量下载';
  173. save.onclick = saveComic;
  174. collect.after(save);
  175. var app_html = document.createElement("div");
  176. app_html.innerHTML = `
  177. <div id="app_save">
  178. 下载范围:
  179. <el-select v-model="begin" placeholder="起始话" size="mini" style="width:150px;">
  180. <el-option
  181. v-for="item in content_comic"
  182. :key="item.index"
  183. :label="item.name"
  184. :value="item.index">
  185. </el-option>
  186. </el-select>
  187. <el-select v-model="end" placeholder="终止话" size="mini" style="width:150px;margin-right:20px;">
  188. <el-option
  189. v-for="item in content_comic"
  190. :key="item.index"
  191. :label="item.name"
  192. :value="item.index">
  193. </el-option>
  194. </el-select>
  195. </div>
  196. `
  197. collect.after(app_html);
  198. if (isPC)
  199. document.getElementById('app_save').setAttribute('style', 'margin-top:18px;');
  200. else
  201. document.getElementsByClassName('detailsTextContentItem')[0].setAttribute('style', 'flex-wrap:wrap;');
  202. app = new Vue({
  203. el: '#app_save',
  204. data: {
  205. content_comic: [],
  206. begin: '',
  207. end: '',
  208. },
  209. })
  210. GM_addStyle('.el-input__suffix {display:none !important;}');
  211. apiChapters(comic,isPC)
  212. .then(function (response) {
  213. content_comic = response.groups.default.chapters;
  214. app.content_comic = content_comic;
  215. app.begin = content_comic.at(0).index;
  216. app.end = content_comic.at(-1).index;
  217. }).catch(function (error) {
  218. save.innerHTML = '下载失败';
  219. })
  220. }
  221. cookieStore.get('token')
  222. .then(function (token) {
  223. if (token) {
  224. axios.get('https://api.mangacopy.com/api/v3/comic2/query/' + comic, {
  225. headers: {
  226. 'authorization': 'Token ' + token.value
  227. }
  228. }).then(function (response) {
  229. if (response.data.results.browse != null) {
  230. var read = document.getElementsByClassName('comicParticulars-botton')[0];
  231. read.innerHTML = response.data.results.browse.chapter_name;
  232. read.href = 'https://mangacopy.com/comic/' + comic + '/chapter/' + response.data.results.browse.chapter_uuid;
  233. GM_addStyle('.comicParticulars-botton {max-width:80px; overflow: hidden;text-overflow: ellipsis; white-space: nowrap;}');
  234. }
  235. });
  236. }
  237. })
  238. })
  239. let sleep = function (time) {
  240. return new Promise((resolve) => {
  241. setTimeout(resolve, time)
  242. })
  243. }
  244. async function saveComic() {
  245. let zip = new JSZip();
  246. let task_cnt = 0;
  247. let date_created_chapter_map = new Map();
  248. let comic_name = null;
  249. for (let idx = app.begin; idx <= app.end; idx++) {
  250. let c = content_comic[idx];
  251. task_cnt++;
  252. save.innerHTML = task_cnt + '/' + (app.end - app.begin + 1);
  253. await axios.get('https://api.mangacopy.com/api/v3/comic/' + comic + '/chapter/' + c.id)
  254. .then(async function (response) {
  255. let task_chapter = [];
  256. var chpt_index = Number(response.data.results.chapter.index) + 1
  257. let dir_chpt_name = response.data.results.chapter.name
  258. if (c.type_name !== undefined) {
  259. dir_chpt_name = "【" + chpt_index + "】【" + c.type_name + "】" + dir_chpt_name;
  260. }
  261. let dir_comic = zip.folder(response.data.results.comic.name)
  262. let content = response.data.results.chapter.contents,
  263. size = content.length,
  264. dict = {};
  265. comic_name = response.data.results.comic.name;
  266. let date_created_chapter = new Date(response.data.results.chapter.datetime_created);
  267. if (idx != app.begin && date_created_chapter.getTime() <= date_created_chapter_map.get(idx - 1).getTime()) {
  268. date_created_chapter = new Date(date_created_chapter_map.get(idx - 1).getTime() + 2000);
  269. }
  270. date_created_chapter_map.set(idx, date_created_chapter);
  271. dir_comic.file(dir_chpt_name, null, {
  272. dir: true,
  273. date: date_created_chapter,
  274. });
  275. let dir_chpt = dir_comic.folder(dir_chpt_name);
  276. for (let i = 0; i < size; i++) {
  277. (() => {
  278. let self = i;
  279. let img_url = content[i].url;
  280. if (largeMode) img_url = img_url.replace('c800x.jpg', 'c1500x.jpg');
  281. task_chapter.push(axios.get(img_url, { responseType: 'arraybuffer' })
  282. .then(function (response) {
  283. let date_created_file = new Date(date_created_chapter.getTime() + 2000 * self);
  284. dir_chpt.file((self + 1) + '.jpg', response.data, { date: date_created_file });
  285. }).catch(function (error) {
  286. save.innerHTML = '下载失败';
  287. }))
  288. })()
  289. }
  290. await axios.all(task_chapter);
  291. }).catch(function (error) {
  292. save.innerHTML = '下载失败';
  293. })
  294. await sleep(1000);
  295. }
  296. zip.generateAsync({ type: "blob" }, function (metadata) {
  297. save.innerHTML = metadata.percent.toFixed(0) + '%';
  298. }).then(function (blob) {
  299. saveAs(blob, comic_name + ".zip");
  300. save.innerHTML = '下载完成';
  301. })
  302. }
  303. }
  304.  
  305. async function comicPage(isPC) {
  306. // 停止加载原生网页
  307. window.stop();
  308.  
  309. // 解析 URL
  310. if (isPC) {
  311. comic = window.location.pathname.split('/')[2];
  312. chapter = window.location.pathname.split('/')[4];
  313. } else {
  314. comic = window.location.pathname.split('/')[3];
  315. chapter = window.location.pathname.split('/')[4];
  316. }
  317.  
  318. // 加载 HTML
  319. document.querySelectorAll('html')[0].innerHTML = `
  320. <head></head>
  321. <body>
  322. <div id="app">
  323. <div @mouseleave="drawer=false">
  324. <div @mouseover="drawer=true" style="top:0px;left:0px;height:100vh;width:10vw;position: fixed;"></div>
  325. <el-drawer
  326. id="sidebar"
  327. :size="size"
  328. :modal="modal"
  329. :visible="drawer"
  330. :with-header="false"
  331. :direction="direction"
  332. @open="handleOpen">
  333. <el-menu
  334. background-color="transparent"
  335. text-color="#fff"
  336. active-text-color="#ffd04b"
  337. @select="handleSelect">
  338. <template v-for="(item, index) in sidebar_data">
  339. <el-menu-item v-bind:index="index">{{item.title}}</el-menu-item>
  340. </template>
  341. </el-menu>
  342. </el-drawer>
  343. </div>
  344. <div id="matrix">
  345. <template v-for="(item, index) in comic_data">
  346. <img class="inner_img" v-bind:src="item.url">
  347. </template>
  348. </div>
  349. <ul style="margin-top:20px;">
  350. <template v-for="(item, index) in comment_data">
  351. <li style="display:inline-block;">
  352. <p class="comment" v-bind:index="index">{{item.user_name}} : {{item.comment}}</p>
  353. </li>
  354. </template>
  355. <ul>
  356. <el-input v-model="comment_input" placeholder="吐槽" style="width:300px;margin:20px;" @keyup.enter.native="send_comment" @focus="is_input=1" @blur="is_input=0">
  357. <el-button slot="append" type="primary" @click="send_comment">发表</el-button>
  358. </el-input>
  359. <ul style="margin-bottom:20px;">
  360. <el-button type="primary" @click="prev_chapter">上一章</el-button>
  361. <el-button type="primary" @click="next_chapter">下一章</el-button>
  362. </ul>
  363. <div id="info" @mouseover="show=1" @mouseleave="show=0">
  364. <transition name="custom-classes-transition" enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
  365. <template v-if="show"><div id="info_scroll" class="info_item" @click="switch_scroll" style="cursor:pointer;">{{message_scroll}}</div></template></transition>
  366. <transition name="custom-classes-transition" enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
  367. <template v-if="show"><div id="info_page" class="info_item" @click="switch_page" style="cursor:pointer;">{{message_page}}</div></template></transition>
  368. <transition name="custom-classes-transition" enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
  369. <template v-if="show"><div id="info_skip" class="info_item" @click="switch_skip" style="cursor:pointer;">{{message_skip}}</div></template></transition>
  370. <transition name="custom-classes-transition" enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
  371. <template v-if="show"><div id="info_switch" class="info_item" @click="switch_night" style="cursor:pointer;">{{message_switch}}</div></template></transition>
  372. <transition name="custom-classes-transition" enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
  373. <template v-if="show"><div id="info_full" class="info_item" @click="switch_full" style="cursor:pointer;">{{message_full}}</div></template></transition>
  374. <transition name="custom-classes-transition" enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
  375. <template v-if="show"><div id="info_home" class="info_item" @click="switch_home" style="cursor:pointer;">{{message_home}}</div></template></transition>
  376. <template><div id="info_count" class="info_item">{{message_count}}</div></template>
  377. </div>
  378. </div>
  379. <style>
  380. body {
  381. text-align: center;
  382. font-size: 12px;
  383. line-height: normal;
  384. background: #edecea;
  385. margin: unset !important;
  386. }
  387. body.dark {
  388. background: #212121;
  389. }
  390. ::-webkit-scrollbar {
  391. width: 4px;height: 0px;
  392. }
  393. ::-webkit-scrollbar-thumb {
  394. background-color: rgb(48,48,48);
  395. border-radius: 2px;
  396. }
  397. #matrix {
  398. display: grid;
  399. justify-items: center;
  400. justify-content: center;
  401. overflow-x: hidden;
  402. user-select: none;
  403. margin: 0 auto;
  404. max-width: 190vh;
  405. }
  406. .page #matrix {
  407. display: flex;
  408. flex-direction: row-reverse;
  409. flex-wrap: wrap;
  410. }
  411. .scroll #matrix {
  412. display: flex;
  413. flex-direction: row-reverse;
  414. flex-wrap: wrap;
  415. justify-content: unset;
  416. flex-wrap: unset;
  417. overflow-x: scroll;
  418. }
  419. .scroll .inner_img {
  420. height: 100vh;
  421. object-fit: contain;
  422. }
  423. .page .inner_img {
  424. height: 100vh;
  425. object-fit: contain;
  426. }
  427. .inner_img {
  428. max-width: 100%;
  429. }
  430. .el-menu {
  431. border-right: 0px;
  432. }
  433. .el-drawer__wrapper {
  434. width: 20%;
  435. }
  436. .el-drawer {
  437. background: transparent;
  438. }
  439. .el-drawer__body {
  440. background: rgba(0,0,0,.8);
  441. overflow-y: auto
  442. }
  443. #info {
  444. bottom: 2%;
  445. right: 2%;
  446. padding: 5px 5px;
  447. background: rgba(48,48,48,.7);
  448. position: fixed;
  449. color: rgba(255,255,255,.7);
  450. border-radius: 3px;
  451. }
  452. .info_item {
  453. padding:5px 0px;
  454. width:120px;
  455. }
  456. .skip .blank {
  457. display:none;
  458. }
  459. .dark .comment {
  460. color: rgba(255,255,255,.7);
  461. }
  462. .dark .el-input__inner {
  463. background-color:rgba(255,255,255,.05);
  464. color: rgba(255,255,255,.7);
  465. }
  466. .dark .el-input-group__append {
  467. background-color:rgba(255,255,255,.2);
  468. color: rgba(255,255,255,.7);
  469. }
  470. .dark .el-button {
  471. background-color:rgba(255,255,255,.2);
  472. color: rgba(255,255,255,.7);
  473. border: 1px solid #DCDFE6;
  474. }
  475. .comment {
  476. padding: 10px 13px;
  477. margin:5px 5px;
  478. font-size:14px;
  479. background-color:rgb(127,127,127,.15);
  480. }
  481. ul {
  482. margin: 5px;
  483. padding: unset;
  484. }
  485. </style>
  486. </body>
  487. `;
  488.  
  489. loadCSS();
  490.  
  491. // 加载 LocalStorage
  492. let dark = store.get('dark');
  493. let skip = store.get('skip');
  494. let page = store.get('page');
  495. let scroll = store.get('scroll');
  496. if (dark == undefined)
  497. dark = true;
  498. if (skip == undefined)
  499. skip = true;
  500. if (page == undefined)
  501. page = true;
  502. if (scroll == undefined)
  503. scroll = false;
  504. if (dark) {
  505. document.body.classList.add('dark');
  506. }
  507. if (skip) {
  508. document.body.classList.add('skip');
  509. }
  510. if (page) {
  511. document.body.classList.add('page');
  512. }
  513. if (scroll) {
  514. document.body.classList.add('scroll');
  515. }
  516.  
  517. // 加载 Vue
  518. var app = new Vue({
  519. el: '#app',
  520. data: {
  521. drawer: false,
  522. size: '100%',
  523. modal: false,
  524. direction: 'ltr',
  525. sidebar_data: [], // 章节数据源
  526. comic_data: [], // 图片数据源
  527. comment_data: [], // 评论数据源
  528. comment_input: '',
  529. is_input: 0,
  530. cur_lock: 0,
  531. cur_id: 0,
  532. cur_ch: 0,
  533. dark: dark,
  534. page: page,
  535. skip: skip,
  536. scroll: scroll,
  537. show: 0,
  538. full: 0,
  539. },
  540. computed: {
  541. message_home: function () {
  542. return '⬅️返回目录';
  543. },
  544. message_full: function () {
  545. return this.full ? '↩️退出全屏' : '↕️进入全屏';
  546. },
  547. message_switch: function () {
  548. return this.dark ? '☀️日间模式' : '🌙夜间模式';
  549. },
  550. message_page: function () {
  551. return this.page ? '1️⃣单页排布' : '2️⃣双页排布';
  552. },
  553. message_skip: function () {
  554. return this.skip ? '📑添加空页' : '📄移除空页';
  555. },
  556. message_scroll: function () {
  557. return this.scroll ? '⏬纵向滚动' : '⏪横向滚动';
  558. },
  559. message_count: function () {
  560. return (this.skip ? (this.cur_id <= 1 ? this.cur_id : this.cur_id - 1) : this.cur_id) + '/' + (this.comic_data.length + 1 - this.skip);
  561. }
  562. },
  563. methods: {
  564. handleSelect(key) {
  565. location.href = this.sidebar_data[key].href;
  566. },
  567. handleOpen() {
  568. setTimeout(() => {
  569. let sidebar = document.getElementsByClassName('el-drawer__body')[0],
  570. ch_list = sidebar.children[0].children;
  571. sidebar.scrollTop = ch_list[Math.max(app.cur_ch - 2, 0)].offsetTop;
  572. }, 0);
  573. },
  574. switch_home: function () {
  575. location.href = 'https://mangacopy.com/comic/' + comic;
  576. },
  577. switch_full: function () {
  578. this.full = !this.full;
  579. if (this.full) {
  580. document.documentElement.requestFullscreen();
  581. } else {
  582. document.exitFullscreen();
  583. }
  584. },
  585. switch_night: function () {
  586. this.dark = !this.dark;
  587. store.set('dark', this.dark);
  588. document.body.classList.toggle('dark');
  589. },
  590. switch_skip: function () {
  591. this.skip = !this.skip;
  592. store.set('skip', this.skip);
  593. document.body.classList.toggle('skip');
  594. },
  595. switch_page: function () {
  596. this.page = !this.page;
  597. store.set('page', this.page);
  598. document.body.classList.toggle('page');
  599. },
  600. switch_scroll: function () {
  601. this.scroll = !this.scroll;
  602. store.set('scroll', this.scroll);
  603. document.body.classList.toggle('scroll');
  604. },
  605. send_comment: async function () {
  606. let token = await cookieStore.get('token');
  607. await axios.post('https://api.mangacopy.com/api/v3/member/roast', 'chapter_id=' + chapter + '&roast=' + this.comment_input + '&_update=true', {
  608. headers: {
  609. 'authorization': 'Token ' + token.value
  610. }
  611. }).then(function (response) {
  612. app.comment_input = response.data.message;
  613. });
  614. await this.load_comment();
  615. },
  616. load_comment: async function () {
  617. await axios.get('https://api.mangacopy.com/api/v3/roasts?chapter_id=' + chapter + '&limit=100&offset=0&_update=true')
  618. .then(function (response) {
  619. app.comment_data = response.data.results.list;
  620. })
  621. },
  622. prev_chapter: function () {
  623. location.href = app.sidebar_data[app.cur_ch - 1].href;
  624. },
  625. next_chapter: function () {
  626. location.href = app.sidebar_data[app.cur_ch + 1].href;
  627. },
  628. }
  629. });
  630.  
  631. // 加载图片
  632. makeRequest('https://api.mangacopy.com/api/v3/comic/' + comic + '/chapter/' + chapter,isPC)
  633. .then(function (response) {
  634. document.title = response.data.results.comic.name + ' - ' + response.data.results.chapter.name;
  635. var content = response.data.results.chapter.contents,
  636. matrix = document.getElementById('matrix'),
  637. size = content.length,
  638. dict = {};
  639. for (var i = 0; i < size; i++) {
  640. var img_url = content[i].url;
  641. if (largeMode) img_url = img_url.replace('c800x.jpg', 'c1500x.jpg');
  642. app.comic_data.push({
  643. url: img_url
  644. })
  645. }
  646. // TODO
  647. setTimeout(() => {
  648. let $blank = $('.inner_img:eq(0)').clone();
  649. $blank.addClass('blank');
  650. $blank.css('filter', 'brightness(0) invert(1)');
  651. $('#matrix').prepend($blank);
  652. }, 0);
  653. })
  654.  
  655. // 加载章节
  656. apiChapters(comic,isPC)
  657. .then(function (response) {
  658. // var content = response.groups.default.chapters;
  659. var content = Object.values(response.groups).flatMap(obj => obj.chapters);
  660. content.forEach((i,index) => {
  661. if (location.href.indexOf(i.id) >= 0) {
  662. app.cur_ch = index;
  663. GM_addStyle('.el-menu>li:nth-child(' + (index + 1) + '){background:rgba(255,165,0,.5) !important}');
  664. }
  665. app.sidebar_data.push({
  666. title: i.name,
  667. href: 'https://mangacopy.com/comic/' + comic + '/chapter/' + i.id
  668. })
  669. })
  670. })
  671.  
  672. // 加载评论
  673. app.load_comment();
  674.  
  675. //上下方向键滚动页面,左右方向键切换章节
  676. function scrollUp() {
  677. let img_list = document.querySelectorAll('.inner_img'),
  678. first_img = img_list[app.skip ? 1 : 0],
  679. last_img = img_list[img_list.length - 1];
  680. if (app.cur_id == 0) return;
  681. var id = img_list.length + 1;
  682. for (var i = (app.skip ? 1 : 0) + 1; i <= Math.min(app.cur_id, img_list.length); i++) {
  683. if (((app.cur_lock && app.cur_id >= 1 && app.cur_id <= img_list.length) ? img_list[app.cur_id - 1].offsetTop : pageYOffset) < img_list[i - 1].offsetTop + img_list[i - 1].offsetHeight + 5) {
  684. id = i;
  685. break;
  686. }
  687. }
  688. if (((app.cur_lock && app.cur_id >= 1 && app.cur_id <= img_list.length) ? img_list[app.cur_id - 1].offsetTop : pageYOffset) < first_img.offsetTop + 5) {
  689. id = 0;
  690. }
  691. app.cur_lock++;
  692. app.cur_id = id;
  693. setTimeout(function () { app.cur_lock--; }, 500);
  694. // TODO
  695. $("html").stop();
  696. if (id == 0) {
  697. $("html").animate({ scrollTop: 0 }, 500);
  698. } else {
  699. $("html").animate({ scrollTop: img_list[id - 1].offsetTop }, 500);
  700. }
  701. }
  702. function scrollDown() {
  703. let img_list = document.querySelectorAll('.inner_img'),
  704. first_img = img_list[app.skip ? 1 : 0],
  705. last_img = img_list[img_list.length - 1];
  706. if (app.cur_id == img_list.length + 1) return;
  707. var id = img_list.length + 1;
  708. for (var i = Math.max(app.cur_id, (app.skip ? 1 : 0) + 1); i <= img_list.length; i++) {
  709. if (((app.cur_lock && app.cur_id >= 1 && app.cur_id <= img_list.length) ? img_list[app.cur_id - 1].offsetTop : pageYOffset) < img_list[i - 1].offsetTop - 5) {
  710. id = i;
  711. break;
  712. }
  713. }
  714. app.cur_lock++;
  715. app.cur_id = id;
  716. setTimeout(function () { app.cur_lock--; }, 500);
  717. // TODO
  718. $("html").stop();
  719. if (id == img_list.length + 1) {
  720. $("html").animate({ scrollTop: last_img.offsetTop + last_img.offsetHeight }, 500);
  721. } else {
  722. $("html").animate({ scrollTop: img_list[id - 1].offsetTop }, 500);
  723. }
  724. }
  725. document.getElementById('matrix').onclick = function (event) {
  726. if (event.clientY > $(window).height() / 2) {
  727. if (app.page) scrollDown();
  728. } else {
  729. if (app.page) scrollUp();
  730. }
  731. }
  732. document.body.onkeydown = function (event) {
  733. if (!app.is_input) {
  734. if (event.keyCode == 38) {
  735. if (app.page) scrollUp();
  736. } else if (event.keyCode == 40) {
  737. if (app.page) scrollDown();
  738. }
  739. if (!app.scroll) {
  740. if (event.keyCode == 37) {
  741. app.prev_chapter();
  742. } else if (event.keyCode == 39) {
  743. app.next_chapter();
  744. }
  745. }
  746. if (event.keyCode == 13) {
  747. app.switch_full();
  748. } else if (event.keyCode == 8) {
  749. location.href = 'https://mangacopy.com/comic/' + comic;
  750. }
  751. }
  752. }
  753.  
  754. // 加载当前页码
  755. function getID() {
  756. let cur_id = 0,
  757. img_list = document.querySelectorAll('.inner_img'),
  758. first_img = img_list[app.skip ? 1 : 0],
  759. last_img = img_list[img_list.length - 1];
  760. if (img_list.length > 0) {
  761. img_list.forEach((i, index) => {
  762. if (pageYOffset > i.offsetTop - 5 && pageYOffset < i.offsetTop + i.offsetHeight - 5 && cur_id == 0) {
  763. cur_id = index + 1;
  764. }
  765. });
  766. if (pageYOffset > last_img.offsetTop + last_img.offsetHeight - 5)
  767. cur_id = img_list.length + 1;
  768. if (app.cur_lock == 0) app.cur_id = cur_id;
  769. }
  770. }
  771. setInterval(getID, 100);
  772. window.addEventListener('mousewheel', getID);
  773. window.addEventListener('error', event => {
  774. let target = event.target || event.srcElement;
  775. let isElementTarget = target instanceof HTMLImageElement;
  776. if (!isElementTarget) return false;
  777. let url = target.src;
  778. setTimeout(() => {
  779. target.src = url;
  780. }, Math.floor(Math.random() * 2000) + 1);
  781. }, true);
  782. }