您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在浏览器中自由使用百度在线输入法
// ==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.2 // @grant GM_xmlhttpRequest // ==/UserScript== // // DONE: // : 弹窗相对于body的位置 // : 插入词而不是在结束时附加 // : 最上层!! // : ff/chromium兼容 // // TODO: CHIANFIND_RES特性 // TODO: 边沿检测特性 // TODO: 完善中文标点 // // `+/-` 翻页 // `Space/1/2/3/4/5` 选词 // `Shift` 全角/半角逗号句号 // document.body.addEventListener('keydown', configQuanjiao); function configQuanjiao(e) { if (e.which == 16) { IME.quanjiao = !IME.quanjiao; console.log(e); e.preventDefault(); } } var IME = { status: 'hidden', output: '', inputString: '', TEXTS: [], page: 0, quanjiao: true, } 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]); } }, 2000); // 为了等待文本框装载进DOM function initIME(tt) { console.log("[DEBUG]", tt); var imePop = document.createElement('div'); initImePop(); tt.addEventListener('keydown', checkNonCharacter); tt.addEventListener('keyup', reqAndRefresh); tt.addEventListener('keypress', intercept); function checkNonCharacter(e) { if (IME.status == 'POPUP') { switch (String.fromCharCode(e.which)) { case String.fromCharCode(8): e.preventDefault(); IME.inputString = IME.inputString.substr(0, IME.inputString.length - 1); if (IME.inputString.length == 0) { IME.status = 'hidden'; showImePop(false); } break; case String.fromCharCode(13): e.preventDefault(); var curStart = tt.selectionStart; var curEnd = tt.selectionEnd; tt.value = tt.value.substring(0, curStart) + IME.inputString + tt.value.substring(curEnd, tt.value.length); tt.selectionStart = curStart + IME.inputString.length; tt.selectionEnd = curStart + IME.inputString.length; IME.inputString = ""; IME.status = 'hidden'; showImePop(false); break; } } imePop.querySelector('p').innerHTML = IME.inputString; } function reqAndRefresh(e) { 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=100&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 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.height = "1.5em"; //只为防止抖动 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" hr.style.color = "grey"; 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 (String.fromCharCode(e.which)) { case " ": case "1": case "2": case "3": case "4": case "5": e.preventDefault(); var index = String.fromCharCode(e.which) == " "?0:parseInt(String.fromCharCode(e.which)) - 1; console.log(index); var curStart = tt.selectionStart; var curEnd = tt.selectionEnd; var selectedText = imePop.querySelector('ol').children[index].textContent; tt.value = tt.value.substring(0, curStart) + selectedText + tt.value.substring(curEnd, tt.value.length); tt.selectionStart = curStart + selectedText.length; tt.selectionEnd = curStart + selectedText.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 += String.fromCharCode(e.which); 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 (String.fromCharCode(e.which)) { case ",": if (IME.quanjiao) { e.preventDefault(); var curStart = tt.selectionStart; var curEnd = tt.selectionEnd; tt.value = tt.value.substring(0, curStart) + ',' + tt.value.substring(curEnd, tt.value.length); tt.selectionStart = curStart + ','.length; tt.selectionEnd = curStart + ','.length; return; } break; case ".": if (IME.quanjiao) { e.preventDefault(); var curStart = tt.selectionStart; var curEnd = tt.selectionEnd; tt.value = tt.value.substring(0, curStart) + '。' + tt.value.substring(curEnd, 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 += String.fromCharCode(e.which); IME.status = 'POPUP'; showImePop(true); } IME.page = 0; break; default: void(0); } } }; 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; } }