remove the jump link in BAIDU

去除百度搜索跳转链接

当前为 2016-04-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name remove the jump link in BAIDU
  3. // @author axetroy
  4. // @description 去除百度搜索跳转链接
  5. // @version 2016.4.9.2
  6. // @grant GM_xmlhttpRequest
  7. // @include *www.baidu.com*
  8. // @connect tags
  9. // @connect *
  10. // @compatible chrome 完美运行
  11. // @compatible firefox 完美运行
  12. // @supportURL http://www.burningall.com
  13. // @run-at document-start
  14. // @contributionURL troy450409405@gmail.com|alipay.com
  15. // @namespace https://greasyfork.org/zh-CN/users/3400-axetroy
  16. // @license The MIT License (MIT); http://opensource.org/licenses/MIT
  17. // ==/UserScript==
  18.  
  19. /* jshint ignore:start */
  20. ;(function (window, document) {
  21.  
  22. 'use strict';
  23.  
  24. var ES6Support = true;
  25.  
  26. try {
  27. let test_let = true;
  28. const test_const = true;
  29. var test_tpl_str = `233`;
  30. var test_arrow_fn = (a = '233') => {
  31. };
  32. var test_promise = new Promise(function (resolve, reject) {
  33. resolve();
  34. });
  35. class test_class {
  36.  
  37. }
  38. } catch (e) {
  39. /**
  40. * 促进大家升级浏览器,拯救前端,就是拯救我自己
  41. */
  42. alert('你的浏览器不支持ECMA6,去除百度搜索跳转链接将失效,请升级浏览器和脚本管理器');
  43. ES6Support = false;
  44. }
  45.  
  46. if (!ES6Support) return;
  47.  
  48. let noop = function () {
  49. };
  50.  
  51. class jqLite {
  52. constructor(selectors = '') {
  53. this.selectors = selectors;
  54. let elements = typeof selectors === 'string' ?
  55. document.querySelectorAll(selectors) :
  56. selectors.length ? selectors : [selectors];
  57. for (let i = 0; i < elements.length; i++) {
  58. this[i] = elements[i];
  59. }
  60. this.length = elements.length;
  61. }
  62.  
  63. each(fn = noop) {
  64. for (let i = 0; i < this.length; i++) {
  65. fn.call(this, this[i], i);
  66. }
  67. return this;
  68. }
  69.  
  70. bind(types = '', fn = noop) {
  71. this.each((ele)=> {
  72. types.trim().split(/\s{1,}/).forEach((type)=> {
  73. ele.addEventListener(type, (e) => {
  74. let target = e.target || e.srcElement;
  75. if (fn.call(target, e) === false) {
  76. e.returnValue = true;
  77. e.cancelBubble = true;
  78. e.preventDefault && e.preventDefault();
  79. e.stopPropagation && e.stopPropagation();
  80. return false;
  81. }
  82. }, false);
  83. });
  84. });
  85. }
  86.  
  87. observe(fn = noop, config = {childList: true, subtree: true}) {
  88. this.each((ele) => {
  89. let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
  90. let observer = new MutationObserver((mutations) => {
  91. mutations.forEach((mutation) => {
  92. fn.call(this, mutation.target, mutation.addedNodes, mutation.removedNodes);
  93. });
  94. });
  95. observer.observe(ele, config);
  96. });
  97. return this;
  98. }
  99.  
  100. static visible(ele) {
  101. let pos = ele.getBoundingClientRect();
  102. let w;
  103. let h;
  104. let inViewPort;
  105. if (document.documentElement.getBoundingClientRect) {
  106. w = document.documentElement.clientWidth || document.body.clientWidth;
  107. h = document.documentElement.clientHeight || document.body.clientHeight;
  108. inViewPort = pos.top > h || pos.bottom < 0 || pos.left > w || pos.right < 0;
  109. return inViewPort ? false : true;
  110. }
  111. }
  112.  
  113. static text(ele) {
  114. return ele.innerText ? ele.innerText : ele.textContent;
  115. }
  116.  
  117. static debounce(fn, delay) {
  118. let timer;
  119. return function () {
  120. let agm = arguments;
  121. window.clearTimeout(timer);
  122. timer = window.setTimeout(()=> {
  123. fn.apply(this, agm);
  124. }, delay);
  125. }
  126. }
  127.  
  128. }
  129.  
  130. let $ = (selectors = '') => {
  131. return new jqLite(selectors);
  132. };
  133.  
  134. let $cache = {};
  135.  
  136. let $ajax = (url, aEle) => {
  137. var deferred = $q.defer();
  138.  
  139. // if in BAIDU home page
  140. if (new RegExp(`${window.location.host}\/?$`, 'im').test(url)) {
  141. deferred.resolve({aEle, url, response: ''});
  142. return deferred.promise;
  143. }
  144.  
  145. // if has cache
  146. if ($cache[url]) {
  147. deferred.resolve({aEle, url, response: $cache[url]});
  148. return deferred.promise;
  149. }
  150.  
  151. // not match the url
  152. if (!/w{3}\.baidu\.com\/link\?url=/im.test(url) && !/w{3}\.baidu\.com\/s/.test(url)) {
  153. deferred.resolve({aEle, url, response: {finalUrl: url}});
  154. return deferred.promise;
  155. }
  156.  
  157. if (config.debug) console.info(`ajax:${url}`);
  158.  
  159. if (aEle && aEle.setAttribute) aEle.setAttribute("decoding", "true");
  160.  
  161. GM_xmlhttpRequest({
  162. method: "GET",
  163. url: url,
  164. onreadystatechange: function (response) {
  165. if (response.readyState !== 4) return;
  166. var data = {aEle, url, response};
  167. $cache[url] = response;
  168. if (/^(2|3)/.test(response.status)) {
  169. aEle && aEle.setAttribute('decoded', 'true');
  170. deferred.resolve(data);
  171. } else {
  172. aEle && aEle.setAttribute('decoded', 'false');
  173. deferred.reject(data);
  174. }
  175. }
  176. });
  177.  
  178. return deferred.promise;
  179. };
  180.  
  181. let $q = function (fn = noop) {
  182. return new Promise(fn);
  183. };
  184.  
  185. $q.defer = function () {
  186. let deferred = {};
  187.  
  188. deferred.promise = new Promise(function (resolve, reject) {
  189. deferred.resolve = function (data) {
  190. resolve(data);
  191. };
  192. deferred.reject = function (data) {
  193. reject(data);
  194. };
  195. });
  196.  
  197. return deferred;
  198. };
  199.  
  200. let redirect = `www.baidu.com/link?url`;
  201. let config = {
  202. rules: `
  203. a[href*="${redirect}"]
  204. :not(.m)
  205. :not([decoding])
  206. :not([decoded])
  207. :not([decodeAll])
  208. `.trim().replace(/\n/img, '').replace(/\s{1,}([^a-zA-Z])/g, '$1'),
  209. reloadRules: `a[decoded*="false"]`,
  210. debug: false
  211. };
  212. let isDecodingAll = false;
  213.  
  214. class main {
  215. constructor(agm = '') {
  216. if (!agm) return this;
  217. // 可视区域内的A链接
  218. this.inViewPort = [];
  219.  
  220. $(agm).each((ele) => {
  221. if (jqLite.visible(ele)) this.inViewPort.push(ele);
  222. });
  223. }
  224.  
  225. all() {
  226. var deferred = $q.defer();
  227.  
  228. let url = window.top.location.href.replace(/(\&)(tn=\w+)(\&)/img, '$1' + 'tn=baidulocal' + '$3');
  229.  
  230. isDecodingAll = true;
  231. $ajax(url)
  232. .then(function (data) {
  233. isDecodingAll = false;
  234.  
  235. if (!data.response) return;
  236. let response = data.response.responseText;
  237. let html = document.createElement('html');
  238. html.innerHTML = response;
  239.  
  240. $('.t>a:not(.OP_LOG_LINK):not([decoded]):not([decodeAll])').each((sourceEle)=> {
  241. $(html.querySelectorAll('.f>a')).each((targetEle) => {
  242. if (jqLite.text(sourceEle) === jqLite.text(targetEle)) {
  243. sourceEle.href = targetEle.href;
  244. sourceEle.setAttribute('decodeAll', 'true');
  245. if (config.debug) sourceEle.style.background = 'green';
  246. }
  247. });
  248. });
  249.  
  250. deferred.resolve(data);
  251. }, function (data) {
  252. isDecodingAll = false;
  253. deferred.reject(data);
  254. });
  255.  
  256. return deferred.promise;
  257. }
  258.  
  259. one() {
  260. $(this.inViewPort).each(function (aEle) {
  261. if (/www\.baidu\.com\/link\?url=/im.test(aEle.href) === false)return;
  262. $ajax(aEle.href, aEle)
  263. .then(function (data) {
  264. if (!data) return;
  265. data.aEle.href = data.response.finalUrl;
  266. if (config.debug) data.aEle.style.background = 'green';
  267. });
  268. });
  269. return this;
  270. }
  271.  
  272. }
  273.  
  274. console.info('去跳转启动...');
  275.  
  276. $(document).bind('DOMContentLoaded', ()=> {
  277.  
  278. // init
  279. new main(config.rules).all()
  280. .then(function () {
  281. new main(config.rules).one();
  282. });
  283.  
  284. let observeDebounce = jqLite.debounce((target, addList, removeList) => {
  285. if (!addList || !addList.length) return;
  286. if (isDecodingAll === true) {
  287. new main(config.rules).one();
  288. } else {
  289. new main(config.rules).all()
  290. .then(function () {
  291. new main(config.rules).one();
  292. });
  293. }
  294. }, 200);
  295.  
  296. $(document).observe(function (target, addList, removeList) {
  297. observeDebounce(target, addList, removeList);
  298. });
  299.  
  300. $(document).bind('mouseover', (e) => {
  301. let aEle = e.target;
  302. if (aEle.tagName !== "A" || !aEle.href || !/w{3}\.baidu\.com\/link\?url=/im.test(aEle.href)) return;
  303. $ajax(aEle.href, aEle)
  304. .then(function (data) {
  305. data.aEle.href = data.response.finalUrl;
  306. });
  307. });
  308.  
  309. let scrollDebounce = jqLite.debounce(() => {
  310. new main(config.rules).one();
  311. }, 200);
  312. $(window).bind('scroll', ()=> {
  313. scrollDebounce();
  314. });
  315.  
  316. });
  317.  
  318. })(window, document);
  319.  
  320. /* jshint ignore:end */
  321.  
  322.