// ==UserScript==
// @name tianteng
// @namespace https://greasyfork.org/xmlspy
// @version 0.5.0
// @description 1. 绅士漫画,下拉浏览页面,由原先的一列模式改为两列模式 2. javbus, 添加 "粘贴并搜索" 按钮; 添加 "打开srbt" 链接; 详情页面添加复制车牌号按钮.
// @author xmlspy
// @license MIT
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @include *
// @connect *
// @run-at document-start
// @grant GM_xmlhttpRequest
// @grant GM.setClipboard
// @grant GM.xmlhttpRequest
// @grant unsafeWindow
// @grant GM_log
// @grant GM_info
// ==/UserScript==
(function () {
"use strict";
const enableDebug = true;
let seq = 0;
//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////// 事件拦截
/////////////////////////////////////////////////////////////////////////////////////////////
debug(document.querySelector("title"));
//松鼠症倉庫 地址发布页 https://ahrilist.top/
const ep = EventTarget.prototype;
if (ep.addEventListenerOriginal == null) {
ep.addEventListenerOriginal = ep.addEventListener;
function addEventListenerHook(type, callback, options) {
// if (/ahri8\.top/.test(window.location.href)) {
if (this.className && this.className.includes("apo")) {
debug("松鼠症倉庫-禁止点击广告");
return;
}
// }
this.allListeners = this.allListeners || [];
this.allListeners.push({ type, callback, options });
this.addEventListenerOriginal(type, callback, options);
}
ep.addEventListener = addEventListenerHook;
}
const skrbtDomain = "skrbtqx";
const skrbtHost = skrbtDomain + ".top";
const skrbtUrl = "https://" + skrbtHost;
// execute(()=>{},()=>{});
$(function () {
// execute("松鼠症倉庫", /ahri8\.top/, () => {
// removeEvents(document.querySelectorAll("a.apo"), "click");
// debug("-------------------------------------------------------xxxx");
// });
execute("开源中国", /oschina\.net(\/)?$/, () => {
$("div.headline > div.head-news > div").each(function (index) {
const $this = $(this);
const href = $this.attr("data-href");
const classCss = $this.attr("class");
const title = $this.attr("title");
const html = `<a target="_blank" title="${title}" href="${href}" class="${classCss}">${title}</a>`;
$this.replaceWith(html);
});
});
//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////// 紳士漫畫永久域名: wnacg.com 紳士漫畫永久地址發佈頁: wnacg.date
/////////////////////////////////////////////////////////////////////////////////////////////
execute("紳士漫畫", '$head>title:contains("紳士漫畫")', () => {
if (location.href.includes("photos-slide-aid")) {
const nodeToObserve = document.querySelector("#img_list");
$(nodeToObserve).css({
width: "100%",
display: "flex",
"flex-wrap": "wrap",
"justify-content": "flex-start",
"overflow-x": "hidden",
});
const imgWidth = document.documentElement.clientWidth / 2 - 10;
const imgHeight = document.documentElement.clientHeight - 50;
const observer = new MutationObserver((mutations, observer) => {
$("#img_list>div").css({
flex: "1",
"background-color": "#cacaca",
margin: "0 5px 5px 0",
width: "calc((100% - 10px) / 2)",
"min-width": "calc((100% - 10px) / 2)",
"max-width": "calc((100% - 10px) / 2)",
});
$("#img_list>div>img").on("load", (e) => {
drawImage(e.target, imgWidth, imgHeight);
});
});
observer.observe(nodeToObserve, { childList: true });
}
});
//////////////////////////////////////////////////////////////////////////////////////////////
// 永久域名:https://www.javbus.com 防屏蔽地址:https://www.fanbus.help
// 防屏蔽地址:https://www.javsee.help 防屏蔽地址:https://www.buscdn.help
/////////////////////////////////////////////////////////////////////////////////////////////
execute(
"javbus",
[
'$head>title:contains("JavBus")',
// /genre/hd , /genre/sub
"$body > nav > div > div.navbar-header.mh50 > a > img[alt='JavBus']",
// 论坛
"#toptb.jav-nav",
],
() => {
const searchButton = $(
"button[onclick=\"searchs('search-input')\"]:first"
);
const searchInput = $("#search-input:first");
addPasteAndSearchButton(searchButton, searchInput);
// 调整样式
$(".nav>li>a").attr("style", "padding-left:8px;padding-right:8px;");
//添加skrbt链接
$(".nav-title.nav-inactive:last,ul.nav.navbar-nav:first").append(`
<li class="hidden-md hidden-sm">
<a href="${skrbtUrl}" target="_blank">打开skrbt</a>
</li>
`);
let chePaiNode = document.querySelector(
"body > div.container > div.row.movie > div.col-md-3.info > p:nth-child(1) > span:nth-child(2)"
);
// 明细页面
if (chePaiNode) {
const chePai = chePaiNode.innerText.trim();
const toAppendElement = document.querySelector(
"body > div.container > div.row.movie > div.col-md-3.info > p:nth-child(1)"
);
appendCopyButton(chePai, toAppendElement);
// 删除磁力链接中的onclick事件
setInterval(() => $("#magnet-table td").removeAttr("onclick"), 1000);
}
}
);
//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////// 添加 粘贴&搜索 按钮
/////////////////////////////////////////////////////////////////////////////////////////////
[
// 知乎
{
title: "知乎-添加'粘贴&搜索'按钮",
condition: /zhihu\.com/,
searchButton: '$button[class*="SearchBar-searchButton"]',
searchInput: "#Popover1-toggle",
},
// skrbt 永 久 地 址 ( 务 必 收 藏 ): skrfabu.top skrso.link
{
title: "skrbt-添加'粘贴&搜索'按钮",
condition: `$head>link[rel='shortcut icon'][href*='skrbt']`,
searchButton: `$button.search-btn`,
searchInput: `$input.search-input[name='keyword']`,
},
// 百度
{
title: "百度-添加'粘贴&搜索'按钮",
condition: /baidu\.com/,
searchButton: `#su`,
searchInput: `#kw`,
},
// 必应
// {
// condition: /bing\.com/,
// searchButton: ()=>{
// let inputs = document.querySelectorAll('input');
// return inputs[1];
// },
// searchInput: `#sb_form_q`,
// },
].forEach((v, k) => {
let { title, condition, searchButton, searchInput, callback } = v;
execute(title, condition, () => {
searchButton = evalParam(searchButton);
searchInput = evalParam(searchInput);
addPasteAndSearchButton(searchButton, searchInput, callback);
});
});
});
//////////////////////////////////////////////////////////////////////////////////////////////
/////////////////// 公共方法
/////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* @param {String|Function} param
* 当类型为String时,
* - 如果param是以$开头,则认为这个param为Jquery表达式,会去掉字符$,并返回;
* - 否则,认为param为常规css选择符,会返回document.querySelector(param)
* 当类型为Function时,
* 直接执行此方法,并使用$()包装并返回
*
* @returns {Jquery} 返回Jquery对象
*
* @example
* - evalParam('$div[style="btn"]'); // param为jquery的css selector,返回$('div[style="btn"]')
* - evalParam('#container'); // param为常规的css selector,返回 $(document.querySelecotr('#container'))
* - evalParam(()=>{
* return document.querySelector('#container');
* }); // param为方法,返回$(param())
*
*/
function evalParam(param) {
if ($.type(param) === "string") {
if (param.startsWith("$")) {
return $(param.substring(1));
}
return $(document.querySelector(param));
}
if ($.isFunction(param)) {
return $(param());
}
}
function execute(title, condition, callback) {
if (checkCondition(condition) === true) {
if (callback) {
debug(condition.toString(), title);
callback();
}
}
// if ($.isArray(condition)) {
// for (let c of condition) {
// if (checkCondition(c)) {
// debug(condition.toString(), title);
// callback && callback();
// return;
// }
// }
// }
}
/**
*
* @param {RegExp|Function|Boolean|String} condition
* @returns {Boolean}
*/
function checkCondition(condition) {
if ($.type(condition) === "regexp") {
const href = window.location.href;
return condition.test(href);
}
if ($.isFunction(condition)) {
return condition() === true;
}
if ($.type(condition) === "boolean") {
return condition === true;
}
if ($.type(condition) === "string") {
if (condition.startsWith("$")) {
return $(condition.substring(1)).length > 0;
}
return document.querySelector(condition) != null;
}
if ($.isArray(condition)) {
for (let c of condition) {
if (checkCondition(c)) {
return true;
}
}
}
return false;
}
function addPasteAndSearchButton($searchButton, $searchInput, callback) {
const styleMap = {};
$searchButton[0].computedStyleMap().forEach((value, key) => {
styleMap[key] = value;
});
const $pasteAndSearchButton = $(
`<input type="button" value="粘贴&搜索" id="pasteAndSearch"></input>`
);
$pasteAndSearchButton.css(styleMap);
$searchButton.after($pasteAndSearchButton);
$pasteAndSearchButton.click(() => {
navigator.clipboard.readText().then((clipText) => {
if (clipText != null && $.trim(clipText) != "") {
$searchInput.val($.trim(clipText));
$searchButton.click();
}
});
});
callback && callback($searchButton, $searchInput, $pasteAndSearchButton);
}
function appendCopyButton(chePai, toAppendElement) {
var copyButton = document.createElement("button");
copyButton.innerHTML = "复 制";
copyButton.setAttribute("id", "copyButton");
toAppendElement.appendChild(copyButton);
document.addEventListener("click", (e) => {
if (e.target.getAttribute("id") === "copyButton") {
GM.setClipboard(chePai, "text");
}
});
}
function debug(str, title) {
if (enableDebug) {
if (!str) {
str = "";
}
if (!Array.isArray(str)) {
str = [str];
}
seq++;
console.log(
`%c【tianteng ${GM_info.script.version}】 ${title ? title : "debug"}:`,
"color: yellow;font-size: large;font-weight: bold;background-color: darkblue;",
seq,
...str
);
}
}
/**
* 图片按宽高比例进行自动缩放
* @param ImgObj
* 缩放图片源对象
* @param maxWidth
* 允许缩放的最大宽度
* @param maxHeight
* 允许缩放的最大高度
* @usage
* 调用:<img src="图片" onload="javascript:drawImage(this,300,200)">
*/
function drawImage(ImgObj, maxWidth, maxHeight) {
var image = new Image();
//原图片原始地址(用于获取原图片的真实宽高,当<img>标签指定了宽、高时不受影响)
image.src = ImgObj.src;
// 用于设定图片的宽度和高度
var tempWidth;
var tempHeight;
if (image.width > 0 && image.height > 0) {
//原图片宽高比例 大于 指定的宽高比例,这就说明了原图片的宽度必然 > 高度
if (image.width / image.height >= maxWidth / maxHeight) {
if (image.width > maxWidth) {
tempWidth = maxWidth;
// 按原图片的比例进行缩放
tempHeight = (image.height * maxWidth) / image.width;
} else {
// 按原图片的大小进行缩放
tempWidth = image.width;
tempHeight = image.height;
}
} else {
// 原图片的高度必然 > 宽度
if (image.height > maxHeight) {
tempHeight = maxHeight;
// 按原图片的比例进行缩放
tempWidth = (image.width * maxHeight) / image.height;
} else {
// 按原图片的大小进行缩放
tempWidth = image.width;
tempHeight = image.height;
}
}
// 设置页面图片的宽和高
ImgObj.height = tempHeight;
ImgObj.width = tempWidth;
// 提示图片的原来大小
ImgObj.alt = image.width + "×" + image.height;
}
}
/**
*
* @param {EventTarget|NodeList|Array|jQuery} elements
* @param {undefined|null|String|Array} events
*/
function removeEvents(elements, events) {
if (!elements) return;
if (elements instanceof EventTarget) {
elements = [elements];
}
if (elements instanceof jQuery) {
elements = elements.toArray();
}
if (!events) {
elements.forEach((element) => {
for (let t in element) {
if (t.startsWith("on") && element[t] != null) {
element[t] = null;
console.log("cleanup removed listener from " + element.nodeName, t);
}
}
for (let t of element.allListeners || []) {
element.removeEventListener(t.type, t.callback, t.options);
console.log(
"cleanup removed listener from " + element.nodeName,
t.type
);
}
element.allListeners = [];
});
} else {
if (typeof events === "string") {
events = [events];
}
if (!Array.isArray(events)) {
return;
}
events.forEach((event) => {
const onEvent = "on" + event;
elements.forEach((element) => {
for (let t in element) {
if (t.startsWith(onEvent) && element[t] != null) {
element[t] = null;
console.log(
"cleanup removed listener from " + element.nodeName,
t
);
}
}
// const toRemoved = [];
element.allListeners = element.allListeners || [];
let allListenersNew;
element.allListeners.forEach((t, i) => {
if (t.type === event) {
element.removeEventListener(t.type, t.callback, t.options);
allListenersNew = element.allListeners.slice(i, 1);
// toRemoved.push(i);
console.log(
"cleanup removed listener from " + element.nodeName,
t.type
);
}
});
// toRemoved.forEach((item, index) => {
// element.allListeners = element.allListeners.slice(item, 1);
// });
if (allListenersNew) {
element.allListeners = allListenersNew;
}
});
});
}
}
})();