Moegirlpedia Preview Fix

Fix Moegirlpedia Mouse Hover Preview

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        Moegirlpedia Preview Fix
// @name:zh-CN  萌娘百科预览修复
// @namespace   https://greasyfork.org/zh-CN/users/163820-ysc3839
// @description Fix Moegirlpedia Mouse Hover Preview
// @description:zh-CN 修复萌娘百科鼠标悬停预览
// @license     MIT
// @grant       GM_xmlhttpRequest
// @grant       unsafeWindow
// @version     3
// @author      ysc3839
// @match       *://zh.moegirl.org.cn/*
// @run-at      document-idle
// ==/UserScript==

(function () {
  let first_load = true;
  (function f() {
    const $ = unsafeWindow.jQuery;
    if (!$) {
      if (first_load) {
        first_load = false;
        setTimeout(f, 100);
      }
      return;
    }

    const API_PATH = '/api.php';
    // https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
    const FORBIDDEN_HEADERS = new Set([
      'Accept-Charset',
      'Accept-Encoding',
      'Access-Control-Request-Headers',
      'Access-Control-Request-Method',
      'Connection',
      'Content-Length',
      'Cookie',
      'Date',
      'DNT',
      'Expect',
      'Host',
      'Keep-Alive',
      'Origin',
      'Permissions-Policy',
      'Referer',
      'TE',
      'Trailer',
      'Transfer-Encoding',
      'Upgrade',
      'Via',
    ]);

    $.ajaxTransport('json', function (options) {
      if (options.crossDomain || !options.async) return;
      if (!options.url.startsWith(API_PATH)) return;
      const u = new URL(options.url, document.baseURI);
      if (u.pathname !== API_PATH || u.host !== location.host) return;

      const s = u.searchParams;
      const prop = s.get('prop');
      if (prop) {
        const p = new Set(prop.split('|'));
        p.delete('revisions');
        s.set('prop', Array.from(p).join('|'));
      }
      s.delete('rvprop');
      s.delete('uselang');

      let callback;
      return {
        send: function (headers, complete) {
          // Apply custom fields if provided
          if (options.xhrFields) {
            console.warn('options.xhrFields unsupported', options.xhrFields);
          }

          // X-Requested-With header
          // For cross-domain requests, seeing as conditions for a preflight are
          // akin to a jigsaw puzzle, we simply never set it to be sure.
          // (it can always be set on a per-request basis or even using ajaxSetup)
          // For same-domain requests, won't change header if already provided.
          if (!options.crossDomain && !headers['X-Requested-With']) {
            headers['X-Requested-With'] = 'XMLHttpRequest';
          }

          // Remove forbidden headers
          for (let i in headers) {
            if (i.startsWith('Proxy-') || i.startsWith('Sec-') || FORBIDDEN_HEADERS.has(i)) {
              delete headers[i];
            }
          }

          let controller;

          // Callback
          callback = function (type) {
            return function (xhr) {
              if (callback) {
                callback = null;

                if (type === 'abort') {
                  if (controller)
                    controller.abort();
                } else if (type === 'error') {
                  complete(
                    xhr.status,
                    xhr.statusText
                  );
                } else {
                  complete(
                    (xhr.status === 0) ? 200 : xhr.status,
                    xhr.statusText,
                    { text: xhr.responseText },
                    xhr.responseHeaders
                  );
                }
              }
            };
          };

          // Listen to events
          const onload = callback();
          const onerror = callback('error');

          // Create the abort callback
          callback = callback('abort');

          try {
            // Do send the request (this may raise an exception)
            controller = GM_xmlhttpRequest({
              url: u.href,
              method: options.type,
              user: options.username,
              password: options.password,
              overrideMimeType: options.mimeType,
              headers,
              data: options.hasContent && options.data || null,
              anonymous: true, // Sending cookie may cause server return api error
              onload,
              onabort: onerror,
              onerror,
              ontimeout: onerror,
            });
          } catch (e) {
            // trac-14683: Only rethrow if this hasn't been notified as an error yet
            if (callback) {
              throw e;
            }
          }
        },

        abort: function () {
          if (callback) {
            callback();
          }
        }
      };
    });
  })();
})();