baiduCloudInput

input method in browser based on baidu online input method.

От 19.10.2015. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name        baiduCloudInput
// @name:zh-CN  百度云输入法
// @namespace   [email protected]
// @description input method in browser based on baidu online input method.
// @description:zh-CN 在浏览器中自由使用百度在线输入法
// @include     *
// @version     1.0
// @grant       GM_xmlhttpRequest
// ==/UserScript==
//
// DONE:
// : 弹窗相对于body的位置
// : 插入词而不是在结束时附加
// : 最上层!!
//
// TODO: CHIANFIND_RES特性
// TODO: 边沿检测特性
// TODO: 完善中文标点


setTimeout(function() {
  var tts = document.getElementsByTagName("textarea");
  for(var i = 0; i < tts.length; i++) {
    initIME(tts[i]);
  }
  var tts = document.getElementsByTagName("input");
  for(var i = 0; i < tts.length; i++) {
    initIME(tts[i]);
  }
}, 3000); // 为了等待文本框装载进DOM

function initIME(tt) {
  //console.log("[DEBUG]", tt);
  var IME = {
    status: 'hidden',
    output: '',
    inputString: '',
    TEXTS: [],
    page: 0
  }
  
  var imePop = document.createElement('div');
  
  initImePop();
  tt.addEventListener('keydown', intercept); 
  
  function initImePop() {
    imePop.setAttribute('id', 'baidu-cloud-input-imePop');
    imePop.style.position = "absolute";
    imePop.style.width = "300px";
    //imePop.style.height = "80px";
    imePop.style.background = "lightblue";
    imePop.style.borderRadius = "5px";
    imePop.style.display = "none";
    imePop.style.boxShadow = "0 0 3px 0px black"
    imePop.style.zIndex = "9999999";
    var echo = document.createElement('p');
    echo.style.lineHeight = "1.5em";
    echo.style.fontSize = "1em";
    echo.style.margin = "0";
    echo.style.padding = "0";
    echo.style.paddingLeft = "0.5em";
    echo.style.color = "darkblue";
    echo.style.fontStyle = "bold";
    imePop.appendChild(echo);
    var tips = document.createElement('ol');
    tips.style.margin = "0px";
    tips.style.padding = "0px";
    tips.style.color = "black";
    var tip = [];
    for (var i = 0; i < 5; i++) {
      tip[i] = document.createElement('li');
      tip[i].style.margin = "0px";
      tip[i].style.padding = "0px";
      tip[i].style.marginLeft = "2em";
      tip[i].style.listStyleType = "decimal";
      tips.appendChild(tip[i]);
    }
    document.body.appendChild(imePop);
    var hr = document.createElement('hr')
    hr.style.marginTop = "0";
    hr.style.marginBottom = "0.2em"
    imePop.appendChild(hr);
    imePop.appendChild(tips);
  }
  
  function showImePop(state) {
    if (state) {
      var coordinates = getCaretCoordinates(tt, tt.selectionEnd);
      var textAreaTop = findPos(tt)[1] + 20;
      var textAreaLeft = findPos(tt)[0];
      imePop.style.left = textAreaLeft + coordinates.left + "px";
      imePop.style.top = textAreaTop -tt.scrollTop + coordinates.top + "px";
      imePop.style.display = "block";
    } else {
      imePop.style.display = 'none';
    }
  }
  
  function findPos(obj) {
	  var curleft = curtop = 0;
    if (obj.offsetParent) {
      do {
    			curleft += obj.offsetLeft;
    			curtop += obj.offsetTop;
      } while (obj = obj.offsetParent);
    }
    return [curleft,curtop];
  }

  
  function intercept(e){
    // control keys
    if (e.ctrlKey) {
      return;
    }
    if (IME.status == 'POPUP') {
      switch (e.key) {
        case " ":
        case "1":
        case "2":
        case "3":
        case "4":
        case "5":
          e.preventDefault();
          var index = e.key == " "?0:parseInt(e.key) - 1;
          var curStart = tt.selectionStart;
          var selectedText = imePop.querySelector('ol').children[index].textContent;
          tt.value = tt.value.substring(0, curStart) + selectedText + tt.value.substring(curStart, tt.value.length);
          tt.selectionStart = curStart + selectedText.length;
          tt.selectionEnd = curStart + selectedText.length;
          IME.inputString = "";
          IME.status = 'hidden';
          showImePop(false);
          break;
        case "Backspace":
          e.preventDefault();
  
          IME.inputString = IME.inputString.substr(0, IME.inputString.length - 1);
          if (IME.inputString.length == 0) {
            IME.status = 'hidden';
            showImePop(false);
          }
          break;
        case "Enter":
          e.preventDefault();
          var curStart = tt.selectionStart;
          tt.value = tt.value.substring(0, curStart) + IME.inputString + tt.value.substring(curStart, tt.value.length);
          tt.selectionStart = curStart + IME.inputString.length;
          tt.selectionEnd = curStart + IME.inputString.length;
  
          IME.inputString = "";
          IME.status = 'hidden';
          showImePop(false);
          break;
        case "a":
        case "b":
        case "c":
        case "d":
        case "e":
        case "f":
        case "g":
        case "h":
        case "i":
        case "j":
        case "k":
        case "l":
        case "m":
        case "n":
        case "o":
        case "p":
        case "q":
        case "r":
        case "s":
        case "t":
        case "u":
        case "v":
        case "w":
        case "x":
        case "y":
        case "z":
        case "'":
          e.preventDefault();
          IME.inputString += e.key;
          break;
        // {
        case "=":
          e.preventDefault();
          IME.page += 1;
          //console.log("[DEBUG]", IME.page);
          if (IME.page < IME.TEXTS.length / 5) {
            updateList(IME.page);
          } else {
            IME.page -= 1;
          }
          return;
        case "-":
          e.preventDefault();
          IME.page = IME.page == 0?IME.page:IME.page - 1;
          //console.log("[DEBUG]", IME.page);
          updateList(IME.page);
          return;
        // }
        default:
          e.preventDefault();
      }
    } else if (IME.status == 'hidden') {
      switch (e.key) {
        case ",":
          e.preventDefault();
          var curStart = tt.selectionStart;
          tt.value = tt.value.substring(0, curStart) + ',' + tt.value.substring(curStart, tt.value.length);
          tt.selectionStart = curStart + ','.length;
          tt.selectionEnd = curStart + ','.length;
          return;
          break;
        case ".":
          e.preventDefault();
          var curStart = tt.selectionStart;
          tt.value = tt.value.substring(0, curStart) + '。' + tt.value.substring(curStart, tt.value.length);
          tt.selectionStart = curStart + '。'.length;
          tt.selectionEnd = curStart + '。'.length;
          return;
          break;
        case "a":
        case "b":
        case "c":
        case "d":
        case "e":
        case "f":
        case "g":
        case "h":
        case "i":
        case "j":
        case "k":
        case "l":
        case "m":
        case "n":
        case "o":
        case "p":
        case "q":
        case "r":
        case "s":
        case "t":
        case "u":
        case "v":
        case "w":
        case "x":
        case "y":
        case "z":
        case "'":
          e.preventDefault();
          if (IME.inputString.length == 0) {
            IME.inputString += e.key;
            IME.status = 'POPUP';
            showImePop(true);
          }
          IME.page = 0;
          break;
        default:
          void(0);
      }
    }
    imePop.querySelector('p').innerHTML = IME.inputString;
    // reconize key finish
    console.log("[IME.inputString] ", IME.inputString);
  
    var p = new Promise(function(resolve, reject) {
      var ret = GM_xmlhttpRequest({
        method: "GET",
        url: `http://olime.baidu.com/py?input=${IME.inputString}&inputtype=py&bg=0&ed=20&result=hanzi&resultcoding=unicode&ch_en=0&clientinfo=web&version=1`,
        onload: function(res) {
          //console.log("[DEBUG connect]")
          resolve(res.responseText);
        }
      })
    });
  
    p.then(parseJSON).then(parseRes, printError);
  };
  
  function printError(err) {
    console.log(err);
  };
  
  function parseRes(resObj) {
    // console.log("[resObj]", resObj);
    if (resObj['errno'] != 0) {
      return;
    }
    var text = resObj['result'][0];
    console.log("[text]", text[0][0])
    for (var i = 0; i < text.length; i++) {
      IME.TEXTS[i] = text[i][0];
    }
    updateList(IME.page);
  }
  
  function updateList(page) {
    for (var i = 0; i < 5; i++) {
      imePop.querySelector('ol').children[i].innerHTML = IME.TEXTS[page * 5 + i];
      if (page * 5 + i >= IME.TEXTS.length) {
        imePop.querySelector('ol').children[i].innerHTML = "--"
      }
    }
  }
  
  function parseJSON(text) {
    // console.log("JSON response from baidu: ", text);
    var resObj = JSON.parse(text);
    return resObj;
  }
  
  // this function comes from https://github.com/component/textarea-caret-position/blob/master/index.js
  function getCaretCoordinates(element, position) {
    var properties = [
      'direction',  // RTL support
      'boxSizing',
      'width',  // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does
      'height',
      'overflowX',
      'overflowY',  // copy the scrollbar for IE
  
      'borderTopWidth',
      'borderRightWidth',
      'borderBottomWidth',
      'borderLeftWidth',
      'borderStyle',
  
      'paddingTop',
      'paddingRight',
      'paddingBottom',
      'paddingLeft',
  
      // https://developer.mozilla.org/en-US/docs/Web/CSS/font
      'fontStyle',
      'fontVariant',
      'fontWeight',
      'fontStretch',
      'fontSize',
      'fontSizeAdjust',
      'lineHeight',
      'fontFamily',
  
      'textAlign',
      'textTransform',
      'textIndent',
      'textDecoration',  // might not make a difference, but better be safe
  
      'letterSpacing',
      'wordSpacing',
  
      'tabSize',
      'MozTabSize'
  
    ];
    // mirrored div
    var div = document.createElement('div');
    div.id = 'input-textarea-caret-position-mirror-div';
    document.body.appendChild(div);
  
    var style = div.style;
    var computed = window.getComputedStyle? getComputedStyle(element) : element.currentStyle;  // currentStyle for IE < 9
  
    // default textarea styles
    style.whiteSpace = 'pre-wrap';
    if (element.nodeName !== 'INPUT')
      style.wordWrap = 'break-word';  // only for textarea-s
  
    // position off-screen
    style.position = 'absolute';  // required to return coordinates properly
    style.visibility = 'hidden';  // not 'display: none' because we want rendering
  
    // transfer the element's properties to the div
    properties.forEach(function (prop) {
      style[prop] = computed[prop];
    });
  
    var isFirefox = window.mozInnerScreenX != null;
    if (isFirefox) {
      // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275
      if (element.scrollHeight > parseInt(computed.height))
        style.overflowY = 'scroll';
    } else {
      style.overflow = 'hidden';  // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll'
    }
  
    div.textContent = element.value.substring(0, position);
    // the second special handling for input type="text" vs textarea: spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037
    if (element.nodeName === 'INPUT')
      div.textContent = div.textContent.replace(/\s/g, "\u00a0");
  
    var span = document.createElement('span');
    // Wrapping must be replicated *exactly*, including when a long word gets
    // onto the next line, with whitespace at the end of the line before (#7).
    // The  *only* reliable way to do that is to copy the *entire* rest of the
    // textarea's content into the <span> created at the caret position.
    // for inputs, just '.' would be enough, but why bother?
    span.textContent = element.value.substring(position) || '.';  // || because a completely empty faux span doesn't render at all
    div.appendChild(span);
  
    var coordinates = {
      top: span.offsetTop + parseInt(computed['borderTopWidth']),
      left: span.offsetLeft + parseInt(computed['borderLeftWidth'])
    };
  
    document.body.removeChild(div);
  
    return coordinates;
  }
}