// ==UserScript== // @name Enhanced word highlight // @name:en Enhanced word highlight // @namespace http://userscripts.org/users/86496 // @description 在搜索引擎和任意网页获得增强的高亮! // @description:en Enhanced keywords highlight for Search Engines and All ! // @match *://*/* // @exclude http://maps.google.com/* // @exclude https://maps.google.com/* // @grant GM_log // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_openInTab // @grant GM_registerMenuCommand // @license GNU GPLv3 // @version 1.7.3 // @downloadURL none // ==/UserScript== // great credit for original script wright os0x [http://userscripts.org/scripts/show/43419] // hzhbest modded | detail in http://userscripts.org/scripts/show/64877 // 173: fix charset recognition, update look //console.time("highlight"); function l() { //debug var args = Array.prototype.slice.call(arguments); console.log.apply(console, args); } (function word_hightlight(loaded){ //if (window.top != window.self) return; //don't run on frames or iframes // check browser if (!loaded && window.opera && document.readyState == 'interactive') { document.addEventListener('DOMContentLoaded', function(){ loaded = true; word_hightlight(true); }, false); window.addEventListener('load', function(){ if (!loaded) { word_hightlight(true); } }, false); return; } if (document.contentType && !/html/i.test(document.contentType)) return; // check api var getv, setv if (typeof GM_getValue == "function") { getv = GM_getValue; setv = GM_setValue; } else { // workaround functions, creadit to ww_start_t setv = function(cookieName, cookieValue, lifeTime){ if (!cookieName) {return;} if (lifeTime == "delete") {lifeTime = -10;} else {lifeTime = 31536000;} document.cookie = escape(cookieName)+ "=" + escape(getRecoverableString(cookieValue))+ ";expires=" + (new Date((new Date()).getTime() + (1000 * lifeTime))).toGMTString() + ";path=/"; }; getv = function(cookieName, oDefault){ var cookieJar = document.cookie.split("; "); for (var x = 0; x < cookieJar.length; x++ ) { var oneCookie = cookieJar[x].split("="); if (oneCookie[0] == escape(cookieName)) { try { eval('var footm = '+unescape(oneCookie[1])); } catch (e) {return oDefault;} return footm; } } return oDefault; }; } //{ values > var isOpera = !!this.opera, isFirefox = !!this.Components, isChromium = !!this.chromium, isSafari = this.getMatchedCSSRules && !isChromium; var STYLE_COLOR = ['#FFFF80','#99ccff','#ff99cc','#66cc66','#cc99ff','#ffcc66','#66aaaa','#dd9966','#aaaaaa','#dd6699']; var BORDER_COLOR = ['#aaaa20','#4477aa','#aa4477','#117711','#7744aa','#aa7711','#115555','#884411','#555555','#881144']; //浅一点的一套配色 var STYLE_COLOR_2 = ['#FFFFa0','#bbeeff','#ffbbcc','#88ee88','#ccbbff','#ffee88','#88cccc','#ffbb88','#cccccc','#ffaabb']; var BORDER_COLOR_2 = ['#aaaa40','#6699aa','#aa6699','#339933','#9966aa','#aa9933','#337777','#aa6633','#777777','#aa3366']; // button color: normal bg / active bg / disabled bg / border color. var but_c = '#cce5cc', but_ca = '#FFD000', but_cd = '#999999', but_cb = '#669966'; // lexicon 词汇表 // highlighted word:高亮词:已用于被高亮的单个【字符串】 // highlight(s):高亮:在页面中针对高亮词形成的带高亮效果【节点】 // navigate:寻觅:在页面中对寻找某高亮词或不指定高亮词的高亮的位置的【动作】 // keyword:关键词:通过自动高亮功能识别到的高亮词候选的单个【字符串】 // word:词:任何输入到Ewh的单个【字符串】 // map:分布图:显示各高亮在页面分布相对位置的【画布节点】 // viewport:指示窗:显示当前窗口可见区域在页面相对位置的【画布节点】 // panel:高亮面板:Ewh主要功能【节点】,上面有控制按钮和高亮按钮 // highlight button:高亮按钮:在高亮面板上代表并可用于寻觅各高亮词的高亮的【节点】 // turn off:停用:保留高亮面板但不显示各高亮的【动作】,可重新turn on // disable:禁用:不保留高亮功能在当前窗口不显示并不再显示高亮的【动作】,可重新添加词来重启高亮 // window:窗口:显示网页的浏览器【组件】;不同于浏览器窗口,一个浏览器窗口包含了其功能按钮和窗口,甚至通过多标签页包含多个窗口 // Initialize value var PRE = 'wordhighlight', ID_PRE = PRE + '_id', ST_PRE = PRE + '_store', PO_PRE = PRE + '_position', CO_PRE = PRE + '_config'; var STYLE_CLASS = '0123456789'.split('').map(function(a,i){return PRE + '_word'+i;}); var setuped = false; //初始启动状态:否 var highlight_off = false; //初始停用状态:否 var addKeyword = true; //初始添加状态:是(关键词输入框为“Add”而非“New”) var keyword = ''; //关键词文本 var words = []; //关键词数组 var word_lists = []; //放在word_inputs_list内的含单个关键词的li元素的数组 var word_inputs_list; //容纳word_lists的ul元素,位于panel内 var layers; //高亮元素span的数组 var positions = []; //高亮元素的指针位置,[#0:不指定高亮,#1:第一个词,#2:第二个词,……] var words_off = []; //略过不高亮状态数组,其顺序对应words,略过时为“1” var xp_all = new $XE('descendant::span[starts-with(@name,"' + PRE + '_word")]', document.body); //body中所有name属性为高亮标识开头的span元素进行xpath检索的检索体对象,其get方法输出span元素数组 //其使用get方法的目的是,每次调用时就是重新获取所有span元素生成数组,而不是固定数组 var keyCodeStr = { //key press 事件返回的which代码对应按键键名对应表对象 8: 'BAC', 9: 'TAB', 10: 'RET', 13: 'RET', 27: 'ESC', 33: 'PageUp', 34: 'PageDown', 35: 'End', 36: 'Home', 37: 'Left', 38: 'Up', 39: 'Right', 40: 'Down', 45: 'Insert', 46: 'Delete', 112: 'F1', 113: 'F2', 114: 'F3', 115: 'F4', 116: 'F5', 117: 'F6', 118: 'F7', 119: 'F8', 120: 'F9', 121: 'F10', 122: 'F11', 123: 'F12' }; var whichStr = { 32: 'SPC' }; var htmlDoc = isChromium ? document.implementation.createHTMLDocument('hogehoge') : document; //文档根节点 var highlight_reset = function(){}; //重置高亮的预定义函数 var canvas; //分布图底板canvas var cw; //分布图位置指示canvas var c2context; //分布图指示标canvas 2D图形 var nav; //分布图容器 var root = /BackCompat/i.test(document.compatMode) ? document.body : document.documentElement; //页面根节点 const CanvasWidth = 150; //分布图基础宽度 var ratio = 1; //分布图位置指示与实际比例 var aside, section, td0, lock, edit, off, text_input, posi_tip, posi_tip_timer, inputBOX; // panel elements var sheet, main_sheet, move_sheet, inst_sheet, config_sheet; // style sheets var pausetimer, onpause; //language detection const lang = (navigator.appName=="Netscape")? navigator.language : navigator.userLanguage; var _L; if (lang.indexOf('zh-') == -1) { _L = 0; } else _L = 1; // string arrays var _ti = { // en/zh locale string for tooltip. edit: ['Edit current keywords','编辑现有关键词'], edit_a: ['Confirm editing keywords','确认编辑关键词'], off: ['Toggle all keywords\' highlight','切换全部关键词的高亮'], td0: ['Double-click to minimize the panel','双击最小化面板'], td0_a: ['Double-click to restore Ewh panel','双击恢复 Ewh 面板'], lock: ['Lock current set of keywords','锁定当前的关键词组'], lock_a: ['Current locked keyword(s):','当前锁定的关键词组:'], lock_u: ['Function not supported by this browser','此浏览器不支持该功能'], close: ['Close Enhanced word highlight','关闭关键词高亮'], kwL: ['Left click to the next; Right click to the previous','左击跳到下一个;右击跳到上一个'], check: [['Toggle highlight of "','"'],['切换“','”的高亮']], mapl:['Toggle highlight map locking status','切换高亮分布图的锁定状态'], ad_nw: ['Toggle add/new keywords for highlight','切换添加/取代关键词的高亮'], subm: ['Submit keywords','提交关键词'], clos: ['Close input box','关闭输入框'] }; var _di = { // en/zh locale string for dialog. confT: ['Enhanced word highlight Advanced Config','Enhanced word highlight 高级设置'], ap_option: ['What auto-pager tool do you mostly use?','你主要用那种自动翻页工具?'], ap_option1: ['Autopagerize GM script','Autopagerize GM 脚本'], ap_option2: ['Autopager extension','Autopager 扩展'], ap_option3: ['Other (can handle all auto-paging tools','其他(能应付任何自动翻页工具)'], off_short_words: ['Skip highlighting of short words?','是否忽略短词的高亮?'], off_short_words1: ['Don\'t skip','否'], off_short_words2: ['One-letter/digit word','忽略单个字母/数字的词'], off_short_words3: ['One- and two-letter/digit word','忽略一至两个字母/数字的词'], skip_word_list: ['Skip highlighting of words below: (separate words with a space in between)','忽略以下词的高亮:(使用空格分隔各词)'], no_auto_hili: ['Disable auto-highlight (auto-capture keywords for highlight) ?','是否禁用自动高亮(自动抓取关键词来高亮)?'], no_auto_hili1: ['Enable','不禁用'], no_auto_hili2: ['Completely disable','完全禁用'], no_auto_hili3: ['Disable only on pages from search result links','仅在搜索结果链接打开的页面上禁用'], no_auto_hili4: ['Disable only on search result pages','仅在搜索结果页面上禁用'], sort_keywords: ['Sort words so that longer word will be highlighted earlier. Recommended, except for highlighting regular expression.', '从长到短排序目标词然后依此进行高亮,推荐选择,需要高亮正则表达式的除外。'], sort_keywords1: ['Sort words before highlight','高亮前先排序目标词'], save_panel_pos: ['If you have your own favorite panel position, check this.', '如果你希望面板默认在你喜欢的位置显示,就开启这功能。'], save_panel_pos1: ['Save panel position','保存面板位置'], show_indc_bar: ['Check this and a transparent indicator bar will appear when navigating through highlights, telling which row the current highlight is.', '若该功能开启,在查找高亮时会显示半透明指示条以标示当前高亮所在的行。'], show_indc_bar1: ['Show indicator bar when navigating','查找关键词时显示指示条'], button_yes: ['OK', '确定'], button_no: ['Cancel', '取消'], }; //} //{ Config I > // #### Config I #### --------------------------{{ // List of url patterns; Array('NAME', 'KEYWORD_PREFIX', 'URL_PATTERN')搜索引擎信息数组 var urlArr = [ ['Google', 'q', '.google.'], ['Yahoo', 'p', 'search.yahoo.'], ['Baidu', 'wd', '.baidu.com'], ['Baidu', 'word', '.baidu.com'], ['Ask', 'q', 'www.ask.com'], ['Bing', 'q', '.bing.com'], ['Youdao', 'q', 'www.youdao.com'], ['cnki', 'keyValue', 'kns.cnki.net'], ['sogou', 'query', 'www.sogou.com'], ['weibo', 'q', 's.weibo.com'], ['duckduckgo', 'q', 'duckduckgo.com'], //https://duckduckgo.com/?q=DDG&t=h_&ia=web ['greasyfork', 'q', 'greasyfork.org'], //https://greasyfork.org/zh-CN/scripts?q=%E9%AB%98%E4%BA%AE ['ecosia', 'q', 'www.ecosia.org'] //https://www.ecosia.org/search?q=%E7%9C%BC%E7%90%83%E8%B7%9F%E8%B8%AA ]; // List of IDs of query input boxes; Array('#SEARCHBOX_ID#', 'SEARCHPAGE SPEC_URL') var queryArr = [ ['query', '/search'], // most common ['search', ''], // most common ['script_q', 'userscripts.org/scripts/search'], // userscripts.org ['search-q', '/search'] // addons.mozilla.org ]; // keybinds var KEY_NEXT = 'n'; // "n" Next occurrence var KEY_PREV = 'N'; // "Shift-n" Previous occurrence var KEY_SEARCH = 'M-/'; // "Alt-/" Add keywords var KEY_OFF = 'M-,'; // "Alt-," Suspend highlight var KEY_CLOSE = 'C-M-/'; // "Ctrl-Alt-/" Disable highlight var KEY_EDIT = 'M-.'; // "Alt-." Edit highlight var KEY_REFRESH = 'r'; // "r" Refresh highlight // delay of highlighting (ms) var delay = 500; // instant highlight selected keywords var instant = true; // restore focus and scroll position after closing keyword input box with shortcut key? // mainly useful for keyboard navigation, not recommend for mouse navigation. var refocus = false; // minimize the panel initially? var panel_hide = false; // #### Config I #### --------------------------}} //} if (window.top.document.location.href != window.self.document.location.href) panel_hide = true; //hide panel in iframes //l(window.top.document.location.href != window.self.document.location.href); // #### Config II #### --------------------------{{ var CFG = { ap_option: 2, // What's your main auto-pager tool? // 0 - Autopagerize (GM script, obsolete) // 1 - AuroPager (Firefox extension, obsolete) // 2 - Other (Other auto-paging scripts or extensions) off_short_words: 1, // turn off short keywords (one or two letters or number) by default? // 0-no, 1-one letter, 2-one or two letters skip_word_list: "the to in on among between and a an of by with", // skip highlighting word list no_auto_hili: 0, // Stop auto-highlight on supported pages? // 0-no, 1-yes, 2-only those from search results, 3-only search results sort_keywords: 1, // sort keywords? 0-no, 1-yes // Setting this to "yes" will produce better highlight result, // while "no" will perform faster and support ReExp input better. save_panel_pos: 0, // save panel position? 0-no, 1-yes show_indc_bar: 1, // show indicator bar when navigating? 0-no, 1-yes };//l(JSON.stringify(CFG)); // #### Config II #### --------------------------}} // GM APIs available? if (typeof GM_getValue == "function") var gm_ok = true; var panel_pos_arr = ['right:-1px;','bottom:-1px;']; if (gm_ok) { // Configs var confstr = GM_getValue(CO_PRE); //提取GM储存的设置 if (!!confstr && confstr.slice(0,1) === "{") { //如果设置是对象文本(JSON) CFG = JSON.parse(confstr); //则载入 } else { //否则将默认设置写入储存 GM_setValue(CO_PRE, JSON.stringify(CFG)); } // Locked keywords var keyword_locked = GM_getValue(ST_PRE); // Saved position if (CFG.save_panel_pos) panel_pos_arr = GM_getValue(PO_PRE, panel_pos_arr.join('|')).split('|'); // Configs menu window.addEventListener('load', function(){GM_registerMenuCommand(_di.confT[_L], config_box);}, false); unsafeWindow.EWH_iSearch = function() {instant_search(false, null);}; unsafeWindow.EWH_cClose = function() {command_close();}; } // main process init_keyboard(); if (load_keyword() !== false || init_keyword() !== false) { //window.addEventListener('load', setup, false); setTimeout(setup, delay); } // var oldurl = window.location.href; // window.addEventListener('DOMNodeInserted', function(e){ l(window.location.href); // if (window.location.href !== oldurl) { // if (load_keyword() !== false || init_keyword() !== false) { // setTimeout(setup, delay*2); // } // } // }, false); // Functions // 对目标文档(元素)对指定关键词数组进行高亮 ## function highlight(doc, ext_word) { var _words = words.filter(function(w,i){return !words_off[i];}); if (_words.length <= 0) { return; } var _index; if (ext_word && ext_word.words) { _words = ext_word.words; _index = ext_word.index; } var exd_words, xw; if (_words.length === 1 && _words[0].exp) { exd_words = _words.map(function(e){return e.exp;}); xw = ''; } else { exd_words = _words.map(function(w){return w.test ? w : new RegExp('(' + w.replace(/\W/g,'\\$&') + ')(?!##)', 'ig');}); xw = ' and (' + _words.map(function(w){return ' contains(translate(self::text(),"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ"),'+escapeXPathExpr(w.toUpperCase())+') ';}).join(' or ') + ') '; } $X('descendant::text()[string-length(normalize-space(self::text())) > 0 ' + xw +' and not(ancestor::textarea or ancestor::script or ancestor::style or ancestor::aside)]', doc).forEach(function(text_node) { var df, text = text_node.nodeValue, id_index = 0, parent = text_node.parentNode, range = document.createRange(), replace_strings = [], new_text = reduce(exd_words, function(text,ew,i) { var _i = _index || i; return text.replace(ew,function($0,$1) { replace_strings[id_index] = '' + $1 + ''; return '##'+(id_index++)+'##'; }); }, text). replace(/&/g, '&').replace(//g, '>'). replace(/##(\d+)##/g, function($0,$1) { return replace_strings[$1] || ''; }); if (replace_strings.length) { try { if (isChromium) { range.selectNodeContents(htmlDoc.documentElement); } else { range.selectNode(text_node); } df = range.createContextualFragment(new_text); if (df.firstChild) parent.replaceChild(df, text_node); range.detach(); } catch (e) { error(e); } } }); } // 生成主css样式表 function addsheet() { if (!main_sheet) { var hilistyles = STYLE_COLOR.map(function(rgb,i){ return 'span.' + PRE + '_word'+i+',.' + PRE + '_item'+i+'{background:'+rgb+'!important;}'; }); var borderstyles = BORDER_COLOR.map(function(rgb,i){ return 'li.' + PRE + '_item'+i+'{outline:1px solid '+rgb+'!important;}'; }); sheet = addCSS([ //Additional Style 'span[class^="' + PRE + '_word"]{color:black!important;font:inherit!important;display:inline!important;margin:0!important;padding:0!important;text-align:inherit!important;float:none!important;position:static!important;}', //vertical-align:inherit !important; '#' + PRE + '_words, #' + PRE + '_words *{font-family: Arial ;}', '#' + PRE + '_words{line-height:1;position:fixed;z-index:60000;opacity:0.8;list-style-type:none;margin:0;padding:0;width:auto;max-width:100%;' + panel_pos_arr[0] + panel_pos_arr[1] +'}', '#' + PRE + '_words > section{clear:right;line-height:1;border:1px solid #d6d6d6;background:#fff;display:block;position:relative;}', '#' + PRE + '_words * {margin:0;padding:0;width:auto;height:auto;}', '#' + PRE + '_words:hover{opacity:1;}', '#' + PRE + '_words:hover > section{opacity:1;border-color:#333;}', '#' + PRE + '_words #_ewh_handle{background:#666;width:10px;cursor:move;}', '#' + PRE + '_words:hover #_ewh_handle{background:#333;}', '#' + PRE + '_words.ewh_hide #_ewh_handle{cursor:pointer;}', '#' + PRE + '_words > nav{display:none;width:100%;padding:3px;position:relative;}', '#' + PRE + '_words > nav > canvas.backport{background:rgba(0,0,0,0.5);cursor:pointer;position:absolute;right:6px;z-index:3;}', '#' + PRE + '_words > nav > canvas.viewport{background:rgba(79,168,255,0.7);cursor:default;position:absolute;bottom:0px;}',//outline:6px solid rgba(79,168,255,0.7); '#' + PRE + '_words:hover > nav{display:block;}', '#' + PRE + '_words > nav._locked{display:block;}', '#' + PRE + '_words:hover > nav > canvas.backport{bottom:0px;}', '#' + PRE + '_words > nav._locked > canvas.backport{bottom:0px;}', '#' + PRE + '_words.ewh_edit{opacity:1;}', '#' + PRE + '_words.ewh_edit #' + PRE + '_word_inputs_list{display:none;}', '#' + PRE + '_words form.' + PRE + '_editor{display:none;}', '#' + PRE + '_words.ewh_edit form.' + PRE + '_editor{display:inline-block;}', '#' + PRE + '_words.ewh_edit form.' + PRE + '_editor input{min-width:80px;}', '#' + PRE + '_words li{display:inline-block;margin:0.1em 0.2em;line-height:1.3em;font-size:12pt !important;}', '#' + PRE + '_words > section > table {border:0;margin:0;padding:0;border-spacing:2px;border-collapse:separate!important;}', '#' + PRE + '_words > section > * {vertical-align:middle;}', '#' + PRE + '_words > section td {border:none;}', '#' + PRE + '_words > section form.' + PRE + '_ctrl{max-width: 0; max-height: 0; overflow: hidden; transition: all 0.3s ease-in-out 5s;}', '#' + PRE + '_words:hover > section form.' + PRE + '_ctrl{display:inline-block !important;max-width: 200px; max-height: 1.3em; transition: all 0.1s ease-in-out 0s;}', '#' + PRE + '_words > section form.' + PRE + '_ctrl > input{display:inline;width:1.3em;height:1.6em;margin-right:1px;background:'+ but_c +';border:0px solid '+ but_cb +';cursor:pointer;font-size:10pt !important;color:black;}', '#' + PRE + '_words > section form.' + PRE + '_ctrl > input._active{background:'+ but_ca +';}', '#' + PRE + '_words > section form.' + PRE + '_ctrl > input._disable{background:'+ but_cd +' !important;cursor:default;}', '#' + PRE + '_words > section form.' + PRE + '_ctrl > input:hover{outline:1px solid '+ but_cb +'!important;}', '#' + PRE + '_word_inputs_list {padding:0!important;margin:0.2em!important;display:inline-block;border:none!important;}', '#' + PRE + '_word_inputs_list > li{position:relative;padding:0 4px;user-select: none;}', '#' + PRE + '_word_inputs_list > li.ewh_disable{background:white!important;outline:1px solid #999!important;}', '#' + PRE + '_word_inputs_list > li > label{cursor:pointer;color:black!important;}', '#' + PRE + '_word_inputs_list > li > input{cursor:pointer;}', '#' + PRE + '_word_inputs_list > li > input[type=checkbox]{display:none;position:absolute;right:0px;top:0px;opacity:0.7;}', '#' + PRE + '_word_inputs_list > li:hover{outline-width:2px!important;}', '#' + PRE + '_word_inputs_list > li:hover > input[type=checkbox]{display:block;}', '#' + PRE + '_word_inputs_list > li > input[type=checkbox]:hover{opacity:1;}', '#' + PRE + '_words > section td#_ewh_maplock > input {display:inline;width:1.3em;margin:0.1em 0.1em;background:#FAFAFA;border:none;cursor:pointer;font-size:10pt;color:#4d4d4d;}', '@keyframes fadeit {from {opacity:1;} to {opacity:0;}}', '#' + PRE + '_words > section td#_ewh_maplock > input:hover {background:'+ but_c +';}', '#' + PRE + '_words > section td#_ewh_maplock > input._active {background:'+ but_ca +';}', ].concat(hilistyles, borderstyles).join('\n')); main_sheet = true; } if (!move_sheet) addmovesheet() } // 生成突出高亮样式表 function addmovesheet() { addCSS('.wordhighlight_em{outline:4px solid #FF7B00;-webkit-outline:4px solid #FF7B00;text-decoration:blink;}'); move_sheet = true; } // 启动:生成面板,形成面板互动机制,生成分布图和分布图互动机制,可拖动绑定,自动翻页响应,开始高亮全文档 ## function setup(init) { //输入:若init=true,则无视panel_hide的状态,不隐藏面板 setuped = true; addsheet(); // build ui aside = creaElemIn('aside', document.body); //基底容器 aside.id = PRE + '_words'; section = creaElemIn('section', aside); //主容器 var table_COL = creaElemIn('table', section); //排版容器 table_COL.setAttribute('cellspacing', '0'); table_COL.setAttribute('cellpadding', '0'); var tbdy_COL = creaElemIn('tbody', table_COL); //表主体 var tr_COL = creaElemIn('tr', tbdy_COL); //表行体 td0 = creaElemIn('td', tr_COL); //拖动手柄 td0.id = '_ewh_handle'; td0.title = _ti.td0[_L]; var td1 = creaElemIn('td', tr_COL); //功能按钮区 var td2 = creaElemIn('td', tr_COL); //高亮按钮区 var td3 = creaElemIn('td', tr_COL); //分布图按钮区 td3.id = '_ewh_maplock'; var editor = creaElemIn('form', td2); //关键词编辑表单 editor.className = PRE + '_editor'; text_input = creaElemIn('input', editor); //关键期编辑输入框 text_input.type = 'text'; var ctrl = creaElemIn('form', td1); //控制按钮表单 ctrl.className = PRE + '_ctrl'; var close_button = creaElemIn('input', ctrl); //关闭按钮 close_button.type = 'button'; close_button.className = 'c_b'; close_button.value = 'X'; close_button.title = _ti.close[_L]; off = creaElemIn('input', ctrl); //停用按钮 off.type = 'button'; off.value = 'O'; off.title = _ti.off[_L]; lock = creaElemIn('input', ctrl); //锁定按钮 lock.type = 'button'; lock.value = 'L'; edit = creaElemIn('input', ctrl); //编辑按钮 edit.type = 'button'; edit.value = 'E'; edit.title = _ti.edit[_L]; word_inputs_list = creaElemIn('ul', td2); //高亮按钮列表容器 word_inputs_list.id = PRE + '_word_inputs_list'; word_inputs_list.className = PRE + '_inputs'; var maplock = creaElemIn('input', td3); //分布图按钮 maplock.type = 'button'; maplock.value = '▲'; maplock.title = _ti.mapl[_L]; // add interactivity edit.addEventListener('click',command_edit,false); off.addEventListener('click',command_off,false); close_button.addEventListener('click',command_close,false); editor.addEventListener('submit',function(e){ command_edit(); e.preventDefault(); e.stopPropagation(); },false); if (gm_ok) { lock.title = _ti.lock[_L]; lock.className = (keyword_locked)? '_active' : ''; lock.addEventListener('click',function(){ if (aside.className == 'ewh_edit') return; if (keyword_locked) { lock.className = ''; lock.title = _ti.lock[_L]; GM_setValue(ST_PRE, ''); keyword_locked = ''; // lock.value = 'Lock: Off'; } else { lock.className = '_active'; lock.title = _ti.lock_a[_L] + ' ' + keyword; GM_setValue(ST_PRE, keyword); keyword_locked = keyword; // lock.value = 'Lock: On'; } },false); } else { lock.title = _ti.lock_u[_L]; lock.className = '_disable'; } td0.addEventListener('dblclick',function(evt){//l(panel_hide,window.innerWidth - aside.offsetLeft,1); if (panel_hide) {//l('O'); aside.style.right = '0px'; aside.className = ''; panel_hide = false; this.title = _ti.td0[_L]; }else{//l(panel_hide); aside.style.right = (14 - aside.offsetWidth) +'px'; aside.className = 'ewh_hide'; panel_hide = true;//l(panel_hide,3); this.title = _ti.td0_a[_L]; } }, false); maplock.addEventListener('click',function(){ if(!nav.className) { nav.className = '_locked'; this.value = '▼'; this.className = '_active'; } else { nav.className = ''; this.value = '▲'; this.className = ''; } },false); // enable drag var drag = endrag(aside,{x:'right',y:'bottom'}); // 对aside面板的right和bottom属性进行拖动联动(实现可拖动功能) drag.hook('__drag_begin', function(e){ // 对drag对象的hook方法进行输入:如果面板是编辑或隐藏状态,则不允许拖动 if (this.element && ((this.element.className === 'ewh_edit') || (this.element.className === 'ewh_hide'))) {// || /^canvas$/i.test(e.target.localName) || /^lable$/i.test(e.target.localName)) return false; } }); // build map nav = document.createElement('nav'); aside.insertBefore(nav,aside.firstChild); canvas = creaElemIn('canvas', nav); canvas.className='backport'; cw = creaElemIn('canvas', nav); cw.className='viewport'; var c2 = c2context = canvas.getContext('2d'); // /+drag codes by grea // scrolling per events var perf = 2, perfic = 0; var moveTo = function(evt){ if (perfic++ % perf || !window.drgg) return; var x = (evt.offsetX || evt.layerX)/ratio - root.clientWidth/2; var y = (evt.offsetY || evt.layerY)/ratio - root.clientHeight/2; window.scrollTo(x, y); } canvas.addEventListener('mousedown', function(e){ window.drgg = true; moveTo(e); },false); canvas.addEventListener('mousemove', function(e){ moveTo(e); },false); canvas.addEventListener('mouseup', function(e){ window.drgg = false; moveTo(e); },false); canvas.addEventListener('mouseout', function(e){ window.drgg = false; moveTo(e); },false); // +/codes end // add AutoPager page change detector if (CFG.ap_option) { var pagef = 5, pagefic = 0; var docHeight = document.body.scrollHeight, pageChanged; var checkpage = function(){ if ((pagefic++ % pagef == 0) && (document.body.scrollHeight > docHeight)) { switch (CFG.ap_option) { case 1: after_load(); break; case 2: resetup(); break; } docHeight = document.body.scrollHeight; } } } // sync with map & check page window.addEventListener('scroll',function(){ var x = window.pageXOffset * ratio; var y = window.pageYOffset * ratio; cw.style.bottom = (canvas.height - cw.height - y) + 'px'; cw.style.right = (canvas.width - cw.width - x + 6) + 'px'; //l("s:" + onpause); if (CFG.ap_option && (window.name != PRE + '::CLOSED::') && !onpause) checkpage(); },false); // go to highlight highlight(document.body); word_lists = create_inputlist(words); layers = xp_all.get(); draw_wordmap(); if (!CFG.ap_option) init_autopager(); if (panel_hide && !init){ aside.style.right = (14 - aside.offsetWidth) +'px'; aside.className = 'ewh_hide'; td0.title = _ti.td0_a[_L]; } } // function restore_words(layer_s) { // 针对必应国际版,由于其鼠标取词功能会和新高亮清除算法冲突,只能用旧算法 // if (location.host == 'cn.bing.com' && (getUriParam(location.href, 'ensearch') == '1' || parseCookie(document.cookie)["ensearch"][1] == 1)) { // old_restore_words(layer_s); // } else { // new_restore_words(layer_s); // } // } // // 去除高亮:新算法,重组高亮元素所在的文本节点(无则新增)再去除高亮元素,使文本节点不再“断裂”,从而避免去掉短词高亮无法实现长词高亮问题。 // function new_restore_words(layer_s) { // // 如果指定layer_s高亮元素,则用,否则用xp_all.get()来获取全部高亮元素;对获取的高亮元素遍历 // var ls = (layer_s||xp_all.get()); // for (let i = ls.length - 1; i >= 0; i--) { // 为避免移除靠前元素致靠后元素定位失败,从末尾开始遍历 // var _t = ls[i].innerHTML; // 提取高亮元素的文本内容 // var _par = ls[i].parentNode;//l(_par); // 提取高亮元素父元素(避免一会移除高亮元素后无法追踪) // var _nt = '', _pt = ''; // 后一节点和前一节点的文本内容,默认为空 // var _n = ls[i].nextSibling; // 提取高亮元素后一节点 // if (!!_n && _n.nodeName == "#text") { // 如果后一节点存在且为文本节点,则使用其文本内容(非文本节点则不处理) // _nt = _n.nodeValue; // } // var _p = ls[i].previousSibling; // 同上处理后一节点内容 // if (!!_p && _p.nodeName == "#text") { // _pt = _p.nodeValue; // } // _par.removeChild(ls[i]); // 移除高亮元素 // if (!!_pt) { // 如果前一节点文本非空,则移除存在的后一节点,在前一节点内容后添加高亮元素和后一节点的内容 // if (!!_nt) _n.parentNode.removeChild(_n); // _p.nodeValue = _pt + _t + _nt; // } else if (!!_nt) { // 如果无前一节点内容有后一节点内容,则在后一节点内容前添加高亮元素内容 // _n.nodeValue = _t + _nt; // } else { // 如果既无前又无后,则在父元素中新建文本节点承载高亮元素内容 // _par.appendChild(document.createTextNode(_t)); // } // } // } // 去除高亮:旧算法 // function old_restore_words(layer_s) { function restore_words(layer_s) { (layer_s||xp_all.get()).forEach(function(layer,i){ var parent = layer.parentNode; while (layer.firstChild){ parent.insertBefore(layer.firstChild, layer); } parent.removeChild(layer); parent.normalize(); //文本节点整合 }); } // 绘制分布图 function draw_wordmap() { var c2 = c2context; var _height = root.clientHeight * 0.95; if (_height > CanvasWidth * (root.scrollHeight / root.scrollWidth)) { canvas.width = CanvasWidth; canvas.height = CanvasWidth * (root.scrollHeight / root.scrollWidth); ratio = CanvasWidth / root.scrollWidth; } else { canvas.height = _height; canvas.width = _height * (root.scrollWidth / root.scrollHeight); ratio = _height / root.scrollHeight; } cw.width = root.clientWidth * ratio; cw.height = root.clientHeight * ratio; cw.style.bottom = (canvas.height - cw.height - window.pageYOffset * ratio) + 'px'; cw.style.right = (canvas.width - cw.width + 6) + 'px'; c2.clearRect(0, 0, window.innerWidth, window.innerHeight); c2.beginPath(); word_lists.forEach(function(item, i){ if(!words_off[i]) { c2.fillStyle = STYLE_COLOR[i%10]; item.get_w().forEach(function(ly, j){ var recs = ly.getClientRects(); for (var i = 0, l = recs.length; i < l; ++i){ var rec = recs[i]; var x = Math.max(ratio * (root.scrollLeft + rec.left), 2); var y = Math.max(ratio * (root.scrollTop + rec.top), 2); var width = Math.max(ratio * (rec.width || (rec.right-rec.left)), 2); var height = Math.max(ratio * (rec.height || (rec.bottom-rec.top)), 2); c2.fillRect(x, y, width, height); } }); } }); c2.fill(); } // 添加新的关键词高亮及相关 function add_word(word) { var word_tmp = init_words(word); var word_tmp_len = word_tmp.length, words_len = words.length; // 前者:要添加的新词数组长度;后者:已有的旧词数组长度 for (var m=0; m w 代表单个词 var _i = i + (start||0); //当前词的高亮编号;如果无 start ,则为零否则加上已有高亮词数量; _i+1 为高亮指针位置专用高亮编号 var li = creaElemIn('li', word_inputs_list); li.className = PRE + '_item' + _i%10; //根据位置定颜色(最多十种,所以求余) var label = creaElemIn('div', li); //高亮按钮主体 (!CFG.sort_keywords && positions[_i+1]) || (positions[_i+1] = -1); //如果不排序且当前高亮编号已有则略过;否则当前高亮编号指针位置“归零” var xp = new $XE('descendant::span[@name="' + PRE + '_word' + _i +'"]', document.body); //提取当前高亮编号的所有高亮的函数 var xp_count = new $XE('count(descendant::span[@name="' + PRE + '_word' + _i +'"])', document.body); //对当前高亮编号的所有高亮计数的函数 var wheelfunction = (evt) => { //l(evt); //响应鼠标滚轮事件 if (words_off[_i]) return; evt.stopPropagation(); evt.preventDefault(); onpause = true; clearTimeout(pausetimer); pausetimer = setTimeout(() => { onpause = false; }, 1000); //l("p:" + onpause); var layers = xp.get(); var ct = (-evt.deltaY || -evt.detail); ct < 0 ? next(_i+1,layers) : prev(_i+1,layers); //return false; }; label.addEventListener('click',function(){ //左键点击高亮按钮 if (words_off[_i]) return; //如果是略过词,则忽略 var layers = xp.get(); //获取全部高亮 next(_i+1,layers); //跳到当前词的下一个高亮 },false); label.addEventListener('contextmenu',function(evt){ //右键点击高亮按钮(其余同上) if (words_off[_i]) return; evt.preventDefault(); //prevent activating context menu evt.stopPropagation(); var layers = xp.get(); prev(_i+1,layers); },false); // https://stackoverflow.com/a/32215631/2755602 var wheelEvt = "onwheel" in document.createElement("div") ? "wheel" : (document.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll"); //compatibility fix for Chrome-core browsers label.addEventListener(wheelEvt, wheelfunction, false); //在高亮按钮上滚动滚轮 label.className = PRE + '_label' + _i % 10; label.title = _ti.kwL[_L]; label.textContent = w + ' (' + xp_count.get({result_type:XPathResult.NUMBER_TYPE}).numberValue + ')'; var check = creaElemIn('input', li); check.type = 'checkbox'; if (words_off[_i]) { check.checked = false; li.className += ' ewh_disable'; } else check.checked = true; check.title = _ti.check[_L][0] + w + _ti.check[_L][1]; var _id = check.id = ID_PRE + '_check' + _i; var list = {item:li,word:w,label:label,check:check,get_count:xp_count.get,get_w:xp.get}; check.addEventListener('change', function(){ if (check.checked) { words_off[_i] = false; highlight(document.body,{words:[w],index:_i}); after_load(null, _i); this.parentNode.className = this.parentNode.className.replace(' ewh_disable', ''); } else { words_off[_i] = true; restore_words(xp.get()); draw_wordmap(); this.parentNode.className += ' ewh_disable'; } },false); return list; }); } // 对element添加可拖动的功能 function endrag(element,opt) { // 输入:目标元素element,拖动位置参考系opt(对象{参考left或right,参考top或bottom}) var p_x, p_y, isDragging endrag = function(element,opt){ return new endrag.proto(element,opt||{}); } endrag.proto = function(elem,opt){ var self = this; this.element = elem; this.style = elem.style; var _x = opt.x !== 'right'; var _y = opt.y !== 'bottom'; this.x = _x ? 'left' : 'right'; this.y = _y ? 'top' : 'bottom'; p_x = this.x; p_y = this.y; this.xd = _x ? -1 : 1; this.yd = _y ? -1 : 1; this.computed_style = document.defaultView.getComputedStyle(elem, ''); this.drag_begin = function(e){self.__drag_begin(e);}; td0.addEventListener('mousedown', this.drag_begin, false); //only drag on handler this.dragging = function(e){self.__dragging(e);}; document.addEventListener('mousemove', this.dragging, false); this.drag_end = function(e){ if (CFG.save_panel_pos && isDragging && elem.style[p_x] && gm_ok) { var h_pos = p_x + ':' + elem.style[p_x] + ';'; var v_pos = p_y + ':' + elem.style[p_y] + ';'; GM_setValue(PO_PRE, h_pos + '|' + v_pos); } // if (panel_hide && isDragging && ((window.innerWidth - aside.offsetLeft) > 14)){ // section.className = ''; // panel_hide = false; // } self.__drag_end(e); }; document.addEventListener('mouseup', this.drag_end, false); }; endrag.proto.prototype = { __drag_begin:function(e){ if (e.button == 0) { var _c = this.computed_style; this.isDragging = isDragging = true; this.position = { _x:parseFloat(_c[this.x]), _y:parseFloat(_c[this.y]), x:e.pageX, y:e.pageY }; e.preventDefault(); } }, __dragging:function(e){ if (!this.isDragging) return; var x = Math.floor(e.pageX), y = Math.floor(e.pageY), p = this.position; // prevent moving out of window var x_border = window.innerWidth - 40, y_border = window.innerHeight - 20; if (x - window.pageXOffset > x_border) x = window.pageXOffset + x_border; if (y - window.pageYOffset > y_border) y = window.pageYOffset + y_border; p._x = p._x + (p.x - x) * this.xd; p._y = p._y + (p.y - y) * this.yd; this.style[this.x] = p._x + 'px'; this.style[this.y] = p._y + 'px'; p.x = x; p.y = y; }, __drag_end:function(e){ if (e.button == 0) { if (this.isDragging) { this.isDragging = isDragging = false; } } }, hook:function(method,func){ if (typeof this[method] === 'function') { var o = this[method]; this[method] = function(){ if (func.apply(this,arguments) === false) { return; } o.apply(this,arguments); }; } } }; return endrag(element,opt); } // 从设置中加载锁定关键词 function load_keyword() { if (keyword_locked) { keyword = keyword_locked; prep_keyword(); return true; }else { return false; } } // 获取自动高亮的关键词:通过各获取过程得到keyword文本,转递给prep_keyword()生成words关键词列表 function init_keyword() { if (CFG.no_auto_hili == 1) return false; // 如果不自动高亮,直接退出 var name = window.name; if (CFG.no_auto_hili == 2 || name == (PRE + '::CLOSED::')) var _no_refer = true; // 如果不继承高亮或本窗口已清理高亮,则不再继承高亮 if (CFG.no_auto_hili != 3) init_KW_SR(); //l(101,keyword); // 如果允许搜索网站高亮,从搜索网站获取 if (!keyword) init_KW_IH(); //l(102,keyword); // 如果上一步找不到,则进行继承高亮 if (!_no_refer && !keyword) init_KW_RF(); //l(103,keyword); // 如果继承高亮了还找不到,则从页面来源获取 if (CFG.no_auto_hili != 3 && !keyword) init_KW_SRo(); //l(104,keyword); // 如果允许搜索网站高亮且还是找不到,则从搜索框获取 keyword = trim(keyword); // 清除前后杂物 if (keyword) { window.name = PRE + '::' + encodeURIComponent(keyword); // 在窗口内名中加入关键词,用于继承高亮 prep_keyword();//l(104,keyword); // 生成words列表 return true; } else {//l(105,'false'); return false; } } // 从已知特征的搜索网站网址中获取关键词(一般搜索结果页面内高亮) function init_KW_SR() { //for Search Results var host = location.host, h = document.location.href, e = -1; for (let i = 0; i < urlArr.length; i++) { if (host.indexOf(urlArr[i][2]) != -1 && h.indexOf(urlArr[i][1]) != -1) e = i;//l(e); } if (e >= 0) { keyword = get_KW_from_URL(h, urlArr[e]);//l(keyword); } } // 从已知id的搜索输入框中获取关键词(其他搜索结果页面内高亮) function init_KW_SRo() { //for other search result pages var locationhref = escape(document.location.href); for (var z = 0; z < queryArr.length; z++) { var input_query = document.getElementById(queryArr[z][0]); if (!input_query || locationhref.indexOf(queryArr[z][1]) == -1) continue; if (input_query.tagName.toLowerCase() == "input") keyword = clean(input_query.value); if (keyword) break; } } // 从referer(页面打开来源,通常是点开的链接所在的页面url;但若页面有防跟踪机制不给出正确referer的话这功能无法正常运作)获取关键词(搜索结果跳转高亮):输出keyword文本 function init_KW_RF() { //for Pages from Results var host = location.host, ref = document.referrer, e = -1; for (let i = 0; i < urlArr.length; i++) { if (CFG.no_auto_hili == 3 && host.indexOf(urlArr[i][2]) != -1) return; if (ref.indexOf(urlArr[i][2]) != -1 && ref.indexOf(urlArr[i][1]) != -1) e = i;//l(e); } if (e >= 0) { keyword = get_KW_from_URL(ref, urlArr[e]);//l(keyword); } } // 从窗口(标签页)内名(window.name)中获取关键词(继承高亮):输出keyword文本 function init_KW_IH() { //look for keywords in name if (window.name.indexOf(PRE) == 0 && window.name != (PRE + '::CLOSED::')) { keyword = (new RegExp(PRE + '\\d*::(.+)').exec(decodeURIComponent(window.name))[1]) || '';//l(102.1,keyword); }//l(102.2,keyword); } // 从url中提取搜索关键词(针对Google、百度作专门处理),输入url和对应搜索引擎信息数组,输出关键词文本 function get_KW_from_URL(sUrl, sArr) { if (sArr[0] =='Google' && !!getUriParam(sUrl, 'url')) sUrl = sUrl.replace(/%25/g,'%'); // if it is from Google's redirect link var kwtmp, KW; KW = getUriParam(sUrl, sArr[1]); //l(Arr[1] + ", " + KW); if (!!KW) KW = KW.replace(/\+/g,' ');//l('gku:',KW); if (sArr[0] =='Baidu' && document.characterSet !== "UTF-8") { kwtmp = urlDecode(KW); // 如果是百度且非utf-8 //2017-5-27百度似乎已经主用utf-8 //20170922 helloBibo 增加了“&& urlsearch.indexOf('ie=UTF-8') == -1” //20230505百度真的默认utf-8,修改识别方式 } else { kwtmp = decodeURIComponent(KW); } return clean(kwtmp); } // 从url中提取名为【param】的参数的值;提取不到返回空字符 function getUriParam(surl, param) { var result = surl.match(new RegExp("(\\?|&)" + param +"(\\[\\])?=([^&]*)")); return result ? result[3] : ''; } // 将给定关键词文本整理成关键词列表(数组) function prep_keyword() { words = init_words(keyword); } // 去除回行、前后空格、【句点加号】组合 function trim(str) { return str.replace(/[\n\r]+/g,' ').replace(/^\s+|\s+$/g,'').replace(/\.+\s|\.+$/g,''); } // 清除搜索关键词中的修饰功能部分,例如站内、标题搜索,逻辑运算词等 function clean(str) { return str.replace(/(?:(?:\s?(?:site|(?:all)?in(?:url|title|anchor|text)):|(?:\s|^)-)\S*|(\s)(?:OR|AND)\s|[()])/g,'$1'); } // 输出不重复元素的数组 function uniq(arr) { return arr.reduce((output, current) => { return output.includes(current) ? output : output.concat(current); }, []); } // arr.sort 使用,比较前后两字符串的长度,输出差值 function word_length_Comp(a,b) { return (b.length - a.length); } // 将自动获取的关键词转化为关键词列表(数组) function init_words(word) { var erg = word.match(new RegExp("^ ?/(.+)/([gim]+)?$")); // 判别是否正则表达式 var word_s; // 输出关键词列表 if (erg) { var ew = erg[1], flag = erg[2] || ''; // 提取出正则表达式的表达式部分和标记部分 word_s = [{exp:new RegExp('(' + ew + ')(?!##)', flag), text:ew, toString:function(){return ew;}}]; //输出单元素数组:正则对象 } else if (word) { // 如果非正则且有内容 var ret=[]; // 限定词数组 var eword = word.replace(/"([^"]+)"/g,function($0,$1){$1 && ret.push($1);return '';}); // 提取限定词(用半角双引号括起来的部分)并从word中去除,单独加入限定词数组 word_s = eword.split(/[\+\s:\|#]/).filter(function(w){return !!w;}).concat(ret); // 切分关键词,去除非内容切分形成数组,并在数组后加上限定词数组 word_s = (CFG.sort_keywords)? uniq(word_s).sort(word_length_Comp) : uniq(word_s); // ###如果启用了关键词排序,则按长度排序;关键词列表去重 if (CFG.off_short_words) { // 如果启用了不高亮短词 for (var i in word_s) { // 对关键词列表中短词设置对应不高亮标记 if (/^[a-z0-9]$/i.test(word_s[i]) || (CFG.off_short_words == 2 && /^[a-z0-9]{2}$/i.test(word_s[i]))) { words_off[i] = true; } else { words_off[i] = false; } } } if (CFG.skip_word_list.length) { // 如果有不高亮词表 var swl = CFG.skip_word_list.split(' '); for (i in word_s) { for (var j in swl) { // 对匹配不高亮词表的设置对应不高亮标记 if (word_s[i].toLowerCase() == swl[j]) words_off[i] = true; } } } }//l(word_s[0].exp); return word_s; } // 移动到下一个高亮的位置并提示 function next(index,_layers) { // 输入:当前关键词index(序号),当前关键词layers(高亮元素) _layers || (_layers = (layers || (layers = xp_all.get()) )); // 如果有输入则使用,否则以当前全局layers或者即时获取来使用 index || (index = 0); // 如果有输入则使用,否则为不指定关键词 move(_layers[++positions[index]] || (positions[index] = 0, _layers[positions[index]])); // 移动到layers下一个高亮位置;如果没有下一个位置则重置为零并移动过去 position_box(index); // 显示指针位置提示 } // 移动到上一个高亮的位置并提示 function prev(index,_layers) { _layers || (_layers = (layers || (layers = xp_all.get()) )); index || (index = 0); move(_layers[--positions[index]] || (positions[index] = _layers.length - 1, _layers[positions[index]])); position_box(index); } // 当前高亮指针位置提示:在面板或当前正在查找的高亮关键词按钮上方,显示当前查找到的高亮的指针位置 function position_box(index) { if (!posi_tip) { // 如果提示框不存在则新建 posi_tip = creaElemIn('div', section); posi_tip.setAttribute('style', 'background:white;color:black;border:1px solid black;text-align:center;position:absolute;left:30px;z-index:1025;font-size:16px;height:20px;top:-20px;width:40px;box-shadow:0 2px 4px #444444;'); } clearTimeout(posi_tip_timer); // 提示框计时重置 posi_tip.style.display = 'block'; // 显示 posi_tip.innerHTML = positions[index]+1; // 提示框内容为指针位置(index为关键词(按钮)序号,index=0即不指定关键词) if (index == 0) { posi_tip.style.left = '30px'; } else { posi_tip.style.left = (word_lists[index-1].item.offsetLeft + (word_lists[index-1].item.clientWidth - 40)/2) + 'px'; } posi_tip_timer = setTimeout(function(){posi_tip.style.display = 'none';},3000); // 三秒后隐藏提示框 } // 决定击键处理方式:如果有Minibuffer则调用,否则直接由keydown触发按键识别 function init_keyboard() { if (isOpera) { } else if (window.Minibuffer) { init_minibuffer(); return; } else { window.addEventListener('GM_MinibufferLoaded', init_minibuffer, false); } if (!window.chromium && false) { //实际上不执行第一条,只执行keydown document.addEventListener('keypress', keyhandler, false); } else { document.addEventListener('keydown', keyhandler, false); } } // minibuffer处理:跟keyhandler一样 function init_minibuffer() { if (window.Minibuffer) { document.removeEventListener('keypress', keyhandler, false); } var mini = window.Minibuffer; mini.addCommand({ name: 'keyword-search', command: function(stdin){ keyword += ' ' + this.args.join(' '); keyword = trim(keyword); prep_keyword(); if (setuped) resetup(); else setup(); return stdin; } }); mini.addShortcutkey({ key:KEY_NEXT, command:next, description: 'emphasis next keyword' }); mini.addShortcutkey({ key:KEY_PREV, command:prev, description: 'emphasis prev keyword' }); mini.addShortcutkey({ key:KEY_SEARCH, command:function(e){ instant_search(); }, description: 'emphasis prev keyword' }); } // 按键evt.which转换为键名 function get_key(evt) { var key = String.fromCharCode(evt.which), ctrl = evt.ctrlKey ? 'C-' : '', meta = (evt.metaKey || evt.altKey) ? 'M-' : ''; if (!evt.shiftKey){ key = key.toLowerCase(); } if (evt.ctrlKey && evt.which >= 186 && evt.which < 192) { key = String.fromCharCode(evt.which - 144); } if (evt.key && evt.key !== 'Enter' && !/^U\+/.test(evt.key) ) { key = evt.key; } else if ( evt.which !== evt.keyCode ) { key = keyCodeStr[evt.keyCode] || whichStr[evt.which] || key; } else if (evt.which <= 32) { key = keyCodeStr[evt.keyCode] || whichStr[evt.which]; } return ctrl+meta+key; } // 按键识别:根据键名调用相应过程 function keyhandler(evt) { //console.log(evt); var fullkey = get_key(evt); //console.log(fullkey); if (fullkey == KEY_SEARCH) { evt.preventDefault(); evt.stopPropagation(); instant_search((evt.target.id == PRE + '_textinput') ? true : false, evt.target); } else if (/^(?:input|textarea)$/i.test(evt.target.localName)) { return; } else if (setuped){ switch (fullkey) { case KEY_NEXT: next(); break; case KEY_PREV: prev(); break; case KEY_OFF: command_off(); break; case KEY_CLOSE: command_close(); break; case KEY_EDIT: command_edit(); break; case KEY_REFRESH: resetup(); break; } } } // X按钮:清理高亮并关闭面板 function command_close() { document.body.removeChild(aside); if (document.getElementById(PRE + '_textinputbox')) document.body.removeChild(inputBOX); instant_search.input = null; restore_words(); // sheet.disable = true; if (addCSS.__style.parentNode) addCSS.__root.removeChild(addCSS.__style); window.name = PRE + '::CLOSED::'; word_lists = []; // _words = []; setuped = false; highlight_reset(); } // O按钮:切换全部高亮状态 function command_off() { if (aside.className == 'ewh_edit') return; if (!highlight_off) { restore_words(); for (var i in word_lists) { word_lists[i].check.checked = false; word_lists[i].item.className += ' ewh_disable' } off.className = '_active' highlight_off = true; } else { word_lists = []; word_inputs_list.innerHTML = ''; resetup(); for (i in word_lists) { word_lists[i].check.checked = true; word_lists[i].item.className = word_lists[i].item.className.replace(' ewh_disable', ''); } off.className = ''; highlight_off = false; } draw_wordmap(); } // E按钮:切换编辑状态(显隐关键词编辑框/关键词列表) function command_edit() { if (aside.className == 'ewh_edit') { // aside.style.width = 'auto'; // edit.value = 'Edit'; edit.className = ''; edit.title = _ti.edit[_L]; if (gm_ok) lock.className = lock.className.replace(' _disable',''); off.className = ''; highlight_off = false; aside.className = ''; keyword = trim(text_input.value); prep_keyword(); window.name = PRE + '::' + encodeURIComponent(keyword); resetup(); } else { var _aside_w = aside.offsetWidth; // edit.value = 'Set'; edit.className = '_active'; edit.title = _ti.edit_a[_L]; if (gm_ok) lock.className += ' _disable'; off.className += ' _disable'; aside.className = 'ewh_edit'; text_input.value = keyword; text_input.focus(); var t_width = (Math.max(320,_aside_w) - 135) +'px'; text_input.style = 'width:' + t_width + ';height:22px;margin:2px 0;font-size:15px;'; // aside.style.width = Math.max(320,_aside_w) +'px'; } } // 激活关键词输入框,并进行即时查找当前输入的单个关键词 function instant_search(_r, e_target) { var input_cancel = function(){ if (refocus) { var top = document.body.scrollTop || document.documentElement.scrollTop; var left = document.body.scrollLeft || document.documentElement.scrollLeft; } document.body.removeChild(inputBOX); instant_search.input = null; if (refocus && e_target) { e_target.focus(); document.body.scrollTop = document.documentElement.scrollTop = top; document.body.scrollLeft = document.documentElement.scrollLeft = left; } }; var input_position = function(){ inputBOX.style.bottom = '30px' ;//window.innerHeight - aside.offsetTop + 4 + 'px'; } var input_comfirm = function(text, bAdd){ if (!text && setuped) return; if (bAdd) { keyword = trim(((setuped)?keyword:'') + ' ' + text); prep_keyword(); if (setuped) { if (CFG.sort_keywords) resetup(); else add_word(text); } else setup(true); } else { keyword = trim(text); prep_keyword(); if (setuped) { resetup(); } else setup(true); } window.name = PRE + '::' + encodeURIComponent(keyword); if (instant_search.input) {input_position(); instant_search.input.select();} }; if (_r) {input_cancel(); return;} var selectedText; if (/^(?:input)$/i.test(e_target.localName)) { selectedText = e_target.value; } else { selectedText= getSelection(); } if (instant && selectedText.toString()) { input_comfirm(selectedText.toString(), true); return; } if (instant_search.input) { (instant_search.input.value = selectedText) && instant_search.input.select(); // instant_search.input.focus(); return; } if (!inst_sheet) { addCSS([ '#' + PRE + '_textinputbox input[type=button]{padding:0;display:inline;margin:0.1em 0.2em;width:1.4em;height:1.4em;background:'+ but_c +';border:1px solid #996666;cursor:pointer;font-size:12pt;color:black;}', '#' + PRE + '_textinputbox label{padding:0;display:inline;}', '#' + PRE + '_textinputbox{border:1px solid #333;margin:0px;padding:0px;position:fixed;bottom:34px;left:5%;z-index:1023;background:#fff;box-shadow: #333 3px 3px 2px;color:#000;color:#000;font-weight:bold;max-width:70%;font-size:16pt;height:auto;opacity:0.95;}', '#' + PRE + '_textinputbox,#' + PRE + '_textinputbox *{font-family: Arial;}', '#' + PRE + '_textinput{border:none;margin:0 0 0 5px;padding:0px;max-width:80%;height:100%;background:#fff;color:#000;font-weight:bold;font-size:inherit;}' ].join('\n')); inst_sheet = true; } if (!move_sheet) addmovesheet(); inputBOX = creaElemIn('div', document.body); inputBOX.id = PRE + '_textinputbox'; inputBOX.setAttribute('class', PRE + '_inbox'); if (setuped) { input_position(); } var inputCHECK = creaElemIn('input', inputBOX); inputCHECK.type = 'checkbox'; inputCHECK.checked = addKeyword; inputCHECK.title = _ti.ad_nw[_L]; var inputCHECKlabel = creaElemIn('label', inputBOX); inputCHECKlabel.textContent = (addKeyword) ? 'Add':'New'; inputCHECKlabel.title = inputCHECK.title; var i_C_id = inputCHECK.id = 'Add_Check'; inputCHECKlabel.htmlFor = i_C_id; var input = instant_search.input = creaElemIn('input', inputBOX); input.id = PRE + '_textinput'; var go_button = creaElemIn('input', inputBOX); go_button.type = 'button'; go_button.value = '\u2192'; go_button.title = _ti.subm[_L]; var close_button = creaElemIn('input', inputBOX); close_button.type = 'button'; close_button.value = 'X'; close_button.title = _ti.clos[_L]; inputCHECK.addEventListener('change', function(){ inputCHECKlabel.textContent = (this.checked) ? 'Add':'New'; addKeyword = this.checked; input.focus(); },false); go_button.addEventListener('click', function(){input_comfirm(input.value, addKeyword);}, false); close_button.addEventListener('click', input_cancel, false); input.addEventListener('keypress',function(evt){ var fullkey = get_key(evt); switch (fullkey) { case 'RET': evt.preventDefault(); evt.stopPropagation(); input_comfirm(this.value, addKeyword); break; case 'ESC': input_cancel(); } },false); input.addEventListener('input',function(e) { var text = input.value.toUpperCase(); if (!/\S/.test(text) || text.length <2) return; var x = 'descendant::text()[contains(translate(self::text(),"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ"),'+escapeXPathExpr(text)+') and not(ancestor::textarea) and not(ancestor::script) and not(ancestor::style)]/parent::*'; var node = document.evaluate(x, document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if (node) move(node); },false); if (selectedText.toString()) { input.value = selectedText.toString(); input.select(); } else if (keyword && !setuped) { input.value = keyword; input.select(); } else input.focus(); } // 构建设置对话框并且建立存取设置机制 function config_box() { var dbox = { //设置项构造器 addsetting: function(setting, basenode, config){ var setbox = creaElemIn('div', basenode); setbox.className = '_setbox'; creaElemIn('br',setbox); var titlebox = this.addtextnode(setting.title, setbox); titlebox.className = '_titlebox'; var i; switch(setting.type) { case 'fill': //填空 var inputbox = creaElemIn('input', setbox); inputbox.type = 'text'; inputbox.style = 'width: 90%; height: 2em;'; inputbox.id = PRE + setting.id; inputbox.value = config[setting.id]; inputbox.addEventListener('blur', () => { config[this.id.replace(PRE, '')] = this.value; }); break; case 'check': if (setting.choices.length == 1) { //复选 var checknode = this.addcheckbox(PRE + setting.id, setting.choices[0], setbox); checknode.name = PRE + setting.id; checknode.checked = !!(config[setting.id] == 1); checknode.addEventListener('change', (event) => { config[event.target.id.replace(PRE, '')] = (event.target.checked)? 1 : 0; }); } else if (setting.choices.length > 1) { //单选 var radionodes = []; for (i in setting.choices) { radionodes[i] = this.addradiobox(PRE + setting.id + i, setting.choices[i], setbox); radionodes[i].name = PRE + setting.id; radionodes[i].checked = (i == config[setting.id]); radionodes[i].addEventListener('change', (event) => { config[event.target.name.replace(PRE, '')] = Number(event.target.id.replace(event.target.name, ''))//;l(CFG); }); } } break; case 'text': //纯文本/描述 for (i in setting.choices) { this.addtextnode(setting.choices[i], setbox); } } }, addtextnode: function(text, basenode){ var textnode = creaElemIn('p', basenode); textnode.innerHTML = text; return textnode; }, addcheckbox: function(id, text, basenode){ var checknode = creaElemIn('input', basenode); checknode.id = id; checknode.type = 'checkbox'; var checklabel = creaElemIn('label',basenode); checklabel.innerHTML = text; checklabel.htmlFor = checknode.id; creaElemIn('br',basenode); return checknode; }, addradiobox: function(id, text, basenode){ var radionode = creaElemIn('input', basenode); radionode.id = id; radionode.type = 'radio'; var radiolabel = creaElemIn('label',basenode); radiolabel.innerHTML = text; radiolabel.htmlFor = radionode.id; creaElemIn('br',basenode); return radionode; }, }; var cancconfig = function(){ document.body.removeChild(confBOXBack); CFG = JSON.parse(GM_getValue(CO_PRE)); //从GM储存中恢复原有设置(放弃更改) } var saveconfig = function(){ document.body.removeChild(confBOXBack); GM_setValue(CO_PRE, JSON.stringify(CFG)); //将设置存入GM储存 location.reload(); }; // 设置对话框规范题目设置 var config_setting = [ { id: "ap_option", title: _di.ap_option[_L], type: "check", //text:文本描述,fill:填空,check: 复选框(choices=1)或单选(choices>1) choices: [_di.ap_option1[_L], _di.ap_option2[_L], _di.ap_option3[_L]] }, { id: "off_short_words", title: _di.off_short_words[_L], type: "check", choices: [_di.off_short_words1[_L], _di.off_short_words2[_L], _di.off_short_words3[_L]] }, { id: "skip_word_list", title: _di.skip_word_list[_L], type: "fill", choices: [] }, { id: "no_auto_hili", title: _di.no_auto_hili[_L], type: "check", choices: [_di.no_auto_hili1[_L], _di.no_auto_hili2[_L], _di.no_auto_hili3[_L], _di.no_auto_hili4[_L]] }, { id: "sort_keywords", title: _di.sort_keywords[_L], type: "check", choices: [_di.sort_keywords1[_L]] }, { id: "save_panel_pos", title: _di.save_panel_pos[_L], type: "check", choices: [_di.save_panel_pos1[_L]] }, { id: "show_indc_bar", title: _di.show_indc_bar[_L], type: "check", choices: [_di.show_indc_bar1[_L]] }, ]; // 设置对话框构建 var confBOXBack = creaElemIn('div', document.body); //半透明白色背景 confBOXBack.className = PRE + '_cb'; var confBOX = creaElemIn('div', confBOXBack); //设置对话框主框 confBOX.className = PRE + '_cbox'; var confTOP = creaElemIn('div', confBOX); //设置对话框标题头 var confTitle = creaElemIn('h3', confTOP); //设置对话框标题 confTitle.innerHTML = _di.confT[_L]; var confBk = creaElemIn('div', confTOP); //按钮框 confBk.className = PRE + '_bbox'; var confBy = creaElemIn('input', confBk); //确定按钮 confBy.type = 'button'; confBy.value = _di.button_yes[_L]; confBy.addEventListener('click', saveconfig, false); var confBn = creaElemIn('input', confBk); //取消按钮 confBn.type = 'button'; confBn.value = _di.button_no[_L]; confBn.addEventListener('click', cancconfig, false); var confSk = creaElemIn('div', confBOX); //选项框 confSk.className = PRE + '_cobox'; config_setting.forEach((item, index) => { dbox.addsetting(item, confSk, CFG); }); if (!config_sheet) { addCSS([ '.' + PRE + '_cb{background:#ffffffaa;position:fixed;top:0;left:0;width:100%;height:100%;text-align:center;z-index:30000;}', '.' + PRE + '_cbox{line-height:1.2;border:1px solid #333;border-left-width:10px;width:600px;height:90%;margin:30px auto auto auto;padding:5px;color: black !important;background: white; box-shadow: 0 0 15px 2px #b5b5b5;text-align:left;position: relative;}', '.' + PRE + '_cbox h3{font-weight:800;border-bottom:1px solid black;width:100%;margin:15px auto 10px auto;font-size: 15pt !important;}', '.' + PRE + '_bbox{width: 150px; text-align: center; position: absolute; top: 20px; right: 20px;}', '.' + PRE + '_bbox input{width: 56px;margin: 0 6px;}', '.' + PRE + '_cobox{height: 92%; overflow-y:scroll;}', '.' + PRE + '_cobox ._setbox{display: block; padding: 3px 0;}', '.' + PRE + '_cobox ._titlebox{background: #e0f5ff; padding: 3px 0;}', '.' + PRE + '_cobox input{color: black !important;}', '.' + PRE + '_cobox label{min-width: 80%;color: black !important;display:inline-block;}', '.' + PRE + '_cobox label:hover{background: #eeeeff;}' ].join('\n')); config_sheet = true; } } // 在高亮(关键词或页面)改变后,延时重新计数高亮数和重绘分布图 function after_load(e, _ind) { var cmd = function(_ind){ if (!_ind) { word_lists.forEach(function(item){ item.label.textContent = item.word + ' (' + item.get_count({result_type:XPathResult.NUMBER_TYPE}).numberValue + ')'; }); } else { word_lists[_ind].label.textContent = word_lists[_ind].word + ' (' + word_lists[_ind].get_count({result_type:XPathResult.NUMBER_TYPE}).numberValue + ')'; } layers = xp_all.get(); draw_wordmap(); if (panel_hide){aside.style.right = (14 - aside.offsetWidth) +'px';} } setTimeout(cmd, delay+100, _ind); } // add event listener to insert / renew highlights when autopager add pages function init_autopager(e) { var page = 0, disabled = false; var inserted_highlight = function(e){ setTimeout(highlight, delay, e.target); }; window.addEventListener('AutoPatchWork.DOMNodeInserted', inserted_highlight,false); window.addEventListener('AutoPatchWork.pageloaded', after_load,false); window.addEventListener('AutoPagerize_DOMNodeInserted', inserted_highlight,false); window.addEventListener('GM_AutoPagerizeNextPageLoaded', after_load,false); window.addEventListener('Super_preloaderPageLoaded', resetup ,false); highlight_reset = function(){ window.removeEventListener('AutoPatchWork.DOMNodeInserted', inserted_highlight,false); window.removeEventListener('AutoPatchWork.pageloaded', after_load,false); window.removeEventListener('AutoPagerize_DOMNodeInserted', inserted_highlight,false); window.removeEventListener('GM_AutoPagerizeNextPageLoaded', after_load,false); window.removeEventListener('Super_preloaderPageLoaded', resetup ,false); } } // get an element by css selector function qElem(cssselector) { return document.querySelector(cssselector); } // get all elements by css selector function qaElem(cssselector) { return document.querySelectorAll(cssselector); } // get elements as an arry from xpath, detail ver function $X(exp, context, resolver, result_type) { context || (context = document); var Doc = context.ownerDocument || context; var result = Doc.evaluate(exp, context, resolver, result_type || XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); if (result_type) return result; for (var i = 0, len = result.snapshotLength, res = new Array(len); i < len; i++) { res[i] = result.snapshotItem(i); } return res; } // get elements as an arry from xpath, simple ver function $XE(exp, context) { var xe = new XPathEvaluator(); var resolver = xe.createNSResolver(document.documentElement); //var defaultNS = document.lookupNamespaceURI(window.opera ? '' : null); var defaultNS = (document.documentElement.nodeName !== 'HTML') ? context.namespaceURI : null; if (defaultNS) { var defaultPrefix = '__default__'; if (!isChromium) { exp = addDefaultPrefix(exp, defaultPrefix); } var defaultResolver = resolver; resolver = function (prefix) { return (prefix == defaultPrefix) ? defaultNS : defaultResolver.lookupNamespaceURI(prefix); }; } var ex = xe.createExpression(exp, resolver); this.get = function(param) { param || (param={}); var result = this.result = ex.evaluate(param.context||context, param.result_type||XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,this.result); if (param.result_type) return result; for (var i = 0, len = result.snapshotLength, res = new Array(len); i < len; i++) { res[i] = result.snapshotItem(i); } return res; }; } // via AutoPagerize Thx! nanto_vi function addDefaultPrefix(xpath, prefix) { var tokenPattern = /([A-Za-z_\u00c0-\ufffd][\w\-.\u00b7-\ufffd]*|\*)\s*(::?|\()?|(".*?"|'.*?'|\d+(?:\.\d*)?|\.(?:\.|\d+)?|[\)\]])|(\/\/?|!=|[<>]=?|[\(\[|,=+-])|([@$])/g; var TERM = 1, OPERATOR = 2, MODIFIER = 3; var tokenType = OPERATOR; prefix += ':'; function replacer(token, identifier, suffix, term, operator, modifier) { if (suffix) { tokenType = (suffix == ':' || (suffix == '::' && (identifier == 'attribute' || identifier == 'namespace'))) ? MODIFIER : OPERATOR; } else if (identifier) { if (tokenType == OPERATOR && identifier != '*') { token = prefix + token; } tokenType = (tokenType == TERM) ? OPERATOR : TERM; } else { tokenType = term ? TERM : operator ? OPERATOR : MODIFIER; } return token; } return xpath.replace(tokenPattern, replacer); } // http://d.hatena.ne.jp/amachang/20090917/1253179486 function escapeXPathExpr(text) { var matches = text.match(/[^"]+|"/g); function esc(t) { return t == '"' ? ('\'' + t + '\'') : ('"' + t + '"'); } if (matches) { if (matches.length == 1) { return esc(matches[0]); } else { var results = []; for (var i = 0, len = matches.length; i < len; i ++) { results.push(esc(matches[i])); } return 'concat(' + results.join(', ') + ')'; } } else { return '""'; } } // reduce https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce#Compatibility function reduce(arr, fun) { var len = arr.length, i = 0, rv; if (arguments.length >= 3) rv = arguments[2]; else {do { if (i in arr) { rv = arr[i++];break; } if (++i >= len) throw new TypeError(); } while (true)}; for (; i < len; i++) {if (i in arr) rv = fun.call(null, rv, arr[i], i, arr);} return rv; } function error(e) { if (isOpera) { opera.postError(e); } else if (window.console) { console.error(e); } } // add style sheet to the document function addCSS(css) { var sheet, self = arguments.callee; if (document.createStyleSheet) { // for IE sheet = document.createStyleSheet(); sheet.cssText = css; return sheet; } else if (!self.__style || !self.__root) { sheet = document.createElement('style'); sheet.type = 'text/css'; self.__style = sheet; self.__root = document.getElementsByTagName('head')[0] || document.documentElement; } sheet = self.__style.cloneNode(false); sheet.textContent = css; return self.__root.appendChild(sheet).sheet; } // get the accumulative offsetTop of target element function getY(oElement) { var iReturnValue = 0; while (oElement != null) { iReturnValue += oElement.offsetTop; oElement = oElement.offsetParent; } return iReturnValue; } // create some element and append to the destination function creaElemIn(tagname, destin) { var theElem = destin.appendChild(document.createElement(tagname)); return theElem; } /** Get elements by className * @function getElementsByClassName * @param string className * @param optional string tag restrict to specified tag * @param optional node restrict to childNodes of specified node * @return Array of nodes * @author Jonathan Snook, http://www.snook.ca/jonathan * @author Robert Nyman, http://www.robertnyman.com */ function getElementsByClassName(className, tag, elm) { var testClass = new RegExp("(^|\\s)" + className + "(\\s|$)"); tag = tag || "*"; elm = elm || document; var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag); var returnElements = []; var current; var length = elements.length; for(var i=0; i 0x7f) { ret += asc2str(parseInt("0x" + asc+str.substring(i+4, i+6))); i += 5; } else { ret += asc2str(parseInt("0x"+asc)); i += 2; } } else { ret += chr; } } return ret; } function parseCookie(cookie) { var cookies = {}; if(!cookie){ return cookies; } var list = cookie.split(';'); for (var i=0; i