MCD

Manga Comic Downloader. Shortcut: Alt+Y.

  1. // ==UserScript==
  2. // @name MCD
  3. // @namespace https://lelinhtinh.github.io
  4. // @description Manga Comic Downloader. Shortcut: Alt+Y.
  5. // @version 1.5.1
  6. // @icon https://i.imgur.com/GAM6cCg.png
  7. // @author Zzbaivong
  8. // @license MIT; https://baivong.mit-license.org/license.txt
  9. // @match https://www.kuaikanmanhua.com/*
  10. // @match https://newtoki*.*/webtoon/*
  11. // @match https://manhwa18.net/*
  12. // @match https://manytoon.com/comic/*
  13. // @match https://18comic.org/album/*
  14. // @require https://code.jquery.com/jquery-3.5.1.min.js
  15. // @require https://unpkg.com/jszip@3.1.5/dist/jszip.min.js
  16. // @require https://unpkg.com/file-saver@2.0.2/dist/FileSaver.min.js
  17. // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js?v=a834d46
  18. // @noframes
  19. // @connect *
  20. // @supportURL https://github.com/lelinhtinh/Userscript/issues
  21. // @run-at document-start
  22. // @grant GM_addStyle
  23. // @grant GM_xmlhttpRequest
  24. // @grant GM.xmlHttpRequest
  25. // @grant GM_registerMenuCommand
  26. // ==/UserScript==
  27.  
  28. window._URL = window.URL || window.webkitURL;
  29.  
  30. jQuery(function ($) {
  31. /**
  32. * Output extension
  33. * @type {String} zip
  34. * cbz
  35. *
  36. * Tips: Convert .zip to .cbz
  37. * Windows
  38. * $ ren *.zip *.cbz
  39. * Linux
  40. * $ rename 's/\.zip$/\.cbz/' *.zip
  41. */
  42. var outputExt = 'zip'; // or 'zip'
  43.  
  44. /**
  45. * Multithreading
  46. * @type {Number} [1 -> 32]
  47. */
  48. var threading = 4;
  49.  
  50. /**
  51. * The number of times the download may be attempted.
  52. * @type {Number}
  53. */
  54. var tries = 5;
  55.  
  56. /**
  57. * Image list will be ignored
  58. * @type {Array} url
  59. */
  60. var ignoreList = [];
  61.  
  62. /**
  63. * Keep the original url
  64. * @type {Array} key
  65. */
  66. var keepOriginal = ['kkmh.com'];
  67.  
  68. /**
  69. * HTTP referer
  70. * @param {Object} hostname
  71. */
  72. var referer = {};
  73.  
  74. /* === DO NOT CHANGE === */
  75.  
  76. window.URL = window._URL;
  77.  
  78. function getImageType(arrayBuffer) {
  79. if (!arrayBuffer.byteLength)
  80. return {
  81. mime: null,
  82. ext: null,
  83. };
  84.  
  85. var ext = '',
  86. mime = '',
  87. dv = new DataView(arrayBuffer, 0, 5),
  88. numE1 = dv.getUint8(0, true),
  89. numE2 = dv.getUint8(1, true),
  90. hex = numE1.toString(16) + numE2.toString(16);
  91.  
  92. switch (hex) {
  93. case '8950':
  94. ext = 'png';
  95. mime = 'image/png';
  96. break;
  97. case '4749':
  98. ext = 'gif';
  99. mime = 'image/gif';
  100. break;
  101. case 'ffd8':
  102. ext = 'jpg';
  103. mime = 'image/jpeg';
  104. break;
  105. case '424d':
  106. ext = 'bmp';
  107. mime = 'image/bmp';
  108. break;
  109. case '5249':
  110. ext = 'webp';
  111. mime = 'image/webp';
  112. break;
  113. default:
  114. ext = null;
  115. mime = null;
  116. break;
  117. }
  118.  
  119. return {
  120. mime: mime,
  121. ext: ext,
  122. };
  123. }
  124.  
  125. function noty(txt, status) {
  126. function destroy() {
  127. if (!$noty.length) return;
  128. $noty.fadeOut(300, function () {
  129. $noty.remove();
  130. $noty = [];
  131. });
  132. clearTimeout(notyTimeout);
  133. }
  134.  
  135. function autoHide() {
  136. notyTimeout = setTimeout(function () {
  137. destroy();
  138. }, 2000);
  139. }
  140.  
  141. if (!$noty.length) {
  142. var $wrap = $('<div>', {
  143. id: 'baivong_noty_wrap',
  144. }),
  145. $content = $('<div>', {
  146. id: 'baivong_noty_content',
  147. class: 'baivong_' + status,
  148. html: txt,
  149. }),
  150. $close = $('<div>', {
  151. id: 'baivong_noty_close',
  152. html: '&times;',
  153. });
  154.  
  155. $noty = $wrap.append($content).append($close);
  156. $noty.appendTo('body').fadeIn(300);
  157. } else {
  158. $noty
  159. .find('#baivong_noty_content')
  160. .attr('class', 'baivong_' + status)
  161. .html(txt);
  162.  
  163. $noty.show();
  164. clearTimeout(notyTimeout);
  165. }
  166.  
  167. $noty
  168. .click(function () {
  169. destroy();
  170. })
  171. .hover(
  172. function () {
  173. clearTimeout(notyTimeout);
  174. },
  175. function () {
  176. autoHide();
  177. },
  178. );
  179. if (status !== 'warning' && status !== 'success') autoHide();
  180. }
  181.  
  182. function targetLink(selector) {
  183. return configs.link
  184. .split(/\s*,\s*/)
  185. .map((i) => i + selector)
  186. .join(',');
  187. }
  188.  
  189. function linkError() {
  190. $(targetLink('[href="' + configs.href + '"]')).css({
  191. color: 'red',
  192. textShadow: '0 0 1px red, 0 0 1px red, 0 0 1px red',
  193. });
  194. hasDownloadError = true;
  195. }
  196.  
  197. function linkSuccess() {
  198. var $currLink = $(targetLink('[href="' + configs.href + '"]'));
  199. if (!hasDownloadError)
  200. $currLink.css({
  201. color: 'green',
  202. textShadow: '0 0 1px green, 0 0 1px green, 0 0 1px green',
  203. });
  204. }
  205.  
  206. function beforeleaving(e) {
  207. e.preventDefault();
  208. e.returnValue = '';
  209. }
  210.  
  211. function cancelProgress() {
  212. linkError();
  213. window.removeEventListener('beforeunload', beforeleaving);
  214. }
  215.  
  216. function notyError() {
  217. noty('ERR! Cannot download <strong>' + chapName + '</strong>', 'error');
  218. inProgress = false;
  219. cancelProgress();
  220. }
  221.  
  222. function notyImages() {
  223. noty('ERR! <strong>' + chapName + '</strong> empty data', 'error');
  224. inProgress = false;
  225. cancelProgress();
  226. }
  227.  
  228. function notySuccess(source) {
  229. if (threading < 1) threading = 1;
  230. if (threading > 32) threading = 32;
  231.  
  232. dlImages = source.map(function (url) {
  233. return {
  234. url: url,
  235. attempt: tries,
  236. };
  237. });
  238. dlTotal = dlImages.length;
  239. addZip();
  240.  
  241. noty('Start downloading <strong>' + chapName + '</strong>', 'warning');
  242. window.addEventListener('beforeunload', beforeleaving);
  243. }
  244.  
  245. function notyWait() {
  246. document.title = '[…] ' + tit;
  247.  
  248. noty('<strong>' + chapName + '</strong> is getting ready...', 'warning');
  249.  
  250. dlAll = dlAll.filter(function (l) {
  251. return configs.href.indexOf(l) === -1;
  252. });
  253.  
  254. $(targetLink('[href="' + configs.href + '"]')).css({
  255. color: 'orange',
  256. fontWeight: 'bold',
  257. fontStyle: 'italic',
  258. textDecoration: 'underline',
  259. textShadow: '0 0 1px orange, 0 0 1px orange, 0 0 1px orange',
  260. });
  261. }
  262.  
  263. function dlAllGen() {
  264. dlAll = [];
  265. $(configs.link).each(function (i, el) {
  266. dlAll[i] = $(el).attr('href');
  267. });
  268. if (configs.reverse) dlAll.reverse();
  269. }
  270.  
  271. function notyReady() {
  272. noty('Script is <strong>now ready</strong> to use', 'info');
  273.  
  274. dlAllGen();
  275.  
  276. $doc
  277. .on('click', configs.link, function (e) {
  278. if (!e.ctrlKey && !e.shiftKey) return;
  279.  
  280. e.preventDefault();
  281. var _link = $(this).attr('href');
  282.  
  283. if (e.ctrlKey && e.shiftKey) {
  284. dlAll = dlAll.filter(function (l) {
  285. return _link.indexOf(l) === -1;
  286. });
  287.  
  288. $(targetLink('[href="' + _link + '"]')).css({
  289. color: 'gray',
  290. fontWeight: 'bold',
  291. fontStyle: 'italic',
  292. textDecoration: 'line-through',
  293. textShadow: '0 0 1px gray, 0 0 1px gray, 0 0 1px gray',
  294. });
  295. } else {
  296. if (!inCustom) {
  297. dlAll = [];
  298. inCustom = true;
  299. }
  300.  
  301. dlAll.push(_link);
  302.  
  303. $(targetLink('[href="' + _link + '"]')).css({
  304. color: 'violet',
  305. textDecoration: 'overline',
  306. textShadow: '0 0 1px violet, 0 0 1px violet, 0 0 1px violet',
  307. });
  308. }
  309. })
  310. .on('keyup', function (e) {
  311. if (e.which === 17 || e.which === 16) {
  312. e.preventDefault();
  313.  
  314. if (dlAll.length && inCustom) {
  315. if (e.which === 16) inMerge = true;
  316. downloadAll();
  317. }
  318. }
  319. });
  320. }
  321.  
  322. function downloadAll() {
  323. if (inProgress || inAuto) return;
  324. if (!inCustom && !dlAll.length) dlAllGen();
  325. if (!dlAll.length) return;
  326. inAuto = true;
  327. $(targetLink('[href*="' + dlAll[0] + '"]')).trigger('contextmenu');
  328. }
  329.  
  330. function downloadAllOne() {
  331. inMerge = true;
  332. downloadAll();
  333. }
  334.  
  335. function genFileName() {
  336. chapName = chapName
  337. .replace(/\s+/g, '_')
  338. .replace(/・/g, '·')
  339. .replace(/(^_+|_+$)/, '');
  340. if (hasDownloadError) chapName = '__ERROR__' + chapName;
  341. return chapName;
  342. }
  343.  
  344. function endZip() {
  345. if (!inMerge) {
  346. dlZip = new JSZip();
  347. dlPrevZip = false;
  348. }
  349.  
  350. dlCurrent = 0;
  351. dlFinal = 0;
  352. dlTotal = 0;
  353. dlImages = [];
  354.  
  355. hasDownloadError = false;
  356. inProgress = false;
  357.  
  358. if (inAuto) {
  359. if (dlAll.length) {
  360. $(targetLink('[href*="' + dlAll[0] + '"]')).trigger('contextmenu');
  361. } else {
  362. inAuto = false;
  363. inCustom = false;
  364. }
  365. }
  366. }
  367.  
  368. function genZip() {
  369. noty('Create archive of <strong>' + chapName + '</strong>', 'warning');
  370.  
  371. dlZip
  372. .generateAsync(
  373. {
  374. type: 'blob',
  375. compression: 'STORE',
  376. },
  377. function updateCallback(metadata) {
  378. noty('Zipping <strong>' + metadata.percent.toFixed(2) + '%</strong>', 'warning');
  379. },
  380. )
  381. .then(
  382. function (blob) {
  383. var zipName = genFileName() + '.' + outputExt;
  384.  
  385. if (dlPrevZip) URL.revokeObjectURL(dlPrevZip);
  386. dlPrevZip = blob;
  387.  
  388. noty(
  389. '<a href="' +
  390. URL.createObjectURL(dlPrevZip) +
  391. '" download="' +
  392. zipName +
  393. '"><strong>Click here</strong></a> if not automatically download',
  394. 'success',
  395. );
  396. linkSuccess();
  397.  
  398. window.removeEventListener('beforeunload', beforeleaving);
  399. saveAs(blob, zipName);
  400.  
  401. document.title = '[⇓] ' + tit;
  402. endZip();
  403. },
  404. function () {
  405. noty('ERR! Cannot zip file <strong>' + chapName + '</strong>', 'error');
  406. cancelProgress();
  407.  
  408. document.title = '[x] ' + tit;
  409. endZip();
  410. },
  411. );
  412. }
  413.  
  414. function dlImgError(current, success, error, err, filename) {
  415. if (dlImages[current].attempt <= 0) {
  416. dlFinal++;
  417. error(err, filename);
  418. return;
  419. }
  420.  
  421. setTimeout(function () {
  422. dlImg(current, success, error);
  423. dlImages[current].attempt--;
  424. }, 2000);
  425. }
  426.  
  427. function dlImg(current, success, error) {
  428. var url = dlImages[current].url,
  429. filename = ('0000' + dlCurrent).slice(-4),
  430. urlObj = new URL(url),
  431. urlHost = urlObj.hostname,
  432. headers = {};
  433.  
  434. if (referer[urlHost]) {
  435. headers.referer = referer[urlHost];
  436. headers.origin = referer[urlHost];
  437. } else {
  438. headers.referer = location.origin;
  439. headers.origin = location.origin;
  440. }
  441.  
  442. GM.xmlHttpRequest({
  443. method: 'GET',
  444. url: url,
  445. responseType: 'arraybuffer',
  446. headers: headers,
  447. onload: function (response) {
  448. var imgExt = getImageType(response.response).ext;
  449.  
  450. if (imgExt === 'gif') {
  451. dlFinal++;
  452. next();
  453. return;
  454. }
  455.  
  456. if (
  457. !imgExt ||
  458. response.response.byteLength < 100 ||
  459. (response.statusText !== 'OK' && response.statusText !== '')
  460. ) {
  461. dlImgError(current, success, error, response, filename);
  462. } else {
  463. filename = filename + '.' + imgExt;
  464. dlFinal++;
  465. success(response, filename);
  466. }
  467. },
  468. onerror: function (err) {
  469. dlImgError(current, success, error, err, filename);
  470. },
  471. });
  472. }
  473.  
  474. function next() {
  475. noty('Downloading <strong>' + dlFinal + '/' + dlTotal + '</strong>', 'warning');
  476. if (dlFinal < dlCurrent) return;
  477.  
  478. if (dlFinal < dlTotal) {
  479. addZip();
  480. } else {
  481. if (inMerge) {
  482. if (dlAll.length) {
  483. linkSuccess();
  484. endZip();
  485. } else {
  486. inMerge = false;
  487. genZip();
  488. }
  489. } else {
  490. genZip();
  491. }
  492. }
  493. }
  494.  
  495. function addZip() {
  496. var max = dlCurrent + threading,
  497. path = '';
  498.  
  499. if (max > dlTotal) max = dlTotal;
  500. if (inMerge) path = genFileName() + '/';
  501.  
  502. for (dlCurrent; dlCurrent < max; dlCurrent++) {
  503. dlImg(
  504. dlCurrent,
  505. function (response, filename) {
  506. dlZip.file(path + filename, response.response);
  507.  
  508. next();
  509. },
  510. function (err, filename) {
  511. dlZip.file(path + filename + '_error.txt', err.statusText + '\r\n' + err.finalUrl);
  512.  
  513. noty(err.statusText, 'error');
  514. linkError();
  515.  
  516. next();
  517. },
  518. );
  519. }
  520. }
  521.  
  522. function imageIgnore(url) {
  523. return ignoreList.some(function (v) {
  524. return url.indexOf(v) !== -1;
  525. });
  526. }
  527.  
  528. function decodeUrl(url) {
  529. var parser = new DOMParser(),
  530. dom = parser.parseFromString('<!doctype html><body>' + url, 'text/html');
  531.  
  532. return decodeURIComponent(dom.body.textContent);
  533. }
  534.  
  535. function imageFilter(url) {
  536. url = decodeUrl(url);
  537. url = url.trim();
  538. url = url.replace(/^.+(&|\?)url=/, '');
  539. url = url.replace(/(https?:\/\/)lh(\d)(\.bp\.blogspot\.com)/, '$1$2$3');
  540. url = url.replace(/(https?:\/\/)lh\d\.(googleusercontent|ggpht)\.com/, '$14.bp.blogspot.com');
  541. url = url.replace(/\?.+$/, '');
  542. if (url.indexOf('imgur.com') !== -1) {
  543. url = url.replace(/(\/)(\w{5}|\w{7})(s|b|t|m|l|h)(\.(jpe?g|png|webp))$/, '$1$2$4');
  544. } else if (url.indexOf('blogspot.com') !== -1) {
  545. url = url.replace(/\/([^/]+-)?(Ic42)(-[^/]+)?\//, '/$2/');
  546. url = url.replace(/\/(((s|w|h)\d+|(w|h)\d+-(w|h)\d+))?-?(c|d|g)?\/(?=[^/]+$)/, '/');
  547. url += '?imgmax=16383';
  548. } else {
  549. url = url.replace(/(\?|&).+/, '');
  550. }
  551. url = encodeURI(url);
  552.  
  553. return url;
  554. }
  555.  
  556. function checkImages(images) {
  557. var source = [];
  558.  
  559. if (!images.length) {
  560. notyImages();
  561. } else {
  562. $.each(images, function (i, v) {
  563. v = v.replace(/^[\s\n]+|[\s\n]+$/g, '');
  564.  
  565. var keep = keepOriginal.some(function (key) {
  566. return v.indexOf(key) !== -1;
  567. });
  568. if (keep) {
  569. source.push(v);
  570. return;
  571. }
  572.  
  573. if (imageIgnore(v) || typeof v === 'undefined') return;
  574. if (/[><"']/.test(v)) return;
  575.  
  576. if (
  577. (v.indexOf(location.origin) === 0 || (v.indexOf('/') === 0 && v.indexOf('//') !== 0)) &&
  578. !/^(\.(jpg|png)|webp|jpeg)$/.test(v.slice(-4))
  579. ) {
  580. return;
  581. } else if (v.indexOf('http') !== 0 && v.indexOf('//') !== 0) {
  582. v = location.origin + (v.indexOf('/') === 0 ? '' : '/') + v;
  583. } else if (v.indexOf('http') === 0 || v.indexOf('//') === 0) {
  584. v = imageFilter(v);
  585. } else {
  586. return;
  587. }
  588.  
  589. source.push(v);
  590. });
  591.  
  592. notySuccess(source);
  593. }
  594. }
  595.  
  596. function getImages($contents) {
  597. var images = [];
  598. $contents.each(function (i, v) {
  599. var $img = $(v);
  600. images[i] = !configs.imgSrc
  601. ? $img.data('src') || $img.data('original')
  602. : $img.attr(configs.imgSrc) || $img.attr('src');
  603. });
  604.  
  605. checkImages(images);
  606. }
  607.  
  608. function getContents($source) {
  609. var method = 'find';
  610. if (configs.filter) method = 'filter';
  611.  
  612. var $entry = $source[method](configs.contents).find('img');
  613. if (!$entry.length) {
  614. notyImages();
  615. } else {
  616. getImages($entry);
  617. }
  618. }
  619.  
  620. function cleanSource(response) {
  621. var responseText = response.responseText;
  622. if (configs.imgSrc) return $(responseText);
  623.  
  624. responseText = responseText.replace(/[\s\n]+src[\s\n]*=[\s\n]*/gi, ' data-src=');
  625. responseText = responseText.replace(/^[^<]*/, '');
  626. return $(responseText);
  627. }
  628.  
  629. function rightClickEvent(_this, callback) {
  630. var $this = $(_this),
  631. name = configs.name;
  632.  
  633. configs.href = $this.attr('href');
  634. chapName = $this.text().trim();
  635.  
  636. if (typeof name === 'function') {
  637. chapName = name(_this, chapName);
  638. } else if (typeof name === 'string') {
  639. chapName = $(name).text().trim() + ' ' + chapName;
  640. }
  641.  
  642. notyWait();
  643.  
  644. GM.xmlHttpRequest({
  645. method: 'GET',
  646. url: configs.href,
  647. onload: function (response) {
  648. var $data = cleanSource(response);
  649. if (typeof callback === 'function') {
  650. callback($data);
  651. } else {
  652. getContents($data);
  653. }
  654. },
  655. onerror: function () {
  656. notyError();
  657. },
  658. });
  659. }
  660.  
  661. function oneProgress() {
  662. if (inProgress) {
  663. noty('Only <strong>one chapter</strong> can be downloaded at a time', 'error');
  664. return false;
  665. }
  666. inProgress = true;
  667. return true;
  668. }
  669.  
  670. function getSource(callback) {
  671. var $link = $(configs.link);
  672. if (!$link.length) return;
  673.  
  674. $link.on('contextmenu', function (e) {
  675. e.preventDefault();
  676. hasDownloadError = false;
  677. if (!oneProgress()) return;
  678.  
  679. rightClickEvent(this, callback);
  680. });
  681.  
  682. notyReady();
  683. }
  684.  
  685. /* global __NUXT__ */
  686. function getKuaikanManhua() {
  687. getSource(function ($data) {
  688. $data = $data.filter('script:not([src]):contains("window.__NUXT__")');
  689. if (!$data.length) {
  690. notyImages();
  691. return;
  692. }
  693.  
  694. eval($data.text());
  695.  
  696. if (!__NUXT__) {
  697. notyImages();
  698. return;
  699. }
  700.  
  701. var images = __NUXT__.data[0].comicInfo.comicImages;
  702. images = images.map(function (v) {
  703. return v.url;
  704. });
  705.  
  706. if (!images.length) {
  707. notyImages();
  708. return;
  709. }
  710.  
  711. checkImages(images);
  712. });
  713. }
  714.  
  715. /* global html_data */
  716. function getNewToki69() {
  717. function html_encoder(s) {
  718. var i = 0,
  719. out = '',
  720. l = s.length;
  721. for (; i < l; i += 3) {
  722. out += String.fromCharCode(parseInt(s.substr(i, 2), 16));
  723. }
  724. return out;
  725. }
  726.  
  727. getSource(function ($data) {
  728. var $images = $data.find('img[data-original^="https://"]:not([style])');
  729. if (!$images.length) {
  730. $images = $data.find('script:not([src]):contains("html_data")');
  731. if (!$images.length) {
  732. notyImages();
  733. return;
  734. }
  735.  
  736. $images = $images.text();
  737. $images = /(var\s+html_data[\s\S]+?)(?=(document\.write|[\s\n]+$))/.exec($images);
  738. if (!$images) {
  739. notyImages();
  740. return;
  741. }
  742.  
  743. eval($images[1]);
  744. $images = html_encoder(html_data);
  745. $images = $($images).find('img[data-original^="https://"]:not([style])');
  746. }
  747.  
  748. var images = [];
  749. $images.each(function (i, v) {
  750. var $img = $(v);
  751. images[i] = $img.data('original');
  752. });
  753.  
  754. checkImages(images);
  755. });
  756. }
  757.  
  758. var configsDefault = {
  759. reverse: true,
  760. link: '',
  761. name: '',
  762. contents: '',
  763. imgSrc: '',
  764. filter: false,
  765. init: getSource,
  766. },
  767. configs,
  768. chapName,
  769. $noty = [],
  770. notyTimeout,
  771. domainName = location.host,
  772. tit = document.title,
  773. $doc = $(document),
  774. dlZip = new JSZip(),
  775. dlPrevZip = false,
  776. dlCurrent = 0,
  777. dlFinal = 0,
  778. dlTotal = 0,
  779. dlImages = [],
  780. dlAll = [],
  781. hasDownloadError = false,
  782. inProgress = false,
  783. inAuto = false,
  784. inCustom = false,
  785. inMerge = false;
  786.  
  787. GM_registerMenuCommand('Download All Chapters', downloadAll);
  788. GM_registerMenuCommand('Download All To One File', downloadAllOne);
  789.  
  790. $doc.on('keydown', function (e) {
  791. if (e.which === 89 && e.altKey) {
  792. // Alt+Y
  793. e.preventDefault();
  794. e.shiftKey ? downloadAllOne() : downloadAll();
  795. }
  796. });
  797.  
  798. GM_addStyle(
  799. '#baivong_noty_wrap{display:none;background:#fff;position:fixed;z-index:2147483647;right:20px;top:20px;min-width:150px;max-width:100%;padding:15px 25px;border:1px solid #ddd;border-radius:2px;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 1px 10px rgba(0,0,0,.35);cursor:pointer}#baivong_noty_content{color:#444}#baivong_noty_content strong{font-weight:700}#baivong_noty_content.baivong_info strong{color:#2196f3}#baivong_noty_content.baivong_success strong{color:#4caf50}#baivong_noty_content.baivong_warning strong{color:#ffc107}#baivong_noty_content.baivong_error strong{color:#f44336}#baivong_noty_content strong.centered{display:block;text-align:center}#baivong_noty_close{position:absolute;right:0;top:0;font-size:18px;color:#ddd;height:20px;width:20px;line-height:20px;text-align:center}#baivong_noty_wrap:hover #baivong_noty_close{color:#333}',
  800. );
  801.  
  802. if (/(www\.)?kuaikanmanhua\.com/.test(domainName)) {
  803. configs = {
  804. link: '.title.fl a[href^="/web/comic/"]',
  805. name: 'h3.title',
  806. init: getKuaikanManhua,
  807. };
  808. } else if (/newtoki\d*\.(com|net)/.test(domainName)) {
  809. configs = {
  810. link: '.item-subject',
  811. name: function (_this) {
  812. return (
  813. $('[itemprop="description"] .view-content:first span').text().trim() +
  814. ' ' +
  815. $(_this)
  816. .contents()
  817. .filter(function (i, el) {
  818. return el.nodeType === 3;
  819. })
  820. .text()
  821. .trim()
  822. );
  823. },
  824. init: getNewToki69,
  825. };
  826. } else if (domainName === 'manhwa18.net') {
  827. configs = {
  828. link: '#tab-chapper .chapter',
  829. name: '[itemprop="name"]:last',
  830. contents: '.chapter-content',
  831. imgSrc: 'data-original',
  832. filter: true,
  833. };
  834. } else if (domainName === 'manytoon.com') {
  835. configs = {
  836. link: '.wp-manga-chapter a',
  837. name: function (_this) {
  838. return (
  839. $('.post-title h3')
  840. .contents()
  841. .filter(function (i, el) {
  842. return el.nodeType === 3;
  843. })
  844. .text()
  845. .trim() +
  846. ' ' +
  847. $(_this).text().trim()
  848. );
  849. },
  850. contents: '.reading-content',
  851. };
  852. } else if (domainName === '18comic.org') {
  853. configs = {
  854. link: '.episode:visible a, .dropdown-toggle.reading:visible',
  855. name: function (_this) {
  856. var $this = $(_this),
  857. mangaName = $('.panel-heading [itemprop="name"]:visible').text().trim();
  858. if ($this.hasClass('reading')) return mangaName;
  859. return (
  860. mangaName +
  861. ' ' +
  862. $this
  863. .find('li')
  864. .contents()
  865. .filter(function (i, el) {
  866. return el.nodeType === 3;
  867. })
  868. .text()
  869. .trim()
  870. );
  871. },
  872. contents: '.panel-body',
  873. imgSrc: 'data-original',
  874. };
  875. }
  876.  
  877. if (Array.isArray(configs)) {
  878. var isMobile = /mobi|android|touch|mini/i.test(navigator.userAgent.toLowerCase());
  879. configs = configs[isMobile ? 1 : 0];
  880. }
  881. if (!configs) return;
  882.  
  883. configs = $.extend(configsDefault, configs);
  884. configs.init();
  885. });