translator

划词翻译

当前为 2018-12-24 提交的版本,查看 最新版本

(function () {
  'use strict';

  // ==UserScript==
  // @name translator
  // @namespace https://lufei.so
  // @supportURL https://github.com/intellilab/translator.user.js
  // @description 划词翻译
  // @version 1.5.14
  // @run-at document-start
  // @grant GM_xmlhttpRequest
  // @require https://cdn.jsdelivr.net/npm/vm.jsx
  // @include *
  // ==/UserScript==

  var css = ".panel{color:#333;font-size:14px;max-width:300px;position:fixed;z-index:10000}.body{background-color:#fff;border:1px solid #eaeaea;border-radius:4px;color:#555;font-family:monospace,consolas;line-height:24px;padding:8px;position:relative;word-break:break-all}.header{border-bottom:1px dashed #aaa;padding:0 0 8px}.header>a{color:#7cbef0;cursor:pointer;font-size:12px;margin-left:8px}.detail{font-size:12px;line-height:22px;list-style:none;margin:8px 0 0;padding:0}.detail>li{line-height:26px}";

  var h = VM.createElement;
  var translator = initialize();

  function render(data) {
    var body = translator.body,
        audio = translator.audio;
    body.innerHTML = '';
    var basic = data.basic,
        query = data.query,
        translation = data.translation;

    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 = h("div", {
        className: "header",
        onClick: handleClick
      }, h("span", null, query), h("a", {
        "data-type": "1",
        dangerouslySetInnerHTML: {
          __html: `uk: [${uk || noPhonetic}]`
        }
      }), h("a", {
        "data-type": "2",
        dangerouslySetInnerHTML: {
          __html: `us: [${us || noPhonetic}]`
        }
      }), h("a", {
        target: "_blank",
        href: `http://dict.youdao.com/search?q=${encodeURIComponent(query)}`
      }, "\u8BE6\u60C5"));
      body.append(header);

      if (explains) {
        var lis = [];
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = explains[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var item = _step.value;
            lis.push(h("li", {
              dangerouslySetInnerHTML: {
                __html: item
              }
            }));
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator.return != null) {
              _iterator.return();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }

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

  function translate(e) {
    var sel = window.getSelection();
    var text = sel.toString();
    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;
    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);
          var root = translator.root,
              panel = translator.panel;
          var _window = window,
              innerWidth = _window.innerWidth,
              innerHeight = _window.innerHeight;

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

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

          document.body.append(root);
        }
      }

    });
  }

  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 = h("audio", {
      autoplay: true
    });
    var root = h("div", {
      id: "translator.user.js"
    });
    var shadow = root.attachShadow({
      mode: 'open'
    });
    var panel = h("div", {
      className: "panel"
    });
    var panelBody = h("div", {
      className: "body"
    });
    shadow.append(h("style", null, css), panel);
    panel.append(panelBody);
    var debouncedTranslate = debounce(translate);
    var isSelecting;
    document.addEventListener('mousedown', function (e) {
      isSelecting = false;
      if (e.target === root) return;
      root.remove();
      panelBody.innerHTML = '';
    }, true);
    document.addEventListener('mousemove', function () {
      isSelecting = true;
    }, true);
    document.addEventListener('mouseup', function (e) {
      if (panel.contains(e.target) || !isSelecting) return;
      debouncedTranslate(e);
    }, true);
    document.addEventListener('dblclick', function (e) {
      if (panel.contains(e.target)) return;
      debouncedTranslate(e);
    }, true);
    return {
      audio,
      root,
      panel,
      body: panelBody
    };
  }

}());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址