translator

划词翻译

Versión del día 15/09/2020. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name translator
// @namespace https://lufei.so
// @supportURL https://github.com/intellilab/translator.user.js
// @description 划词翻译
// @version 1.6.2
// @run-at document-start
// @grant GM_xmlhttpRequest
// @require https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@1,npm/@violentmonkey/ui
// @include *
// ==/UserScript==

(function () {
'use strict';

var css = ".header{padding:0 0 8px;border-bottom:1px dashed #aaa}.header>a{margin-left:8px;color:#7cbef0;cursor:pointer;font-size:12px}.detail{margin:8px 0 0;padding:0;line-height:22px;list-style:none;font-size:12px}.detail>li{line-height:26px}";

function render(data, panel, audio) {
  var basic = data.basic,
      query = data.query,
      translation = data.translation;
  panel.clear();

  if (basic) {
    var explains = basic.explains,
        us = basic['us-phonetic'],
        uk = basic['uk-phonetic'];
    var noPhonetic = '♥';

    var handleClick = function handleClick(e) {
      var type = e.target.dataset.type;

      if (type) {
        audio.src = `https://dict.youdao.com/dictvoice?audio=${encodeURIComponent(query)}&type=${type}`;
      }
    };

    var header = VM.createElement("div", {
      className: "header",
      onClick: handleClick
    }, VM.createElement("span", null, query), VM.createElement("a", {
      "data-type": "1",
      dangerouslySetInnerHTML: {
        __html: `uk: [${uk || noPhonetic}]`
      }
    }), VM.createElement("a", {
      "data-type": "2",
      dangerouslySetInnerHTML: {
        __html: `us: [${us || noPhonetic}]`
      }
    }), VM.createElement("a", {
      target: "_blank",
      rel: "noopener noreferrer",
      href: `http://dict.youdao.com/search?q=${encodeURIComponent(query)}`
    }, "\u8BE6\u60C5"));
    panel.append(header);

    if (explains) {
      var lis = [];

      for (var _iterator = explains, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
        var _ref;

        if (_isArray) {
          if (_i >= _iterator.length) break;
          _ref = _iterator[_i++];
        } else {
          _i = _iterator.next();
          if (_i.done) break;
          _ref = _i.value;
        }

        var item = _ref;
        lis.push(VM.createElement("li", {
          dangerouslySetInnerHTML: {
            __html: item
          }
        }));
      }

      var ul = VM.createElement("ul", {
        className: "detail"
      }, lis);
      panel.append(ul);
    }
  } else if (translation) {
    var div = VM.createElement("div", {
      dangerouslySetInnerHTML: {
        __html: translation[0]
      }
    });
    panel.append(div);
  }
}

function translate(e, panel, audio) {
  var sel = window.getSelection();
  var text = sel.toString().trim();
  if (/^\s*$/.test(text)) return;
  var _document = document,
      activeElement = _document.activeElement;
  if (['input', 'textarea'].indexOf(activeElement.tagName.toLowerCase()) < 0 && !activeElement.contains(sel.getRangeAt(0).startContainer)) return;
  /**
   * 采用 Bing 翻译句子
   * PS: 对比了一下有道发现各有千秋或许某个版本会使用有道翻译(不是词典)句子
    */

  if (/\s/.test(text)) {
    GM_xmlhttpRequest({
      method: 'POST',
      url: 'https://www.bing.com/ttranslatev3',
      data: `fromLang=auto-detect&to=zh-Hans&text=${text}`,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },

      onload(res) {
        if (res.status !== 200) return;
        var data = JSON.parse(res.responseText); // 与有道词典结果格式保持一致

        render({
          translation: [data[0].translations[0].text]
        }, panel, audio);
        var wrapper = panel.wrapper;
        var _window = window,
            innerWidth = _window.innerWidth,
            innerHeight = _window.innerHeight;

        if (e.clientY > innerHeight * 0.5) {
          wrapper.style.top = 'auto';
          wrapper.style.bottom = `${innerHeight - e.clientY + 10}px`;
        } else {
          wrapper.style.top = `${e.clientY + 10}px`;
          wrapper.style.bottom = 'auto';
        }

        if (e.clientX > innerWidth * 0.5) {
          wrapper.style.left = 'auto';
          wrapper.style.right = `${innerWidth - e.clientX}px`;
        } else {
          wrapper.style.left = `${e.clientX}px`;
          wrapper.style.right = 'auto';
        }

        panel.show();
      }

    });
  } else {
    var query = {
      type: 'data',
      doctype: 'json',
      version: '1.1',
      relatedUrl: 'http://fanyi.youdao.com/',
      keyfrom: 'fanyiweb',
      key: null,
      translate: 'on',
      q: text,
      ts: Date.now()
    };
    var qs = Object.keys(query).map(function (key) {
      return `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`;
    }).join('&');
    GM_xmlhttpRequest({
      method: 'GET',
      url: `https://fanyi.youdao.com/openapi.do?${qs}`,

      onload(res) {
        var data = JSON.parse(res.responseText);

        if (!data.errorCode) {
          render(data, panel, audio);
          var wrapper = panel.wrapper;
          var _window2 = window,
              innerWidth = _window2.innerWidth,
              innerHeight = _window2.innerHeight;

          if (e.clientY > innerHeight * 0.5) {
            wrapper.style.top = 'auto';
            wrapper.style.bottom = `${innerHeight - e.clientY + 10}px`;
          } else {
            wrapper.style.top = `${e.clientY + 10}px`;
            wrapper.style.bottom = 'auto';
          }

          if (e.clientX > innerWidth * 0.5) {
            wrapper.style.left = 'auto';
            wrapper.style.right = `${innerWidth - e.clientX}px`;
          } else {
            wrapper.style.left = `${e.clientX}px`;
            wrapper.style.right = 'auto';
          }

          panel.show();
        }
      }

    });
  }
}

function debounce(func, delay) {
  var timer;

  function exec() {
    timer = null;
    func.apply(void 0, arguments);
  }

  return function () {
    if (timer) clearTimeout(timer);

    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    timer = setTimeout.apply(void 0, [exec, delay].concat(args));
  };
}

function initialize() {
  var audio = VM.createElement("audio", {
    autoPlay: true
  });
  var panel = VM.getPanel({
    css
  });
  var debouncedTranslate = debounce(function (e) {
    return translate(e, panel, audio);
  });
  var isSelecting;
  document.addEventListener('mousedown', function (e) {
    isSelecting = false;
    if (e.target === panel.host) return;
    panel.hide();
  }, true);
  document.addEventListener('mousemove', function () {
    isSelecting = true;
  }, true);
  document.addEventListener('mouseup', function (e) {
    if (panel.body.contains(e.target) || !isSelecting) return;
    debouncedTranslate(e);
  }, true);
  document.addEventListener('dblclick', function (e) {
    if (panel.body.contains(e.target)) return;
    debouncedTranslate(e);
  }, true);
}

initialize();

}());