// ==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();
}
}
};
});
})();
})();