// ==UserScript== // @name 我的搜索 // @namespace http://tampermonkey.net/ // @version 4.8.5 // @description 打造订阅式搜索,让我的搜索,只搜精品! // @license MIT // @author zhuangjie // @match *://*/* // @exclude http://127.0.0.1* // @exclude http://localhost* // @exclude http://192.168.* // @icon data:image/png;base64,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 // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.2/jquery.min.js // @require https://unpkg.com/pinyin-pro // @require https://cdn.jsdelivr.net/npm/showdown@1.9.0/dist/showdown.min.js // @resource markdown-css https://sindresorhus.com/github-markdown-css/github-markdown.css // @require https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js // @resource code-css https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css // @grant window.onurlchange // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_getResourceURL // @grant GM_deleteValue // @grant GM_registerMenuCommand // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 模块一:快捷键触发某一事件 (属于触发策略组) // 模块二:搜索视图(显示与隐藏)(属于搜索视图组) // 模块三:触发策略组触发策略触发搜索视图组视图 // 模块四:根据用户提供的策略(策略属于数据生成策略组)生成搜索项的数据库 // 模块五:视图接入数据库 // 脚本引入css文件 GM_addStyle(GM_getResourceText("code-css")); GM_addStyle(GM_getResourceText("markdown-css")); // 正则捕获 function captureRegEx(regex, text) { let m; let result = []; regex.lastIndex = 0; // 重置lastIndex while ((m = regex.exec(text)) !== null) { result.push(...m); } return result; } // 重写console.log方法 let originalLog = console.log; console.logout = function() { const prefix = "[我的搜索log]>>> "; const args = [prefix].concat(Array.from(arguments)); originalLog.apply(console, args); } // markdown转html 转换器 【1】 const converter = new showdown.Converter({ simpleLineBreaks:true, openLinksInNewWindow: true, metadata:true }); // 引入js /*$(document.head).html($(document.head).html()+` `)*/ // 提取URL根域名 function getUrlRoot(url,isRemovePrefix = true,isRemoveSuffix = true) { if(! (typeof url == "string" || url.length >= 3)) return url; // 可处理 // 判断是否有前缀 let prefix = ""; let root = ""; let suffix = ""; // 提取前缀 if(url.indexOf("://") != -1) { // 存在前缀 let prefixSplitArr = url.split("://") prefix = prefixSplitArr[0]; url = prefixSplitArr[1]; } // 提取root 和suffix if(url.indexOf("/") != -1) { let twoLevelIndex = url.indexOf("/") root = url.substr(0,twoLevelIndex); suffix = url.substr(twoLevelIndex,url.length-1); }else { root = url; suffix = ""; } return ((!isRemovePrefix && prefix != "")?(prefix+"://"):"") + root + (isRemoveSuffix?"":suffix); } // 解析出http url 结构 function parseUrl(url) { const regex = /(https?:|)(\/\/[^\/]*|[^\/]*)(\/[^\s\?]*|)(\??[^\s]*|)/; const matches = regex.exec(url); if (matches) { const protocol = matches[1]; const domain = matches[2]; const path = matches[3]; const params = matches[4]; return {protocol,domain,path,params} } return null; } // 检查网站是否可用 function checkUsability(templateUrl,isStopCheck = false) { return new Promise(function (resolve, reject) { // 判断是否要检查 if(isStopCheck) { reject(null); return; } var img=document.createElement("img"); img.src = templateUrl.replace("","https://www.baidu.com"); img.style= "display:none;"; img.onerror = function(e) { setTimeout(function() {img.remove();},20) reject(null); } img.onload = function(e) { setTimeout(function() {img.remove();},20) resolve(templateUrl); } document.body.appendChild(img); }); } // 数据缓存器 let cache = { get(key) { return GM_getValue(key); }, set(key,value) { GM_setValue(key,value); }, jGet(key) { let value = GM_getValue(key); if( value == null) return value; return JSON.parse(value); }, jSet(key,value) { value = JSON.stringify(value) GM_setValue(key,value); }, remove(key) { GM_deleteValue(key); }, cookieSet(cname,cvalue,exdays) { var d = new Date(); d.setTime(d.getTime()+exdays); var expires = "expires="+d.toGMTString(); document.cookie = cname + "=" + cvalue + "; " + expires; }, cookieGet(cname) { var name = cname + "="; var ca = document.cookie.split(';'); for(var i=0; i { ERROR.tell("视图未初始化,但你使用了它的未初始化的注册表信息!") }, viewDocument: null, setButtonVisibility: () => { ERROR.tell("按钮未初始化!") }, titleHandlerFuns: [], onViewFirstShow: [], menuActive: false, // 视图延时隐藏时间 delayedHideTime: 200 }, other: { // registry.other.UPDATE_CDNS_CACHE_KEY UPDATE_CDNS_CACHE_KEY: "UPDATE_CDNS_CACHE_KEY" }, searchData: { //registry.searchData.isSearchAll registry.searchData.NEW_DATA_EXPIRE_DAY_NUM data: [], // 旧的新数据 OLD_SEARCH_DATA_KEY: "OLD_SEARCH_DATAS_KEY", subscribeKey: "subscribeKey", showSize: 15, isSearchAll: false, searchEven: { event:{}, send(keyword) { keyword = (keyword??"").trim().toUpperCase(); let fun = this.event[keyword]; if(fun != null) { return fun(); } return null; } }, // 新数据设置的过期天数 NEW_DATA_EXPIRE_DAY_NUM:7, // 搜索逻辑,可用来手动触发搜索 triggerSearchHandle: function (keyword=""){ // 获取input元素 const inputEl = document.getElementById('my_search_input'); // 如果有传入搜索值,就要设置值 if(keyword != null) { inputEl.value = keyword; } // 创建一个事件对象 const event = new Event('input', { bubbles: true }); // 手动触发input事件 inputEl.dispatchEvent(event); // 维护全局搜索keyword this.keyword = keyword; }, // 事件函数 dataChange: [], onSearch: [], // 第一次处理数据块前 onFirstHandleNewDataBlock: [], // 新数据块处理完成事件 onNewDataBlockHandleAfter: [], // 新数据的flag NEW_ITEMS_FLAG: "[新]", // 搜索的keyword keyword: "", // 持久化Key SEARCH_DATA_KEY: "SEARCH_DATA_KEY", SEARCH_NEW_ITEMS_KEY:"SEARCH_NEW_ITEMS_KEY", // 搜索搜索出来的数据 searchData: [], pos: 0, clearUrlSearchTemplate(url) { return url.replace(/\[\[[^\[\]]*\]\]/gm,""); }, faviconSources: ["https://ico.di8du.com/get.php?url=","https://www.google.com/s2/favicons?domain=","/favicon.ico"], CACHE_FAVICON_SOURCE_KEY: "CACHE_FAVICON_SOURCE_KEY", CACHE_FAVICON_SOURCE_TIMEOUT: 1000*60*60, // 60分钟重新检测一下favicon源/过期时间 getFaviconAPI: (function(){ let faviconUrlTemplate = "/favicon.ico"; let isRemoteTemplate = false; // 查看是否已经检查模板 function checkTemplateAndUpdateTemplate() { if( !isRemoteTemplate && cache.get(registry.searchData.CACHE_FAVICON_SOURCE_KEY) != null ) { faviconUrlTemplate = cache.get(registry.searchData.CACHE_FAVICON_SOURCE_KEY).sourceTemplate; // 设置已经是远程Favicon模板 isRemoteTemplate = true; } } return function(url) { checkTemplateAndUpdateTemplate(); // 去掉模板,才是真正的URL url = registry.searchData.clearUrlSearchTemplate(url); // 获取网站的favicon没有使用第三方的api,而是直接请求网站的 协议://域名/favicon.ico 来获取 // 备选的第三方api: https://github.com/antongunov/favicongrabber.com 或 https://www.google.com/s2/favicons?domain= // return `https://s.qwant.com/fav/q/q/${getUrlRoot(url,true,true)}.ico`; return faviconUrlTemplate.replace("",url); } })(), tmpVar: null, // 用于防抖 getDataLength(target = "SELECT") { // 持久化的数据项数 let cacheData = cache.get(this.SEARCH_DATA_KEY) == null?[]:cache.get(this.SEARCH_DATA_KEY).data; let tmpData = this.data.length === 0?cacheData:this.data; let dataLength = (tmpData == null)?0:tmpData.length; // 全部的输入提示 let inputDescs = ["我的搜索,只搜精品"]; // 当前应用“输入提示” let inputDesc = inputDescs[Math.floor(Math.random()*inputDescs.length)]; if(target == "UPDATE") { if(this.tmpVar != null) { clearTimeout(this.tmpVar); } this.tmpVar = setTimeout(()=>{ $("#my_search_input").attr("placeholder",this.getDataLength()); },1200) return `可以搜索( 🔁 数据库更新到 ${dataLength}条)`; } return inputDesc; }, searchBoundary: " : ", dataAddBeforeHandler: { handlers: [], handler(items) { for(let handlerFun of this.handlers) { items = handlerFun(items); } return items; } }, // 存储着text转pinyin的历史 registry.searchData.TEXT_PINYIN_KEY TEXT_PINYIN_KEY: "TEXT_PINYIN_MAP", // 默认数据不应初始化,不然太占内存了,只用调用了toPinyin才会初始化 getGlobalTextPinyinMap() getGlobalTextPinyinMap: (function() { let textPinyinMap = null; return function (){ if(textPinyinMap != null) return textPinyinMap; return (textPinyinMap = cache.jGet("TEXT_PINYIN_MAP")??{}); } })(), isSearchPro: false, searchProFlag: "[可搜索]" } } let dao = {} /*cache.remove(registry.searchData.SEARCH_DATA_KEY) cache.remove(registry.searchData.OLD_SEARCH_DATA_KEY) cache.remove(registry.searchData.SEARCH_NEW_ITEMS_KEY)*/ // 判断是否只是url且不应该是URL文本 (用于查看类型) function isUrlNoUrlText(str = "") { str = str.trim().split("#")[0]; // 不能存在换行符,如果存在不满足 if(str.indexOf("\n") != -1 ) return false; // 被“空白符”切割后只能有一个元素 if(str.split(/\s+/).length != 1) return false; // 如果不满足url,返回false if(! /^https?:\/\/.+/i.test(str) ) return false; return true; } /*cache.remove(registry.searchData.SEARCH_DATA_KEY); cache.remove(registry.searchData.SEARCH_DATA_KEY+"2"); cache.remove(registry.searchData.SEARCH_NEW_ITEMS_KEY); */ // 设置远程可用Favicon源 let setFaviconSource = function () { function startTestFaviconSources(sources,pos,setFaviconUrlTemplate) { if(pos > sources.length - 1) return; console.logout(`${pos}/${sources.length-1}: 正在测试 `+sources[pos]) checkUsability(sources[pos]).then(function(result) { console.logout("使用的源:"+ sources[pos]) setFaviconUrlTemplate(result); }).catch(function() { startTestFaviconSources(sources,++pos,setFaviconUrlTemplate) }); } let cacheFaviconSourceData = cache.get(registry.searchData.CACHE_FAVICON_SOURCE_KEY); let currentTime = new Date().getTime(); let timeout = registry.searchData.CACHE_FAVICON_SOURCE_TIMEOUT; if(cacheFaviconSourceData == null || currentTime - cacheFaviconSourceData.updateTime > timeout ) { if(cacheFaviconSourceData != null) { console.logout(`==超时${(currentTime - cacheFaviconSourceData.updateTime - timeout)/1000}s,重新设置Favicon源==`); } function setFaviconUrlTemplate(source = null) { console.logout("Test compled, set source! "+source) if(source != null) { cache.set(registry.searchData.CACHE_FAVICON_SOURCE_KEY, { updateTime: new Date().getTime(), sourceTemplate: source }) } } let faviconSources = registry.searchData.faviconSources; let pos = 0; let promise = null; // 去测试index=0的源, 当失败,会向后继续测试 if(faviconSources.length < 1) return; startTestFaviconSources(faviconSources,0,setFaviconUrlTemplate); }else { console.logout(`Favicon源${(timeout - (currentTime - cacheFaviconSourceData.updateTime))/1000}s后测试`); } } // 判断是否要执行设置源,如果之前没有设置过的话就要设置,而不是通过事件触发 if(cache.get(registry.searchData.CACHE_FAVICON_SOURCE_KEY) == null ) setTimeout(()=>{setFaviconSource();},2000); // 添加事件(视图在页面中初次显示时) registry.view.onViewFirstShow.push(setFaviconSource); // 【函数库】 // 加载样式 function loadStyleString(css) { var style = document.createElement("style"); style.type = "text/css"; try { style.appendChild(document.createTextNode(css)); } catch(ex) { style.styleSheet.cssText = css; } var head = document.getElementsByTagName('head')[0]; head.appendChild(style); } // 异步函数 function asyncFun(fun,time = 20) { setTimeout(()=>{ fun(); },time) } // Date对象转年月日 Date.prototype.toDateString = function(separator = "-") { const year = this.getFullYear(); const month = this.getMonth() + 1; const day = this.getDate(); return `${year}${separator}${month}${separator}${day};`; }; // 往字符原型中添加新的方法 matchFetch String.prototype.matchFetch=function (regex,callback) { let str = this; // Alternative syntax using RegExp constructor // const regex = new RegExp('\\[\\[[^\\[\\]]*\\]\\]', 'gm') let m; let length = 0; while ((m = regex.exec(str)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regex.lastIndex) { regex.lastIndex++; } // 结果可以通过`m变量`访问。 m.forEach((match, groupIndex) => { length++; callback(match, groupIndex); }); } return length; }; // 往字符原型中添加新的方法 matchFetch String.prototype.fillByObj=function (obj) { if(obj == null ) return null; let template = this; let resultUrl = template; for(let key of Object.keys(obj)) { let regexStr = "\\$.*?{.*?"+key+".*?}"; resultUrl = resultUrl.replace(new RegExp(regexStr),obj[key]); } if(/\$.*?{.*?}/.test(resultUrl)) return null; return resultUrl; } // 比较两个数组是否相等(顺序不相同不影响) Array.prototype.isEqual = function (arr2) { let arr1 = this; if( arr2 == null || arr1.length != arr2.length ) return false; for(let arr1Item of arr1) { let f = false; for(let arr2Item of arr2) { if(arr1Item == arr2Item ) { f = true; break; } } if(! f) return false; } return true; } Array.prototype.diff = function ( arr2, idFun = () => null,diffRange = 3) { // diffRange值:“1”是左边多的,“2”是右边数组多的,3是左右合并,0是相同的部分,30是两个数组去重的 if(window.hashString == null) { window.hashString = function (obj) { let str = JSON.stringify(obj); let hash = 0; for (let i = 0; i < str.length; i++) { hash += str.charCodeAt(i); } return hash; } } let arr1 = this; if (arr2 == null && arr2.length == 0) return arr1; // arr1与arr2都为数组对象 // 将arr1生成模板 let template = {}; for (let item of arr1) { let itemHash = hashString(idFun(item) ?? item); if (template[itemHash] == null) template[itemHash] = []; template[itemHash].push(item); } let leftDiff = []; let rightDiff = []; let overlap = []; // arr2根据arr1的模板进行比对 for (let item of arr2) { let itemHash = window.hashString(idFun(item) ?? item); let hitArr = template[itemHash]; let item2Json = idFun(item) ?? JSON.stringify(item); if (hitArr != null) { // 模板中存在 for (let hitIndex in hitArr) { let hashItem = hitArr[hitIndex]; // 判断冲突是否真的相同 let item1Json = idFun(hashItem) ?? JSON.stringify(hashItem); if (item1Json == item2Json) { // 命中-将arr1命中的删除 delete hitArr.splice(hitIndex, 1); overlap.push(hashItem); break; } } } else { // 模板不存在,是差异项 rightDiff.push(item); } } // 将模板中未命中的收集 for (let templateKey in template) { let templateValue = template[templateKey]; //templateValue 是数组 if (templateValue == null || !(templateValue instanceof Array)) continue; for (let templateValueItem of templateValue) { leftDiff.push(templateValueItem); } } // 根据参数,返回指定的数据 switch (diffRange) { case 0: return overlap; break; case 1: return leftDiff; break; case 2: return rightDiff; break; case 3: return [...leftDiff, ...rightDiff]; break; case 30: return [...leftDiff, ...rightDiff, ...overlap]; } } // 保证replaceAll方法替换后也可以正常 String.prototype.toReplaceAll = function(str1,str2) { return this.split(str1).join(str2); } // 向原型中添加方法:文字转拼音 String.prototype.toPinyin = function (isOnlyFomCacheFind= false,options = { toneType: 'none', type: 'array' }) { let textPinyinMap = registry.searchData.getGlobalTextPinyinMap(); // 如果字典中没有,再转拼音 if(textPinyinMap[this] != null) { // console.logout("命中了") return textPinyinMap[this]; } // 如果 isOnlyFomCacheFind = true,那返回原数据 if(isOnlyFomCacheFind) return null; // console.logout("字典没有,将进行转拼音",Object.keys(textPinyinMap).length) let {pinyin} = pinyinPro; let text = this; let space = "" let spaceChar = " "; text = text.toReplaceAll(spaceChar,space) let pinyinArr = pinyin(text,options); // 保存到全局字典对象 ( 会话级别 ) textPinyinMap[this] = pinyinArr.join("").toReplaceAll(space,spaceChar).toUpperCase(); return textPinyinMap[this]; } // 加载全局样式 loadStyleString(` /*定义字体*/ @font-face { font-family: 'HarmonyOS'; src: url('https://s1.hdslb.com/bfs/static/jinkela/long/font/HarmonyOS_Medium.a1.woff2'); } #my_search_view { font-family: 'HarmonyOS', sans-serif !important; } .searchItem { background-image: url(data:image/jpeg;base64,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); background-size: 100% 100%; background-clip: content-box; background-origin: content-box; } #my_search_input { animation-duration: 1s; animation-name: my_search_view; } .resultItem { animation-duration: 0.5s; animation-name: resultItem; } @-webkit-keyframes my_search_view { 0% { width: 0px; } 50% { width: 50%; } 100% { width: 100%; } } @-webkit-keyframes resultItem { 0% { opacity: 0; } 40% { opacity: 0.6; } 50% { opacity: 0.7; } 60% { opacity: 0.8; } 100% { opacity: 1; } } /*简述超链接样式*/ #text_show a { color: #1a0dab !important; text-decoration:none; } /*自定义markdown的html样式*/ #text_show>p>code { padding: 2px 0.4em; font-size: 95%; background-color: rgba(188, 188, 188, 0.2); border-radius: 5px; line-height: normal; font-family: SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace; color: #558eda; } #my_search_input::placeholder { color: #757575; } /*当视图大于等于1400.1px时*/ @media (min-width: 1400.1px) { #my_search_box { left: 27%; right:27%; } } /*当视图小于等于1400px时*/ @media (max-width: 1400px) { #my_search_box { left: 24%; right:24%; } } /*当视图小于等于1200px时*/ @media (max-width: 1200px) { #my_search_box { left: 21%; right:21%; } } /*当视图小于等于1100px时*/ @media (max-width: 1100px) { #my_search_box { left: 18%; right:18%; } } /*当视图小于等于800px时*/ @media (max-width: 800px) { #my_search_box { left: 15%; right:15%; } } /*输入框右边按钮*/ #controlButton { position: absolute; font-size: 12px; right: 5px; padding: 0px; border: none; display: block; background: rgba(255, 255, 255, 0); margin: 0px 7px 0px 0px; cursor: pointer; outline: none; } #controlButton img { display: block; width: 25px; } /*代码颜色*/ #text_show code,#text_show pre{ color:#5f6368; } `) //防抖函数模板 function debounce(fun, wait) { let timer = null; return function (...args) { // 清除原来的定时器 if (timer) clearTimeout(timer) // 开启一个新的定时器 timer = setTimeout(() => { fun.apply(this, args) }, wait) } } // 判断是否为指定指令 function isInstructions(val,cmd) { return val == ":"+cmd; } // 向数据项中加入拼音项 如:title加了titlePinyin, desc加了descPinyin function genDataItemPinyin(threadHandleItems){ let textPinyinMap = registry.searchData.getGlobalTextPinyinMap(); // console.logout("分配的预热item:",threadHandleItems) asyncFun(()=>{ if(threadHandleItems.length < 1) return; for(let item of threadHandleItems) { // 查看字典是否存在,只有没有预热过再预热 if( textPinyinMap[threadHandleItems.title] != null ) continue; item.title.toPinyin(); item.desc.toPinyin(); } // 持久化-textPinyinMap字典 (这里需要判断是否值已经被初始化) if(textPinyinMap != null ) { cache.jSet(registry.searchData.TEXT_PINYIN_KEY,textPinyinMap); } }); } // 当页面加载完成时触发-转拼音库操作 const refresh = debounce(()=>{ console.logout("==pinyin word==") let threadHandleItemSize = 100; let threadHandleItems = []; let currentSize = 0; let data = registry.searchData.data; for(let item of data) { // 加入处理容器中 threadHandleItems.push(item); currentSize++; // 判断是否已满 if(currentSize >= threadHandleItemSize || data[data.length-1] == item ) { // 已满-去操作 genDataItemPinyin(threadHandleItems); // 重置数据 currentSize = 0; threadHandleItems = []; } } }, 2000) registry.searchData.dataChange.push(refresh); // 从数据库获取新数据 let newDataLine = []; function getNewDataLine(data) { } registry.searchData.dataChange.push(getNewDataLine); // 实现模块一:使用快捷键触发指定事件 function triggerAndEvent(goKeys = "ctrl+alt+s", fun, isKeyCode = false) { // 监听键盘按下事件 let handle = function (event) { let isCtrl = goKeys.indexOf("ctrl") >= 0; let isAlt = goKeys.indexOf("alt") >= 0; let lastKey = goKeys.toReplaceAll("alt", "").toReplaceAll("ctrl", "").replace(/\++/gm,"").trim(); // 判断 Ctrl+S if (event.ctrlKey != isCtrl || event.altKey != isAlt) return; if (!isKeyCode) { // 查看 lastKey == 按下的key if (lastKey.toUpperCase() == event.key.toUpperCase()) fun(); } else { // 查看 lastKey == event.keyCode if (lastKey == event.keyCode) fun(); } } // 如果使用 document.onkeydown 这种,只能有一个监听者 $(document).keyup(handle); } // 解决有些网站不加载图片 // 插入 meta 标签 /* var oMeta = document.createElement('meta'); oMeta.content = "default-src 'self' data: * 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src * ;img-src *"; oMeta['http-equiv'] = "Content-Security-Policy"; document.getElementsByTagName('head')[0].appendChild(oMeta); */ // 【数据初始化】 // 获取存在的订阅信息 function getSubscribe() { // 查看是否有订阅信息 let subscribeKey = registry.searchData.subscribeKey; let subscribeInfo = cache.get(subscribeKey); if(subscribeInfo == null ) { // 初始化订阅信息(初次) subscribeInfo = ` `; cache.set(subscribeKey,subscribeInfo); } return subscribeInfo; } function editSubscribe(subscribe) { // 判断导入的订阅是否有效 // 获取订阅信息(得到的值肯定不会为空) let pageTextHandleChainsY = pageTextHandleChains.init(subscribe); let tisHasFetchFun = pageTextHandleChainsY.parseSingleTab("tis","fetchFun"); let tisNotFetchFun = pageTextHandleChainsY.parseSingleTabValue("tis"); let tis = [...tisHasFetchFun, ...tisNotFetchFun]; // 生成订阅信息存储 let subscribeText = "\n"; for(let aTis of tisHasFetchFun) { subscribeText += `\n` } for(let aTis of tisNotFetchFun) { subscribeText += `\n` } // 持久化 let newSubscribeInfo = subscribeText.replace(/\n+/gm,"\n\n"); cache.set(registry.searchData.subscribeKey,newSubscribeInfo); return tis.length; } // 存储订阅信息,当指定 sLineFetchFun 时,表示将解析“直接页”的配置,如果没有指定 sLineFetchFun 时,只解析内容 // 在提取函数中 \n 要改写为 \\n let dataSources = getSubscribe()+ ` function(pageText) { let type = "sketch"; // url sketch let lines = pageText.split("\\n"); let search_data_lines = []; // 扫描的搜索数据 {},{} let current_build_search_item = {}; let current_build_search_item_resource = ""; let point = 0; // 指的是上面的 current_build_search_item let default_desc = "--无描述--" function getTitleLineData(titleLine) { const regex = /^# ([^()()]+)[((]?([^()()]*)[^))]?/; let matchData = regex.exec(titleLine) return { title: matchData[1], desc: ((matchData[2]==null || matchData[2] == "")?default_desc:matchData[2]) } } for (let i = 0; i < lines.length; i++) { let line = lines[i]; if(line.indexOf("# ") == 0) { // 当前新的开始工作 point++; // 创建新的搜索项目容器 current_build_search_item = {...getTitleLineData(line)} // 重置resource current_build_search_item_resource = ""; continue; } // 如果是刚开始,没有标题的内容行,跳过 if(point == 0) continue; // 向当前搜索项目容器追加当前行 current_build_search_item_resource += (line+"\\n"); // 如果是最后一行,打包 let nextLine = lines[i+1]; if(i === lines.length-1 || ( nextLine != null && nextLine.indexOf("# ") == 0 )) { // 加入resource,最后一项 current_build_search_item.resource = current_build_search_item_resource; // 打包装箱 search_data_lines.push(current_build_search_item); } } // 添加种类 for(let line of search_data_lines) { line.type = type; } return search_data_lines; } function(pageText) { let type = "url"; // url sketch let lines = pageText.split("\\n"); let search_data_lines = [] for (let line of lines) { let search_data_line = (function(line) { const baseReg = /([^::\\n(())]+)[((]([^()()]*)[))]\\s*[::]\\s*(.+)/gm; const ifNotDescMatchReg = /([^::]+)\\s*[::]\\s*(.*)/gm; let title = ""; let desc = ""; let resource = ""; let captureResult = null; if( !(/[()()]/.test(line))) { // 兼容没有描述 captureResult = ifNotDescMatchReg.exec(line); if(captureResult == null ) return; title = captureResult[1]; desc = "--无描述--"; resource = captureResult[2]; }else { // 正常语法 captureResult = baseReg.exec(line); if(captureResult == null ) return; title = captureResult[1]; desc = captureResult[2]; resource = captureResult[3]; } return { title: title, desc: desc, resource: resource }; })(line); if (search_data_line == null || search_data_line.title == null) continue; search_data_lines.push(search_data_line) } for(let line of search_data_lines) { line.type = type; } return search_data_lines; } `; // 判断是否是github文件链接 let githubUrlFlag = "raw.githubusercontent.com"; // cdn模板+数据=完整资源加速链接 -> 返回 function cdnTemplateWrapForUrl(cdnTemplate,initUrl) { let result = parseUrl(initUrl)??{}; if(Object.keys(result) == 0 ) return null; return cdnTemplate.fillByObj(result); } // github CDN加速包装器 // 根据传入的状态,返回适合的新状态(状态中包含资源加速下载链接|原始链接|null-表示不再试) let cdnPack = (function () { // index = 1 用原始的(不加速链接), -2 表示原始链接打不开此时要退出 let cdnrs = cache.get(registry.other.UPDATE_CDNS_CACHE_KEY); // 提供的加速模板(顺序会在后面的请求中进行重排序-请求错误反馈的使重排序) let initCdnrs = ["https://ghproxy.net/${protocol}${domain}${path}","https://ghps.cc/${protocol}${domain}${path}","https://github.moeyy.xyz/${protocol}${domain}${path}"]; // 如果我们修改了最开始提供的加速模板,比如新添加/删除了一个会使用新的 if(cdnrs == null || ! initCdnrs.isEqual(cdnrs) ) { cdnrs = initCdnrs; cache.set(registry.other.UPDATE_CDNS_CACHE_KEY,cdnrs); } return function ({index,url,initUrl}) { if( index <= -2 ) return null; // 如果已经遍历完了 或 不满足github url 不使用加速 if(index == -1 || index > cdnrs.length -1 || (index == 0 && ! url.includes(githubUrlFlag)) ) { url = initUrl; index--; console.logout("无法加速,将使用原链接!") return {index,url,initUrl}; } let cdnTemplate = cdnrs[index++]; url = cdnTemplateWrapForUrl(cdnTemplate,initUrl); if(index == cdnrs.length) index = -1; return {index,url,initUrl}; } })(); // 模块四:初始化数据源 // 使用责任链模式——对pageText进行操作的工具 const pageTextHandleChains = { pageText: "", setPageText(newPageText) { this.pageText = newPageText; }, getPageText() { return this.pageText; }, init(newPageText = "") { // 深拷贝一份实例 let wo = {...this}; // 初始化 wo.setPageText(newPageText); return wo; }, // 解析双标签-获取指定标签下指定属性下的值 parseDoubleTab(tabName,attrName) { // 返回指定标签下指定属性下的值 const regex = RegExp(`<\\s*${tabName}[^<>]*\\s*${attrName}="([^<>]*)"\\s*>([\\s\\S]*?)<\/\\s*${tabName}\\s*>`,"gm"); let m; let tabNameArr = []; let copyPageText = this.pageText; // 注意下面的 copyPageText 不能改变 while ((m = regex.exec(copyPageText)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regex.lastIndex) { regex.lastIndex++; } tabNameArr.push({ attrValue: m[1], tabValue: m[2] }) const newPageText =this.pageText.replace(m[0], ""); this.pageText = newPageText; } return tabNameArr; }, // 解析双标签-只获取值 parseDoubleTabValue(tabName) { // 返回指定标签下指定属性下的值 const regex = RegExp(`<\\s*${tabName}[^<>]*\\s*>([\\s\\S]*?)<\/\\s*${tabName}\\s*>`,"gm"); let m; let tabNameArr = []; let copyPageText = this.pageText; while ((m = regex.exec(copyPageText)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regex.lastIndex) { regex.lastIndex++; } tabNameArr.push({ tabValue: m[1] }) const newPageText =this.pageText.replace(m[0], ""); this.pageText = newPageText; } return tabNameArr; }, // 获取指定单标签指定属性与标签值(标签::值) parseSingleTab(tabName,attrName) { // 返回指定标签下指定属性下的值 const regex = RegExp(`<${tabName}::([^\\s<>]*)\\s*${attrName}="([^"<>]*)"\\s*\/>`,"gm"); let m; let tabNameArr = [] let copyPageText = this.pageText; while ((m = regex.exec(copyPageText)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regex.lastIndex) { regex.lastIndex++; } tabNameArr.push({ tabValue: m[1], attrValue: m[2] }) const newPageText =this.pageText.replace(m[0], ""); this.pageText = newPageText; } return tabNameArr; }, parseSingleTabValue(tabName) { // 返回指定标签下指定属性下的值 const regex = RegExp(`<${tabName}::([^\\s<>]*)[^<>]*\/>`,"gm"); let m; let tabNameArr = [] let copyPageText = this.pageText; while ((m = regex.exec(copyPageText)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regex.lastIndex) { regex.lastIndex++; } tabNameArr.push({ tabValue: m[1] }) const newPageText =this.pageText.replace(m[0], ""); this.pageText = newPageText; } return tabNameArr; }, // 清除指定单双标签 cleanTabByTabName(tabName) { const regex = RegExp(`<\\s*${tabName}[^<>]*>([^<>]*)(<\/[^<>]*>)*`,"gm"); // 替换的内容 const subst = ``; // 被替换的值将包含在结果变量中 const cleanedText = this.pageText.replace(regex, subst); this.pageText = cleanedText; } } // 从 订阅信息(或页) 中解析出配置(json) function getConfigFromDataSource(pageText) { let config = { // {url、fetchFun属性} tis: [], // {name与fetchFun属性} fetchFuns: [] } // 从config中放在返回对象中 let pageTextHandleChainsX = pageTextHandleChains.init(pageText); let fetchFunTabDatas = pageTextHandleChainsX.parseDoubleTab("fetchFun","name"); for(let fetchFunTabData of fetchFunTabDatas) { config.fetchFuns.push( { name:fetchFunTabData.attrValue,fetchFun:fetchFunTabData.tabValue } ) } // 获取tis let tisHasFetchFun = pageTextHandleChainsX.parseSingleTab("tis","fetchFun"); let tisNotFetchFun = pageTextHandleChainsX.parseSingleTabValue("tis"); let tisArr = [...tisHasFetchFun, ...tisNotFetchFun] for(let tis of tisArr) { config.tis.push( { url:tis.tabValue, fetchFun:tis.attrValue } ) } return config; } // 将url转为文本(url请求得到的就是文本),当下面的dataSourceUrl不是http的url时,就会直接返回,不作请求 function urlToText(dataSourceUrl) { // dataSourceUrl 转text return new Promise(function (resolve, reject) { if((dataSourceUrl.trim().indexOf("http") != 0 ) ) return resolve(dataSourceUrl) ; let allCdns = cache.get(registry.other.UPDATE_CDNS_CACHE_KEY); function rq( cdnRequestStatus ) { let {index,url,initUrl} = cdnRequestStatus??{}; // -2 表示加速链接+原始链接都不会请求成功(异常) ,null表示index状态已经是-2了还去请求返回null if(index == null || index < -2 ) return; $.ajax({ url: `${url}?t=${+new Date().getTime()}`, timeout: 5000, // 设置超时时间为 5 秒钟 success: function (result) { resolve(result) }, error: function(xhr, status, errorThrown){ console.log("cdn失败,不加速请求!"); // 反馈错误,调整请求顺序,避免错误还是访问 // 获取请求错误的根域名 let { domain } = parseUrl(url); // 根据根域名从模板中找出完整域名 let templates = allCdns.filter(item=>item.includes(domain)); // 反馈 if(templates.length > 0 ) { if(index > 0 || index <= cache.get(registry.other.UPDATE_CDNS_CACHE_KEY).length ) feedbackError(registry.other.UPDATE_CDNS_CACHE_KEY,templates[0]); } console.logout("反馈重调整后:",cache.get(registry.other.UPDATE_CDNS_CACHE_KEY)); // 反馈的结果只会在下次起作用 // 处理失败后的回调函数代码 rq(cdnPack({index,url,initUrl})); } }); } rq(cdnPack({index:0,url:dataSourceUrl,initUrl:dataSourceUrl})); }); } // 下面的 dataSourceHandle 函数 let globalFetchFun = []; // tis处理队列 let waitQueue = []; registry.searchData.dataAddBeforeHandler.handlers.push(function (items) { for(let searchItem of items) { let url = searchItem.resource; let isSearchableItem = /\[\[[^\[\]]+keyword[^\[\]]+\]\]/.test(url); if(isSearchableItem) searchItem.title = registry.searchData.searchProFlag+searchItem.title; } return items; }) // 将解析出来的部分数据push 到 registry.searchData.data的操作队列 (push到全局不能并发,所以这里必须是单线程操作) let searchDataController = { // 加入到全局的等待队列 queueData: [], // 是否空闲 isIdle: true, // 是否第一次 pushToGlobal isFirsPush: true, // 给外面触发,加入到队列中 pushToGlobal:function(newItems) { // 如果是第一次push,清空全局数据 if(this.isFirsPush) { this.isFirsPush = false; // 清空全局数据容器的数据 registry.searchData.data = []; // 触发第一次处理Block事件 for(let f of registry.searchData.onFirstHandleNewDataBlock) f(); } // 在添加前,进行额外处理添加,如给有”{keyword}“的url搜索项添加”可搜索“标签 newItems = registry.searchData.dataAddBeforeHandler.handler(newItems); this.queueData.push(...newItems); // 如果当前不是空闲的,其它线程当会处理刚才push的数据到全局中 if(!this.isIdle) return; // 设置当前为工作模式 this.isIdle = false; // 处理队列中的数据 let newItem = null; while((newItem = this.queueData.pop() ) != null) { // 下一个编号索引号 let nextIndex = registry.searchData.data.length; newItem.index = nextIndex; registry.searchData.data[nextIndex] = newItem; } // 设置当前空闲 this.isIdle = true; // 更新视图显示条数 $("#my_search_input").attr("placeholder",registry.searchData.getDataLength("UPDATE")); // 触发“新数据块处理完成事件” (function(){ // 排序(根据配置在obj中的权重) registry.searchData.onNewDataBlockHandleAfter.sort((obj1,obj2)=>obj2.weights-obj1.weights); // 触发 for(let obj of registry.searchData.onNewDataBlockHandleAfter) obj.fun(newItems); })(); } } // 转义与恢复,数据进行解析前进行转义,解析后恢复——比如文本中出现“/”,就会出现:SyntaxError: Octal escape sequences are not allowed in template strings. function CallBeforeParse() { this.obj = { "`":"<反引号>", "\\":"<转义>" } this.escape = function(text) { let obj = this.obj; for (var key in obj) { text = text.toReplaceAll(key,obj[key]); } return text; } this.recovery = function(text) { let obj = this.obj; for (var key in obj) { text = text.toReplaceAll(obj[key],key); } return text; } } let callBeforeParse = new CallBeforeParse(); function dataSourceHandle(resourcePageUrl,tisTabFetchFunName) { urlToText(resourcePageUrl).then(text => { if(tisTabFetchFunName == null) { // --> 是配置 <-- let data = [] // 解析配置 let config = getConfigFromDataSource(text); console.logout("解析的配置:",config) // 解析FetchFun:将FetchFun放到全局解析器中 globalFetchFun.push(...config.fetchFuns); // 解析订阅:将tis放到处理队列中 waitQueue.push(...config.tis); let tis = null; while((tis = waitQueue.pop()) != undefined) { // tis第一个是url,第二是fetchFun dataSourceHandle(tis.url,tis.fetchFun); } // 清理内容 pageTextHandleChains.setPageText(""); }else { // --> 是内容 <-- // 解析内容 if(tisTabFetchFunName === "") return; let fetchFunStr = getFetchFunGetByName(tisTabFetchFunName); let search_data_line =(new Function('text', "return ( " + fetchFunStr + " )(`"+callBeforeParse.escape(text)+"`)"))(); // 将之前修改为 改为真正的换行符 \n // 处理并push到全局数据容器中 for(let item of search_data_line) { item.title = callBeforeParse.recovery(item.title); item.desc = callBeforeParse.recovery(item.desc); item.resource = callBeforeParse.recovery(item.resource); } // 加入到push到全局的搜索数据队列中,等待加入到全局数据容器中 searchDataController.pushToGlobal(search_data_line); // 触发搜索数据改变事件(做缓存等操作,观察者模式) for(let fun of registry.searchData.dataChange) { fun(search_data_line); } } }) } // 根据fetchFun名返回字符串函数 function getFetchFunGetByName(fetchFunName) { for(let fetchFunData of globalFetchFun) { if(fetchFunData.name == fetchFunName) { return fetchFunData.fetchFun; } } } // 缓存数据 function cacheSearchData(newSearchData) { console.logout("触发了缓存,当前数据",registry.searchData.data) // 当有数据加入到全局数据容器时,会触发缓存,当前函数会执行 let SEARCH_DATA_KEY = registry.searchData.SEARCH_DATA_KEY; cache.remove(SEARCH_DATA_KEY) cache.set(SEARCH_DATA_KEY,{ data: registry.searchData.data, expire: new Date().getTime() + (1000*60*60*1) // 一个小时 //expire: new Date().getTime() + (2000) // 测试,一秒过期 }) } // 检查是否已经执行初始化 function checkIsInitializedAndSetInitialized(secondTime) { let key = "DATA_INIT"; let value = cache.cookieGet(key); if(value != null && value != "") return true; cache.cookieSet(key,key,1000*secondTime); return false; } // 【数据初始化主函数】 let isInitialized = false; function dataInitFun() { // 检查是否已经执行初始化 if(isInitialized) return; // 设置为已初始化,保障只初始化一次,如果不能保障,同时初始化时,出现重复数据 isInitialized = true; // 从缓存中获取数据,判断是否还有效 const SEARCH_DATA_KEY = registry.searchData.SEARCH_DATA_KEY; // cache.remove(SEARCH_DATA_KEY) let dataBox = cache.get(registry.searchData.SEARCH_DATA_KEY); if(dataBox != null) { // 只要数据不为空,不管是否过期,先用着,直接将之前缓存的数据放在全局搜索数据容器中 registry.searchData.data = dataBox.data // 缓存信息不为空,深入判断是否使用缓存的数据 let dataExpireTime = dataBox.expire; let currentTime = new Date().getTime(); // console.logout("缓存的数据:",dataBox.data) // 数据多大时,才开启缓存 const TRIGGER_CACHE_DATA_LENGTH = 300; // 判断是否有效,有效的话放到全局容器中 let isValid = (dataExpireTime != null && dataExpireTime > currentTime && dataBox.data != null && dataBox.data.length > 0); // 如果网站比较特殊,忽略数据过期时间 if(!isValid && window.location.host.toUpperCase().indexOf("GITHUB.COM") >= 0) { isValid = true; } // 如果数据过期,或数据量不满足缓存大小,会去请求数据 if(isValid && dataBox.data.length >= TRIGGER_CACHE_DATA_LENGTH ) { console.logout("本次从缓冲中获取, 数据有效期还有"+parseInt((dataExpireTime - currentTime)/1000/60)+"分钟!", dataBox.data) return }; } // 内部将使用递归,解析出信息 dataSourceHandle(dataSources,null); // 监听数据改变 registry.searchData.onNewDataBlockHandleAfter.push({weights:1000 ,fun:cacheSearchData}) } // 该函数作用是为了防止一个页面多次加载数据,导致页面一直在加载 function checkIsCanInit() { let initFlagKey = "initFlagKey"; let initFlagValue = cache.get(initFlagKey)??0; let currentTime = new Date().getTime(); let vailTime = 8*1000; console.logout("是否过期(为负过期):",initFlagValue , currentTime,initFlagValue +vailTime - currentTime) let b = initFlagValue == 0 || initFlagValue + vailTime < currentTime; if(b) cache.set(initFlagKey,currentTime); return b; } // 判断是否要直接执行初始化函数-如果没有数据,这里要直接执行 (function() { console.logout("请求初始化数据!"); if(cache.get( registry.searchData.SEARCH_DATA_KEY) == null && checkIsCanInit() ) { console.logout("===初始化数据!==="); // 执行初始化函数 dataInitFun(); } })(); const refreshNewData = debounce(()=>{ console.logout("开始筛选~"); // 新数据加载完成-进行数据对比 // 旧数据,也就是上一次数据,用于与本次比较,得出新添加数据 let oldSearchData = cache.get(registry.searchData.OLD_SEARCH_DATA_KEY)??[]; // 当前最新数据,用于搜索 let currentSearchData = registry.searchData.data; // 当前时间戳 let currentTime = new Date().getTime(); // 如果脚本还没有初始化,需要初始化-也就是放开第一次加载数据,否则都是新数据 if(cache.get("SCRIPT_INITIALIZED") == null ) { setTimeout(()=>cache.set("SCRIPT_INITIALIZED",true),500); return; } // 数组差异-获取不同的元素比较的基值 let idFun = function(item) { // 自定义比较 if(item == null || !( item instanceof Object && item.title != null)) return null; // return item.title.toReplaceAll(registry.searchData.NEW_ITEMS_FLAG,"")+item.desc+(item.resource??'').substring(0,20); return item.title.toReplaceAll(registry.searchData.NEW_ITEMS_FLAG,"")+item.desc; } // 准备一个存储新数据项的容器 let newDataItems = []; // 只要 oldSearchData 与 currentSearchData有就可以比较,得到新数据 if( oldSearchData != null && oldSearchData.length != 0 && currentSearchData != null && currentSearchData.length != 0 ) { console.logout("开始对比:",oldSearchData.length,currentSearchData.length) // 这里没有旧的 newDataItems = currentSearchData.diff(oldSearchData,idFun,1); // 给新添加的过期时间(新数据有效期) let dayNumber = registry.searchData.NEW_DATA_EXPIRE_DAY_NUM; newDataItems.forEach(item=>item.expires=(currentTime++) + (1000*60*60*24*dayNumber)); console.log("新差异=",JSON.parse(JSON.stringify(newDataItems))); } // 不要新添加/改变的有“带注释”的 newDataItems = newDataItems.filter(item=> !item.title.startsWith("#")); // 以前的新增数据 let oldNewItems = cache.get(registry.searchData.SEARCH_NEW_ITEMS_KEY)??[]; // 确保oldNewItems是合法值 if( ! oldNewItems instanceof Array) oldNewItems = []; // 如果还没有过期的,保留下来放在最新数据中 for(let item of oldNewItems) { if(item != null && item.expires > currentTime) newDataItems.push(item); } // 总新增去重 (标记 - 过滤标记的 ) for(let i = 0; i< newDataItems.length; i++ ) { let item1 = newDataItems[i]; for(let j = i+1; j< newDataItems.length; j++ ) { let item2 = newDataItems[j]; if(item1.title == item2.title && item1.resource == item2.resource) { newDataItems[i] = null; break; } } } newDataItems = newDataItems.filter((item, index) => item != null); // 与“总新增去重”顺序不能乱!!! console.logout("总新增:", JSON.parse(JSON.stringify(newDataItems))); // 如果过多,不加入最新 if( !(newDataItems.length < 10 || newDataItems.length/currentSearchData.length < 0.4) ) { // 不加入新数据,但需要维护旧新数据在现在新数据的索引 newDataItems = oldNewItems; } // 将 index 给 newDataItems ,不然new中的我们选择与实际选择的不一致问题 ! for(let nItem of newDataItems) { for(let cItem of currentSearchData) { if(nItem.title === cItem.title && nItem.desc === cItem.desc) { nItem.index = cItem.index; break; } } } cache.set(registry.searchData.SEARCH_NEW_ITEMS_KEY,newDataItems); // 改变全局搜索并持久化 let cacheData = cache.get(registry.searchData.SEARCH_DATA_KEY); for(let item of currentSearchData) { for(let nItem of newDataItems) { if(item.title == nItem.title && item.resource == nItem.resource) { item.title = registry.searchData.NEW_ITEMS_FLAG+item.title; break; } } } cacheData.data = currentSearchData; cache.set(registry.searchData.SEARCH_DATA_KEY,cacheData); //currentSearchData 追加-> oldSearchData , 更新旧数据 let newOldSearchData = currentSearchData.diff(oldSearchData,idFun,30); cache.set(registry.searchData.OLD_SEARCH_DATA_KEY,newOldSearchData??[]); }, 1300); registry.searchData.onNewDataBlockHandleAfter.push({weights:1 ,fun:refreshNewData}); // 模块二 registry.view.viewVisibilityController = (function() { // 整个视图对象 let viewDocument = null; let searchInputDocument = null; let matchItems = null; let searchBox = null; let isInitializedView = false; let controlButton = null; let textShow = null; let matchResult = null; let initView = function () { // 初始化视图 let view = document.createElement("div") view.id = "my_search_box"; let menu_icon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjc3MDgxNTk3NzA3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjEzNDYxIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik0yMjQuMiA0NzIuM2MtMTMtNS43LTMuNy0yMy41IDguMi0xOSA5MSAzNCAxNDYuOCAxMDguNyAxODIuNCAxMzguNSA1LjYgNC43IDE0IDIuOSAxNy4zLTMuNSAxNi44LTMyIDQ1LjgtMTEzLjctNTcuMS0xNjguNi04Ny4zLTQ2LjUtMTg4LTUzLjYtMjQ3LjMtODIuMi0xNC41LTctMzEuMSA0LjYtMjkuOSAyMC43IDUgNjkuNyAyOC45IDEyNC43IDYyLjMgMTgxLjUgNjcuMyAxMTQuMyAxNDAuNiAxMzIuOSAyMTYuNiAxMDQgMi4yLTAuOSA0LjUtMS44IDctMyA3LTMuNCA4LjMtMTIuOSAyLjUtMTguMSAwLjEgMC00NS43LTY5LjMtMTYyLTE1MC4zeiIgZmlsbD0iI0ZGRDQwMSIgcC1pZD0iMTM0NjIiPjwvcGF0aD48cGF0aCBkPSJNMjgyLjcgODQ5LjljNzkuNS0xMzcgMTcyLjQtMjYzLjEgMzg1LjQtNDAxLjMgOS44LTYuNCAyLjEtMjEuNS04LjktMTcuNEM0OTcuNyA0OTIuOCA0MjkuNyA1ODUgMzczLjMgNjQwLjhjLTguNyA4LjctMjMuNCA2LjMtMjkuMS00LjYtMjcuMi01MS44LTY5LjUtMTc0LjEgOTcuMy0yNjMuMSAxNDcuNy03OC44IDMxOS45LTkxLjQgNDI5LjctOTMuMyAxOC45LTAuMyAzMS41IDE5LjQgMjMuMyAzNi40Qzg2My43IDM4MCA4NDIuNiA0NzggNzg5LjkgNTY3LjYgNjgwLjggNzUzLjEgNTQ1LjUgNzY2LjcgNDIyLjIgNzE5LjhjLTguOC0zLjQtMTguOC0wLjItMjQgNy43LTE2LjYgMjUuMi01MC4zIDgwLjEtNTguNyAxMjIuNC0xMS40IDU2LjgtODIuMiA0My45LTU2LjggMHoiIGZpbGw9IiM4QkMwM0MiIHAtaWQ9IjEzNDYzIj48L3BhdGg+PHBhdGggZD0iTTM3NSA0MTkuNmMtMzAuMSAyOC4yLTQ1LjggNTcuNy01Mi40IDg2LjEgNDAuNiAzMi40IDcwLjIgNjcuNyA5Mi4xIDg1LjkgMS4yIDEgMi41IDEuNiAzLjkgMi4xIDYuNS02LjcgMTMuMy0xMy43IDIwLjQtMjAuNyAxNS4yLTM3LjkgMjUuMy0xMDUuNy02NC0xNTMuNHpNMzE4LjggNTQ4LjJjMS42IDM2LjEgMTQuNyA2Ny42IDI1LjUgODguMSA1LjcgMTAuOSAyMC4zIDEzLjMgMjkuMSA0LjYgNC45LTQuOSAxMC0xMCAxNS4xLTE1LjQtMC42LTEtMS4zLTItMi4yLTIuOCAwLTAuMS0yMC4xLTMwLjUtNjcuNS03NC41eiIgZmlsbD0iIzhCQTAwMCIgcC1pZD0iMTM0NjQiPjwvcGF0aD48L3N2Zz4="; view.innerHTML = (`
`) // 设置样式 view.style = ` position: fixed;top:50px; border:2px solid #cecece;z-index:10000; background: #ffffff; overflow: hidden; `; // 挂载到文档中 document.body.appendChild(view) // 整个视图对象放在组件全局中/注册表中 registry.view.viewDocument = viewDocument = view; // 搜索框对象 searchInputDocument = $("#my_search_input") matchItems = $("#matchItems"); searchBox = $("#searchBox") controlButton = $("#controlButton") textShow = $("#text_show") matchResult = $("#matchResult"); // 菜单函数(点击输入框右边按钮时会调用) controlButton.click( function () { registry.view.menuActive = true; // alert("小彩蛋:可以搜索一下“系统项”了解脚本基本使用哦~"); // 调用手动触发搜索函数,如果已经搜索过,搜索空串(清理) let keyword = "[系统项]"; registry.searchData.triggerSearchHandle(searchInputDocument.val()==keyword?'':keyword); setTimeout(function(){ registry.view.menuActive = false;},registry.view.delayedHideTime+100); }) searchBox.css({ "height": "45px", "background": "#ffffff", "padding": "0px", "box-sizing": " border-box", "z-index": "10001", "position":"relative", "display": "flex", "justify-content": "space-between", "align-items": "center", "flex-wrap": "nowrap" }) searchInputDocument.css({ "width": "100%", "height": "100%", "border": "none", "outline": "none", "font-size": "15px", "background": "#fff", "padding": "0px 10px", "box-sizing": " border-box", "color":"rgba(0,0,0,.87)", "font-weight":"400", "margin":"0px" }) $("#matchResult").css({ "display":"none" }) $("#matchResult > ol").css({ "margin": "0px", "padding": "0px 15px 5px" }) textShow.css({ "display":"none", "width":"100%", "box-sizing": "border-box", "padding": "5px 10px 7px", "font-size": "15px", "line-height":"25px", "max-height":"450px", "overflow": "auto", "text-align":"left", "color":"#000000" }) // 图片放大/还原 textShow.on("click","img",function(e) { let target = e.target; if(target.isEnlarge??false) { $(this).animate({ width: "100%" }); // 还原 target.isEnlarge = false; }else { $(this).animate({ width: "900px" }); target.isEnlarge = true; } }); // 初始化搜索数据 dataInitFun(); // 在搜索的结果集中上下选择移动然后回车(相当点击) searchInputDocument.keyup(function(event){ let keyword = $(event.target).val().trim(); // 当不为空时,放到全局keyword中 if(keyword != "" && keyword != null) { registry.searchData.keyword = event.target.value; } // 处理keyword中的":"字符 if(keyword.endsWith("::") || keyword.endsWith("::")) { keyword = keyword.replace(/::|::/,registry.searchData.searchBoundary).replace(/\s+/," "); // 每次要形成一个" : "的时候去掉重复的" : : " -> " : " keyword = keyword.replace(/((\s{1,2}:)+ )/,registry.searchData.searchBoundary); $(event.target).val(keyword.toUpperCase()); // 设置当前为“搜索PRO”模式 registry.searchData.isSearchPro = true; // registry.view.showControlButton("PRO模式") } // 判断是否要退出搜索模式 if($(event.target).val().indexOf(registry.searchData.searchBoundary) == -1) { // registry.view.hideControlButton(); registry.searchData.isSearchPro = false; } }); searchInputDocument.keydown(function (event){ let e = event || window.event || arguments.callee.caller.arguments[0]; // 判断一个输入框的东西,如果如果按下的是删除,判断一下是不是"搜索模式" let keyword = $(event.target).val().trim(); if(e.key == "Backspace" ) { // 按的是删除键 if(event.target.value.endsWith(registry.searchData.searchBoundary)) { // 取消默认事件 e.preventDefault(); return; } } if(e && e.keyCode!=38 && e.keyCode!=40 && e.keyCode!=13) return; if(e && e.keyCode==38){ // 上 registry.searchData.pos --; } if(e && e.keyCode==40){ //下 registry.searchData.pos ++; } // 如果是回车 && registry.searchData.pos == 0 时,设置 registry.searchData.pos = 1 (这样是为了搜索后回车相当于点击第一个) if(e && e.keyCode==13 && registry.searchData.pos == 0){ // 回车选择的元素 registry.searchData.pos = 1; } // 当指针位置越出时,位置重定向 if(registry.searchData.pos < 1 || registry.searchData.pos > registry.searchData.searchData.length ) { if(registry.searchData.pos < 1) { // 回到最后一个 registry.searchData.pos = registry.searchData.searchData.length; }else { // 回到第一个 registry.searchData.pos = 1; } } // 设置显示样式 let activeItem = $($("#matchItems > li")[registry.searchData.pos-1]); // if(activeItem == null) return; // 设置活跃背景颜色 let activeBackgroundColor = "#dee2e6"; //let activeFontColor = "rgb(26, 13, 171)"; // 如果是搜索项,可用别的颜色 //if(activeItem.find("a").attr("href").indexOf("keyword") != -1) activeFontColor = "rgb(251,182,54)" activeItem.css({ "background":activeBackgroundColor }) /*activeItem.find("a").css({ "color":activeFontColor })*/ // 设置其它子元素背景为默认统一背景 activeItem.siblings().css({ "background":"#fff" }) if(e && e.keyCode==13 && activeItem.find("a").length > 0){ // 回车 // 点击当前活跃的项,点击 activeItem.find("a")[0].click(); } // 取消冒泡 e.stopPropagation(); // 取消默认事件 e.preventDefault(); }); // 将输入框的控制按钮设置可见性函数公开放注册表中 registry.view.setButtonVisibility = function (buttonVisibility = false) { // registry.view.setButtonVisibility controlButton.css({ "display": buttonVisibility?"block":"none" }) } // 向搜索事件(只会触发一个)中添加一个“NEW”搜索关键词 registry.searchData.searchEven.event["NEW"] = function() { let result = null; let data = registry.searchData.data; let tmpData = cache.get(registry.searchData.SEARCH_DATA_KEY); if(data == null ) data = tmpData != null && tmpData.data ; if(data == null ) data = cache.get(registry.searchData.SEARCH_DATA_KEY+BACK); // 只展示 newItems 数据中data也存在的项 let newItems = cache.get(registry.searchData.SEARCH_NEW_ITEMS_KEY)??[]; console.log("==>",newItems,data) if(newItems.length > 0 && data.length > 0) { result = newItems.diff(data,function(item) { // 在这里只要desc与resource两个相等,那就是相等的 if(item == null || !( item instanceof Object && item.title != null)) return null; return item.title[item.title.length-1]+item.desc+(item.resource??'').substring(0,20); },0) } if(result == null) return []; // 对数据进行排序 result.sort(function(item1, item2){return item2.expires - item1.expires}); // 添加标签 result.map((item,index)=>{ let flag = index <= 0?"[最新一条]":registry.searchData.NEW_ITEMS_FLAG; let dayNumber = registry.searchData.NEW_DATA_EXPIRE_DAY_NUM; item.title = flag+item.title+ " | " + Math.floor( (new Date().getTime() - (item.expires - 1000*60*60*24*dayNumber) )/(1000*60*60*24) )+"天前"; //toDateString return item; }) return result; } function searchUnitHandler(beforeData = [],keyword = "") { // 触发搜索事件 for(let e of registry.searchData.onSearch) e(keyword); // 如果没有搜索内容,返回空数据 keyword = keyword.trim().toUpperCase(); if(keyword == "" || registry.searchData.data.length == 0 ) return []; // 看有没有观察者想要直接返回结果 let showItemData = registry.searchData.searchEven.send(registry.searchData.keyword); let isSendSuccess = showItemData != null && showItemData instanceof Array; //if( isSendSuccess ) registry.searchData.isSearchAll = true; if( isSendSuccess ) return showItemData; // registry.searchData.isSearchAll = false; // 切割搜索内容以空格隔开,得到多个 keyword let searchUnits = keyword.split(/\s+/); // 弹出一个 keyword keyword = searchUnits.pop(); // 本次搜索的总数据容器 let searchResultData = []; let searchLevelData = [ [],[],[] // 分别是匹配标题/desc/url 的结果 ] // 数据出来的总数据 //let searchData = [] // 前置处理函数,这里使用观察者模式 // searchPreFun(keyword); // 搜索操作 let pinyinKeyword = keyword.toPinyin(); let searchBegin = new Date().getTime(); for (let dataItem of beforeData) { /* 取消注释会导致虽然是15条,但有些匹配度高的依然不能匹配 // 如果已达到搜索要显示的条数,则不再搜索 && 已经是本次最后一次过滤了 => 就不要扫描全部数据了,只搜出15条即可 let currentMeetConditionItemSize = searchLevelData[0].length + searchLevelData[1].length + searchLevelData[2].length; if(currentMeetConditionItemSize >= registry.searchData.showSize && searchUnits.length == 0 && registry.searchData.isSearchPro ) break; */ // 将数据放在指定搜索层级数据上 if ( (( (dataItem.title.toPinyin(true)??"").indexOf(pinyinKeyword) >= 0 || dataItem.title.indexOf(keyword) >= 0 ) && searchLevelData[0].push(dataItem) ) || (( (dataItem.desc.toPinyin(true)??"").indexOf(pinyinKeyword) >= 0 || dataItem.desc.toUpperCase().indexOf(keyword) >= 0) && searchLevelData[1].push(dataItem) ) || ( dataItem.resource.length <= 1000 && dataItem.resource.toUpperCase().indexOf(keyword) >= 0 && searchLevelData[2].push(dataItem) ) ) { // 向满足条件的数据对象添加在总数据中的索引 } } let searchEnd = new Date().getTime(); console.logout("搜索主逻辑耗时:"+(searchEnd - searchBegin ) +"ms"); // 将【搜索项】放到上面 function searchItemToTop(items) { // 只有是搜索PRO模式 才干扰排序 if(!registry.searchData.isSearchPro) return; let searchableItem = []; let noSearchableItem = []; let currentTop = 0; for(let i = 0; i < items.length; i++) { let item = items[i]; if(item.title.trim().indexOf(registry.searchData.searchProFlag) != -1 ) { // 替换 let tmp = items[currentTop]; items[currentTop] = items[i]; items[i] = tmp; currentTop++; } } } // 将上面层级数据放在总容器中 searchResultData.push(...searchLevelData[0]); searchResultData.push(...searchLevelData[1]); searchItemToTop(searchResultData); // 搜索PRO模式时会干扰排序 searchResultData.push(...searchLevelData[2]); if(searchUnits.length > 0 && searchUnits[searchUnits.length-1].trim() != ":") { // 递归搜索 searchResultData = searchUnitHandler(searchResultData,searchUnits.join(" ")); } return searchResultData; } // 给输入框加事件 // 执行 debounce 函数返回新函数 let handler = function (e) { let key = e.target.value.trim().split(/\s+/).reverse().join(" "); // 过滤 // 数据出来的总数据 let searchData = [] // 递归搜索,根据空字符切换出来的多个keyword let searchResultData = searchUnitHandler(registry.searchData.data,key) // console.logout("搜索总数据:",searchResultData) // 放到视图上 // 置空内容 matchItems.html("") // 最多显示条数 let show_item_number = registry.searchData.showSize ; function getFlag(searchResultItem) { let resource = searchResultItem.resource.trim(); let isSketch = ! isUrlNoUrlText(resource); let sketchFavicon = "data:image/png;base64,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"; if(isSketch) return ``; function loaded() { alert("loaded!") } return `` } /* $("#matchItems").on("load","li img", function (){ alert("加载完成!") })*/ // 标题flag颜色选择器 function titleFlagColorMatchHandler(flagValue) { let vcObj = { "系统项":"rgb(0,210,13)", "非最佳":"#fbbc05", "推荐":"#ea4335", "装机必备":"#9933E5", "好物":"rgb(247,61,3)", "Adults only": "rgb(244,201,13)", "可搜索":"#4c89fb", "新":"#f70000", "最新一条":"#f70000" } let resultFlagColor = "#5eb95e"; Object.getOwnPropertyNames(vcObj).forEach(function(key){ if(key == flagValue) { resultFlagColor = vcObj[key]; } }); return resultFlagColor; } // 标题内容处理程序 function titleFlagHandler(title) { if(!(/[\[]?/.test(title) && /[\]]?/.test(title))) return -1; // 格式是:[flag]title(desc):resource 这种的 const regex = /(\[[^\[\]]*\])/gm; let m; let resultTitle = title; while ((m = regex.exec(title)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regex.lastIndex) { regex.lastIndex++; } let flag = m[0]; if(flag == null || flag.length == 0) return -1; let flagCore = flag.substring(1,flag.length - 1); // 正确提取 let style = ` background: ${titleFlagColorMatchHandler(flagCore)}; color: #fff; height:30px; line-height: 30px; font-size: 10px; padding: 3px 6px; border-radius: 5px; font-weight: 700; box-sizing: border-box; margin-right: 3.5px; `; resultTitle = resultTitle.toReplaceAll(flag,`${flagCore}`); } return resultTitle; } // 标题前面带“#”的titleHandler function title井Handler(title) { // 去掉flag title = title.replace(/\[.*\]/,"").trim(); if(title.indexOf("#") == 0) { let style = `text-decoration:line-through;color:#a8a8a8;`; return `${title.replace("#","")}`; } return -1; } function titleHandler(title) { let titleHandlerFuns = registry.view.titleHandlerFuns; for(let titleHandlerFun of titleHandlerFuns) { let result = titleHandlerFun(title.trim()); if(result != -1) return result; } return title; } // 添加标题处理器 title井Handler (优化级较高) registry.view.titleHandlerFuns.push(title井Handler); // 添加标题处理器 titleFlagHandler registry.view.titleHandlerFuns.push(titleFlagHandler); for(let searchResultItem of searchResultData ) { // 限制条数 if(show_item_number-- <= 0 && !registry.searchData.isSearchAll) { break; } // 将数据放入局部容器中 searchData.push(searchResultItem) let isSketch = !isUrlNoUrlText(searchResultItem.resource);// searchResultItem.resource.trim().toUpperCase().indexOf("HTTP") != 0; // 将符合的数据装载到视图 // let item = `
  • ${getFlag(searchResultItem)} ${titleHandler(searchResultItem.title)} (${searchResultItem.desc})
  • ` matchItems.html(matchItems.html() + item); } let loadErrorFlagIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIwMCAyMDAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj4KCTx0aXRsZT5hcDh6Yy12cm1kbzwvdGl0bGU+Cgk8ZGVmcz4KCQk8aW1hZ2UgIHdpZHRoPSIxOTQiIGhlaWdodD0iMTk0IiBpZD0iaW1nMSIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFNSUFBQURDQ0FZQUFBQWI0UjB4QUFBQUFYTlNSMElCMmNrc2Z3QUFIWjFKUkVGVWVKenRYUXU0SkVWMXZzUmdpQ1lLdmpVKzhCRk5RREJDZ3ZMNTJnQVNnV0FJNXFyTDNlbnFxYTQ3M1QzTDFTV0o4YTJicUtnSmo1QVk4a0dJSmlDSjhqSVFKUVlSQ0JCV1dJS0NLQVo1aU1ndVlGWjJlY2d1N0M0NXA3cm0zcDY1M1QzZFBWVjlxbnU2disvLzdtVjFvYnZPK2F0T1ZaM3puNW1abE9lSko1NW9NUUc2M1lXWkR2ZG5HQTltbUVENE00NEhQejM0TS9rekgrVGZnYi92Q3Z3OWxQL3N3TC9UeFg4djc4OXdIczY0cmt2K3ZYVkg2a1A5WW5VQjUzeW0wdzNCV1FHOG1KUHJSeWpod3UvZHJqK3pkdTFhOHZHcEMxb2lGSVRyd293c0ltZHpTSjArRHlKaTRtclNCZlI2UGZMeHN4VXRFY1pnQmN5cUR1K0I0MVBQOXBxZ1FpeGN3YWpIMWlhMFJFakE3T3h4VVd6UEIrR0dCUTVzQ0xoYXVHNjdVclJFVU1CNE9wcjFHekx6bHlTRjU2MG10MFZMQkFJSUlhSlFvYVR6ZExzTHorNXcvN2RoOVhnSGhCMS93b1IvTXV3ZHpnUkNmUlgrOTNXQVd3RWJBVDlUMkFaNFF1SEJ3Wi9EMy9rUi9QMmJ3Qm12Z1BjNUgvNzVkSWNIZitIeVlKN3gvcUdjaDY5eVhYZTNTa2lCcXdTTXlZb3AybXhQTFJGY1VjejVPZWUvMnVtR2I0SFllalhqL21ud1o5Y29SMzZpWXR3SjRkcEZnT05ody83dWJ0ZmZDMWF6WHpCRkNvZUhzRXA0NVBacWlhRDVZMlg0STNLRUNXNndweVBDVmVCc2Z3ZXo4M2ZnejdZVE9IMU8rQS9BejR0aE5mbG9Wd1FIOVhxOXArZ2xSSFEwN016UGs5dXdKY0lFaU9ML1VHMStrd0Zod0M4NnZQZEdJTXBuNEordnAzZnVpZkFva1AwYkVHSzlIMWF3L2ZXU3dvZEp3aWUzYVV1RWdzQ2xQYzJvczdQSC9iS003WGx3THN4NkQxdmd3S1p3SzZ3V24zTGQzajVhU2RHZ2U0bkdFc0ZOQ1g4d25vWlovMjB3czUzRmFHSjhhbndQUXlqUFcvMFNMV1RnUVNQMkVJMGpRcmZiVDd6eEZVSThWNFlLWG5DN0JjNW9BM1pnK0FRaDR5eUdoWk1TQWsvZVptZG55ZTAvOVVUQTlJR2t5eTlYaEsrSG4rY0FIclBBK1d6RjNSQkNmZ2htOW1kTVNnamNRMUQ3d3RRU2dZMWNoTUd6QzRRL1IrS01aNEdUMVFpd1QrTCthYzc4L0NzbkpjVEttdTBmYWswRTExMHpsUEVaeGYvaE1SQzMza0x2VkxYRzQ3aUhjbDMvMXljaVJJM3ltV3BMaE5Fa09GamFENEdmTjFqZ1JFM0NEb1poWmEvMzh2SmtxTWZxVURzaVlCRktuQVN1Q0ZiQXovVVdPRTJUc1ExVzJWTW0yVU80d3U2OVE2MklnRm1TZzcxQXQ5dC9VWlRURSt5MHdGR21CVC9EazdmWjJka25seUtEeFNkTHRTRUNsanJpcVZDVVBoQWV6L0QybE40eHBoWGZnd25wNERKa1FEZ1doa3JXRTJIRllucTBQSnA3TStCL0xYQ0VGaEhPZ2ZqL1dlVUlZVmVvWkRVUlZzcTdBVXlBVzdPN3l2aHN3eUQ3c0ltSnNGZUdESGp4U2UxajFoTUI4MWdjR1ZkaU9vVE01NmMyZUlzc2lPQ0NVcXNERDZ6WU4xaEpCSWRqaHFPN204b0UzVUZ1NUJaNWNhOHIvTU1Ma3dIMmZxZ0cwaElodmhKd1dVTzdEOFNRTjF0ZzJCYkZzZFAxd2hQTG5DdzVEbDJacUZWRWtDZER3bC9aOEpUb2FjRjZwOWQ3Y1ZFeWNIN3NkQk1CTXlCVktFUnR3QmI2Y0gveFk5WndwaVBFZEJKQmJiS3V0TUJ3TGZUamNjY0xqczFIZ2lWVXJhWkJUZ1RNWTRHQitvRUZCbXRoRWp3NEJmWU5UeHBIZ0RpcURKTklpZUJ3LzBBbWwwOExETldpQW9UL2dXb2dTVTZmQmhSVGJqUVJaSzJ3RjJ5bE4wNkxpbkd0NDZ4K0ppdEFoaW8wVzBtSUVKME1ZYzQ3dVZGYTBPRDdFUGE4Z0JVZ2crbEx0OHFKSUpYYjJrdXlGbDU0UjBlSWx6SkwwakVxSllMTC9RWFc1Z3VaeGxad21xOHhFYndIa3hRZEovaTFZOEp3RHhRdjZIWjcrN2xld0pVazVRTVd2T3VkaGRRMGhEa3lWRVlFeHdzRmEwbGdFdmVnOWlyRTAwL1BOU21oamlxRXFFckZqL0s5YnlzU0ptRk5RMjJKQUJ2ak9kYUdRNmF3RlFqdzRZV0ZoVjhxNlFDN29MUWxvMDFzL0I2S0tlY2hBcFBIcXZySllKd0l3T0NqV0xzeE5vVnJPNkwvbTFyczVLN1pIVmFIZjJSMHEvWU51SnJsSVFMdUYrYm05QjZyR2lXQ085Ly9IWGp4Unl4d21NWUI0M3lVck5ROU14SVhQMTJlTjFsUGQ0aGtqQWl3ZkwwTVFxTDdxQjJtZ2RnT29WQ29td0J4Uk9Xdy9pY1lRVG1zV3BWeWJwNzFrY0VJRWFUYVFhc3RaQUpiWGVFZmJaSUVRNnVEbEhIQlhnd1ZmeWZzZWZLU1laWFh0NU1JTXAra1ZaZ3pBRlNoNng5YUZRbmlRUExCTzJ5bzhIdDNxcjFsWlRmUDJvbmc4T0RUOUU3VE1Jamc1OWl0aDRJRUErQmRSTVdiNlMzWUxpc1BFVndOOXd0YWllQUkvdzhxSEtocHdXT3dHaHhCU1lJNHF0eE1ZMWJ5M056QzAzS1JZY0pWUVJzUmxFN21GZ3NjcDBuWWpyTHQxTTQvaWs2bjgxUjR0MU9yR0FQNC9pL2xJUUlXOUt5ZG9QbWhGaUpBakxZcmJJNnZzOEJ4bW9UdFRJUmRhcWZQblB4UWNOa0xIakkrRmpBT2Vjamc4dkphU1ZxSWdHMkpMSENjSm1Henc4UERxQjA5RHpwZThHcDQzN3NNajhlRHE3eitLOGFUd1M5ZHZ6QXhFYkFCSDdPNjQyVHRjQnUyaktWMjhDSlkxZXM5bjVsWElsOHZJdzlEZHdzVEVTR0tGY003TEhDZXB1QlN2SU9oZHV3eWtPa1Joc05qN1AwMmxnaVlnbEdDREJNUkFmNmpKMW5nUEUzQUR0UUNRaVVQYW9lZUJFbzIva2FENDdUVjdmVitZendaaXU4VlNoUEI4WUlEV0JzU2FVQjRCL1VkZ1U1MGd1QTU4RjAvTkRoZVYyRm5wSEZrY0FwbXFKWWlndXJDMkhhbm1RdzdVZGdZaTltcG5WYzMxRVhZWmxOajUzSS9HRWNFUkpHOWJpa2lZQUdJQlk1VVkvZzNZNnNyYW9jMUNXYysrSDFtckFiRmZ5QlAvWUxEODk4NEZ5YUNXdnFNc2IyaHdNYm1HQzZjaHc0Q3c3c0x0YU5XQWJXNU5UT21Jdno3Y1VSQTVMMWtLMHdFMWFlQTJyRnN3K095NUZHRVowVEx0bit3SThKOXNWNjRiT1ZZRTRBSm1FQ0dLd3lOK1hZYzQzRkV5TnU3clJBUk9QZGZ3OW9OY2h6cjBQR3h1b3ZhNld3RjlycGoyRXpFeVBqN2w0MGpBc3NaSGhVaUFpcVZXZUI4MU1DSjRBdTZTaVNuQVNxTjI0Zzk4QVorSEJuY0hQY0t1WW1nYnBDcG5aQWExN0w1MVh0VE8xWWRBYUhqMllac2NqM3V1YkxKb0pFSThDKzgzQUpISkFOSzFzdVkxN0ltZUhXQmtubTgxNFJ0SEI3OFlUWVJzUGxNdHQxeUVVRjF0U2QzUmlMc1VPcDhha0R0YVlCWE55aTlXeE0yK3U2NFN6Wm56S1k1RnhFTTd2enRod2hYeHdlMENrSGFKb09KNE1zbTdLVDJJWm1yd3NKQ2VtYnFXQ0lvU1JaNmh5UWhRZkRGMGNHMG9RTmtuZUc2L2VjeEUvZFFVY0pmZG5qa3BhL21ZNGtBLzRKenlCMlNCZytwSkxLaHdleDBPdVRPVkhjd3ovOVRFelp6UmZpbUxDSmszVFJuRWtHcEZVL252WUVJejBnYXpHNVhqM3pJTkVQbXFvbmdKdjEyazlJejJXa1hUaklaTW9rQVM4bGZrenNrRVZ6UFB6SnBJQ0ZVSkhla0prQTFHZFJ0dDUycWRqNmREQ21LRjZrUHlna3lZemVDOXNOMWd6MFRpV0JJalhrYUFXVDRxbTY3T2NML3kwd2lwT3dUVWgvSEN4bTFNMUlpclN5d1NFWmppMnlvZW1mZG9mZjltTitWUllTa2pwMnBEL3lGZGRUT1NJbDBNZHIyUWswbmxHaVlWdHRCU1AvdUxDSWtkZDlKZkJSVHlaMlJFa3E3SjNFZ3V3WTd0MHdibkY3dnhUQ20yM1RhRGxidGIyb2hBdXkrajZkMlJHcW9yTW4wd1p5ZkozZWlwb0J4Ly9PYTdiY0RVK0JUN1Nmd1B1aTRzVVRBQktiYnFCMlJHdTU4LzdleWlCQWhyTFFwZGxPaFNqdjFWclBKSG5KWmwydkRJZTZ5WjZwdmt1TkU4TUxaOFVRWTNqc3d0ZXhpWGdzV2p6dndaM2pLaEduQWFlZlhMU0pBWFA5dm1tMTR6VGliWlJQQkMwK2tka0lib0ZTOUN4QWhQenJkTmw5cE9SSGt2WTFPRys0YzE3RXprd2pNcUJSSG5lRGZiSW9JQStES2dRKzFFOW9BcFl5aXRRY0RyTTdIWm8xL1BJRnllRFdJYnVVc2NFSTcwQlhCUWNiSm9NS3Fic0lHYnRxZ094cVIvYWF6OWdteDA3L2gxU0RhWUpBN29FVzQyNW1mZjZWcE1peUhML2NWMDVicGloMkJ0TnBQQkQ5WEdSTEprMUJNQkd4NFJlREIxeTF3UHR1d2hYbmhSNG8weFRaQkRCUzNiWG9ZRlRVeDFOdkFFUFllYjBzZFY1NndJc2hyYVdDUUJZNW5LN0FUME8xS09lRWNXSFpQeDd3V21GVSs1SGgrSDNzRllCcHc1dm0xRG9oUVNwUTBOWXlDYjd4U3M5MU9TaHZMK01YYTR1TncvMEFMbkswcEFNSUVwM2E2dmRkcGNmNUVRcWc0RjFhTEZSTjBpckVOS3YxZHB5MnV6UnJId1lRUzN4KzBNbzVtY0FQMmxwc1pxN1F3R1RBWjBIWHJueUtPSzZ6bThYOU1oVnlwSyt6d2l1Q0Y1MXZnTkkyRlZNTHovRS9nU1ZUV0JtNVNPRjV5TGsxZDRJaHdsZTZ4ZDBXd0ltMjhCdVdiOGZ1RGpkVE9Na1hBRGVIbFFJeVA0YjRpUGROMUFzQUt3UXRLbzlzQUpjMmlkYnpWS3BNeVZ2NFNFZFNKQ0xWelRETWVBVndDUnZrZ0xOWDc2dzJqL0ltNlRWWlBCS2xjcDNkOGVYQnU2dmp3R0JFNjg4SHZXZUFNTFphd1FRb0tDLzlvSFdGVWxucURiVEJSd29uOW10UEdabEJvcGU0UHd2ZFpZUHdXU1lpT3RNL0JYQnlWaGxDS0RERFprVHQ1SHFoemY5M2p1RDFyUWxra0FzdysvMHh1OEJaNWNBL01ZQjlYMmtDTlhCVVlEOTl1Wk93ZzVFd2JteVVpZU1IMUZoaTVSWDVzUTFGZHdHdlRqRHVLK2hBaGVLZUpNWVB2NzZTTkRXcFZEWWp3ZnhZWXQwVng3QURIK1JmT3c1ZWxHWGtSSmZzUFYwNkVTR0xUd0ZqNUgwc2JHOVNxbXNGbWRoWVl0TVZrMklhR3poTENyVU1sbmV1NmVJcnpPU05qRkpXREpxK1c4LzIyVUw5SmdPWC9uNUtOYmJmeVJzZUxhak1Va2U4eU1UYlpCZjMrektEN0lia1JXK2lCT2dvZlBpSzBqQWdRaGNqRVFUekRIM3BQNGIvTDROamNQam91QTJDK0ZzWmtQV3JqdGRBSEpkTTVaR2hxS1h2WFhTTm5mQ2I4bEJrNWlHUmRlSENmd2JGNU5PMi9qYmZ3V0tYekFXcmp0ZENIWlVUZ2RLdEJ0eHVKRjZRNjRHQkdkbnY3T0Y3d0k5TmprNnBWaFNXendJWVRxSTNYUWlORTJCMHlNdEZxZ0tkVWlVNDNSQUIzTjVpSTE4THZXNnNZbXpTdEtoZFhCQVBpU2kzb3NETmVTVWNoV0l6NS9Vbk94a1lJb0xSMWI2MXlmTkswcWlRUkRPakp0Q0NELysyNGdic1Z5OFpndHV0QTN5a0plRUtwMUtxSjdxMWtIbFBpSFF2K2NnbTlBVnZvZ09QNTc0MGJ1RW9TT1BQQk1oTGdIWlVyL01QVjNjQ2QxT09UMXZOaXNGbStndm9GVzJqQmc3MWU3K2xMeHExbWt4eGxKdmdxNUZtek8xYmp3ZWJ6WkJhbDdkalZiU25xNnBsd2ZCcXRDTmVRdjJBTERVYVdzKzZpY1dmNzVzczJjVC9neUJETTN3dWM2VXVzb2sxdmFRaC9KVXNnZ2hSYVkyM0NYUk93bmZWNkwyY1Zoa1dvNVlvaGhicUgwaXJyYmdxd0tYWlpBaEV3UlIxL3VaSDZCVnRNYU9Cb05vNFoxK3hwRVc3Q1ZYNy9GNmkvdmRBNHhSckh4eUZ6alpnSS9vZjZCVnRNQm5ETS9VYU5hMHIzQ05YaG9teFhlVUpGL3UyRk1IckhFcnRyd1YrbXVrVlVBN0JPYWh5SndWSGcwcEdnYnNsSXh2c3FOODEvd0lMdkxnNGV6TEhFZTQwMWtnaTZsY1dvc1JNekRWM3VMK0FGQ2pZRlBDWU05NWdUNG9WU0lRRnYwa1h3RGZqLzNXL0J1Mm93cnU4TmIyQm5wYml0TkxCQUkrdlpMMFNDQXY0SG1lNkdIaFVpcmVmRm9Dam5VdW9YMUlTTllQaFA0YVl4cjNHRkVNL2wzSDhOMXNsaUJSTktOenBlK0g3Wk9vc0hwNmdTMW90VkdHQ2ozTTBqcy8zK3J5U0dNUEVVaHdLNnFaaWdoNkVDa2dnM2tYaTBpUHNCcklpejRIc25nanphVFNDQ0pMcUpYcmVWZnB3WC9BQ2NWNkIycThrTjRvQTRRSklqd01uK3huQ21aRjVjbmhuS2pKeVZZM3dmbDRmRWs1OUJVaHltR1NRNWlkUnk1Y0YxRm56cnhFaEtVV2N4SXB4Ri9ZSWxjUU9HT2xqTVlab0FLVFBucnJDSzhDcXlKdE1SWHBTWENHV2dOdUZhbTNkUVFyVkZXelpCU0NLb3RGM3lsOHdMcVZIRGczZGl6RXBCZ0ZISUJMSklTZTBoZ3ZHNEY4T1dwUGRhaFNIT0JDU0Fiem9FZmo1SWJXK3RHTGxyWWQ1U1U4RVpWZFJNLzVJNWpJNGJROVQyb1hiK0ZNZDdQZ3pxWitBOWYxend1NUJBRzVRSTFmVUtWOHNOZlJ4ZWNHSHM5L1dMRXY2d2o0RU44cE9XclFZcG9VNHVSQTA3N0w0bExnRk1BVm4ycldJUUdobFREZENHeDNIamluazAxTTZlQnhpcXVXNndKODZvS25RNkZqZmdDSG1TNVlYSG9CQXd0dW5DMWFUc2Z3ZWRYNG5iM29yNVl2amZIQ0pDU1JKZ2tReUVYQTliWUhmdGZwUWtwVGxvSGorampwU29YeklONjJCanVpKzFjOXNNSEI4MVZsdnd3bWdRTW81emVOempZS2lBWk1KTkpQcUI4b1ZUQVk5WllIdmQySkEwRHAxQUVRRm1yQU1zZU1sUmJJZFo2Yzl0RFlOc0E0dmRpWUF6Znd0WG83ZzhKSjc4d0o4ZkJiOS9VclVIdTR2WmxobHFIdXVTaURBWVEzVWtTUDZTaTFDbk1HOFlmZEVXbVVUWWxEQ1d1TkZGNVlaV3ZNMUx5c2NhSVlLTW0renBuYllPbHFybnhGOTAyanBMRmdWdTBpMndtL1ZRQnhtWlJNQVRobHVvWHhSdmIrZm1GcDQyK3FLcmlLVkliSWNwcmRER1FRVGhNaUtNZHRXMG9XNVpiZm9TenJOYkltUVN3UXYrbHRwMmRVQlM4M2k4V1I4aUF2YjJJbjdSVFVra2tDOUxvTVJRSnpDWkMwWHZhTFpqTk9SbUkvdlBxSkdnV2FtOVBOaVNSb1RSRjI0eERCWDdranVhNWJoM21WK0pCQ0pnelNueGkyNUxKWUlJR3Q5eGZpSWlSRTFEa2s2TldpemgwbEcvR28wMDVLUE9uQitsZkZtOEtVMGpnOFBiVlNHVERNSi9LOE1iZUhxSHN4VW5qZnJVbkJETGlSRHRFOEtyS0Y5V0hRT21oRWUrc2RMRHBrQVYwZSswd09uc1EzU3lsaG9XalJBaCtDemx5enE4OThaMElrUk50T3ZVSnBVQ21OZlVraUhCdDdDbUl1NUxZcm5tMCtLanFuZm9YamJTd3N4T0NBT3NhTW1RQ1pmN0FhdHhPYVVCM0xuTWo3SldoSlc5M3JNWTdXenlEK09JTUFBMm1xQjJPSnVCR2E2czNUTkVFTUVYUi8wbktWdGg2SUZZL0diQ0YvNUo3bzd6WEY5UmVsT2h5REQxS3dPVzhjWjl4MGxwcWpoQ2hQQ3ZTRjhhVlNaeXJncURqOEtOOU56Y0FybmoyUWhXTXdFdUUwRDFrbEcvR1VzRWRRMU4rT0wrelZrZDBuT1JRKzBsVU8wTkZacXAyeWFSRWtFRUYxQTdJaWxFY05Pb2Y2VGRTUTA5RURzOW1SSFhxWUlqbjVuVkpyVWN3cG1GaGVsYU5Wd2VyaUYzUkdLb1hneER4L0JwNDdYc3NXRVd3VGFwOGNJU1hjamFaTE5Zc2J0VDg5TXAxR2xpMDFkNHM5eVBvbExXUmJ0NjN1b2lSSkRTMmVRZkFiaEVOVVBYUmdRWk5zMHZ5YVd2UkNHcmxHWjNxQUZFN2RCbHdIbjRxdHBLTXVyRnh0RnNoYXh4Vy9hZ2NocjhwVWNzK0JDOEVid09pOUoxa2lGT2l1ei9qMTI5aWZOQUdmNEdjcnRaQUNuQ0ZyZDN3aVZhSmhHaTA2UGdIT29QaVdGTDlGSCttNU1LZDB6QkdUT0QyQWhYK0VkYllDOWJzRmp1bXlmVVRYd3NIbEM4OEx1SG9jcWRGM3dOOXhLd0VmNElLalQzZXIybjZDUkNIVmNFR0pQVExiQ1JEYmhyNkU0cTRTWTVGeEZRUjVUVlR5MzZJYndIVWUrdVpVV29XNzIwRXYraXRvTUZrSVZtaTdic2REcmxpSUNQT25xeTRLTUs0K3JNVFhZays0Y0podXR4VlJsM09rWHQzSVdJMEJDeDNnbXh3L05XdjJUSmh2bFc5dFFIbGRoWVRUTVp3Y0cvRXhkOHhUd3FKVjZGWlkxRGFRY09Edy9MSWdLZUt2R2FuQ0NCMFMrakhuc0xjUEdTL2NKRkFhL1NSSWdHdHZhOUV6WW9uYVFNUWt2dDExeDdCdHNKQWU5NW5nVmpUb3A0RHdTM3dENHZrd2dxOTRmODQ0d09YTFRCekwxM2NIbitwaHNFUkpocVJRdWM5SVlWL3RJdjBBb1JRYVU2ZkovNkE4MUNOa29wdHBrV2R1WXd1U0w0QVAxNEVrSUU3eG5ZS0tubW9EUVI1Q3dUbFFEU2Y2UWg0Q3lTVlMrZENtNmZxQURqdmtNOW5vVFkxT2wwbmpxd1Q5R0phaXdSVUxxY1JYSVkxQjlxRXNlbE9ueFdtRFRtdHJKcU9DSmNaY0ZZRW1IcHlOVGx4ZTB5bGdoeXBvbTZLVnJ3c2Nhd0UwVmlvenlkQWtTdzdIZ1Z3b0dQV2pDV0JBZ2ZqZ3Q0bGJuL3lVVUVsWDlVdHd1Mk10aU9QZVdpaHRvNWlPRDJ0VGl3THNBN2ZkZUNNYXdjY1lGZmg1ZXpTUzRpeUdXWCszOU0vY0VWNGhITTU1OFpVenBLN2ZoRDlpRVdYeURFWnNkWi9VeW05bTFseHk4M0VWVGwyRDBXZkhoMUVNR1gwM0tZOGx6YlZ3VWx2SEEzK1hnUkFNTEJ0VXpkODB5aW5KNmJDQWdsRlVMKzhaV0NCLzgrdWpLNEtRWGdGRkJIM05NcUJQeFQ3SzBuYlRLaEdtSWhJc2pMaXFnT2xIb0FLb1dTVkZ4TXVhQjIvamlVcUJmNUdKSFlKWnFZcFYzU2ZOWUlFUkN3QkIxTVBRQ1ZEN2dYY0J6c0tDUFZIdWxKRmE1dW5PejdzSnJOL3dyOC9xK0FhMWw5RGtWdUhOei91TzdreDlpRmlhREk4QlVMQnFJcVBLb1VwN0dNbGR6NWgrd1E5VU11LzIzYy81emNBNDJFRmJLdkhnL2Z6cnp3K0NpUno3NTJzdzd2LzY2MFNZazdBMzFFd0ZSbWUvcXVtY2Fpa3JKdFlSRjJIcDNndXo2NytGMWowaEdpUzlYd0NGZ1J6MlkyaUlieDRGeW0rZVN1RkJFUTJFQ2JmRURNNDdiNHRUM3o3Q3JoaE5uNlA4dDlWM2pWVUZwSmdieWNxQ0c1YkU5TFpCUC9nWUZ5dXVkNTlFU0lzdno4YjF2Z3JLYXd3eFhobTlqSXNhbWoyUUNURVNINGFZbnZlckFqeEV2WjBDbFlNWEl6RWU1UFpSZVhCL1BTRHBwQ29vbUpFQnVRUm9yTlFoaDA0aWdKRnNrQU1YVzNTeXNZcHFxd1NuemJjQmtqSzdIS3FTTkxDcnY4MTR4c2g2dy9SSjJJQ05Hc2hCc3Flc2ZWaVNnamRiejBaS2RMdDJjb21XbjZhRkpUdmFKRWNIaDRDSUZkSHNLcVNWT2g2Y1JFZ05saFYzako5ZFRPcXhYQ1Aza2NDWllRWWcrNjZvbFFSbkludHNrY3hTb3ZmNDRPM3JoWGJoUHVlOHpnOGZYRVJKQXp4UHo4SzIwOFlpc0x6RVROVDRUQmhoTUpVVTBTbnRLbzNWTDR1NkxDblpSdnlFZm1UcmYzT2xiOXlkRjVqdUVrUnkxRWtHU0lkT2pKblZnVEhvZUJQNkF3R1ZBK3NvTDBDOVJ4S3ZWZHNRcXVKSXduSUlTTDFXY1czTzE1M2pQWWZQNnlTMUlpUkdRSXpyVEFpYlVBOXduU0FJWEpVT3c0c2d6S1htZ3VKYWlsSEFKa2tBRnZid2tFeExZNTNEK3dDaDFhclVUQVcwcVVVcUYyWW0xazRNRi9kN3NMenk1RGh0bSttV1ZjM3ZwNndXT2x2aWU2RU10ODc0RmlOQ3Ayb0Y2bzdFNkV2U1lvVm53UnJoNm5XV29sRWRRQVlsSEx6NmlkV0NOdTczVER0eFFsZ3FsYmFKZ2RQMVQyVzVTMFRXRlNLM24xYlZXT08wWVhvMDNCYTBVRWhOTG5iOWo5UW5oUnQrdnZWY1NCSmgzSFVjZ1luUWYzVGZJZHE3eitLNHA4ZzlvclZkMDhaajErYTFVa01FWUVTWVptMWk0OERzdjFHZDF1LzBWNW5NalZITnRpcURENU44ajY4N3duWVhoaHVxbktNY1pWQzVNY3F5U0JVU0pJd3hFM0p6U0g4R0hsbE9OblZLNUgvd2huU0JYYVRQcitOK1o3YnludTlsREZZN3VaemEvZXUyb1NHQ2VDcXA2eXFkZUNYb2pndzNtY2FwSVN3cVZKUlVwVGFubHZJTlJyMDk1VjNkNmVUVENlMi9ER21vSUV4b21BUUpsMmx3ZGZKM2RhTTNoRUtYeU1JWU0va2NTOHlpdlMyY1hvbW9GaU5CNXVPTUovRit3OVRvQU4vcmNZamZEemRuaUhQNklpUVNWRVFPQ3hxaXVDS3l4d1hPMVFqYjF6eGR3bERiUUwxazFUZjZkQjdIQzhrRkdTb0RJaUlMQm5nWnB4cUFkZU42NmVHU1A3TWtEUm9uOXNpY3NpRlQ3cWJ6U0ZuWTduOTZsSlVDa1JFQ3A5OTJvTERLQVZMZy9mbDRjSW1JSXhONWN2ZlJ2REtkWGpvZEx6KzRwSjhGNXFBcEFRQVlFeHRjT0RiMXBnQ0oxNHpPRzlOK1lodzdnMFl0ZGRJMjl6VldlZnB1cEliYmNoSENJbEFpTHFjOVk0QVlDTmd4TENjVUFOSHB6eE1VRVBrOGxrSGc5WC8reGgwUS9lVTRSM1dQQk5KckNOZW1Oc0RSRVFzbzZCKzUrM3dEQWE0VjlXU21JK3ZtTElsUGJnaC9UZllnU2JLWTlJclNUQ0FOZ2VsdFcwVjFzS1BsbWFDRkpDSmRoc3dUZVl3RjBkTDNnMXRjTmJTd1JKQmg3TXdVQnR0Y0JZT3JBOXFlZy9DeGdxcWk2bVRab1E0cmdldzBacVo3ZWVDQWh3bnRlenhnalpobmZNelMwOExSOFJwSEpnZytYYy9iUHdIb25hMFd0REJJUXFMTCtjM25pVFkxeVRRdGRkczN2RCt5SnZRMTFXYWdldkpSRVFVaStKQnlldytvY0pHMXdldUhMR3h5ek8rZFY3eXp3ZTdoK29Fdlp1dGVBZHpVQUVQNEdmYjZCMjdsb1RZUUFsR2RMVWMvVG1RZ1FYWU9NT2F2OXBEQkVRcWt6eVFuTGp0c2hEZ0ovYmRGUGNLQ0tvRjl4RnlmdzE5Vml4Q2JnUzd6K29mYVhSUkJoQVNiTTN0N2FobnRpTXF3RFduVkQ3eDlRUVlaRVF3ajlhYmNhb25XQ3E0WGpoK1p3Zit3SnFmNWhhSWlCVWZjUGFLZXJSWUE5NGNJdkR3OE9vZmFBbFFneHpRcnhRaVlyVi9haTFEdGlFWVJBZWIxUGJ2U1ZDQ3FRZVo3T3J1QWdSUG93TnZZOEp3ejJvN2R3U0lTZkFjRytJZW41Uk8wOGpzSlZ4L3pSVTFxTzJhMHVFMG9UQS9KMnliWldtSGRobE16eWVRbHVvSllJaE9DTGNWKzBoU21tRlRoazI0Z0VFNWtCUjI2MGxnaUhJVFRVUFBnN0cvckVGRG1jVGRzaVZrd2Z2d0VJcGFqdTFSS2dJV0QwVzlSWUlMMkxUdlVyY0F4UERwMUhUaU5vbXRtQnFIendGa1gzSW90T21oZ2tXSjJJVGhvbXU1eCtKUjZEVTQ5OCtGajR5ZlVPRVBjeWNaTlVyUHhzRE9QNFBzQitjSy95M3RzN2ZQb1VlN0UvV0ZjRkJlSGFPalVKWXZVcElOMkREUUJUTndyQ0hlaXpicDBHUDY3cTdLYzJpUDJPWTlNZURXNWdkb2RSbTJPdGNCVDlQZGIyZzB6cCsrMVQrWUxGOXQ5dmJ6eEhoS25VYTlRVjFrWGM3MDZwTTV6K2dXbkZkQ0FROHhlWGhHZ2h6RG5kNnZSZFRqMEg3dE0vWVIrcTh1c0dlc2tTVDl3OTF2WEJXQWtzMzVWNGtEbitsK3QrUGt1b1hzcVN6L3p3TTBhaS9ZNXFlL3dmaFNwMGVHdnoyYkFBQUFBQkpSVTVFcmtKZ2dnPT0iLz4KCTwvZGVmcz4KCTxzdHlsZT4KCTwvc3R5bGU+Cgk8dXNlIGlkPSJCYWNrZ3JvdW5kIiBocmVmPSIjaW1nMSIgeD0iNiIgeT0iMCIvPgo8L3N2Zz4="; //let loadErrorFlagIcon = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjc1NzkxNTQ0MDA3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjgxNjUiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTMyIDQwNy41ODRhMjc5LjU4NCAyNzkuNTg0IDAgMCAxIDQ4MC0xOTQuOTQ0IDI3OS41ODQgMjc5LjU4NCAwIDAgMSA0ODAgMTk0Ljk0NCAyNzguMTQ0IDI3OC4xNDQgMCAwIDEtMTEzLjAyNCAyMjQuNTEyTDU2Mi41OTIgODkyLjhhOTYgOTYgMCAwIDEtMTI0LjQxNi0xLjk1MmwtMzA4LjE2LTI3MC42ODhBMjc4Ljk3NiAyNzguOTc2IDAgMCAxIDMyIDQwNy41ODR6IiBmaWxsPSIjZDgxZTA2IiBwLWlkPSI4MTY2Ij48L3BhdGg+PC9zdmc+"; // 给刚才添加的img添加事件 for(let imgObj of $("#matchItems").find('img')) { // 加载完成事件,去除加载背景 imgObj.onload = function(e) { $(e.target).css({ "background": "rgba(0,0,0,0)" }) } // 加载失败,设置自定义失败的本地图片 imgObj.onerror = function(e,a,b,c) { $(e.target).attr("src",loadErrorFlagIcon) } } // 隐藏文本显示视图 textShow.css({ "display":"none" }) // 让搜索结果显示 let matchResultDisplay = "block"; if(searchResultData.length < 1) matchResultDisplay="none"; matchResult.css({ "display":matchResultDisplay, "overflow":"hidden" }) // 将搜索的数据放入全局容器中 registry.searchData.searchData = searchData; // 指令归位(置零) registry.searchData.pos = 0; // 设置li样式 $("#matchResult li").css({ "line-height": "30px", "height": "30px", "color": "#0088cc", "list-style": "none", //decimal "width":"100%", "margin":"0px", "display":"flex", "justify-content":"left", "align-items":"center", "padding":"0px", "margin":"0px" }) $("#matchResult li>a").css({ "display":"inline-block", "font-size":"15px", "color": "#1a0dab", "text-decoration":"none", "text-align":"left", "overflow":"hidden", //超出的文本隐藏 "text-overflow":"ellipsis", //溢出用省略号显示 "white-space":"nowrap", //溢出不换行 "cursor":"pointer", "font-weight":"400", "background":"rgb(255 255 255 / 0%)" }) $("#matchResult .desc").css({ "color":"#4d5156" }) $("#matchResult img").css({ "display": "inline-block", "width": "21px", "height":"21px", "margin":"0px 7px 0px 5px", "box-shadow": "0 0 2px rgba(0,0,0,0.5)", "border-radius": "30%", "box-sizing": " border-box", "border":"3px solid #fff0", "flex-shrink":"0" // 当容量不够时,不压缩图片的大小 }) } // 简述内容转markdown前 function sketchResourceToHtmlBefore(txtStr = "") { // 1、“换行”转无意义中间值 txtStr = txtStr.replace(/<\s*br\s*\/\s*>/gm,"?br?"); // 单行简述下的换行,注意要在"<",">"转意前就要做了,注意顺序 // 2、特殊字符 转无意义中间值 txtStr = txtStr.replace(//gm,"?gt?").replace(/"/gm,"?quot?").replace(/'/gm,"?#39?"); return txtStr; } //简述内容转markdown function sketchResourceToHtmlAfter(txtStr = "") { // 1、链接变超链接,这里必需要使用“先匹配再替换” const regexParam = /[^("?>]\s*(https?:\/\/[^\s()()\[\]<>"`]+)/gm; let m; let textStrClone = txtStr; while ((m = regexParam.exec(textStrClone)) !== null) { // 这对于避免零宽度匹配的无限循环是必要的 if (m.index === regexParam.lastIndex) { regexParam.lastIndex++; } let match = m[0]; // 为简讯内容的url添加可链接 const regex = /(https?:\/\/[^\s()()\[\] `]+)/gm; const subst = `$1`; // 被替换的值将包含在结果变量中 let aTab = match.replace(regex, subst); txtStr = txtStr.replace(match, aTab); } // 2、无意义中间值 转有意符 function revert(text) { let obj = { "?lt?":"<", "?gt?":">", "?quot?":""", "?#39?":"'", "?br?":"
    " } for(let key in obj) { text = text.toReplaceAll(key,obj[key]); } return text; } txtStr = revert(txtStr); return txtStr; } $("#matchItems").on("click","li > a",function(e) { // 设置为阅读模式 // $("#my_search_input").val(":read"); // 获取当前结果在搜索数组中的索引 let dataIndex = parseInt($(e.target).attr("index")); let itemData = registry.searchData.data[dataIndex]; // 如果是简述搜索信息,那就取消a标签的默认跳转事件 if( ! isUrlNoUrlText(itemData.resource) ) { // 取消默认事件 e.preventDefault(); matchResult.css({ "display": "none" }) textShow.css({ "display":"block" }) textShow.html("标题:"+itemData.title+"
    "+ "描述:"+itemData.desc+"
    "+"简述内容:
    "+sketchResourceToHtmlAfter(converter.makeHtml(sketchResourceToHtmlBefore(itemData.resource))) ); textShow.find("img").css({"width":"100%"}) /*使用code代码块样式*/ document.querySelectorAll('#text_show pre code').forEach((el) => { hljs.highlightElement(el); }); return; } // 取消冒泡 window.event? window.event.cancelBubble = true : e.stopPropagation(); // 隐藏视图 registry.view.viewVisibilityController(false) // 解析URL(主要将keyword补充上去) let targetObj = e.target; const initUrl = $(targetObj).attr("href"); // 不作改变的URL let url = initUrl; // 进行修改,形成要跳转的真正url let temNum = url.matchFetch(/\[\[[^\[\]]*\]\]/gm, function (matchStr,index) { // temNum是url中有几个 "[[...]]", 得到后,就已经得到解析了 let templateStr = matchStr; // 使用全局的keyword, 构造出真正的keyword let keyword = registry.searchData.keyword.split(":").reverse(); keyword.pop(); keyword = keyword.reverse().join(":").trim(); let parseAfterStr = matchStr.replace(/{keyword}/g,keyword).replace(/\[\[+|\]\]+/g,""); url = url.replace(templateStr,parseAfterStr); }); // 如果搜索的真正keyword为空字符串,则去掉模板跳转 if( registry.searchData.keyword.split(registry.searchData.searchBoundary).length < 2 || registry.searchData.keyword.split(registry.searchData.searchBoundary)[1].trim() == "" ) { url = registry.searchData.clearUrlSearchTemplate(initUrl); } // 如果是URL存在模板 if(temNum > 0 ) { window.open(url); // 取消默认事件 e.preventDefault(); return; } // 否则是URL跳转 }) //registry.searchData.searchHandle = handler; const refresh = debounce(handler, 460) // 第一次触发 scroll 执行一次 fn,后续只有在停止滑动 1 秒后才执行函数 fn searchBox.on('input', refresh) // 初始化后将isInitializedView变量设置为true isInitializedView = true; } let hideView = function () { // 隐藏视图 // 如果视图还没有初始化,直接退出 if (!isInitializedView) return; // 如果正在查看查看“简讯”,先退出简讯 if($("#text_show").css("display")=="block") { // 让简讯隐藏 $("#text_show").css({"display":"none"}) // 让搜索结果显示 $("#matchResult").css({ "display":"block", "overflow": "hidden", }) return; } // 让视图隐藏 viewDocument.style.display = "none"; // 将输入框内容置空,在置空前将值备份,好让未好得及的操作它 searchInputDocument.val("") // 将之前搜索结果置空 matchItems.html("") // 隐藏文本显示视图 textShow.css({ "display":"none" }) // 让搜索结果显示 matchResult.css({ "display":"none" }) } let showView = function () { // 让视图可见 viewDocument.style.display = "block"; //聚焦 searchInputDocument.focus() // 当输入框失去焦点时,隐藏视图 searchInputDocument.blur(function() { setTimeout(function(){ // 判断输入框的内容是不是":debug"或是否正处于阅读模式,如果是,不隐藏 if(isInstructions(searchInputDocument.val(),"debug") || isInstructions(searchInputDocument.val(),"read")) return; // 当前视图是否在展示数据,如搜索结果,简述内容?如果在展示不隐藏 let isNotExhibition = (($("#matchResult").css("display") == "none" || $("#matchItems > li").length == 0 ) && ($("#text_show").css("display") == "none" || $("#text_show").text().trim() == "") ); if(!isNotExhibition || registry.view.menuActive ) return; registry.view.viewVisibilityController(false); },registry.view.delayedHideTime) }); } // 返回给外界控制视图显示与隐藏 return function (isSetViewVisibility) { if (isSetViewVisibility) { // 让视图可见 >>> // 如果还没初始化先初始化 // 初始化数据 initData(); if (!isInitializedView) { // 初始化视图 initView(); // 初始化数据 // initData(); } // 让视图可见 showView(); } else { // 隐藏视图 >>> if (isInitializedView) hideView(); } } })(); // 触发策略——快捷键 let useKeyTrigger = function (viewVisibilityController) { let isFirstShow = true; // 将视图与触发策略绑定 triggerAndEvent("ctrl+alt+s", function () { // 让视图可见 viewVisibilityController(true); // 触发视图首次显示事件 if(isFirstShow) { for(let e of registry.view.onViewFirstShow) e(); isFirstShow = false; } }) triggerAndEvent("Escape", function () { // 如果视图还没有初始化,就跳过 if(registry.view.viewDocument == null ) return; // 让视图不可见 viewVisibilityController(false); }) } // 触发策略组 let trigger_group = [useKeyTrigger]; // 初始化入选的触发策略 (function () { for (let trigger of trigger_group) { trigger(registry.view.viewVisibilityController); } })(); // 打开视图进行配置 // 显示配置视图 // 是否显示进度 - 进度控制 GM_registerMenuCommand("订阅管理",function() { showConfigView(); }); GM_registerMenuCommand("清理缓存",function() { cache.remove(registry.searchData.SEARCH_DATA_KEY); }); // 显示配置规则视图 function showConfigView() { if($("#subscribe_save")[0] != null) return; // 显示视图 var configViewContainer = document.createElement("div"); configViewContainer.style=` width:450px; background:pink; position: fixed;right: 0px; top: 0px; z-index:10000; padding: 20px; border-radius: 14px; ` configViewContainer.innerHTML = `

    订阅总览:

    `; // 设置样式 document.body.appendChild(configViewContainer); document.getElementById("title").style="margin-bottom: 10px; font-size: 16px;"; document.getElementById("all_subscribe").style="width:100%;height:150px"; document.getElementById("subscribe_save").style=" margin-top: 20px; border: none; border-radius: 3px; padding: 4px 20px; cursor: pointer;"; // 回显 document.getElementById("all_subscribe").value = getSubscribe(); // 保存 document.getElementById("subscribe_save").onclick=function() { // 保存到对象 let allSubscribe = document.getElementById("all_subscribe").value; let validCount = editSubscribe(allSubscribe); // 清除视图 configViewContainer.remove(); alert("保存配置成功!有效订阅数:"+validCount); } } })();