您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
给你的百度和360搜索一个类似Mac/Gnome的浏览体验
当前为
// ==UserScript== // @name BeautyToMac // @namespace http://tampermonkey.net/ // @version 0.1.4 // @icon data:image/png;base64,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 // @description 给你的百度和360搜索一个类似Mac/Gnome的浏览体验 // @author CloundMark // @match *://www.baidu.com/* // @match *://baidu.com/* // @match *://www.so.com/* // @match *://so.com/* // @license GPLv3 // @run-at document-start // @grant GM_xmlhttpRequest // @grant GM.xmlHttpRequest // @connect * // ==/UserScript== (function () { /* 全局配置项 start*/ // 网站开关 true启用,false关闭; const baidu = true; const sou360 = true; const userGlobalConfigs = { isDebug:false, // 0、全局背景; // used is true will be get picture, false not! // bg代表背景图像,你可以从网络上引用自己喜欢的,used 为true表示启用当前分类或具体的图像,false弃用; // 内部的name建议您始终加上(但也不是必须),主要是便于哪天不需要的时候可以快速定位; // 请仿照示例添加背景 bg: { used: true, sources: { 'cartoon': { used: true, sources: [ { used: true, name: '可爱猫', url: 'https://i0.hdslb.com/bfs/article/dec6e7a1969748f2b4462c688367d772798d4134.png@942w_668h_progressive.png' }, { used: true, name: '粉红猫', url: 'https://i0.hdslb.com/bfs/article/f45de07102e3fe83331d68dd455336ab0b2a08b3.png@942w_566h_progressive.png' }, { used: true, name: '两仪式', url: 'https://i0.hdslb.com/bfs/article/c7cb8fecdd0cd7d34e89319c4b9eeb4ab543cfd0.jpg@1320w_740h.jpg' }, { used: true, name: '远坂凛', url: 'https://img9.51tietu.net/pic/2019-091402/jr354qpj03ujr354qpj03u.jpg' }, { used: true, name: '红猫', url: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F011e9ec28bb2ef90550a090d2121296ee2cfe88233f56-lV1t8X_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1661358344&t=a4aeb02bf2d960bed1a219c17de13543' }, { used: true, name: '蓝狗', url: 'https://i0.hdslb.com/bfs/article/78c032adb6df5717fc4ae465982a9d2096984223.jpg@942w_531h_progressive.jpg' }, { used: true, name: '两仪式2', url: 'https://img2.baidu.com/it/u=4177750439,794883634&fm=253&fmt=auto&app=138&f=JPEG?w=935&h=500' }, { used: true, name: 'saber', url: 'https://i0.hdslb.com/bfs/article/49c217c9ec1e9cd707a228a509df0e7d57e0dd6e.jpg@1320w_740h.jpg' }, { used: true, name: '雅尔贝德', url: 'https://i0.hdslb.com/bfs/article/38f4427201389a1fe54c12d9a62ca57681e06b3a.jpg@1320w_740h.webp' }, { used: true, name: 'saber2', url: 'https://i0.hdslb.com/bfs/article/0fa36a51ebd26d1d2a5621385910d9b35cdc681a.jpg@942w_590h_progressive.jpg' }, { used: true, name: '雅尔贝德2', url: 'https://i0.hdslb.com/bfs/article/e7cfb2d9768b707f16685a83d7126db4787042b2.jpg@942w_668h_progressive.webp' }, { used: true, name: '白猫', url: 'https://i0.hdslb.com/bfs/article/64f062d70fdba96b39dd346ab9d33dc5bf77c17f.jpg@942w_498h_progressive.webp' }, { used: true, name: '贞子', url: 'https://i0.hdslb.com/bfs/article/87f6a5c7b25307c4cc7bd86acca8ed51dec25dcc.jpg@942w_666h_progressive.webp' }, { used: true, name: '大鱼', url: 'https://i0.hdslb.com/bfs/article/73b003ea1c04fb3d1ed736773b3f752a8f3acfc5.jpg@942w_596h_progressive.webp' }, { used: true, name: '空蓝', url: 'https://i0.hdslb.com/bfs/article/86bed00f68bbff662e202aa91215336bb3dd251e.jpg@942w_587h_progressive.webp' }, { used: true, name: '樱', url: 'https://i0.hdslb.com/bfs/article/fa372e5e0a70b630b5b7eb9b4a0d1125a83535ce.png' }, { used: true, name: '薇尔莉特', url: 'https://i0.hdslb.com/bfs/article/59af6572e5ea374e434a0d0cbb78c5a83e159939.jpg@942w_531h_progressive.webp' }, { used: true, name: '紫', url: 'https://img1.baidu.com/it/u=2725650397,3822860203&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800' }, { used: true, name: '伊莉雅', url: 'https://i0.hdslb.com/bfs/article/59b79113f3a8be33ae75009c862a4bb47fc6d87a.jpg@942w_531h_progressive.webp' }, { used: true, name: '薇尔莉特2', url: 'https://i0.hdslb.com/bfs/article/0a5f6397857f8f840e9c4bb6a7b52b9623aefcd7.jpg@942w_590h_progressive.webp' }, { used: true, name: '薇尔莉特3', url: 'https://i0.hdslb.com/bfs/article/ec68b813ca3a56a1e23190f9e8a255feb1d2d5e7.jpg@1320w_740h.webp' }, { used: false, name: '蒂法', url: 'https://i0.hdslb.com/bfs/article/watermark/dce44704fb2b9853a970e0f2745ea15d64ac8716.jpg' }, { used: true, name: '薇尔莉特3', url: 'https://i0.hdslb.com/bfs/article/7085568797e6a1923056990f71888fe9706d644b.jpg@1320w_740h.webp' }, { used: true, name: '伊莉雅2', url: 'https://img0.baidu.com/it/u=3169921451,1554186398&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800' }, { used: true, name: '希丝缇娜', url: 'https://i0.hdslb.com/bfs/article/65da66f0bd9e888ad572c9ab9100e4b741f2019d.jpg@942w_530h_progressive.webp' }, // 引自B站 https://www.bilibili.com/read/cv6710298?from=search { used: true, name: '深', url: 'https://i0.hdslb.com/bfs/article/5c40299551a8dc24fe8c55016283206b370132aa.jpg@942w_545h_progressive.webp' }, // 引自B站 https://www.bilibili.com/read/cv17176820?from=search 原画师:杉87 { used: true, name: '晚霞', url: 'https://i0.hdslb.com/bfs/article/61e693800a1f2dbab634bbb30524b151523d09fa.jpg@942w_531h_progressive.webp' }, // 引自B站 https://www.bilibili.com/read/cv17176820?from=search 原画师:Teardrops { used: true, name: '望', url: 'https://i0.hdslb.com/bfs/article/7ff810942db808887d96ee65cb0f469bb68eb9aa.jpg@942w_531h_progressive.webp' }, // 原画师 ohara_tometa(小原トメ太) { used: true, name: 'Bunnies', url: 'https://iknow-pic.cdn.bcebos.com/c2fdfc039245d6888182ff59a5c27d1ed31b244e' }, { used: true, name: '艾莉丝', url: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp2.itc.cn%2Fimages01%2F20210309%2Ff14fae9f68c443f1835e002513fe34fd.jpeg&refer=http%3A%2F%2Fp2.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1670083703&t=5b75cd70ffbb1e67877a30ae22c5d140' }, { used: true, name: '紫幻', url: 'https://pic2.zhimg.com/v2-49315dbd98922e638d923d7062980529_r.jpg' }, { used: true, name: '路紫', url: 'https://pic1.zhimg.com/80/v2-c88f661054c710e45db23afa98b3b204_720w.webp' }, { used: true, name: '', url: '' }, ] }, 'natures': { used: true, sources: [ { used: true, name: '', url: '' }, { used: true, name: '', url: '' }, ] }, 'Girls': { used: true, sources: [ { used: false, name: '', url: '' }, ] }, } }, // 1、背景切换方式 bgSwitchMode: 'multi',// 可选枚举值:['once','multi']; // 2、设置背景切换的最短时间间隔,单位s;小于3s将设为3s; duration: 5, // 3、启用低速网络模式,非false以开启;建议始终选择low;一下网速依次越差; // 可选枚举值:[false,'low','middle','heigh','infinity']; netThrottlMode: 'low', // 4、枚举值,设定中央内容版块的主题色 // 可选枚举值:[各种颜色的16进制表示];false禁用中央内容版块色彩(当前全局为豆沙绿护眼色#C7EDCC50)并启用默认背景 // 你可以在下方给每个站点分别配置 centerColor: '#C7EDCC80', // 5、请求任意站点;如果你的图像来自可能被拒的其他网站服务提供者,那么启用此选项可以绕过浏览器限制 requestAllWebsite:true, }; class debugLog{ static get Priority(){ return ['log','summary','warn','error','critical','dir']; } constructor(isDebuger,debugPriority){ this.isDebuger = isDebuger; // 要使用全局配置 this.debugPriority = debugPriority||this.constructor.Priority; this.log = this.wrapperLogPriority(); this.summary = this.wrapperLogPriority('summary','color:cyan'); this.warn = this.wrapperLogPriority('warn','color:yellow'); this.error = this.wrapperLogPriority('error','color:red'); this.critical = this.wrapperLogPriority('critical','color:black;background-color:red;'); this.dir = this.wrapperObjPriority('dir'); Object.freeze(this); Object.freeze(this.debugPriority); } wrapperLogPriority(actionPriority,style){ let priority = actionPriority||'log'; // log,即info消息级别 let outpuMethod = console.log; return (...rest)=>{ if(this.isDebuger && this.debugPriority.includes(priority)){ outpuMethod(`%c${rest}`,style||'color:lime'); } } } wrapperObjPriority(actionPriority){ let priority = actionPriority; let outpuMethod = console.dir; return (...rest)=>{ if(this.isDebuger && this.debugPriority.includes(priority)){ outpuMethod(...rest); } } } } const logger = new debugLog(userGlobalConfigs.isDebug); const URL = window.URL ||window.webkitURL || window.mozURL; const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; // eslint-disable-next-line no-undef const GMxmlHttpRequest = typeof GM_xmlhttpRequest==='function'?GM_xmlhttpRequest:GM.xmlHttpRequest; /* 基类 */ class Base { constructor() { this.priority = 'Base'; } setBFInterval(fn, interval, ...rest) { let id = setInterval((...rest) => { fn.call(this, ...rest); }, interval, ...rest); fn.call(this, ...rest); return id; } findElement(selector, timoout = 5, fq = false, intvl = 100, signal = null) { if (!(selector && typeof selector === 'string')) throw TypeError('selector must be non-zero string.') return new Promise((success, reject) => { let timer4min, timer4big, mt, timer4HTML; // 启用用户中断 if (signal && Object.prototype.toString.call(signal).slice(8, -1) === 'AbortSignal') { signal.addEventListenner('abort', () => { logger.warn('异步查询,用户中断查找:', selector); reject(signal.reason); clearInterval(timer4min); clearTimeout(timer4big); mt && mt.disconnect(); }); } // 开启快查询 if (fq) { timer4min = this.setBFInterval(() => { let result = document.querySelectorAll(selector); if (result.length) { success(result); clearInterval(timer4min); clearTimeout(timer4big); mt && mt.disconnect(); } }, intvl); } //开启超时策略 timer4big = setTimeout(() => { reject(`TimeOut[${selector}]`); clearInterval(timer4min); mt && mt.disconnect(); }, timoout * 1000); //开启DOM监测 mt = new MutationObserver((_, obs) => { let result = document.querySelectorAll(selector); if (result.length) { success(result); clearInterval(timer4min); clearTimeout(timer4big); obs.disconnect(); } }); if(document&&document.documentElement&&document.documentElement.tagName.toUpperCase()==='HTML'){ mt.observe(document.documentElement, { childList: true, subtree: true }); }else{ //说明网速极度不好 timer4HTML = this.setBFInterval(function(){ if(document&&document.documentElement&&document.documentElement.tagName.toUpperCase()==='HTML'){ mt.observe(document.documentElement, { childList: true, subtree: true }); clearInterval(timer4HTML); } },15) } }); } sendGMXHR(url, method, loadCB, errorCB, timeoutCB, timeout) { GMxmlHttpRequest({ url, method: method || 'get', timeout, responseType: 'blob', onload: loadCB, onerror: errorCB, ontimeout: timeoutCB }); } getHeadersFromGMXHR(headersString=''){ // 油猴响应头转换成对象 let head = new Headers(); let items=headersString.split('\n').filter(o=>o); for( let item of items){ item = item.trim(); let [k,v] = item.split(':'); head.append(k.trim(),v.trim()); } return head; } shuffle(arr=[]){ if(arr.length<2) return arr; let l = arr.length; while(l){ let j = Math.floor(Math.random()*l--); [arr[l],arr[j]]=[arr[j],arr[l]]; } return arr; } openCaches(cacheName){ if(window.caches&&window.isSecureContext){ return caches.open(cacheName) }else{ logger.log('Current context is not secure'); return Promise.resolve(); } } setCaches(cacheName,url,response){ this.openCaches(cacheName).then(myCache=>{ myCache&&myCache.keys().then(urls=>{ let urlsStr=urls.map(item=>item.url); if(!urlsStr.includes(url)){ myCache.put(url,response) .then( ()=>logger.log(`Cache set ok.-->[${url}]`) ).catch( (e)=>logger.error(`Cache set is wrong.->info[${e.message}]-->[${url}]`) ) } }) }) } getCache(cacheName,url,options){ return new Promise((success)=>{ this.openCaches(cacheName).then(myCache=>{ if(myCache){ myCache.match(url,options||{ignoreVary:true}) .then(rsp=>success(rsp)) .catch(err=>{ logger.error(`Get cache failed. Info-->${err}\nfor ${url}`); success(false); }) }else{ success(null); } }) }) } addCaches(cacheName,url,failCallBack,initSuccessCallBack){ // 无则添加,有则忽略 this.openCaches(cacheName).then(myCache=>{ myCache&&myCache.keys().then(urls=>{ let urlsStr=urls.map(item=>item.url); if(urlsStr.includes(url)){ logger.log(`Add cache, but url exists, ignore. [${url}]`); }else{ myCache.add(url) .then(()=>{ logger.log(`First add cache success, [${url}]`); if(initSuccessCallBack) logger.log('InitSuccessCallBack add cache Callback exist. Try to callback.'); initSuccessCallBack&&initSuccessCallBack(); }) .catch(err=>{ logger.error(`Add cache failed. Info-->${err}\nfor ${url}`); if(failCallBack) logger.log('Add cache Callback exist. Try to callback.'); failCallBack&&failCallBack(); }) } }) }) } updateCaches(cacheName,url,failCallBack){ this.openCaches(cacheName).then(myCache=>{ myCache&&myCache.delete(url,{ignoreVary:true}).then(()=>{ myCache.add(url).catch(err=>{ logger.error(`Update cache failed. Info-->${err}\nfor ${url}`); if(failCallBack) logger.log('Update cache Callback exist. Try to callback.') failCallBack&&failCallBack(); }) },()=>{ logger.error('Update cache something is wrong, get fault.') }) }) } deleteCaches(cacheName,url,options){ this.openCaches(cacheName).then(myCache=>{ myCache&&myCache.keys().then(urls=>{ if(urls.includes(url)){ myCache.delete(url,options||{ignoreVary:true}).then( ok=>logger.log(`Delete cache success. Info-->${ok}\nfor ${url}`) ).catch( err=>logger.error(`Delete cache failed. Info-->${err}\nfor ${url}`) ) }else{ logger.warn(`No url -->[${url}],ignore.`); } }) }) } getAllCachesUrl(cacheName){ return new Promise(success=>{ this.openCaches(cacheName).then(myCache=>{ if(myCache){ myCache.keys().then(urls=>{ success(urls.map(item=>item.url)); }) }else{ success(null); } }) }) } hasCaches(cacheName,url){ return new Promise(success=>{ this.openCaches(cacheName).then(myCache=>{ if(myCache){ myCache.keys().then(urls=>{ let urlsStr = urls.map(item=>item.url); if(urlsStr.includes(url)){ success(true); }else{ success(false); } }) }else{ success(null); } }) }) } run() { throw TypeError('ChildrenClass must have this method!'); } } class NoAdBase extends Base { constructor(selector) { super(); this.selector = selector; this.allElements = []; } } /* 反广告 */ class AntiAdSoft extends NoAdBase { constructor(selector) { super(selector); this.allElements = [...document.querySelectorAll(this.selector)]; Reflect.defineProperty(this, 'priority', { value: 'NoAdSoft', configurable: false, enumerable: true, writable: false }); } run() { for (let i = 0; i < this.allElements.length; i++) { this.allElements[i].style.cssText += 'display:none !important'; } } } class AntiAdHard extends NoAdBase { constructor(selector) { super(selector); this.allElements = null; Reflect.defineProperty(this, 'priority', { value: 'NoAdHard', configurable: false, enumerable: true, writable: false }); } run() { this.findElement(this.selector).then(results => { this.allElements = [...results]; if (this.allElements.length) { this.allElements.forEach(item => item.remove()); } }).catch(info => { logger.warn(`AntiHard No ${info}.`); }) } } class AntiAdDynamic extends NoAdBase { constructor(adDynamic) { super(); //dynamicRules if (!this.constructor.UNIQUEINSTANCE) { this.dynamicRules = adDynamic; this.maid = []; Reflect.defineProperty(this, 'priority', { value: 'NoAdDynamic', configurable: false, enumerable: true, writable: false }); Reflect.defineProperty(this.constructor, 'UNIQUEINSTANCE', { value: this, configurable: false, enumerable: false, writable: false }); } else { return this.constructor.UNIQUEINSTANCE; } } run() { for (let item of this.dynamicRules) { let mt = new MutationObserver(() => { let tmpNodeList = document.querySelectorAll(item.ref); if (tmpNodeList.length) { let resultWrapper = item.fire(tmpNodeList); resultWrapper.forEach(o => o.remove()); } }) this.maid.push(mt); let eye = document.querySelector(item.eyeSlector); if (eye) { mt.observe(eye, item.options); } else { this.findElement(item.eyeSlector) .then(eles => mt.observe(eles[0], item.options)) .catch(info => logger.warn(`${info}---无广告参照点:${item.ref} from ${item.eyeSlector}.`)); } } } } /* 处理样式的基类 */ class ModifyStyleBase extends Base { constructor(selector) { super(selector); } getNewElement(tag, options = {}) { let tmp = document.createElement(tag); Object.assign(tmp, options); return tmp; } setStyle() { throw TypeError('ChildrenClass must have this method!'); } removeStyle() { throw TypeError('ChildrenClass must have this method!'); } getStyle() { throw TypeError('ChildrenClass must have this method!'); } observeStyle() { throw TypeError('ChildrenClass must have this method!'); } stringToMap(styleText) { // 将 “div{height:20px;width:30px;content:':;'}span a{color:'red';text-align:center;}” 转换成 // Map对象 {'selector':{'attr1':'value1','attr2':'value2'}} // 原因是用户输入可能不规范,转成统一格式便于处理 // 输入content属性不能包含 {}; let result = new Map(); if (!styleText) return result; let pattern4InnerText = /[a-z0-9+~|*$()[\]. \n\t\f\r\v#:=>,_"'-^]+{[^}]*?}/gi; let pattern4PerRule = /([a-z0-9+~|*$()[\]. \n\t\f\r\v#:=>,_"'-^]+){([^}]*?)}/i; let cssRulesText = [...styleText.matchAll(pattern4InnerText)].flat(); for (let rule of cssRulesText) { let [selector, values] = rule.match(pattern4PerRule).slice(1); // if (!selector || !values) throw Error(selector + 'someThing is Wrong'); selector = selector.trim(); values = values.split(';').filter(o => o.trim()); let style = result.get(selector); if (!style) { style = new Map(); result.set(selector, style); } for (let attr of values) { let idx = attr.indexOf(":"); let k = attr.slice(0, idx).trim(); let v = attr.slice(idx + 1).trim(); if (!k) throw Error(selector + ' attr is Wrong'); if (v == '') continue; style.set(k, v.replace('\uea60', ';')); } } return result; } mapToString(map) { if (Object.prototype.toString.call(map).slice(8, -1) !== 'Map') throw TypeError('map Must be Map'); let result = ''; for (let [selector, valMaps] of map) { result = result + selector + '{'; for (let [k, v] of valMaps) { result = result + k + ":" + v + ";"; } result += "}"; } return result } string2DToMap(styleText) { let result = new Map(); if (!styleText) return result; for (let item of styleText.split(';').filter(i => i.trim())) { let key = item.slice(0, item.indexOf(':')); let val = item.slice(item.indexOf(':') + 1); result.set(key.trim(), val.trim()); } return result; } getAllAvailableBg() { //与项目的配置项耦合在一起了 let tempImgLists = []; if (this.configs.bg.used) { let bg = this.configs.bg.sources; for (let modules of Object.values(bg)) { if (modules.used) { for (let item of modules.sources) { item.url=item.url.trim(); if (item.used && item.url && (/^https?:/i).test(item.url)) { tempImgLists.push(item); } } } } } return tempImgLists; } getSingleBg(availableList) { // 为空的时候会返回undefined if (window.isSecureContext&&window.crypto && window.crypto.getRandomValues) { return availableList[Math.floor(crypto.getRandomValues((new Uint32Array(1)))[0] / 0xFFFFFFFF * availableList.length)]; } else { return availableList[Math.floor(Math.random() * availableList.length)]; } } getDifferPreviousSingleBg(previousBgObj, availableList) { let curObj = this.getSingleBg(availableList); for (; ;) { if (previousBgObj === curObj) { curObj = this.getSingleBg(availableList); } else { return curObj; } } } pageTest(urls=[]) { // 成功则返回该有的对象,否则就解决为null if(!urls.length) return Promise.resolve(true); let controllers=[],flag=true,errCounter=0; return new Promise(success=>{ // 1、先检查本地 this.getAllCachesUrl(this.configs.cache).then(urlArrs=>{ for(let item of urls){ if(urlArrs.includes(item.url)){ this.getCache(this.configs.cache,item.url) .then(rsp=>{ rsp.blob().then(bb=>{ item.blobUrl = URL.createObjectURL(bb); success(item); flag = false; for(let obj of controllers){ obj.ctrl.abort(); } logger.log('首页取到了缓存对象'); logger.dir(item); }) }) logger.log('首页查询到了缓存对象'); break; } } }) // 2、同时发送网络请求 for(let item of urls){ let ctrl = new AbortController(); controllers.push({ctrl,item}); fetch(item.url,{signal:ctrl.signal,method:'head'}) .then(rsp=>{ if(rsp.status===200||rsp.status===304){ if(flag){ // 此处有bug flag = false; success(item); logger.log(`首页成功拿到了head的响应`); for(let obj of controllers){ if(obj.item !== item) obj.ctrl.abort(); } // 如果失败就算了,不再使用回调必须成功 this.addCaches(this.configs.cache,item.url) } // 其他成功就已经毫无意义了,不再计数 }else{ errCounter++ if(errCounter===urls.length) success(null); } }) .catch((err)=>{ errCounter++ logger.error(err.message); if(errCounter===urls.length) success(null); }) } }) } getFirstPage(urlObjs=[],perNumber=5){ // 从数据库拿出固定的数据对象 if(!urlObjs.length) return Promise.resolve(null); urlObjs = this.shuffle(urlObjs.slice()); let urlIterators = urlObjs[Symbol.iterator](); let iteratorCounter = 0,flag=true; return new Promise(success=>{ this.pageTest((function(){ let tmpResult = []; if(flag){ for(let i=0;i<perNumber;i++){ if(++iteratorCounter===urlObjs.length) flag = false; tmpResult.push(urlIterators.next().value); } } return tmpResult.filter(o=>o); }())).then(result=>{ logger.log(`首页一共发起${iteratorCounter}次head请求.`) if(result){ if(result === true){ success(true); }else{ logger.log('这里的是真对象'); logger.dir(result); success(result); } }else{ return success(this.getFirstPage(urlObjs)); } }) }) } asyncLoadImg(bgObj, signal4Element, signal4Fetch, timeout4GM = 8000) { // 只加载是403跨域不允许的资源,其他的都由浏览器同步请求; let init=this.getCache(this.configs.cache,bgObj.url) return init.then(result => { // 1、查询缓存 if (result) { logger.summary('Match Cache.'); clearTimeout(signal4Element.timer); clearTimeout(signal4Fetch.timer); return result.blob().then(bb=>{ return URL.createObjectURL(bb); }) } else { logger.log('Start Request.'); return Promise.reject(); } }) .then(result => result, () => new Promise((success, rej) => { // 2、图像跨域测试并短时间获得缓存响应 let img = new Image(); img.onload = function () { if(img.naturalHeight) { success(bgObj.url); }else{ logger.warn(`img假成功-->[${bgObj.name}]--[${bgObj.url}]`); rej(); } clearTimeout(signal4Element.timer); signal4Element.signal.onabort = null; } img.onerror = function () { logger.warn('Wallpaper loading Error,NEXT STEP'); signal4Element.signal.onabort = null; rej(); } img.crossOrigin = ''; img.src = bgObj.url; signal4Element.signal.onabort = () => { rej(); logger.warn('User timeout.'); img.onerror = null; img.src = ''; img = null; } })) .then(url => url, () => { // 3、对可能失败的缓存响应中做处本地应急处理 return fetch(bgObj.url, { signal: signal4Fetch.signal }) .then( rsp => { clearTimeout(signal4Fetch.timer); logger.log('Deal with response'); return rsp }, () => { return Promise.reject('Request Exception...') } ) }) .then(rsp => { // rsp 是来自于缓存的blob,或来自于fetch的rsp,或来自于img元素的url; if (rsp === bgObj.url) { // 缓存url,但是存在一种情况,就是图片src被禁止,返回200,ok,fetch请求也会失败, // 但是当图像作为背景却不会被禁止(远坂凛) return new Promise((resolve,reject)=>{ if (rsp.toLowerCase().startsWith('https:')) { this.addCaches(this.configs.cache,bgObj.url,() => { if(this.configs.requestAllWebsite){ this.sendGMXHR(bgObj.url, null, rsp => { // 对GM补救成功的同样进行blob化处理 resolve(URL.createObjectURL(rsp.response)); let rspDup = new Response(rsp.response.slice(), { status: 200, statusText: 'OK',headers:this.getHeadersFromGMXHR(rsp.responseHeaders)}); this.setCaches(this.configs.cache,bgObj.url,rspDup); }, function () { reject('GM cross Origin Request failture.'); }, function () { reject('GM timeout2'); }, timeout4GM); }else{ console.warn('Maybe,,,,Please switch requestAllWebsite on.'); reject('GM add cache cant cors.'); } },()=>{ // 成功,但是是第一次添加的回调 this.getCache(this.configs.cache,bgObj.url).then(myRsp=>{ myRsp.blob().then(bb=>{ logger.log('初次直接返回了blob'); resolve(URL.createObjectURL(bb)); }) }) }) }else{ resolve(rsp); } }) } if (typeof rsp==='string'&&rsp.startsWith('blob:')) return rsp; return new Promise((resolve, reject) => { if (rsp.status === 403) { logger.error('WebServer rejects request. ' + bgObj.url); if (this.configs.requestAllWebsite) { logger.warn('403,Start Cross Origin Request.'); this.sendGMXHR(bgObj.url, null, rsp => { resolve(URL.createObjectURL(rsp.response)); let rspDup = new Response(rsp.response.slice(), { status: 200, statusText: 'OK' ,headers:this.getHeadersFromGMXHR(rsp.responseHeaders)}); this.setCaches(this.configs.cache,bgObj.url,rspDup); }, function (rsp) { logger.error('Cross Origin Request failture.'); reject(rsp) }, function () { reject('timeout'); }, timeout4GM); } else { console.warn(403,'Maybe,,,,Please switch requestAllWebsite on.'); reject(rsp.status); } } else if (rsp.status > 399 && rsp.status !== 403) { logger.error('Maybe internet or server inter error.') reject(rsp.status); } else { // 几乎都是浏览器请求成功的rsp try{ let rspDup = rsp.clone(); rsp.blob().then(bb => { resolve(URL.createObjectURL(bb)); }); this.setCaches(this.configs.cache,bgObj.url,rspDup); } catch(e){ logger.error(`浏览器几乎请求成功处有异常-->[${e.message}]`); resolve(bgObj.url); } } }); }, reason => { return new Promise((resolve, reject) => { logger.warn(`BrowserInfo: [${reason}]`); if (this.configs.requestAllWebsite) { logger.warn('Start Cross Origin Request.'); this.sendGMXHR(bgObj.url, 'get', rsp => { // 对GM补救成功的同样进行blob化处理 resolve(URL.createObjectURL(rsp.response)); let rspDup = new Response(rsp.response.slice(), { status: 200, statusText: 'OK',headers:this.getHeadersFromGMXHR(rsp.responseHeaders)}); this.setCaches(this.configs.cache,bgObj.url,rspDup); }, function (rsp) { logger.error('Cross Origin Request failture.'); reject(rsp); }, function () { reject('GM timeout2'); }, 8000); } else { reject('cant cors.'); } }) }) } handleImg(context, timeout4Element, timeout4Fetch, timeout4GM) { let bgObj = context.bgObjNext; logger.log('下一张背景图像: ' + (bgObj.name || bgObj.url)); let aborter4Element = new AbortController(), aborter4Fetch = new AbortController(), timer4ele, timer4fet; timer4ele = setTimeout(() => { aborter4Element.abort('UserTimeOutEle.'); }, (timeout4Element || this.configs.duration) * 1000 * 0.8); timer4fet = setTimeout(() => { aborter4Fetch.abort('UserTimeOutDom.') }, (timeout4Fetch || this.configs.duration) * 1000 * 2 * 0.8); this.asyncLoadImg(bgObj, { signal: aborter4Element.signal, timer: timer4ele }, { signal: aborter4Fetch.signal, timer: timer4fet }, timeout4GM) .then(rsp => { logger.summary('success get'); if (rsp.startsWith('data:')) { context.bgStr = rsp.replace(';', '\uea60'); // 60000码点字符 } else { context.bgStr = rsp; } context.lock = true; }) .catch(reason => { logger.warn(reason, '保留上一张'); context.lock = true; }); } wrapperEvent(customEvent, callback) { // 用于自定义包装事件 if (typeof customEvent !== 'string') throw TypeError('EventName must be string.'); if (typeof callback !== 'function') throw TypeError('callback must be function.'); let customElement = new Event(customEvent, { cancelable: false, bubbles: true }); return function () { customElement.content = arguments; let result = callback.apply(this, [...arguments]); window.dispatchEvent(customElement); return result; } } } /* 定义样式在标签内处理的类 */ /* 此处的设置不会被网站设置覆盖的样式,为弱类型设置;全局唯一实例的style标签 */ class ModifyStyleFromTag extends ModifyStyleBase { constructor() { super(); if (!this.constructor.UNIQUEINSTANCE) { this.observers = []; this.styleEle = this.getNewElement('style'); this.styleEle.id = this.styleEle.id || Math.random().toString(36).slice(2); Reflect.defineProperty(this, 'priority', { value: 'Tag', configurable: false, enumerable: true, writable: false }); Reflect.defineProperty(this.constructor, 'UNIQUEINSTANCE', { value: this, configurable: false, enumerable: false, writable: false }); } else { return this.constructor.UNIQUEINSTANCE; } } setStyle(css) { // css 可以是样式对象或者是css文本;filter指定给满足条件的元素添加样式,可以是NodeList或[[HTMLElement]],或者回调函数; // let cssTest = "div {width:30px ;height: 40px;}span a ,b> strong {color:'red';line-height:1rem}"; let newStyleMap = this.stringToMap(css); let currentMap = this.stringToMap(this.styleEle.innerText); for (let [selector, values] of newStyleMap) { let currentStyleMap = currentMap.get(selector); if (!currentStyleMap) { // 空说明是新选择器; currentMap.set(selector, values); } else { // 说明是旧选择器; for (let [k, v] of values) { currentStyleMap.set(k, v); } } } this.styleEle.innerText = this.mapToString(currentMap); } getStyle(selector, styleKey) { // 返回一个样式字符串值 selector = selector.trim(); return this.stringToMap(this.styleEle.innerText).get(selector).get(styleKey); } getAllStyle() { // 返回的是map对象; return this.stringToMap(document.getElementById(this.styleEle.id).innerText); } removeStyle(selector, styleKeys = []) { // styleKey应该是数组;eg,['width','height',...]; selector = selector.trim(); let currentMap = this.stringToMap(this.styleEle.innerText); let flag = true; if (!styleKeys.length) { // 1 如果styleKey为空的话,则删除整个选择器 flag = currentMap.delete(selector); } else { // 2 非空的话则要每个处理 let currentStyleMap = currentMap.get(selector); if (!currentStyleMap) return false; for (let k of styleKeys) { k = k.trim(); if (!currentStyleMap.delete(k)) logger.warn(`${selector}->${k} dont exist`); } if (!currentStyleMap.size) currentMap.delete(selector); } this.styleEle.innerText = this.mapToString(currentMap); return flag; } observeStyle() { // 监测要监视的style标签内容 this.observers[0] = new MutationObserver(() => { if (!this.styleEle.isConnected) { this.connect(); logger.warn('style tag is missing, Re-Connected ' + this.styleEle.id); } }); // this.observer.observe(document.head,{childList:true,subtree:true,characterData:true,attributeFilter:['class','style','id']}); if (document.head) { this.observers[0].observe(document.head, { childList: true }); } else { this.findElement('head').then(rsp => this.observers[0].observe(rsp[0], { childList: true })); } } connect() { if (!this.styleEle) throw Error('无样式对象'); if (!document.head) { this.findElement('head') .then(head => head[0].appendChild(this.styleEle)) .catch((e) => { logger.warn('Tag cant insert to document.head immediately, info: ', e); let timer = this.setBFInterval(() => { let result = document.querySelectorAll('head'); if (result.length) { result[0].appendChild(this.styleEle); clearInterval(timer); logger.log('Tag low performace insert head!') clearTimeout(t4o); } }, 100); let t4o = setTimeout(() => { clearInterval(timer); logger.error('head cant be found.'); }, 60 * 1000); }); } else { document.head.appendChild(this.styleEle); } } run() { this.connect(); this.observeStyle(); } } /* 定义样式在行内处理的类 */ /* 此处的样式设置可能会被网站设置覆盖的样式,为强类型设置 */ class ModifyStyleFromLine extends ModifyStyleBase { constructor() { super(); if (!this.constructor.UNIQUEINSTANCE) { this.observers = null; // 这是一个补救,用于针对固有样式进行保留(选择器:函数对象); this.fixedStyleObj = null; this.configs = null; this.pEvents = {}; this.variableSelector = {}; // 此属性处理每次必须变更的对象;[] Reflect.defineProperty(this, 'priority', { value: 'Line', configurable: false, enumerable: true, writable: false }); Reflect.defineProperty(this, 'styleMaps', { value: new Map(), configurable: false, enumerable: true, writable: false }); Reflect.defineProperty(this.constructor, 'UNIQUEINSTANCE', { value: this, configurable: false, enumerable: false, writable: false }); } else { return this.constructor.UNIQUEINSTANCE; } } getAllStyle() { return this.styleMaps; } getStyle(selector, styleKey) { // 返回一个样式字符串值 return this.styleMaps.get(selector.trim()).get(styleKey.trim()); } setStyle(css) { // css 可以是样式对象或者是css文本;filter指定给满足条件的元素添加样式,可以是NodeList或[[HTMLElement]],或者回调函数; // 需要覆盖固有样式中希望被覆盖掉的; // let cssTest = "div {width:30px ;height: 40px;}span a ,b> strong {color:'red';line-height:1rem}"; let newStyleMap = this.stringToMap(css); let currentMap = this.styleMaps; for (let [selector, values] of newStyleMap) { let currentStyleMap = currentMap.get(selector); if (!currentStyleMap) { currentMap.set(selector, values); } else { for (let [k, v] of values) { currentStyleMap.set(k, v); } } } } updateGlobalStyle() { for (let [selector, styleItems] of this.styleMaps) { let nodeList = document.querySelectorAll(selector); //logger.log(selector,'设置样式===>',nodeList); let cssText = ''; for (let [k, v] of styleItems) { cssText = cssText + k + ":" + v + ";"; } // 处理每个元素固有样式中想被保留的; for (let ele of nodeList) { let cssTmp = cssText; if (selector in this.fixedStyleObj) { let filterStyleObj = this.fixedStyleObj[selector](ele); cssTmp += filterStyleObj.cur; let oldStyle = [...this.string2DToMap(ele.style.cssText).keys()]; oldStyle.sort(); let newStyle = [...this.string2DToMap(cssTmp + filterStyleObj.delay).keys()]; newStyle.sort(); if (newStyle.toString() != oldStyle.toString()) ele.style.cssText = cssTmp; } else { let oldStyle = [...this.string2DToMap(ele.style.cssText).keys()]; oldStyle.sort(); let newStyle = [...this.string2DToMap(cssTmp).keys()]; newStyle.sort(); if (newStyle.toString() != oldStyle.toString()) ele.style.cssText = cssTmp; } // 处理计算样式 if (Object.keys(this.variableSelector).includes(selector)) { for (let stykey of this.variableSelector[selector]) { // stykey 是每个需要计算和检查的stykey; if (styleItems.get(stykey) !== this.string2DToMap(ele.style.cssText).get(stykey)) { ele.style[stykey] = styleItems.get(stykey); } } } } } } removeStyle(selector, styleKeys = []) { // 调试用方法 // styleKey应该是数组;eg,['width','height',...]; let currentMap = this.styleMaps.get(selector); if (!currentMap) return false; for (let key of styleKeys) { if (!currentMap.delete(key)) logger.log(key + ' is not exist.'); } let nodeList = document.querySelectorAll(selector); let cssText = ''; if (!currentMap.size) { this.styleMaps.delete(selector); } else { for (let [k, v] of currentMap) { cssText = cssText + k + " :" + v + ";"; } } // 合并固有样式(始终保留,不能移除的) for (let ele of nodeList) { let cssTmp = cssText; if (selector in this.fixedStyleObj) { cssTmp += this.fixedStyleObj[selector](ele).cur; } ele.style.cssText = cssTmp; } } observeStyle() { for (let item of this.observers) { item.observer = new MutationObserver((_, obs) => { // 主要是根据DOM变化确定每一次变化的时候被观察元素依然存在; let flag = true; let l = 1; while (l--) { // 初始化 空,需要赋值元素 if (!item.target) { item.target = document.querySelector(item.selector); if (!item.target) { logger.error('变更.但是未查找到元素'); break; } this.updateGlobalStyle(); obs.observe(document.querySelector(item.eyeSlector), item.options); logger.warn('变更,填充空target'); } if (!item.target.isConnected) { // 说明DOM变更,前后查找到的元素不一样,但是不影响, item.target = document.querySelector(item.selector); if (!item.target) break; logger.warn('变更,更换旧target-修正'); // 这里还要重新监测observe obs.observe(document.querySelector(item.eyeSlector), item.options); this.updateGlobalStyle(); } else { // 连接,但是变化太慢导致下次触发DOM变更以前为空,出现样式空白; for (let sp of item.specialJudge) { if (!item.target.style.cssText.includes(sp)) { logger.warn(`变更,样式空白-修正`); this.updateGlobalStyle(); break; } } } flag = false; } if (flag) { // 为真说明元素丢失;设置定时器,直到查找到并重新赋值 观察 归位 logger.warn('暂时丢失,等待变更'); } }); } // 开始观察 for (let item of this.observers) { let tmpNode = document.querySelector(item.eyeSlector); if (!tmpNode) { this.updateGlobalStyle(); this.findElement(item.eyeSlector).then(rsp => { item.observer.observe(rsp[0], item.options); // 找到后先执行一次添加样式,因为可能eyeselector要等很久才能变更节点 this.updateGlobalStyle(); }).catch(selectorAsync => logger.error(`异步查询,未找到:${selectorAsync}`)); } else { item.observer.observe(tmpNode, item.options); } } } subcribeGlobal() { //开启全局事件监听--私有事件另行监听 // 1、监听URL地址变化,地址变化就更新this.styleMaps里面的背景样式; let imgsAvi = this.getAllAvailableBg(this.configs.bg); let bgImgObj = this.getSingleBg(imgsAvi); if (!bgImgObj) { this.setStyle(`body{background-image:none;}`); logger.log('当前无有效背景图像'); return; } else { this.getFirstPage(imgsAvi,5).then(imgObj => { if(imgObj !== true){ bgImgObj = imgObj; logger.log('当前背景图像: ' + (imgObj.name || imgObj.url)); this.setStyle(`body{background-image:url("${imgObj.blobUrl||imgObj.url}");}`); }else{ logger.log("所有在线请求失败,无有效图像.") this.setStyle("body{background-image:none;}"); } this.updateGlobalStyle(); }); } if (this.configs.bgSwitchMode === 'multi' && imgsAvi.length > 1) { history.pushState = this.wrapperEvent('pushstate', history.pushState); let changeBg = (() => { // 设定时间阀值,避免可能的重复设置背景 let timePre = new Date(); let timeCur = 0; let context = { lock: false, bgStr: '', bgObjNext: null }; // 初始化第一次的图片 context.bgObjNext = this.getDifferPreviousSingleBg(bgImgObj, imgsAvi); this.handleImg(context); return function () { timeCur = new Date(); if (!context.lock) return; if (timeCur - timePre < this.configs.duration * 1000) return; context.lock = false; let bg = this.getStyle('body','background-image'); if(bg!==context.bgStr){ if(bg.startsWith('blob')) URL.revokeObjectURL(bg); this.setStyle(`body{background-image:url("${context.bgStr}");}`); this.updateGlobalStyle(); logger.log('当前背景图像: ' + (context.bgObjNext.name || context.bgObjNext.url)); timePre = timeCur; } logger.log('全局配置项:'); logger.dir(globalContext); // 准备下一次点击的图片; context.bgObjNext = this.getDifferPreviousSingleBg(context.bgObjNext, imgsAvi); this.handleImg(context); }; })(); window.addEventListener('pushstate', changeBg.bind(this)); window.addEventListener('popstate', changeBg.bind(this)); } } subcribePrivate() { //定义私有事件; for (let key of Object.keys(this.pEvents)) { this.pEvents[key].call(this); } } run() { this.subcribeGlobal(); this.subcribePrivate(); this.updateGlobalStyle(); this.observeStyle(); } } /* 此类用于实例化不同网站的设置 */ class Context extends Base { constructor(tagRules, lineRules, adRules, fixedStyleObj, observers, configs, pEvents) { super(); this.startUrl = location.host; if (!(tagRules || lineRules || adRules)) throw TypeError('无效对象'); this.lineStyleSatndardRules = lineRules.standardStyle || ''; this.lineStyleVariableSelector = lineRules.variableStyleSelector || ''; this.tagStyleRules = tagRules || ''; this.adSoft = adRules.adSoft || []; this.adHard = adRules.adHard || []; this.adDynamic = adRules.adDynamic || []; this.pEvents = pEvents || {}; this.fixedStyleObj = fixedStyleObj || {}; this.observers = observers || []; this.configs = configs || userGlobalConfigs; this.maid = []; } configInit() { // 1、根据网速选择不同,重新包装findElement方法; let curObj = Object.getPrototypeOf(this); while (curObj) { if (Object.prototype.hasOwnProperty.call(curObj, 'findElement')) { let findElement = curObj.findElement; curObj.findElement = (() => { let timeout, fq, intvl; switch (this.configs.netThrottlMode) { // 'low','middle','heigh','infinity' case 'low': ({ timeout, fq, intvl } = { timeout: 7, fq: true, intvl: 100 }); break; case 'middle': ({ timeout, fq, intvl } = { timeout: 12, fq: true, intvl: 120 }); break; case 'heigh': ({ timeout, fq, intvl } = { timeout: 20, fq: true, intvl: 300 }); break; case 'infinity': ({ timeout, fq, intvl } = { timeout: 30, fq: true, intvl: 500 }); break; default: ({ timeout, fq, intvl } = { timeout: 3, fq: false, intvl: 75 }); } return function (selector, signal = null) { return findElement.call(this, selector, timeout, fq, intvl, signal); } })(); break; } else { curObj = Object.getPrototypeOf(curObj); } } // 2、移除禁用配置 if (Object.getPrototypeOf(this.configs) !== Object.prototype) { for (let key of Reflect.ownKeys(this.configs)) { if (['bg','isDebug'].includes(key)) { delete this.configs[key]; logger.warn(`禁用用户 [${key}] 配置项.`) } } } // 3、添加用户不可见全局配置 this.configs.cache = 'myImgs'; // 修订限制性配置 this.configs.duration = this.configs.duration < 3 ? 3 : this.configs.duration; } globalTodo() { //每个网站都要做的事情 // 广告软消除 document.addEventListener('load', () => { this.adSoft.forEach(selector => { (new AntiAdSoft(selector.trim())).run(); }) }); document.addEventListener('abort', () => { this.adSoft.forEach(selector => { (new AntiAdSoft(selector.trim())).run(); }) }); // 广告硬消除 for (let selector of this.adHard) { (new AntiAdHard(selector.trim())).run(); } } go() { // 此方法用于启动所有样式广告监听等 this.configInit(); let line = new ModifyStyleFromLine(); line.pEvents = this.pEvents; line.configs = this.configs; line.observers = this.observers; line.fixedStyleObj = this.fixedStyleObj; line.variableSelector = this.lineStyleVariableSelector; let tag = new ModifyStyleFromTag(); // 动态广告置于此处 let adDy = new AntiAdDynamic(this.adDynamic); line.setStyle(this.lineStyleSatndardRules); //冷处理 tag.setStyle(this.tagStyleRules);//冷处理 for (let item of [line, tag, adDy]) { this.maid.push(item); item.run(); } this.globalTodo(); } } let userPrivateConfigs = Object.create(userGlobalConfigs); let tagStyle, fixedStyleObj, lineStyle, adRules, obs4DOM, pEvents, globalContext; if (baidu && location.href.match(/^https?:\/\/w{0,3}\.baidu\.com/i) && ['/', '/s', '/more/'].includes(location.pathname.toLowerCase())) { // 百度私有用户配置 Object.assign(userPrivateConfigs, { // 此处书写可以覆盖全局的配置 netThrottlMode: 'low', }); // 1、Tag 配置 // #head 用于兼容谷歌浏览器渲染过慢导致的空白显示问题,firefox可以移除此项; tagStyle = ` #s_top_wrap{ background-color: #ffffff53; backdrop-filter: blur(2px); } #s-top-more{ background-color:#ffffff4d; } #head{ background-color:rgba(255, 255, 255, 0.3)!important;box-shadow:rgba(0, 0, 0, 0.5) 2px 0px 5px 2px;backdrop-filter:blur(2px); } #s_tab>div.s_tab_inner:hover{ opacity:0.8; } #s_tab>div.s_tab_inner{ opacity:0; } #head_wrapper input#kw,#form .bdsug,#form .s_ipt_wr{ background-color:#ffffffa0; } #form .s_ipt_wr,#kw{ border-top-left-radius: 1rem; border-bottom-left-radius: 1rem; } #form .bdsug li:hover{ background-color:#ffffffa0; } #foot div.foot-inner { background-color:#fff0 !important; } .wrapper_new #head.peak-down.s_down{ background-color:#fff0 !important; } #wrapper #s_tab div a,wrapper #s_tab div .s-tab-item::before,#wrapper #s_tab div b,#wrapper #s_tab div .s-tab-item::before,#wrapper #s_tab div .cur-tab::before{ color:black !important; } #wrapper #s_tab div a:hover,wrapper #s_tab div .s-tab-item::before:hover,#wrapper #s_tab div b:hover,#wrapper #s_tab div .s-tab-item::before:hover,#wrapper #s_tab div .cur-tab::before:hover{ color:blue !important; } .foot{ background-color:#ffffff4d; } #foot{ opacity:0 !important; } #head_wrapper #s-hotsearch-wrapper,div#bottom_layer,div#s_side_wrapper{ display:none !important; } #head_wrapper.s-ps-islite{ padding-bottom:0px !important; } `; // 2、Line 配置 --- 固有样式 --- fixedStyleObj = { "#head": function (ele) { if ((new URL(ele.baseURI)).pathname === '/') { return { cur: 'background-color:unset!important;backdrop-filter:none;', delay: '' } } else { return { cur: '', delay: '' }; } }, // 侧边栏居中 "#s_tab>div.s_tab_inner": function (ele) { setTimeout(() => { ele.style.cssText += `margin-top:${ele.getBoundingClientRect().height / -2}px;`; }); setTimeout(() => { ele.style.opacity = null; ele.style.left = '0px'; }, 50); return { cur: 'opacity:0;', delay: `margin-top:${ele.getBoundingClientRect().height / -2}px;left:0px;` }; }, }; // 3、Line 配置 --- 标准样式 --- lineStyle = { // #page bottom:-100px;用于解决底部栏闪烁问题;在固有样式中异步恢复 // #s_tab>div.s_tab_inner left:-100px;用于解决左侧栏闪烁问题;在固有样式中异步恢复 standardStyle: ` body{ background-repeat:no-repeat;background-position:center;background-attachment:fixed;background-size:cover; } #page{ background-color:#ffffff4d;box-shadow:rgba(0, 0, 0, 0.5) 0px -1px 5px 2px;backdrop-filter:blur(2px); position:fixed;bottom:0px;left:50%;border-radius:12px;z-index:1400;transform:translateX(-50%);margin-top: 0px; } #foot{ background-color:#ffffff00; } #page>div{ padding:14px; width:min-content; } #page>div>*:last-child{ margin-right:0px; } #container{ background-image:linear-gradient(to right,rgba(255, 255, 255, 0.75) 25%,rgba(255, 255, 255, 0.15));box-sizing:border-box; box-shadow:rgba(0, 0, 0, 0.5) 0px 0px 5px 2px,inset 0px 0px 5px 1px white;margin-bottom:0.5rem;padding:2.5em;border-radius:12px; } #head{ background-color:rgba(255, 255, 255, 0.3)!important;box-shadow:rgba(0, 0, 0, 0.5) 2px 0px 5px 2px;backdrop-filter:blur(2px); } #s_tab>div.s_tab_inner{ position:fixed;left:-100px;z-index:302;top:50%;border-top-right-radius:16px;border-bottom-right-radius:16px;width:60px; transition:all 0.3s;background-color:#fff; } `, // Line 配置 --- 计算样式(选择) --- variableStyleSelector: { 'body': ['background-image'], } }; // 4、Ad 配置 adRules = { // 4、Ad (soft)配置 adSoft: [ '.s-hotsearch-wrapper.s-isindex-wrap', '.s-isindex-wrap.s-bottom-layer', '[tpl=recommend_list]', '#rs_new', '#s_side_wrapper', '#content_right div.hint_right_middle', '#help', '#searchTag', ], // 4.1 Ad (hard配置) adHard: [], adDynamic: [], }; // 5、监测配置(Line) obs4DOM = [ { 'selector': '#head', "target": null, 'eyeSlector': 'body', 'observer': null, 'options': { childList: true }, specialJudge: ['background-color'] }, //{'selector':'#s_tab>div.s_tab_inner',"target":null,'eyeSlector':'#s_tab','observer':null,'options':{childList:true},specialJudge:['opacity']}, //{'selector':'#wrapper',"target":null,'eyeSlector':'body','observer':null,'options':{childList:true},specialJudge:[]}, { 'selector': '#page', "target": null, 'eyeSlector': '#wrapper_wrapper', 'observer': null, 'options': { childList: true, subtree: true }, specialJudge: [] }, ]; // 6、私有事件配置(写在外部便于配置) pEvents = { '#container': function () { if (this.configs.centerColor) this.setStyle(`#container{background-color:${this.configs.centerColor.trim()};}`); }, }; } else if (baidu && location.href.match(/^https?:\/\/w{0,3}\.baidu\.com/i) && location.pathname.toLowerCase() === '/sf/vsearch') { // 百度视频页面 Object.assign(userPrivateConfigs, { // 此处书写可以覆盖全局的配置 netThrottlMode: 'low', }); // 1、Tag 配置 tagStyle = ` #s_tab>div.s_tab_inner:hover{ opacity:0.8; } #s_tab>div.s_tab_inner{ opacity:0; } #wrapper #s_tab div a,wrapper #s_tab div .s-tab-item::before,#wrapper #s_tab div b,#wrapper #s_tab div .s-tab-item::before,#wrapper #s_tab div .cur-tab::before{ color:black !important; } #wrapper #s_tab div a:hover,wrapper #s_tab div .s-tab-item::before:hover,#wrapper #s_tab div b:hover,#wrapper #s_tab div .s-tab-item::before:hover,#wrapper #s_tab div .cur-tab::before:hover{ color:blue !important; }`; fixedStyleObj = { "#s_tab>div.s_tab_inner": function (ele) { setTimeout(() => { ele.style.cssText += `margin-top:${ele.getBoundingClientRect().height / -2}px;`; }); setTimeout(() => { ele.style.opacity = null; ele.style.left = '0px'; }, 50); return { cur: 'opacity:0;', delay: `margin-top:${ele.getBoundingClientRect().height / -2}px;left:0px;` }; }, }; lineStyle = { standardStyle: ` body{ background-repeat:no-repeat;background-position:center;background-attachment:fixed;background-size:cover; } #s_kw_wrap{ background-color: #ffffffad; } #container { background-image: linear-gradient( to right, rgba(255, 255, 255, 0.75) 25%, rgba(255, 255, 255, 0.15) ); box-sizing: border-box; box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 5px 2px, white 0px 0px 5px 1px inset; padding: 2.5em; border-radius: 12px; display: flex; flex-direction: row-reverse; width: min-content; margin: auto; } #content_left { padding-right: 140px; padding-left: 0px; } #head{ background-color:rgba(255, 255, 255, 0.3)!important;box-shadow:rgba(0, 0, 0, 0.5) 2px 0px 5px 2px;backdrop-filter:blur(2px); } #s_tab>div.s_tab_inner{ position:fixed;left:-100px;z-index:302;top:50%;border-top-right-radius:16px;border-bottom-right-radius:16px;width:60px; transition:all 0.3s;background-color:#fff; } .s_side_wrapper{ display: none; }`, variableStyleSelector: {}, }; adRules = { // Ad (soft)配置 adSoft: [], // Ad 硬处理(只能删除节点的类型);给选择器即可 adHard: [], // Ad 专用动态规则集 adDynamic: [] }; obs4DOM = [ { 'selector': '#head', "target": null, 'eyeSlector': 'body', 'observer': null, 'options': { childList: true }, specialJudge: ['background-color'] }, ]; pEvents = { '#container': function () { if (this.configs.centerColor) this.setStyle(`#container{background-color:${this.configs.centerColor.trim()};}`); }, }; } else if (sou360 && location.href.match(/^https?:\/\/w{0,3}\.so\.com/i) && location.pathname.toLowerCase() === '/') { //360首页 Object.assign(userPrivateConfigs, { // 此处书写可以覆盖全局的配置 netThrottlMode: 'low', }); // 1、Tag 配置 tagStyle = ` #footer{ display:none; } #header{ background-color:#ffffff6b; backdrop-filter:blur(2px); } fieldset#input-container{ background-color:rgba(255, 255, 255, 0.75); } div#suggest-align{ background-color:#ffffff00; } div#goto-top,#main .gold-wrap{ display:none!important; } `; // 2、Line 配置 --- 固有样式 --- fixedStyleObj = { //selector:function(ele){} }; // 3、Line 配置 --- 标准样式 --- lineStyle = { standardStyle: ` body{ background-repeat:no-repeat;background-position:center;background-attachment:fixed;background-size:cover; } #skin_bg{ background-color:rgba(255, 255, 255, 0); } #card_container{ display:none; } #bd_search .fixed{ background-color:rgba(255, 255, 255, 0); } `, // Line 配置 --- 计算样式(选择) --- variableStyleSelector: { '#skin_bg': ['background-color'], // 都要有的背景 'body': ['background-image'], } } // 4、Ad 配置 adRules = { // Ad (soft)配置 adSoft: [], // Ad 硬处理(只能删除节点的类型);给选择器即可 adHard: [ '#goto-top', '#often_so' ], // Ad 专用动态规则集 adDynamic: [ //{ref:'#lawnfooter-samll__btne',fire:(eles)=>{return [eles[0]]},options:{childList:true}}, { ref: '#lawnfooter-samll__btn', eyeSlector: 'body', options: { childList: true }, fire: (eles) => { //eles 是重找规则里面的元素集 let pE = eles[0]; for (; ;) { if (pE.parentElement === document.body) { return [pE]; } else { pE = pE.parentElement; } } }, }, ], } // 5、监测配置(Line) obs4DOM = [ { 'selector': '#card_container', "target": null, 'eyeSlector': '#main', 'observer': null, 'options': { childList: true }, specialJudge: ['display'] }, ]; pEvents = {}; } else if (sou360 && location.href.match(/^https?:\/\/w{0,3}\.so\.com/i) && location.pathname.toLowerCase() === '/s') { //360搜索结果页面 Object.assign(userPrivateConfigs, { // 此处书写可以覆盖全局的配置 netThrottlMode: 'low', }); // 1、Tag 配置 tagStyle = ` html{ background-color: unset !important; } div#header{ background-color: transparent; } #header div.inner{ background: #ffffff70; box-shadow: black 0px 0px 5px 1px; backdrop-filter: blur(2px); } ul.g-menu{ background-color: #ffffffe8; } #g-hd #head .round{ background-color: #ffffffb0; } #head #keyword{ background-color: #fff0; } div#tabs-wrap{ border:none; } ul#g-hd-tabs{ position: fixed; width: min-content; left: 0px; top:50%; height:min-content; transform:translateY(-50%); background-color: #eee; z-index:3000; border-top-right-radius:16px; border-bottom-right-radius:16px; opacity:0; transition:opacity 0.3s; } ul#g-hd-tabs:hover{ opacity:0.85; } ul#g-hd-tabs li a{ color:black; } div#container{ margin: 0px auto 6rem; padding: 2rem; border-radius: 12px; background-image: linear-gradient(to right,rgba(255, 255, 255, 0.75) 25%,rgba(255, 255, 255, 0.15)); box-shadow: rgba(0, 0, 0, 0.5) 0px 0px 5px 2px,inset 0px 0px 5px 1px white; border-radius: 12px; display:-webkit-flex; display:-moz-flex; display:flex; justify-content:center; width:min-content; } div#main{ padding-left: 2rem; margin-right:6%; box-sizing: border-box; min-width:608px; } div#container>div:last-child{ display:none!important; } #warper div.mod-relation,#main .inline-recommend{ display: none !important; } #warper #page{ position: fixed; bottom: 0px; margin: 0px; left:0px; right:0px; z-index: 3000; padding-left: 10px; background-color: #ffffff4d; box-shadow: rgba(0, 0, 0, 0.5) 0px -1px 5px 2px; backdrop-filter: blur(2px); border-radius: 10px; padding-top: 14px; padding-bottom: 14px; margin-left: auto; margin-right: auto; width: min-content; } #page span.nums{ display: none !important; } div#page a, div#page strong{ background: #fff; border: 1px solid #eee; border-radius: 8px; color: #666; display: inline-block; font-size: 14px; height: 34px; line-height: 34px; margin-right: 12px; text-align: center; text-decoration: none; vertical-align: middle; width: 34px; } div#page strong{ background: #0fb264; border-color: #0fb264; box-shadow: 0 1px 3px rgba(188,188,188,0.2); color: #ffffff; } div#footer{ display:none; } #warper #side{ left:0px; box-sizing:border-box; padding-right: 1rem; min-width: 400px; } #mohe-hotnews_right .mh-small-box,#side #adwarnTip,#side #so_kw-ad,#side #lm-rightbottom,div.lianmeng-ad,dl#head_rs_top,#rs-top,.kzx-news-rec-info, .lianmeng-ad,#side div.res-mediav-right,dl#head_pdr_guide,div#so_top,div.res-recommend-tag,.double-eleven,div#goto-top{ display:none!important; } #warper #side:hover{ background-color:unset!important; } div#side_wrap.fixed{ background-color:rgba(255, 255, 255, 0)!important; } .so-rich-official-recom .inter .inter-ul{ width:auto !important; } `; // 2、Line 配置 --- 固有样式 --- fixedStyleObj = { // 左侧面板居中 // 底部栏 }; // 3、Line 配置 --- 标准样式 --- lineStyle = { standardStyle: ` body{ background-repeat:no-repeat;background-position:center;background-attachment:fixed;background-size:cover; } `, // Line 配置 --- 计算样式(选择) --- variableStyleSelector: { // 都要有的背景 'body': ['background-image'], } } // 4、Ad 配置 adRules = { // Ad (soft)配置 adSoft: [], // Ad 硬处理 adHard: [ '#so_kw-ad' ], // Ad 专用动态规则集 adDynamic: [ { ref: '#so_top', eyeSlector: 'body', options: { childList: true, subtree: true }, fire: (eles) => { return [...eles] } }, { ref: '#head_pdr_guide', eyeSlector: '#tabs-wrap', options: { childList: true, subtree: true }, fire: (eles) => { return [...eles] } }, ], } // 5、监测配置(Line) obs4DOM = [ { 'selector': 'body', "target": null, 'eyeSlector': 'body', 'observer': null, 'options': { attributeFilter: ['style'] }, specialJudge: ['backgound-image'] }, { 'selector': 'div#container', "target": null, 'eyeSlector': 'div#warper', 'observer': null, 'options': { childList: true }, specialJudge: ['background-color'] }, ]; pEvents = { 'div#container': function () { if (this.configs.centerColor) this.setStyle(`div#container{background-color:${this.configs.centerColor.trim()};}`); }, }; } else{ console.warn('Some Website cant match.'); return; } globalContext = new Context(tagStyle, lineStyle, adRules, fixedStyleObj, obs4DOM, userPrivateConfigs, pEvents).go(); logger.log('running'); })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址