Kitsu MangaDex Links

Adds MangaDex links to Kitsu manga pages

// ==UserScript==
// @name         Kitsu MangaDex Links
// @namespace    https://greasyfork.org/users/649
// @version      3.0.7
// @description  Adds MangaDex links to Kitsu manga pages
// @author       Adrien Pyke
// @match        *://kitsu.io/*
// @require      https://gitcdn.link/repo/fuzetsu/userscripts/b38eabf72c20fa3cf7da84ecd2cefe0d4a2116be/wait-for-elements/wait-for-elements.js
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(() => {
  'use strict';

  const SCRIPT_NAME = 'Kitsu MangaDex Links';
  const REGEX = /^https?:\/\/kitsu\.io\/manga\/[^/]+\/?(?:\?.*)?$/u;

  const Util = {
    log(...args) {
      args.unshift(`%c${SCRIPT_NAME}:`, 'font-weight: bold;color: #233c7b;');
      console.log(...args);
    },
    q(query, context = document) {
      return context.querySelector(query);
    },
    qq(query, context = document) {
      return Array.from(context.querySelectorAll(query));
    },
    encodeQuery(query) {
      return encodeURIComponent(query.trim().replace(/\s+/gu, ' '));
    }
  };

  const App = {
    cache: {},
    getMangaDexPage(title, cb) {
      const self = this;
      if (self.cache[title]) {
        Util.log('Loading cached info');
        cb(self.cache[title]);
      } else {
        const url = `https://mangadex.org/quick_search/${Util.encodeQuery(
          title
        )}`;
        Util.log('Searching MangaDex:', url);
        GM_xmlhttpRequest({
          method: 'GET',
          url,
          onload(response) {
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = response.responseText;

            const manga = Util.q('#search_manga .manga_title', tempDiv);
            if (manga) {
              manga.href = `https://mangadex.org${manga.getAttribute('href')}`;
              Util.log('Link:', manga.href);
              self.cache[title] = manga.href;
              cb(manga.href);
            } else {
              Util.log('No results found');
              self.cache[title] = null;
              cb(null);
            }
          },
          onerror() {
            Util.log('Error searching MangaDex');
          }
        });
      }
    }
  };

  waitForUrl(REGEX, () => {
    waitForElems({
      sel: '.media-sidebar',
      stop: true,
      onmatch(node) {
        const title = Util.q('.media--title h3');
        const url = location.href;
        App.getMangaDexPage(title.textContent, manga => {
          const check = Util.q('.where-to-watch-widget');
          if (!manga && check) check.remove();

          if (location.href === url && manga) {
            if (check) {
              const updateLink = Util.q('#mangadex-link');
              updateLink.href = manga;
            } else {
              const section = document.createElement('div');
              section.className = 'where-to-watch-widget';

              const header = document.createElement('span');
              header.className = 'where-to-watch-header';
              const headerText = document.createElement('span');
              headerText.textContent = 'Read Online';
              header.appendChild(headerText);
              section.appendChild(header);

              const listWrap = document.createElement('ul');
              listWrap.className = 'nav';
              const list = document.createElement('li');
              listWrap.appendChild(list);
              section.appendChild(listWrap);

              const link = document.createElement('a');
              link.id = 'mangadex-link';
              link.href = manga;
              link.target = '_blank';
              link.rel = 'noopener noreferrer';
              link.setAttribute('aria-label', 'MangaDex');
              link.className = 'hint--top hint--bounce hint--rounded';
              const img = document.createElement('img');
              img.src = 'https://mangadex.org/images/misc/navbar.svg';
              img.style.verticalAlign = 'text-bottom';
              link.appendChild(img);
              list.appendChild(link);

              node.appendChild(section);
            }
          }
        });
      }
    });
  });
})();