Greasy Fork is available in English.

remove the jump link in BAIDU (ECMA6)

去除百度搜索跳转链接

Stan na 09-04-2016. Zobacz najnowsza wersja.

// ==UserScript==
// @name              remove the jump link in BAIDU (ECMA6)
// @author            axetroy
// @description       去除百度搜索跳转链接
// @version           2016.4.10
// @grant             GM_xmlhttpRequest
// @include           *www.baidu.com*
// @connect           tags
// @connect           *
// @compatible        chrome  完美运行
// @compatible        firefox  完美运行
// @supportURL        http://www.burningall.com
// @run-at            document-start
// @contributionURL   troy450409405@gmail.com|alipay.com
// @namespace         https://greasyfork.org/zh-CN/users/3400-axetroy
// @license           The MIT License (MIT); http://opensource.org/licenses/MIT
// ==/UserScript==


if (typeof require !== 'undefined' && typeof require === 'function') {
  require("babel-polyfill");
}

/* jshint ignore:start */
;(function (window, document) {

  'use strict';

  var ES6Support = true;

  try {
    let test_let = true;
    const test_const = true;
    var test_tpl_str = `233`;
    var test_arrow_fn = (a = '233') => {
    };
    var test_promise = new Promise(function (resolve, reject) {
      resolve();
    });
    class test_class {

    }
  } catch (e) {
    /**
     * 促进大家升级浏览器,拯救前端,就是拯救我自己
     */
    alert('你的浏览器不支持ECMA6,去除百度搜索跳转链接将失效,请升级浏览器和脚本管理器');
    ES6Support = false;
  }

  if (!ES6Support) return;

  let noop = () => {
  };

  /**
   * a lite jquery mock
   */
  class jqLite {
    constructor(selectors = '', context = document) {
      this.selectors = selectors;
      this.context = context;
      this.length = 0;

      switch (typeof selectors) {
        case 'undefined':
          break;
        case 'string':
          Array.from(context.querySelectorAll(selectors), (ele, i) => {
            this[i] = ele;
            this.length++;
          }, this);
          break;
        case 'object':
          if (selectors.length) {
            Array.from(selectors, (ele, i) => {
              this[i] = ele;
              this.length++;
            }, this);
          } else {
            this[0] = selectors;
            this.length = 1;
          }
          break;
        case 'function':
          this.ready(selectors);
          break;
        default:

      }

    };

    each(fn = noop) {
      for (let i = 0; i < this.length; i++) {
        fn.call(this, this[i], i);
      }
      return this;
    };

    bind(types = '', fn = noop) {
      this.each((ele)=> {
        types.trim().split(/\s{1,}/).forEach((type)=> {
          ele.addEventListener(type, (e) => {
            let target = e.target || e.srcElement;
            if (fn.call(target, e) === false) {
              e.returnValue = true;
              e.cancelBubble = true;
              e.preventDefault && e.preventDefault();
              e.stopPropagation && e.stopPropagation();
              return false;
            }
          }, false);
        });
      });
    };

    ready(fn = noop) {
      this.context.addEventListener('DOMContentLoaded', e => {
        fn.call(this);
      }, false);
    }

    observe(fn = noop, config = {childList: true, subtree: true}) {
      this.each((ele) => {
        let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        let observer = new MutationObserver((mutations) => {
          mutations.forEach((mutation) => {
            fn.call(this, mutation.target, mutation.addedNodes, mutation.removedNodes);
          });
        });
        observer.observe(ele, config);
      });
      return this;
    };

    attr(attr, value) {
      // one agm
      if (!arguments.length === 1) {
        // get attr value
        if (typeof attr === 'string') {
          return this[0].getAttribute(attr);
        }
        // set attr with a json
        else if (typeof attr === 'object') {
          this.each(function (ele) {
            for (let at in attr) {
              if (attr.hasOwnProperty(at)) {
                ele.setAttribute(at, value);
              }
            }
          });
        }
        return value;
      }
      // set
      else if (arguments.length === 2) {
        this.each(function (ele) {
          ele.setAttribute(attr, value);
        });
        return this;
      }
      else {
        return this;
      }
    };

    removeAttr(attr) {
      if (arguments.length === 1) {
        this.each((ele)=> {
          ele.removeAttribute(attr);
        });
      }
      return this;
    }

    get text() {
      let ele = this[0];
      return ele.innerText ? ele.innerText : ele.textContent;
    };

    static get fn() {
      let visible = (ele)=> {
        let pos = ele.getBoundingClientRect();
        let w;
        let h;
        let inViewPort;
        let docEle = document.documentElement;
        let docBody = document.body;
        if (docEle.getBoundingClientRect) {
          w = docEle.clientWidth || docBody.clientWidth;
          h = docEle.clientHeight || docBody.clientHeight;
          inViewPort = pos.top > h || pos.bottom < 0 || pos.left > w || pos.right < 0;
          return inViewPort ? false : true;
        }
      };
      let debounce = (fn, delay)=> {
        let timer;
        return function () {
          let agm = arguments;
          window.clearTimeout(timer);
          timer = window.setTimeout(()=> {
            fn.apply(this, agm);
          }, delay);
        }
      };
      return {
        visible,
        debounce
      }
    };

  }

  let $ = (selectors = '', context = document) => {
    return new jqLite(selectors, context);
  };

  /**
   * cache the ajax response result
   * @type {{}}
   */
  let $$cache = {};

  /**
   * timeout wrapper
   * @param fn
   * @param delay
   * @returns {number}
   */
  let $timeout = (fn = noop, delay = 0) => {
    return window.setTimeout(fn, delay);
  };

  /**
   * cancel timer
   * @param timerId
   * @returns {*}
   */
  $timeout.cancel = function (timerId) {
    window.clearTimeout(timerId);
    return timerId;
  };

  let $$count = 0;

  /**
   * ajax function
   * @param url           the url request
   * @param aEle          the A link element [non essential variables]
   * @returns {Promise}
   */
  let $ajax = (url, aEle) => {
    var deferred = $q.defer();

    // if in BAIDU home page
    if (new RegExp(`${window.location.host}\/?$`, 'im').test(url)) {
      $timeout(function () {
        deferred.resolve({aEle, url, response: ''});
        return deferred.promise;
      });
    }

    // if has cache
    if ($$cache[url]) {
      $timeout(function () {
        deferred.resolve({aEle, url, response: $$cache[url]});
        return deferred.promise;
      });
    }

    // not match the url
    if (!/w{3}\.baidu\.com\/link\?url=/im.test(url) && !/w{3}\.baidu\.com\/s/.test(url)) {
      $timeout(function () {
        deferred.resolve({aEle, url, response: {finalUrl: url}});
        return deferred.promise;
      });
    }

    // make the protocol agree
    if (!new RegExp(`^${window.location.protocol}`).test(url)) {
      url = url.replace(/^(http|https):/im, window.location.protocol);
    }

    if (config.debug) console.info(`${$$count++}-ajax:${url}`);

    if (aEle) $(aEle).attr('decoding', '');

    GM_xmlhttpRequest({
      method: "GET",
      url: url,
      timeout: 5000,
      anonymous: !!aEle,
      onreadystatechange: function (response) {
        if (response.readyState !== 4) return;
        let data = {aEle, url, response};
        if (/^(2|3)/.test(response.status)) {
          $$cache[url] = response;
          aEle && $(aEle).attr('decoded', '');
          deferred.resolve(data);
        } else {
          deferred.reject(data);
        }
        aEle && $(aEle).removeAttr('decoding');
      },
      ontimeout: (response)=> {
        let data = {aEle, url, response};
        config.debug && console.error(data);
        aEle && $(aEle).removeAttr('decoding');
        deferred.reject(data);
        response && response.finalUrl ? deferred.resolve(data) : deferred.reject(data);
      },
      onerror: (response)=> {
        let data = {aEle, url, response};
        config.debug && console.error(data);
        aEle && $(aEle).removeAttr('decoding');
        response && response.finalUrl ? deferred.resolve(data) : deferred.reject(data);
      }
    });

    return deferred.promise;
  };

  /**
   * simple deferred object like angularJS $q or q promise library
   * @param fn                 promise function
   * @returns {Promise}
   */
  let $q = function (fn = noop) {
    return new Promise(fn);
  };

  /**
   * generator a deferred object use like angularJS's $q service
   * @returns {{}}
   */
  $q.defer = function () {
    let deferred = {};

    deferred.promise = new Promise(function (resolve, reject) {
      deferred.resolve = function (data) {
        resolve(data);
      };
      deferred.reject = function (data) {
        reject(data);
      };
    });

    return deferred;
  };

  // config
  const config = {
    rules: `
      a[href*="www.baidu.com/link?url"]
      :not(.m)
      :not([decoding])
      :not([decoded])
    `.trim().replace(/\n/img, '').replace(/\s{1,}([^a-zA-Z])/g, '$1'),
    debug: false
  };

  let isDecodingAll = false;

  /**
   * the main class to bootstrap this script
   */
  class main {
    constructor(agm = '') {
      if (!agm) return this;

      this.inViewPort = [];

      $(agm).each((ele) => {
        if (jqLite.fn.visible(ele)) this.inViewPort.push(ele);
      });
    }

    /**
     * request a url which has origin links
     * @returns {Promise}
     */
    all() {
      var deferred = $q.defer();

      let url = window.top.location.href.replace(/(\&)(tn=\w+)(\&)/img, '$1' + 'tn=baidulocal' + '$3');

      isDecodingAll = true;
      $ajax(url)
        .then(function (data) {
          isDecodingAll = false;

          if (!data.response) return;
          let response = data.response.responseText;

          // remove the image which load with http not https
          response = response.replace(/src=[^>]*/, '');

          let html = document.createElement('html');
          html.innerHTML = response;

          $('.t>a:not(.OP_LOG_LINK):not([decoded])').each((sourceEle)=> {
            $('.f>a', html).each((targetEle) => {
              if ($(sourceEle).text === $(targetEle).text) {
                sourceEle.href = targetEle.href;
                $(sourceEle).attr('decoded', '');
                if (config.debug) sourceEle.style.background = 'green';
              }
            });
          });

          deferred.resolve(data);
        }, function (data) {
          isDecodingAll = false;
          deferred.reject(data);
        });

      return deferred.promise;
    }

    /**
     * request the A tag's href one by one those in view port
     * @returns {main}
     */
    oneByOne() {
      $(this.inViewPort).each(function (aEle) {
        if (/www\.baidu\.com\/link\?url=/im.test(aEle.href) === false)return;
        $ajax(aEle.href, aEle)
          .then(function (data) {
            if (!data) return;
            data.aEle.href = data.response.finalUrl;
            if (config.debug) data.aEle.style.background = 'green';
          });
      });
      return this;
    }

  }

  console.info('去跳转启动...');

  /**
   * bootstrap the script
   */
  $(()=> {

    let init = ()=> {
      new main(config.rules).all()
        .then(function () {
          new main(config.rules).oneByOne();
        }, function () {
          new main(config.rules).oneByOne();
        });
    };

    // init
    init();

    let observeDebounce = jqLite.fn.debounce((target, addList, removeList) => {
      if (!addList || !addList.length) return;
      if (isDecodingAll === true) {
        new main(config.rules).oneByOne();
      } else {
        init();
      }
    }, 200);
    $(document).observe(function (target, addList, removeList) {
      observeDebounce(target, addList, removeList);
    });

    let scrollDebounce = jqLite.fn.debounce(() => {
      new main(config.rules).oneByOne();
    }, 200);
    $(window).bind('scroll', ()=> {
      scrollDebounce();
    });

    let overDebouce = jqLite.fn.debounce((e)=> {
      let aEle = e.target;
      if (aEle.tagName !== "A" || !aEle.href || !/w{3}\.baidu\.com\/link\?url=/im.test(aEle.href)) return;
      $ajax(aEle.href, aEle)
        .then(function (data) {
          data.aEle.href = data.response.finalUrl;
        });
    }, 100);
    $(document).bind('mouseover', (e) => {
      overDebouce(e);
    });


  });

})(window, document);


/* jshint ignore:end */