// ==UserScript== // @name 百度搜索 - 优化 -sdfsdfdsf // @namespace http://tampermonkey.net/ // @home-url https://greasyfork.org/zh-CN/scripts/31642 // @description 1、屏蔽百度推广 2、关闭百度广告联盟信息收集 3、绑定快捷键 4、布局调整 5、居中单列(可选) 6、居中双列(可选) // @version 4.1.9 // @author 浮生未歇 // @run-at document-start // @include https://www.baidu.com/ // @include https://www.baidu.com/s?* // @incluce https://www.baidu.com/#* // @include https://www.baidu.com/baidu?* // @exclude https://www.baidu.com/home* // @exclude https://www.baidu.com/sf* // @exclude https://www.baidu.com/search* // @exclude https://www.baidu.com/link* // @exclude https://www.baidu.com/s*tn=news* // @resource baiduIndexStyle https://cdn.jsdelivr.net/gh/sinlin/baidu@1.0.4/2018-10-30/indexStyle.css // @resource baiduCommonStyle https://cdn.jsdelivr.net/gh/sinlin/baidu@1.0.4/2018-10-30/commonStyle.css // @resource baiduMenu https://cdn.jsdelivr.net/gh/sinlin/baidu@1.0.4/2018-10-30/menu.css // @resource baiduOne https://cdn.jsdelivr.net/gh/sinlin/baidu@1.0.4/2018-10-30/one.css // @resource baiduTwo https://cdn.jsdelivr.net/gh/sinlin/baidu@1.0.4/2018-10-30/two.css // @resource baiduThree https://cdn.jsdelivr.net/gh/sinlin/baidu@1.0.4/2018-10-30/three.css // @connect * // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_getResourceURL // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_xmlhttpRequest // @downloadURL none // ==/UserScript== (() => { //初始化配置 const Config = { //是否调试 IS_DEBUG: false, //壁纸地址 BACKGROUND_URL: "https://ss2.bdstatic.com/lfoZeXSm1A5BphGlnYG/skin/37.jpg", //菜单按钮 ID MENU_BUTTON_ID: "CustomMenu", //菜单功能页 ID MENU_PAGE_ID: "menulist", //菜单保存按钮 ID MENU_SAVA_ID: "menusava", //功能配置 OPTIONS: [ //页面布局:1:普通页面,2:单列居中,3:双列居中,4:三列居中 { name: "SELECT_PAGE", value: 1 }, //重定向 { name: "SWITCH_IS_REDIRECT", value: false }, //加载下一页 { name: "SWITCH_IS_LOADPAGE", value: false }, //固定侧边栏 { name: "SWITCH_IS_FIXEDSILDER", value: false }, //加载背景 { name: "SWITCH_IS_BACKGOUND", value: false }, //百家号 { name: "SWITCH_IS_BAIJIAHAO", value: false } ], //百度样式 BAIDU_STYLES: [ { //百度首页 INDEX: GM_getResourceText("baiduIndexStyle"), //普通页 COMMON: GM_getResourceText("baiduCommonStyle"), //菜单 MENU: GM_getResourceText("baiduMenu"), //单页 ONE: GM_getResourceText("baiduOne"), //双页 TWO: GM_getResourceText("baiduTwo"), //三页 THREE: GM_getResourceText("baiduThree") } ], //过滤功能 ,不能设置 G(已被 Google搜索 占用) FILTERS: [ { name: "B", value: "-baijiahao" }, { name: "T", value: "site:tieba.baidu.com" }, { name: "J", value: "site:juejin.im" }, { name: "S", value: "site:segmentfault.com" }, { name: "V", value: "site:v2ex.com" }, { name: "Z", value: "site:zhihu.com" } ] }; //打包 const BaiduConfig = { window, document, location, Config }; //Baidu const Baidu = (({ window, document, location, Config }) => { //创建空对象 const Baidu = Object.create(null); const Options = Object.create(null); //引用 const BAIDU_STYLES = Config.BAIDU_STYLES[0]; const BAIDU_OPTIONS = Config.OPTIONS; //菜单功能页选项 Options.SELECT_PAGE = BAIDU_OPTIONS[0]; Options.SWITCH_IS_REDIRECT = BAIDU_OPTIONS[1]; Options.SWITCH_IS_LOADPAGE = BAIDU_OPTIONS[2]; Options.SWITCH_IS_FIXEDSILDER = BAIDU_OPTIONS[3]; Options.SWITCH_IS_BACKGOUND = BAIDU_OPTIONS[4]; Options.SWITCH_IS_BAIJIAHAO = BAIDU_OPTIONS[5]; /** * 实现 ready 功能, 文档完成后即执行 * 举例 : Baidu.ready = function(){} */ Reflect.defineProperty(Baidu, "ready", { set: fn => { if (document.readyState === "complete") { fn(); } else if (!!document.addEventListener) { document.addEventListener("DOMContentLoaded", () => { fn(); }, true); } else { throw new Error("Baidu.ready can't use"); } } }); /** * 延时执行 */ Baidu.delayRun = (callback, count = 0) => { if (count === 0) { window.requestAnimationFrame(() => { callback(); }); } else { window.requestAnimationFrame(() => { Baidu.delayRun(callback, --count); }); } }; /** * DOM缓存 */ class DomCache { constructor() { this.cache = {}; } hasDomCache(name) { return this.cache.hasOwnProperty(name) && !!this.cache[name] ? true : false; } deleteCache() { this.cache = {}; } getElementById(name) { if (!this.hasDomCache(name)) { this.cache[name] = document.getElementById(name); } return this.cache[name]; } } Baidu.DOM = new DomCache(); /** * GM数据类 * @class GM */ let GM = class { static getValue(selection) { try { return GM_getValue(selection.name, selection.value); } catch (error) { throw new Error(error); } } static setValue(name, value) { try { GM_setValue(name, value); } catch (error) { throw new Error(error); } } static addStyle(style) { try { GM_addStyle(style); } catch (error) { throw new Error(error); } } static xmlhttpRequest(object) { try { GM_xmlhttpRequest(object); } catch (error) { throw new Error(error); } } }; /** * 减少执行 */ class ReduceExecute { constructor() { this.isNormalExecuteState = true; } /** * 是否正常执行 */ isNormalExecute() { return this.isNormalExecuteState; } /** * 停止正常执行 */ stopNormalExecute() { this.isNormalExecuteState = false; return this; } /** * 恢复正常执行 * @param time 时间片 1 = 16.7ms */ regainNormalExecute(time) { Baidu.delayRun(() => { this.isNormalExecuteState = true; }, time); } } /** * 缓存类 * 用于对数据的缓存 * @class Cache * */ class Cache { constructor() { this.cache = ""; } /** * 删除缓存 */ delCache() { this.cache = ""; } /** * 添加缓存 * @param Content {string} 缓存内容 */ addCache(Content) { this.cache += Content; } /** * 获取缓存内容 */ getCache() { return this.cache; } } /** * 样式 * @class Style * @extends Cache * */ class Style { constructor() { this.Cache = new Cache(); this.GM = GM; } /** * 导入样式 * @method importStyle * */ importStyle() { let styles = this.Cache.getCache(); try { this.GM.addStyle(styles); } catch (error) { throw new Error("can't import styles:" + error); } } /** * 将样式加入缓存 * @param {stirng} style 样式 * @return {Style} this 该实例 */ add(style) { this.Cache.addCache(style); return this; } /** * 结束 * 开始将缓存样式导入,并清空缓存 */ end() { this.importStyle(); this.Cache.delCache(); } } /** * 函数执行器 - 组合执行 */ class Commond { constructor() { this.commondList = []; } /** * 添加数组缓存中 * @param {object} command 对象实例 */ add(command) { this.commondList.push(command); } /** * 执行数值缓存中的实例 * 如果执行实例返回 true 停止执行。 */ execute(isSplitExecute = false) { for (let i = 0, command; (command = this.commondList[i++]);) { command.execute(); } } } /** * 检测功能 - 避免代码多次执行 * @class AvoidMulExecute */ class AvoidMulExecute { constructor() { //标志名称 this.signName = "isRun"; } /** * 增加标志位 */ setSign() { let signName = this.signName; let container = document.getElementById("content_left"); container.setAttribute(signName, true); } /** * 判断是否存在标志位 */ hasSign() { let signName = this.signName; let container = document.getElementById("content_left"); return !!container.hasAttribute(signName); } } /** * 重定向搜索地址 * @class RedirectURL */ class RedirectURL { constructor() { this.end = "&baidu"; } /** * 重定向地址 * @method redirectURL */ redirectURL() { let end = this.end; let URL = location.href; if (!URL.endsWith(end)) { //判断是否存在步进值,没有使用默认值 20 if (!URL.includes("&rn=")) { URL += "&rn=20"; } //拼接地址 URL += end; //去除推广链接 URL = URL.replace(/\&tn=\S+\&/, "&"); //跳转 location.href = URL; } } /** * 初始化 * @method init */ init() { this.redirectURL(); } /** * 启动 * @method execute */ execute() { this.init(); } } /** * 首页样式 * @class IMportIndexStyle */ class ImportIndexStyle { constructor() { //实例化 this.Style = new Style(); } /** * 导入样式 * @method import */ import() { let style = this.Style; style.add(BAIDU_STYLES.INDEX); style.end(); } /** * 初始化 * @method init */ init() { this.import(); } /** * 执行 * @method execute */ execute() { this.init(); } } /** * 百家号 */ class Baijiahao { constructor() { this.GM = GM; this.elementID = "mybaijiahao"; this.filterValue = "baijiahao"; this.templateURL = "https://www.baidu.com/s?ie=utf-8&wd="; //是否执行 this.isExecute = Boolean(this.GM.getValue(Options.SWITCH_IS_BAIJIAHAO)); } /** * 根据关键字搜索 * @param url 部分url 如?wd=1&test=2 * @param keyworld 有搜索的关键词 */ getKeyValue(url, keyworld) { //移除首字母 if (url.startsWith("?")) { url = url.substr(1); } //转化为数组 let arr = url.split("&").map(value => { return value.split("="); }); //获取关键值 for (let [name, value] of arr) { if (name === keyworld) { return value; } } } /** * 获取关键词 * @param url 部分url 如?wd=1&test=2 */ getSearchKeyword(url) { let name = "wd"; return this.getKeyValue(url, name); } /** * 重定向 */ redirect() { let filterValue = this.filterValue; let templateURL = this.templateURL; let keyValue = this.getSearchKeyword(location.search); if (!keyValue.includes(filterValue)) { location.href = templateURL + keyValue + " -" + filterValue; } } /** * 隐藏过滤值 */ hidefilterValue() { let keyValue = " -" + this.filterValue; let reg = RegExp(keyValue); let input = document.getElementById("kw"); input.value = input.value.replace(reg, ""); } /** * 显示输入值 */ showInputValue() { let input = document.getElementById("kw"); input.style.visibility = "visible"; } /** * 隐藏输入值 */ hideinputValue() { let style = new Style(); style.add("#kw{visibility:hidden}"); style.end(); } /** * 判断是否存在新的输入框 */ hasNewInput() { return !!document.getElementById(this.elementID); } /** * 是新的input和旧的input内容保持 */ replaceInputContent() { let input = document.getElementById(this.elementID); let oldInput = document.getElementById("kw"); input.value = oldInput.value.trim().replace(/\s-baijiahao/, ""); } /** * 插入新的输入框:用于映射数据 */ insertNewInput() { let container = document.getElementsByClassName("s_ipt_wr")[0]; let oldInput = document.getElementById("kw"); let input = document.createElement("input"); input.id = this.elementID; input.type = "text"; // input.autofocus = true; input.autocomplete = "off"; input.value = oldInput.value.trim().replace(/\s-baijiahao/, ""); input.setAttribute("maxlength", "255"); container.appendChild(input); Promise.resolve().then(() => { input.blur(); }); } /** * 对新的输入框绑定事件 */ bindNewInputEvent() { let input = document.getElementById(this.elementID); let oldInput = document.getElementById("kw"); input.addEventListener("keyup", () => { if (input.value.trim() !== "") { oldInput.value = input.value.replace(/\s-baijiahao/, "") + " -" + this.filterValue; } }, false); input.blur(); oldInput.blur(); } /** * 初始化 */ init() { //重定向 this.redirect(); //隐藏值 this.hideinputValue(); Baidu.ready = () => { //this.hidefilterValue(); if (!this.hasNewInput()) { this.insertNewInput(); Promise.resolve().then(() => { this.bindNewInputEvent(); }); } else { this.replaceInputContent(); } // this.showInputValue(); }; } /** * 执行 */ execute() { if (this.isExecute) { this.init(); } } } /** * 导入普通结果页样式 * @class ImportCommonStyle */ class ImportCommonStyle { constructor() { //实例化 this.Style = new Style(); this.GM = GM; //功能选项 this.Options = Options; //背景地址 this.backgoundURL = Config.BACKGROUND_URL; //布局类型 this.layoutType = Number(this.GM.getValue(this.Options.SELECT_PAGE)); //是否导入背景 this.isImportBackground = Boolean(this.GM.getValue(this.Options.SWITCH_IS_BACKGOUND)); //是否导入样式 this.isImportFixedSlider = Boolean(this.GM.getValue(this.Options.SWITCH_IS_FIXEDSILDER)); } /** * 样式 - 背景 * @method styleForBackground * @returns {string} 样式 */ styleForBackground() { let defaultURL = this.backgoundURL; if (this.isImportBackground) { return `body{background-color:transparent!important}body:after{content:"";position:fixed;top:0;bottom:0;left:0;right:0;background-image:url(${defaultURL})!important;background-size:cover!important;z-index:-1}#head{background-color:hsla(0, 0%, 100%, 0.65)!important;border-bottom-color:hsla(0, 0%, 52%, 0.3)!important}#content_left .c-container,#rs{border:none!important;background: hsla(0, 0%, 100%, 0.85)!important}#form>.s_ipt_wr.bg{background:#fff!important}#u>a{color: hsl(216, 80%, 63%)!important}#u>a:after{background:transparent!important;border:1px solid!important;}`; } else { return ""; } } /** * 样式 - 固定侧边栏 * @method styleForFixedSilder * @returns {string} 样式 */ styleForFixedSilder() { if (this.isImportFixedSlider) { return "#s_tab{left:0!important;opacity:1!important;}"; } else { return ""; } } /** * 样式 - 页面布局 * @method styleForPageLayout * @returns {string} 样式 */ styleForPageLayout() { let layoutType = this.layoutType; switch (layoutType) { case 1: return ""; case 2: return BAIDU_STYLES.ONE; case 3: return BAIDU_STYLES.TWO; case 4: return BAIDU_STYLES.THREE; } } /** * 样式 - 菜单 * @method styleForMenu * @returns {string} 样式 */ styleForMenu() { return BAIDU_STYLES.MENU; } /** * 样式 - 结果页 * @method styleForCommand * @returns {string} 样式 */ styleForCommand() { return BAIDU_STYLES.COMMON; } /** * 导入样式 * @method import */ import() { let style = this.Style; style.add(this.styleForCommand()); style.add(this.styleForMenu()); style.add(this.styleForPageLayout()); style.add(this.styleForFixedSilder()); style.add(this.styleForBackground()); style.end(); } /** * 初始化 * @method init */ init() { this.import(); } /** * 执行 * @method execute */ execute() { this.init(); } } /** * 菜单功能页 * @class MenuItemsOptions * @extends MenuCommand */ class MenuItemsOptions { constructor() { this.GM = GM; this.Options = Options; this.layoutTagName = "baidupage"; this.switchTagName = "baiduswitch"; //页面布局类型 this.layoutType = Number(this.GM.getValue(this.Options.SELECT_PAGE)); } /** * 获得 HTML - 页面布局选项 * @param content 显示的内容 * @param layoutType 页面布局类型 */ getContentPageSelect(content, layoutType) { let checked = this.layoutType === layoutType ? "checked" : ""; return `
  • ${content}
  • `; } /** * 获得 HTML - 功能选项选项 * @param content 显示的内容 * @param selection 功能配置 */ getContentFunSelect(content, selection) { let switchName = selection.name; let checked = Boolean(this.GM.getValue(selection)) ? "checked" : ""; return `
  • ${content}
  • `; } /** * 获得 HTML - 保存 * @param content 显示的内容 */ getContentSava(content) { let idName = Config.MENU_SAVA_ID; return ``; } //获取整体 HTML getContent() { let content = ""; content += "
      页面选择"; content += this.getContentPageSelect("普通页面", 1); content += this.getContentPageSelect("单页居中", 2); content += this.getContentPageSelect("双页居中", 3); content += this.getContentPageSelect("三页居中", 4); content += "
    "; content += "
      功能选择"; content += this.getContentFunSelect("使用重定向", this.Options.SWITCH_IS_REDIRECT); content += this.getContentFunSelect("自动下一页", this.Options.SWITCH_IS_LOADPAGE); content += this.getContentFunSelect("固定侧边栏", this.Options.SWITCH_IS_FIXEDSILDER); content += this.getContentFunSelect("加载背景", this.Options.SWITCH_IS_BACKGOUND); content += this.getContentFunSelect("屏蔽百家号", this.Options.SWITCH_IS_BAIJIAHAO); content += "
    "; content += this.getContentSava("保存"); return content; } /** * 绑定保存事件 */ bindSavaClick() { let sava = document.getElementById(Config.MENU_SAVA_ID); sava.onclick = event => { let e = event || window.event; //页面布局选项 let radios = document.getElementsByName(this.layoutTagName); for (let i = 0, radio; (radio = radios[i++]);) { if (radio.checked) { let name = Options.SELECT_PAGE.name; let value = radio.value; this.GM.setValue(name, value); break; } } //功能选项 let checkboxs = document.getElementsByName(this.switchTagName); for (let i = 0, checkbox; (checkbox = checkboxs[i++]);) { let name = checkbox.value; if (checkbox.checked) { this.GM.setValue(name, true); } else { this.GM.setValue(name, false); } } e.stopPropagation(); location.href = location.href; }; } /** * 插入节点 */ insertNode() { let container = document.getElementById("u"), content = this.getContent(), div = document.createElement("div"); div.id = Config.MENU_PAGE_ID; div.style.display = "none"; div.innerHTML = `
    ${content}
    `; container.insertBefore(div, container.firstChild); } /** * 初始化 */ init() { let isExecute = document.getElementById(Config.MENU_PAGE_ID); if (!isExecute) { this.insertNode(); //异步执行绑定事件 Baidu.delayRun(() => { this.bindSavaClick(); }, 10); } } /** * 执行 */ execute() { Baidu.ready = () => { try { this.init(); } catch (e) { throw new Error(e); } }; } } /** * 菜单按钮 * @class MenuButton * @extends MenuCommand */ class MenuButton { constructor() { this.MenuItemsOptions = new MenuItemsOptions(); } /** * 修复未登录按钮错位问题 */ fixedNoLoginButtonPosition() { let container = document.getElementById("u"); let isExecute = container.querySelector("#u>a[name='tj_login']"); if (isExecute) { let selector = document.getElementById(Config.MENU_BUTTON_ID); selector.setAttribute("style", "top:-4px!important"); } } /** * 第二次单击隐藏 */ bindClickHide() { document.onclick = event => { let e = event || window.event; let container = document.getElementById("container"); let items = document.getElementById(Config.MENU_PAGE_ID); let isScreenClick = e.target ? e.target == container : e.srcElement == container; if (isScreenClick) { items.style.display = "none"; } }; } /** * 单击打开功能选项页 */ bindClick() { let container = document.getElementById(Config.MENU_BUTTON_ID); container.onclick = event => { let e = event || window.event; let items = document.getElementById(Config.MENU_PAGE_ID); let style = items.style; let isShow = style.display === "block"; if (isShow) { style.display = "none"; } else { style.display = "block"; } //阻止冒泡 e.stopPropagation(); }; } /** * 插入节点 */ insertNode() { let container = document.getElementById("u"); let div = document.createElement("a"); div.id = Config.MENU_BUTTON_ID; div.innerHTML = "自定义"; container.insertBefore(div, container.firstChild); } /** * 初始化 */ init() { let isExecute = document.getElementById(Config.MENU_BUTTON_ID); if (!isExecute) { this.insertNode(); //异步绑定事件 Baidu.delayRun(() => { this.bindClick(); this.bindClickHide(); }, 10); } } /** * 执行 */ execute() { Baidu.ready = () => { Promise.resolve().then(() => { this.init(); }); }; //执行菜单功能面板 Promise.resolve().then(() => { this.MenuItemsOptions.execute(); }); } } /** * 多页布局 * @class MUlPageLayout */ class MulPageLayout { constructor() { this.GM = GM; this.DOM = Baidu.DOM; this.container = null; this.lists = null; //多列布局值集合 this.layoutTypes = [3, 4]; //当前布局类型 this.layoutType = Number(this.GM.getValue(Options.SELECT_PAGE)); //根据 类名 模拟高度 this.VIRTUAL_HEIGHTS_BY_CLASSNAE = [ { name: ".c-container>.op-b2b-straight", value: 420 }, { name: ".c-container>.c-offset", value: 320 }, { name: ".c-container>.c-border", value: 270 }, { name: ".c-container>.op-tieba-general-lookmore", value: 260 }, { name: ".c-container>.op-img-address-desktop-cont", value: 210 }, { name: ".c-container>.c-gap-top-small", value: 130 } ]; //根据 srcid 属性值 模拟高度 this.VIRTUAL_HEIGHTS_BY_SRCID = [ { name: 1599, value: 128 }, { name: 1508, value: 170 }, { name: 1527, value: 170 }, { name: 1528, value: 220 }, { name: 1529, value: 220 }, { name: 1537, value: 510 }, { name: 1539, value: 280 }, { name: 1545, value: 230 }, { name: 1547, value: 230 }, { name: 4515, value: 540 }, { name: 5103, value: 400 }, { name: 8041, value: 260 }, { name: 8191, value: 200 }, { name: 10, value: 260 }, { name: 13, value: 220 }, { name: 19, value: 200 } ]; } /** * 是否为多列布局 * @return {boolean} */ isMulLayout() { let layoutType = this.layoutType; let layoutTypes = this.layoutTypes; return layoutTypes.includes(layoutType); } /** * 初始化 */ resetDOM() { this.container = null; this.lists = null; } /** * 获取 #content_left 节点 */ getDomForContainer() { if (!this.container) { this.container = this.DOM.getElementById("content_left"); } return this.container; } /** * 获取 list 节点 */ getDomForLists() { if (!this.lists) { let container = this.getDomForContainer(); this.lists = container.getElementsByClassName("list"); } return this.lists; } //获取list高度合集 getListsHeight() { let lists = this.getDomForLists(); let heights = []; for (let i = 0, list; (list = lists[i++]);) { heights.push(list.clientHeight); } return heights; } /** * 模拟高度 * 防止获取真实高度导致性能问题 * @param item */ getItemVirtualHeight(item) { // 获取虚拟高度合集 let VIRTUAL_DATAS = this.VIRTUAL_HEIGHTS_BY_SRCID; //默认高度 let height = 122; //匹配 srcid 正则 let reg = /srcid="\d+"/; //获取srcid let srcid = Number(/\d+/.exec(reg.exec(item.outerHTML))); //根据srcid值获取虚拟高度 for (let i = 0, data; (data = VIRTUAL_DATAS[i++]);) { //大于 10000 直接赋予新高度 if (srcid > 10000) { height = 310; break; } else if (srcid === data["name"]) { height = data["value"]; break; } } return height; } /** * 01 - 添加内容到lists * 使用 DOM 到 DOM, 即将 #content_left 下的子元素移动到 lists * 注意:需要提前将 DOM片段 添加到 #content_left下 */ addWebUseDomToDom() { let container = this.getDomForContainer(); let lists = this.getDomForLists(); let items = container.querySelectorAll("#content_left>.c-container"); let heights = this.getListsHeight(); let frames = []; //初始化 for (let i = 0, length = lists.length; i < length; i++) { //缓存 frames.push(document.createDocumentFragment()); } //将 item 添加到虚拟DOM中 for (let i = 0, item; (item = items[i++]);) { //获取最小的高度值 let minHeight = Reflect.apply(Math.min, null, heights); //获取最小的高度的索引值 let index = heights.indexOf(minHeight); //添加到高度 heights[index] += item.clientHeight; //缓存 frames[index].appendChild(item); } //添加到真实DOM for (let i = 0, length = lists.length; i < length; i++) { Baidu.delayRun(() => { lists[i].appendChild(frames[i]); }, i); } } /** * 02 - 添加内容到lists * * * @param frame DOM片段 */ addWebUseFrameToDom(frame) { //获取lists let lists = this.getDomForLists(); //获取列表合集 let items = frame.getElementsByClassName("c-container"); //获取初始化高度集合 let heights = this.getListsHeight(); //将 item 添加到list中 for (let i = 0, item; (item = items[i]);) { //获取高度合集 //获取最小的高度值 let minHeight = Reflect.apply(Math.min, null, heights); //获取最小的高度的索引值 let index = heights.indexOf(minHeight); //添加到list中 lists[index].appendChild(item); //更新高度 heights = this.getListsHeight(); } } /** * 03 - 添加内容到 lists * 使用"模拟高度"进行添加 * * @param frame DOM片段 */ addWebUseVirtualToDom(frame) { let lists = this.getDomForLists(); let frames = []; //获取列表合集 let items = frame.getElementsByClassName("c-container"); if (items.length <= 0) { return; } //获取初始化高度集合 let heights = this.getListsHeight(); //初始化 for (let i = 0, length = lists.length; i < length; i++) { frames[i] = document.createDocumentFragment(); } //将 item 添加到list中 for (let i = 0, item; (item = items[i]);) { //获取最小的高度值 let minHeight = Reflect.apply(Math.min, null, heights); //获取最小的高度的索引值 let index = heights.indexOf(minHeight); //获取模拟高度 heights[index] += this.getItemVirtualHeight(item); //添加到list中 frames[index].appendChild(item); } Baidu.delayRun(() => { //插入内容到list for (let i = 0, length = lists.length; i < length; i++) { lists[i].appendChild(frames[i]); } }, 1); } /** * 是否存在list节点 */ hasListNode() { let lists = this.getDomForLists(); return lists.length > 0; } /** * 向网页添加列表 */ insertListNode() { let layoutType = this.layoutType; let container = this.getDomForContainer(); let frame = document.createDocumentFragment(); //创建list节点 for (let i = 1, div, length = layoutType; i < length; i++) { div = document.createElement("div"); div.id = "list" + i; div.className = "list"; frame.appendChild(div); } //将节点插入到文档中 container.insertBefore(frame, container.firstChild); return this; } /** * 添加内容到 lists */ addListContent(frame) { this.resetDOM(); Baidu.delayRun(() => { this.addWebUseVirtualToDom(frame); }, 0); } /** * 初始化 */ init() { try { this.resetDOM(); if (!this.hasListNode()) { //插入list节点并刷新节点 this.insertListNode(); } this.addWebUseDomToDom(); } catch (error) { console.error(error); } } /** * 执行 */ execute() { if (this.isMulLayout()) { Baidu.ready = () => { this.init(); }; } } } /** * 自动加载下一页 * @class AutoLoadNextPage */ class AutoLoadNextPage { /** * 构造函数 */ constructor() { //赋值 this.GM = GM; //DOM this.DOM = Baidu.DOM; //实例化 - 多页布局 this.MulpageLayout = new MulPageLayout(); //实例化 - 重定向 this.Redirect = new Redirect(); //实例化 - this.Parser = new DOMParser(); //减少频率 this.Reduce = new ReduceExecute(); this.isExecute = Boolean(this.GM.getValue(Options.SWITCH_IS_LOADPAGE)); } /** * 重置 */ reset() { //是否第一次执行 this.isFirstRun = true; //是否导入过 (this.isImport = false), //下一页真实地址 (this.realNextURL = null); //模板地址 this.templateURL = null; //步进值(默认值) this.step = 0; //每页起始值 this.count = 0; //偏移高度 this.offsetHight = 1000; //缓存 this.cache = []; //缓存量 this.cacheSize = 1; //节点缓存 this.container = this.DOM.getElementById("content_left"); } /** * 获取真实下一个的地址 * @returns {string} 下一页地址 */ getNextPageRealURL() { if (!this.realNextURL) { let page = document.getElementById("page"); this.realNextURL = page .getElementsByClassName("n")[0] .getAttribute("href"); } return this.realNextURL; } /** * 获取步进值 * @returns {number} 步进值 */ getNextPageStepValue() { if (!this.step) { //提取 &pn=20 中的20 let regParam = /(&pn=\d+)/; let regValue = /\d+/; let result = regParam.exec(this.getNextPageRealURL()); this.step = Number(regValue.exec(result)); } return this.step; } /** * 获取模板地址 * @returns {string} this.templateURL: 模板地址 */ getTempletURL() { this.templateURL = this.templateURL || this.getNextPageRealURL().replace(/&pn=\d+/, ""); return this.templateURL; } /** * 获取下一页合成地址 * @returns {sting} 下一页的地址 */ getNextPageComposeURL() { this.count += this.getNextPageStepValue(); return this.getTempletURL() + `&pn=${this.count}`; } /** * 判断是否存在缓存 * @returns {boolean} 存在: true * @returns {boolean} 不存在:false */ hasCache() { return this.cache.length >= this.cacheSize; } /** * 将响应文本添加到缓存中 * @param responseText 响应文本 */ addCache(responseText) { //转化为DOM对象 let reg = //; let parser = this.Parser; let htmlDoc = parser.parseFromString(reg.exec(responseText)[0], "text/html"); //获取Items let items = htmlDoc .getElementById("content_left") .getElementsByClassName("c-container"); //添加到缓存 let frame = document.createElement("div"); //appendchild 自动执行迭代器 导致 i++ (小心); for (let i = 0, item; (item = items[i]);) { frame.appendChild(item); } //加入缓存 this.cache.push(frame); } /** * 监测滚动位置 */ checkScrollPosition() { if (this.Reduce.isNormalExecute()) { this.Reduce.stopNormalExecute().regainNormalExecute(5); let element = document.documentElement, clientHeight = element.clientHeight, scrollTop = element.scrollTop || window.pageYOffset || document.body.scrollTop || 0, scrollHeight = Number(element.scrollHeight); //判断 if (clientHeight + scrollTop + this.offsetHight > scrollHeight) { this.removeScrollEvent(); this.task(); } } } /** * 移除滚动事件 */ removeScrollEvent() { document.onscroll = event => { let e = event || window.event; e.preventDefault(); }; return this; } /** * 绑定滚动触发事件 */ bindScrollEvent() { document.onscroll = () => { this.checkScrollPosition(); }; } /** * 将 DOM 插入到相应的位置 */ addItemsToWeb() { //插入内容到DOM节点 if (this.MulpageLayout.isMulLayout()) { this.MulpageLayout.addListContent(this.cache.shift()); } else { //this.container.innerHTML += this.cache.shift().innerHTML; let div = document.createElement("div"); div.id = String("Baidu-" + String(new Date().getTime())); div.innerHTML = this.cache.shift().outerHTML; this.container.appendChild(div); } } /**n * 发送请求 */ requireNextPageContent() { this.GM.xmlhttpRequest({ method: "GET", url: this.getNextPageComposeURL(), timeout: 3000, responseType: "text", onload: response => { if (response.status === 200 || response.status === 304) { //如果不存在缓存,再发一次 if (!this.hasCache()) { this.requireNextPageContent(); } //添加响应文本到缓存 this.addCache(response.responseText); //开始导入数据到网页 if (!this.isImport) { this.import(); } } }, onerror: response => { console.error(response); } }); } /** * 导入数据到网页 */ import() { this.isImport = true; //添加内容到网页 this.addItemsToWeb(); //重定向 this.Redirect.execute(); //绑定滚动事件 Baidu.delayRun(() => { this.removeScrollEvent().bindScrollEvent(); }, 3); } /** * 任务调度 * @param URL */ task() { //设置有导入过 this.isImport = false; //发送请求 this.requireNextPageContent(); //如果有缓存 //如果存在缓存 if (this.hasCache()) { this.import(); } } /** * 隐藏元素 */ hideElement() { let rs = document.getElementById("rs"); let page = document.getElementById("page"); rs.style.visibility = "hidden"; page.style.visibility = "hidden"; } /** * 初始化 */ init() { //重置配置 this.reset(); //开始加载 this.task(); //隐藏元素 Baidu.delayRun(() => { this.hideElement(); }, 3); } /** * 入口 * @returns {void} */ execute() { if (this.isExecute) { Baidu.ready = () => { this.init(); }; } } } /** * 重定向 * @class Redirect */ class Redirect { constructor() { this.GM = GM; this.DOM = Baidu.DOM; //重定向后需添加类名(防止重复重定向) this.redirectClassName = "isredirect"; //是否执行 this.isExecute = Boolean(this.GM.getValue(Options.SWITCH_IS_REDIRECT)); } /** * 重定向 * @param item a节点 */ redirect(item) { this.GM.xmlhttpRequest({ method: "HEAD", url: item.href, onload: response => { let realURL = response.finalUrl; item.href = realURL; //加入重定向标志 item.className = this.redirectClassName; //移除不必要的属性 item.removeAttribute("data-click"); } }); } /** * 开始 */ start() { let container = this.DOM.getElementById("content_left"); let items = container.querySelectorAll("h3>a:not([class])"); for (let i = 0, item; (item = items[i++]);) { //延时执行 Baidu.delayRun(() => { this.redirect(item); }, i); } } /** * 初始化 */ init() { this.start(); } /** * 执行 */ execute() { if (this.isExecute) { Baidu.ready = () => { Baidu.delayRun(() => { this.init(); }, 5); }; } } } /** * 回到顶部 * @class BackToTop */ class BackToTop { /** * 单击回到顶部 */ bindClick() { let container = document.getElementsByClassName("s_form")[0]; container.onclick = event => { let e = event || window.event; let isContainer = e.target ? e.target === container : e.srcElement === container; if (isContainer) { //setInterval方案 let element = document.documentElement; let body = document.body; let node = element.scrollTop ? element : body; let top = node.scrollTop; let step = top / 20; let timer = setInterval(() => { if (node.scrollTop <= 0) { node.scrollTop = 0; clearInterval(timer); } node.scrollTop -= step; }, 10); e.stopPropagation(); } }; } /** * 初始化 */ init() { this.bindClick(); } /** * 执行 */ execute() { Baidu.ready = () => { Promise.resolve().then(() => { Baidu.delayRun(() => { this.init(); }, 10); }); }; } } /** * 谷歌 * 双击使用 google 搜索 * @class Google */ class Google { googleSearch() { let googlePath = "https://www.google.com/search?q="; let searchContent = document.getElementById("kw").value.trim(); let url = googlePath + encodeURIComponent(searchContent); window.open(url); } /** * 绑定双击打开Google搜索 */ bindDoubleClick() { let button = document.getElementById("su"); button.ondblclick = () => { this.googleSearch(); }; } /** * 初始化 */ init() { this.bindDoubleClick(); } /** * 执行 */ execute() { Baidu.ready = () => { Promise.resolve().then(() => { this.init(); }); }; } } /** * 替换首页搜索栏 * @class ReplaceSearch */ class ReplaceSearch { constructor() { this.GM = GM; this.inputId = "baiduinput"; this.searchPath = "https://www.baidu.com/s?ie=UTF-8&wd="; } /** * 搜索 */ search() { let value = document.getElementById(this.inputId).value.trim(); if (value !== "") { let isBaijiahao = this.GM.getValue(Options.SWITCH_IS_BAIJIAHAO); if (!isBaijiahao) { location.href = this.searchPath + encodeURIComponent(value); } else { location.href = this.searchPath + encodeURIComponent(value) + " -baijiahao"; } } } /** * 绑定提交事件 */ bindSubmit() { let button = document.getElementById("su"); button.setAttribute("type", "button"); button.onclick = event => { let e = event || window.event; this.search(); e.stopPropagation(); }; } /** * 检测输入 */ bindKeydown() { let input = document.getElementById(this.inputId); input = document.getElementById("form"); input.onkeydown = event => { let e = event || window.event; let keyCode = e.keyCode || e.which || e.charCode; if (keyCode === 13) { this.search(); } e.stopPropagation(); }; } /** * 插入节点 * 覆盖原来的搜索框 */ insertNode() { //屏蔽原来文本输入 document .getElementById("kw") .setAttribute("disabled", "disabled"); let container = document.getElementById("s_kw_wrap") || document.getElementsByClassName("s_ipt_wr")[0]; let div = document.createElement("input"); div.id = this.inputId; div.type = "text"; div.autofocus = true; div.autocomplete = "off"; container.appendChild(div); //延时聚焦 Promise.resolve().then(() => { document.getElementById(this.inputId).focus(); }); } /** * 初始化 */ init() { try { this.insertNode(); Promise.resolve().then(() => { this.bindSubmit(); this.bindKeydown(); }); } catch (e) { throw new Error(e); } } /** * 执行 */ execute() { Baidu.ready = () => { this.init(); }; } } /** * 执行广告和无用的节点 * @class RemoveNode */ class RemoveNode { constructor() { this.nodes = ["content_right", "content_bottom", "foot"]; this.style = "display:block !important"; } /** * 根据 ID 移除 */ removeNodeForID() { let items = this.nodes; for (let i = 0, item; (item = items[i++]);) { let node = document.getElementById(item); node.parentNode.removeChild(node); } } removeNodeForStyle() { let container = document.getElementById("content_left"); let items = container.querySelectorAll(`#content_left>div[style*="${this.style}"]`); for (let i = 0, item; (item = items[i++]);) { item.parentNode.removeChild(item); } } /** */ init() { try { this.removeNodeForID(); } catch (error) { } } /** * 执行 */ execute() { Baidu.ready = () => { this.init(); }; } } /** * 快捷键 * @class ShortcutKeys */ class ShortcutKeys { constructor() { this.Google = new Google(); this.filters = Config.FILTERS; this.target = null; this.KEY_ENTER = 13; this.KEY_ALT = 18; this.KEY_SHIFT = 16; this.KEY_CTRL = 17; this.KEY_GOOGLE = "G"; } /** * 过滤搜索 */ filterSearch(filterName) { //移除如 "-baijiahao" 正则 let reg1 = /\s\-\S+/; //移除如 "site:baidu" 正则 let reg2 = /\ssite\:\S+/; let URL = "https://www.baidu.com/s?ie=utf-8&wd="; let content = document.getElementById("kw").value.trim(); content = content.replace(reg1, "").trim(); content = content.replace(reg2, "").trim(); location.href = URL + encodeURIComponent(content) + " " + filterName; } /** * 选择全部 */ selectAllContent() { let input = document.getElementById("kw"); input.focus(); input.selectionStart = 0; input.selectionEnd = input.value.length; } /** * 绑定快捷键 */ bindKeys() { let defaultTarget = this.target; document.onkeyup = event => { let e = event || window.event; if (e.target === defaultTarget || e.target === document) { let keyCode = e.keyCode || e.which || e.charCode; //Ctrl + Enter 全选中 if (keyCode == this.KEY_ENTER && e.ctrlKey) { this.selectAllContent(); return; } //谷歌搜索 if (keyCode === this.KEY_GOOGLE.toUpperCase().charCodeAt() && !e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey) { this.Google.googleSearch(); return; } //过滤搜索 for (let { name, value } of this.filters) { if (keyCode === name.toUpperCase().charCodeAt() && !e.altKey && !e.shiftKey && !e.ctrlKey && !e.metaKey) { this.filterSearch(value); return; } } } e.stopPropagation(); }; } /** * 重置 */ reset() { this.target = document.getElementsByTagName("body")[0] || null; } /** * 初始化 */ init() { try { this.reset(); this.bindKeys(); } catch (error) { } } /** * 执行 */ execute() { Baidu.ready = () => { this.init(); }; } } /** * Base地址重置 */ class BaseURL { run() { location.href = location.href.replace("https://www.baidu.com/#", "https://www.baidu.com/s?"); } } /** * 首页 */ class PageIndex { run() { //组合模式 let command = new Commond(); command.add(new ImportIndexStyle()); command.add(new ReplaceSearch()); command.execute(); } } /** * 搜索结果页 */ class PageCommon { run() { /** * 01 - 初始化执行 */ let command = new Commond(); command.add(new ImportCommonStyle()); command.add(new Baijiahao()); command.add(new MenuButton()); command.add(new MulPageLayout()); command.add(new AutoLoadNextPage()); command.add(new Redirect()); command.add(new BackToTop()); command.add(new Google()); command.add(new ShortcutKeys()); command.execute(); /** * 02 - 设置标志位 * 防止后期多次无用执行 */ let avoidMulExecute = new AvoidMulExecute(); //设置标志位 Baidu.ready = () => { avoidMulExecute.setSign(); }; /** * 03 - 监测 DOM */ //调用函数 let mutationfunc = () => { //只执行一次 if (!avoidMulExecute.hasSign()) { //设置标志位 avoidMulExecute.setSign(); //清除缓存 Baidu.DOM.deleteCache(); //执行 command.execute(); } }; //加载完成后 - 根据 DOM 变化重新执行函数( 防止Bash值改变时不触发脚本) window.onload = () => { let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; if (!!MutationObserver) { let observer = new MutationObserver(mutationfunc); let wrapper = document.querySelector("#wrapper"); let observerConfig = { childList: true, subtree: true //"attributes": true, //"characterData":true, //"attributesFilter": ["class"], }; //开始观察 observer.observe(wrapper, observerConfig); } else { console.error("百度搜索-优化: 浏览器不兼容 MutationObserver 接口, 请升级浏览器版本"); } }; } } /** * 简单工厂 */ class Factory { /** * * @param url */ static create(url) { //BASE地址(BASE地址会导致样式跳转,需要重定向) const URL_BASE = "https://www.baidu.com/#"; //普通页 01 const URL_COMMON_01 = "https://www.baidu.com/s"; //普通页 02 const URL_COMMON_02 = "https://www.baidu.com/baidu"; //首页 const URL_INDEX = "https://www.baidu.com"; //返回BASE if (url.startsWith(URL_BASE)) { return new BaseURL(); } //返回结果页 if (url.startsWith(URL_COMMON_01)) { return new PageCommon(); } //返回结果页 if (url.startsWith(URL_COMMON_02)) { return new PageCommon(); } //返回首页 if (url.startsWith(URL_INDEX)) { return new PageIndex(); } } } /** * 启动函数 */ Baidu.start = () => { Factory.create(location.href).run(); }; //返回对象 return Baidu; })(BaiduConfig); //启动 try { Baidu.start(); } catch (msg) { if (Config.IS_DEBUG) { console.error(msg); } } })();