// ==UserScript== // @name Google Translator Tooltip Expanded Fork // @description Translates the selected text into a tooltip automatically. Based on Google Translation Tooltip MLiteKeysOn. // @icon https://raw.githubusercontent.com/Odyseus/GreasemonkeyScripts/master/GoogleTranslatorTooltipExpandedFork/g-translate-logo.png // @version 1.12 // @include http* // @include https* // @include chrome* // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_log // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_openInTab // @grant GM_registerMenuCommand // @grant GM_setValue // @namespace https://greasyfork.org/users/5764 // @downloadURL https://update.greasyfork.icu/scripts/5727/Google%20Translator%20Tooltip%20Expanded%20Fork.user.js // @updateURL https://update.greasyfork.icu/scripts/5727/Google%20Translator%20Tooltip%20Expanded%20Fork.meta.js // ==/UserScript== /** * ColorPicker - pure JavaScript color picker without using images, external CSS or 1px divs. * Copyright © 2011 David Durman, All rights reserved. * http://www.daviddurman.com/flexi-color-picker/# */ (function (window, document, undefined) { var type = (window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML"), picker, slide, hueOffset = 15, svgNS = 'http://www.w3.org/2000/svg'; // This HTML snippet is inserted into the innerHTML property of the passed color picker element // when the no-hassle call to ColorPicker() is used, i.e. ColorPicker(function(hex, hsv, rgb) { ... }); var colorpickerHTMLSnippet = [ '
', '
', '
', '
', '
', '
', '
', '
' ].join(''); /** * Return mouse position relative to the element el. */ function mousePosition(evt) { // IE: if (window.event && window.event.contentOverflow !== undefined) { return { x : window.event.offsetX, y : window.event.offsetY }; } // Webkit: if (evt.offsetX !== undefined && evt.offsetY !== undefined) { return { x : evt.offsetX, y : evt.offsetY }; } // Firefox: var wrapper = evt.target.parentNode.parentNode; return { x : evt.layerX - wrapper.offsetLeft, y : evt.layerY - wrapper.offsetTop }; } /** * Create SVG element. */ function $(el, attrs, children) { el = document.createElementNS(svgNS, el); for (var key in attrs) el.setAttribute(key, attrs[key]); if (Object.prototype.toString.call(children) != '[object Array]') children = [children]; var i = 0, len = (children[0] && children.length) || 0; for (; i < len; i++) el.appendChild(children[i]); return el; } /** * Create slide and picker markup depending on the supported technology. */ if (type == 'SVG') { slide = $('svg', { xmlns : 'http://www.w3.org/2000/svg', version : '1.1', width : '100%', height : '100%' }, [ $('defs', {}, $('linearGradient', { id : 'gradient-hsv', x1 : '0%', y1 : '100%', x2 : '0%', y2 : '0%' }, [ $('stop', { offset : '0%', 'stop-color' : '#FF0000', 'stop-opacity' : '1' }), $('stop', { offset : '13%', 'stop-color' : '#FF00FF', 'stop-opacity' : '1' }), $('stop', { offset : '25%', 'stop-color' : '#8000FF', 'stop-opacity' : '1' }), $('stop', { offset : '38%', 'stop-color' : '#0040FF', 'stop-opacity' : '1' }), $('stop', { offset : '50%', 'stop-color' : '#00FFFF', 'stop-opacity' : '1' }), $('stop', { offset : '63%', 'stop-color' : '#00FF40', 'stop-opacity' : '1' }), $('stop', { offset : '75%', 'stop-color' : '#0BED00', 'stop-opacity' : '1' }), $('stop', { offset : '88%', 'stop-color' : '#FFFF00', 'stop-opacity' : '1' }), $('stop', { offset : '100%', 'stop-color' : '#FF0000', 'stop-opacity' : '1' }) ])), $('rect', { x : '0', y : '0', width : '100%', height : '100%', fill : 'url(#gradient-hsv)' }) ]); picker = $('svg', { xmlns : 'http://www.w3.org/2000/svg', version : '1.1', width : '100%', height : '100%' }, [ $('defs', {}, [ $('linearGradient', { id : 'gradient-black', x1 : '0%', y1 : '100%', x2 : '0%', y2 : '0%' }, [ $('stop', { offset : '0%', 'stop-color' : '#000000', 'stop-opacity' : '1' }), $('stop', { offset : '100%', 'stop-color' : '#CC9A81', 'stop-opacity' : '0' }) ]), $('linearGradient', { id : 'gradient-white', x1 : '0%', y1 : '100%', x2 : '100%', y2 : '100%' }, [ $('stop', { offset : '0%', 'stop-color' : '#FFFFFF', 'stop-opacity' : '1' }), $('stop', { offset : '100%', 'stop-color' : '#CC9A81', 'stop-opacity' : '0' }) ]) ]), $('rect', { x : '0', y : '0', width : '100%', height : '100%', fill : 'url(#gradient-white)' }), $('rect', { x : '0', y : '0', width : '100%', height : '100%', fill : 'url(#gradient-black)' }) ]); } else if (type == 'VML') { slide = [ '
', '', '', '', '
' ].join(''); picker = [ '
', '', '', '', '', '', '', '
' ].join(''); if (!document.namespaces['v']) document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML'); } /** * Convert HSV representation to RGB HEX string. * Credits to http://www.raphaeljs.com */ function hsv2rgb(hsv) { var R, G, B, X, C; var h = (hsv.h % 360) / 60; C = hsv.v * hsv.s; X = C * (1 - Math.abs(h % 2 - 1)); R = G = B = hsv.v - C; h = ~~h; R += [C, X, 0, 0, X, C][h]; G += [X, C, C, X, 0, 0][h]; B += [0, 0, X, C, C, X][h]; var r = Math.floor(R * 255); var g = Math.floor(G * 255); var b = Math.floor(B * 255); return { r : r, g : g, b : b, hex : "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1) }; } /** * Convert RGB representation to HSV. * r, g, b can be either in <0,1> range or <0,255> range. * Credits to http://www.raphaeljs.com */ function rgb2hsv(rgb) { var r = rgb.r; var g = rgb.g; var b = rgb.b; if (rgb.r > 1 || rgb.g > 1 || rgb.b > 1) { r /= 255; g /= 255; b /= 255; } var H, S, V, C; V = Math.max(r, g, b); C = V - Math.min(r, g, b); H = (C == 0 ? null : V == r ? (g - b) / C + (g < b ? 6 : 0) : V == g ? (b - r) / C + 2 : (r - g) / C + 4); H = (H % 6) * 60; S = C == 0 ? 0 : C / V; return { h : H, s : S, v : V }; } /** * Return click event handler for the slider. * Sets picker background color and calls ctx.callback if provided. */ function slideListener(ctx, slideElement, pickerElement) { return function (evt) { evt = evt || window.event; var mouse = mousePosition(evt); ctx.h = mouse.y / slideElement.offsetHeight * 360 + hueOffset; var pickerColor = hsv2rgb({ h : ctx.h, s : 1, v : 1 }); var c = hsv2rgb({ h : ctx.h, s : ctx.s, v : ctx.v }); pickerElement.style.backgroundColor = pickerColor.hex; ctx.callback && ctx.callback(c.hex, { h : ctx.h - hueOffset, s : ctx.s, v : ctx.v }, { r : c.r, g : c.g, b : c.b }, undefined, mouse); } }; /** * Return click event handler for the picker. * Calls ctx.callback if provided. */ function pickerListener(ctx, pickerElement) { return function (evt) { evt = evt || window.event; var mouse = mousePosition(evt), width = pickerElement.offsetWidth, height = pickerElement.offsetHeight; ctx.s = mouse.x / width; ctx.v = (height - mouse.y) / height; var c = hsv2rgb(ctx); ctx.callback && ctx.callback(c.hex, { h : ctx.h - hueOffset, s : ctx.s, v : ctx.v }, { r : c.r, g : c.g, b : c.b }, mouse); } }; var uniqID = 0; /** * ColorPicker. * @param {DOMElement} slideElement HSV slide element. * @param {DOMElement} pickerElement HSV picker element. * @param {Function} callback Called whenever the color is changed provided chosen color in RGB HEX format as the only argument. */ function ColorPicker(slideElement, pickerElement, callback) { if (!(this instanceof ColorPicker)) return new ColorPicker(slideElement, pickerElement, callback); this.h = 0; this.s = 1; this.v = 1; if (!callback) { // call of the form ColorPicker(element, funtion(hex, hsv, rgb) { ... }), i.e. the no-hassle call. var element = slideElement; element.innerHTML = colorpickerHTMLSnippet; this.slideElement = element.getElementsByClassName('slide')[0]; this.pickerElement = element.getElementsByClassName('picker')[0]; var slideIndicator = element.getElementsByClassName('slide-indicator')[0]; var pickerIndicator = element.getElementsByClassName('picker-indicator')[0]; ColorPicker.fixIndicators(slideIndicator, pickerIndicator); this.callback = function (hex, hsv, rgb, pickerCoordinate, slideCoordinate) { ColorPicker.positionIndicators(slideIndicator, pickerIndicator, slideCoordinate, pickerCoordinate); pickerElement(hex, hsv, rgb); }; } else { this.callback = callback; this.pickerElement = pickerElement; this.slideElement = slideElement; } if (type == 'SVG') { // Generate uniq IDs for linearGradients so that we don't have the same IDs within one document. // Then reference those gradients in the associated rectangles. var slideClone = slide.cloneNode(true); var pickerClone = picker.cloneNode(true); var hsvGradient = slideClone.getElementById('gradient-hsv'); var hsvRect = slideClone.getElementsByTagName('rect')[0]; hsvGradient.id = 'gradient-hsv-' + uniqID; hsvRect.setAttribute('fill', 'url(#' + hsvGradient.id + ')'); var blackAndWhiteGradients = [pickerClone.getElementById('gradient-black'), pickerClone.getElementById('gradient-white')]; var whiteAndBlackRects = pickerClone.getElementsByTagName('rect'); blackAndWhiteGradients[0].id = 'gradient-black-' + uniqID; blackAndWhiteGradients[1].id = 'gradient-white-' + uniqID; whiteAndBlackRects[0].setAttribute('fill', 'url(#' + blackAndWhiteGradients[1].id + ')'); whiteAndBlackRects[1].setAttribute('fill', 'url(#' + blackAndWhiteGradients[0].id + ')'); this.slideElement.appendChild(slideClone); this.pickerElement.appendChild(pickerClone); uniqID++; } else { this.slideElement.innerHTML = slide; this.pickerElement.innerHTML = picker; } addEventListener(this.slideElement, 'click', slideListener(this, this.slideElement, this.pickerElement)); addEventListener(this.pickerElement, 'click', pickerListener(this, this.pickerElement)); enableDragging(this, this.slideElement, slideListener(this, this.slideElement, this.pickerElement)); enableDragging(this, this.pickerElement, pickerListener(this, this.pickerElement)); }; function addEventListener(element, event, listener) { if (element.attachEvent) { element.attachEvent('on' + event, listener); } else if (element.addEventListener) { element.addEventListener(event, listener, false); } } /** * Enable drag&drop color selection. * @param {object} ctx ColorPicker instance. * @param {DOMElement} element HSV slide element or HSV picker element. * @param {Function} listener Function that will be called whenever mouse is dragged over the element with event object as argument. */ function enableDragging(ctx, element, listener) { var mousedown = false; addEventListener(element, 'mousedown', function (evt) { mousedown = true; }); addEventListener(element, 'mouseup', function (evt) { mousedown = false; }); addEventListener(element, 'mouseout', function (evt) { mousedown = false; }); addEventListener(element, 'mousemove', function (evt) { if (mousedown) { listener(evt); } }); } ColorPicker.hsv2rgb = function (hsv) { var rgbHex = hsv2rgb(hsv); delete rgbHex.hex; return rgbHex; }; ColorPicker.hsv2hex = function (hsv) { return hsv2rgb(hsv).hex; }; ColorPicker.rgb2hsv = rgb2hsv; ColorPicker.rgb2hex = function (rgb) { return hsv2rgb(rgb2hsv(rgb)).hex; }; ColorPicker.hex2hsv = function (hex) { return rgb2hsv(ColorPicker.hex2rgb(hex)); }; ColorPicker.hex2rgb = function (hex) { return { r : parseInt(hex.substr(1, 2), 16), g : parseInt(hex.substr(3, 2), 16), b : parseInt(hex.substr(5, 2), 16) }; }; /** * Sets color of the picker in hsv/rgb/hex format. * @param {object} ctx ColorPicker instance. * @param {object} hsv Object of the form: { h: , s: , v: }. * @param {object} rgb Object of the form: { r: , g: , b: }. * @param {string} hex String of the form: #RRGGBB. */ function setColor(ctx, hsv, rgb, hex) { ctx.h = hsv.h % 360; ctx.s = hsv.s; ctx.v = hsv.v; var c = hsv2rgb(ctx); var mouseSlide = { y : (ctx.h * ctx.slideElement.offsetHeight) / 360, x : 0 // not important }; var pickerHeight = ctx.pickerElement.offsetHeight; var mousePicker = { x : ctx.s * ctx.pickerElement.offsetWidth, y : pickerHeight - ctx.v * pickerHeight }; ctx.pickerElement.style.backgroundColor = hsv2rgb({ h : ctx.h, s : 1, v : 1 }).hex; ctx.callback && ctx.callback(hex || c.hex, { h : ctx.h, s : ctx.s, v : ctx.v }, rgb || { r : c.r, g : c.g, b : c.b }, mousePicker, mouseSlide); return ctx; }; /** * Sets color of the picker in hsv format. * @param {object} hsv Object of the form: { h: , s: , v: }. */ ColorPicker.prototype.setHsv = function (hsv) { return setColor(this, hsv); }; /** * Sets color of the picker in rgb format. * @param {object} rgb Object of the form: { r: , g: , b: }. */ ColorPicker.prototype.setRgb = function (rgb) { return setColor(this, rgb2hsv(rgb), rgb); }; /** * Sets color of the picker in hex format. * @param {string} hex Hex color format #RRGGBB. */ ColorPicker.prototype.setHex = function (hex) { return setColor(this, ColorPicker.hex2hsv(hex), undefined, hex); }; /** * Helper to position indicators. * @param {HTMLElement} slideIndicator DOM element representing the indicator of the slide area. * @param {HTMLElement} pickerIndicator DOM element representing the indicator of the picker area. * @param {object} mouseSlide Coordinates of the mouse cursor in the slide area. * @param {object} mousePicker Coordinates of the mouse cursor in the picker area. */ ColorPicker.positionIndicators = function (slideIndicator, pickerIndicator, mouseSlide, mousePicker) { if (mouseSlide) { slideIndicator.style.top = (mouseSlide.y - slideIndicator.offsetHeight / 2) + 'px'; } if (mousePicker) { pickerIndicator.style.top = (mousePicker.y - pickerIndicator.offsetHeight / 2) + 'px'; pickerIndicator.style.left = (mousePicker.x - pickerIndicator.offsetWidth / 2) + 'px'; } }; /** * Helper to fix indicators - this is recommended (and needed) for dragable color selection (see enabledDragging()). */ ColorPicker.fixIndicators = function (slideIndicator, pickerIndicator) { pickerIndicator.style.pointerEvents = 'none'; slideIndicator.style.pointerEvents = 'none'; }; window.ColorPicker = ColorPicker; })(window, window.document); const HREF_NO = 'javascript:void(0)'; initCrossBrowserSupportForGmFunctions(); var languagesGoogle = ''; var body = getTag('body')[0]; var imgLookup; var txtSel = encodeURIComponent(txtSel); // text selected var translation2Element = document.createElement('span'); var currentURL; var initialized = false; images(); css(); document.addEventListener('mouseup', showLookupIcon, false); document.addEventListener('mousedown', mousedownCleaning, false); function mousedownCleaning(evt) { var divDic = getId('divDic'); var divLookup = getId('divLookup'); if (divDic) { if (!clickedInsideID(evt.target, 'divDic')) divDic.parentNode.removeChild(divDic); } if (divLookup) divLookup.parentNode.removeChild(divLookup); } function showLookupIcon(evt) { if (evt.ctrlKey && evt.altKey && (!GM_getValue('ctrl') || !GM_getValue('alt'))) return; // XOR http://www.howtocreate.co.uk/xor.html if (evt.ctrlKey ? !GM_getValue('ctrl') : GM_getValue('ctrl')) return; if (evt.altKey ? !GM_getValue('alt') : GM_getValue('alt')) return; if (!initialized) { images(); css(); initialized = true; } var divDic = getId('divDic'); var divLookup = getId('divLookup'); txtSel = getSelection(); // Exit if no text is selected if (!txtSel || txtSel == "") { if (divDic) { if (!clickedInsideID(evt.target, 'divDic')) divDic.parentNode.removeChild(divDic); } if (divLookup) divLookup.parentNode.removeChild(divLookup); return; } // Possible cleanup if (divDic) { if (!clickedInsideID(evt.target, 'divDic')) divDic.parentNode.removeChild(divDic); return; } // Remove div if exists if (divLookup) { divLookup.parentNode.removeChild(divLookup); } // Div container divLookup = createElement('div', { id : 'divLookup', style : 'background-color:transparent; color:#000000; position:absolute; top:' + (evt.clientY + window.pageYOffset + 10) + 'px; left:' + (evt.clientX + window.pageXOffset + 10) + 'px; padding:0px; z-index:10000; border-radius:2px;' }); divLookup.appendChild(imgLookup.cloneNode(false)); divLookup.addEventListener('mouseover', lookup, false); body.appendChild(divLookup); } // Create the tooltip and launch the Google Translate request to get the translation function lookup(evt) { var divResult = null; var divDic = getId('divDic'); var divLookup = getId('divLookup'); var top = divLookup.style.top; var left = divLookup.style.left; // No text selected if (!txtSel || txtSel == "") { if (divDic = getId('divDic')) divDic.parentNode.removeChild(divDic); return; } // Cleanup divs if (divDic = getId('divDic')) { divDic.parentNode.removeChild(divDic); } divLookup.parentNode.removeChild(divLookup); // Div container divDic = createElement('div', { id : 'divDic', style : 'opacity: 1; font-family: "Open Sans", Arial, Helvetica, sans-serif !important; font-size: ' + GM_getValue('fontsize', 'small') + '; background-color: ' + GM_getValue('backgroundColor', '#EDF4FC') + '; color: ' + GM_getValue('textcolor', 'Gray') + '; position:absolute; top:' + top + '; left:' + left + '; min-width:250px; min-height:50px; max-width:50%; padding:5px; text-align:left; z-index:10000; border-radius:4px; box-shadow: -2px 0px 9px 5px #898D91' }); divDic.addEventListener('mousedown', dragHandler, false); body.appendChild(divDic); // Div result // This awfull wall of text is the "+" image divResult = createElement('div', { id : 'divResult', style : 'overflow:auto; padding:3px;' }, null, '
Loading...'); divDic.appendChild(divResult); // Options link var optionLink = createElement('a', { id : 'optionsLink', href : HREF_NO, style : 'opacity:0.2; position:absolute; bottom:3px; right:13px; font-size:18px; text-decoration:none!important;background:#528DDF;padding:1px;color:#fff;border-radius:6px 6px 6px 6px;border:2px solid #EEEEEE;font-weight:bold;width:20px;text-align:center;display:block;' }, 'click openCloseOptions false', '+'); divDic.appendChild(optionLink); optionLink.addEventListener('mouseover', function (e) { e.target.style.opacity = 1.0 }); optionLink.addEventListener('mouseout', function (e) { e.target.style.opacity = 0.2 }); // Send the Google Translate request if ((txtSel + " ").search(/^\s*https?:\/\//) > -1) { divResult.innerHTML = '' + txtSel + ''; } else if ((txtSel + " ").search(/^\s*\S+(\.\S+)+/) > -1) // site.dom { divResult.innerHTML = '' + txtSel + ''; } else { var sl, tl, lang; sl = GM_getValue('from') ? GM_getValue('from') : "auto"; tl = GM_getValue('to') ? GM_getValue('to') : "auto"; lang = sl + "|" + tl; //currentURL = "http://www.google.com/translate_t?text=" + encodeURIComponent(txtSel) + "&langpair=" + lang; // Basic address, for web page parsing //currentURL = "http://translate.google.fr/translate_a/t?client=t&text=" + encodeURIComponent(txtSel) + "&langpair=" + lang; // URL for GET request. This adress return an array as answer currentPostData = "client=t&text=" + encodeURIComponent(txtSel) + "&langpair=" + lang; // Data for a POST request, for handling long requests GM_xmlhttpRequest({ /*method: 'GET', url: currentURL,*/ method : 'POST', url : 'http://translate.google.fr/translate_a/t', data : currentPostData, headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, onload : function (resp) { try { extractResult(resp.responseText); } catch (e) { GM_log(e); } } }); if (GM_getValue('to2', 'Disabled') != 'Disabled') { sl = GM_getValue('from') ? GM_getValue('from') : "auto"; tl = GM_getValue('to2') ? GM_getValue('to2') : "auto"; lang = sl + "|" + tl; currentPostData = "client=t&text=" + encodeURIComponent(txtSel) + "&langpair=" + lang; // Data for a POST request, for handling long requests GM_xmlhttpRequest({ method : 'POST', url : 'http://translate.google.fr/translate_a/t', data : currentPostData, headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, onload : function (resp) { try { extractResult2(resp.responseText); } catch (e) { GM_log(e); } } }); } else { translation2Element.innerHTML = ''; } } } // Lanched when we select an other language in the setup menu function quickLookup() { getId('divDic').style.fontSize = getId('optFontSize').value; getId('divDic').style.color = getId('optTextColor').value; getId('divResult').innerHTML = 'Loading...'; var sl, tl, lang; sl = getId('optSelLangFrom').value; tl = getId('optSelLangTo').value; lang = sl + "|" + tl; currentPostData = "client=t&text=" + encodeURIComponent(txtSel) + "&langpair=" + lang; // Data for a POST request, for handling long requests GM_xmlhttpRequest({ method : 'POST', url : 'http://translate.google.fr/translate_a/t', data : currentPostData, headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, onload : function (resp) { try { extractResult(resp.responseText); } catch (e) { GM_log(e); } } }); if (getId('optSelLangTo2').value != 'Disabled') { var sl, tl, lang; sl = getId('optSelLangFrom').value; tl = getId('optSelLangTo2').value; currentPostData = "client=t&text=" + encodeURIComponent(txtSel) + "&langpair=" + lang; // Data for a POST request, for handling long requests GM_xmlhttpRequest({ method : 'POST', url : 'http://translate.google.fr/translate_a/t', data : currentPostData, headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, onload : function (resp) { try { extractResult2(resp.responseText); } catch (e) { GM_log(e); } } }); } else { translation2Element.innerHTML = ''; } } function extractResult(gTradStringArray) { var arr = eval(gTradStringArray); // eval is used to transform the string to an array. I alse made a custom parsing function, but it doesn't handle antislashed characters, so I prefer using eval() /* Content of the gTrad array : 0 / 0:Translation 1:Source text 1 / i:Grammar / 0:Types (word, verb, ...) 1: Other translations 5 / Array of other translations */ var translation = ''; // 0 - Full translation translation += '[' + arr[2] + '] '; for (var i = 0; i < arr[0].length; i++) translation += arr[0][i][1]; translation += '
'; translation += '[' + GM_getValue('to', 'auto') + '] '; for (var i = 0; i < arr[0].length; i++) translation += arr[0][i][0]; translation += '

'; translation += 'Show details'; translation += 'Hide details
'; // 1 - Grammar if (typeof arr[1] != 'undefined') { for (var i = 0; i < arr[1].length; i++) { translation += '' + arr[1][i][0] + ' : '; for (var j = 0; j < arr[1][i][1].length; j++) { translation += ((j == 0) ? '' : ', ') + arr[1][i][1][j]; } translation += '
'; } translation += '
'; } // 5 - Alternative parts if (typeof arr[5] != 'undefined') { for (var i = 0; i < arr[5].length; i++) { if (typeof arr[5][i][2] != 'undefined') { // 5/i/2 array of alternatives, 5/i/0 the part of the text we are studying translation += '' + arr[5][i][0] + ' : '; for (var j = 0; j < arr[5][i][2].length; j++) { translation += ((j == 0) ? '' : ', ') + arr[5][i][2][j][0]; } translation += '
'; } } } translation += '
'; // Detail end getId('divResult').innerHTML = '

' + translation + '

'; getId('translation2Element').appendChild(translation2Element); // Optional second translation getId('toggleShowDetails').addEventListener('click', function () { getId('toggleShowDetails').style.display = 'none'; getId('divDetails').style.display = 'block'; }, false); getId('toggleHideDetails').addEventListener('click', function () { getId('toggleShowDetails').style.display = 'inline'; getId('divDetails').style.display = 'none'; }, false); // Create the Text to Speach var fromText = ''; var toText = ''; for (var i = 0; i < arr[0].length; i++) fromText += arr[0][i][1]; for (var i = 0; i < arr[0].length; i++) toText += arr[0][i][0]; addTextToSpeachLink(getId('texttospeachbuttonfrom'), arr[2], fromText); // arr[2] contains the detected input language addTextToSpeachLink(getId('texttospeachbuttonto'), GM_getValue('to', 'auto') == 'auto' ? 'en' : GM_getValue('to', 'auto'), toText); // I cannot find a way to get the detected destination language, so if the requested destination is 'auto', I use the english Text to Speach language } function extractResult2(gTradStringArray) { var arr = eval(gTradStringArray); var translation = ''; translation += '#[' + GM_getValue('to2', 'auto') + '] '; for (var i = 0; i < arr[0].length; i++) translation += arr[0][i][0]; translation += '# '; translation2Element.innerHTML = translation; var toText2 = ''; for (var i = 0; i < arr[0].length; i++) toText2 += arr[0][i][0]; addTextToSpeachLink(getId('texttospeachbuttonto2'), GM_getValue('to2', 'auto') == 'auto' ? 'en' : GM_getValue('to2', 'auto'), toText2); } function addTextToSpeachLink(element, lang, text) { if (GM_getValue('tts', false) == false) return; var speachLink = document.createElement('a'); speachLink.href = 'http://translate.google.com/translate_tts?tl=' + lang + '&q=' + text.replace(/[«»'"]/g, ' '); speachLink.target = '_blank'; speachLink.innerHTML = ''; element.appendChild(speachLink); } function getSelection() { var txt = null; //get selected text if (window.getSelection && !window.opera) // window.getSelection() bugs with Opera 12.16 and ViolentMonkey { txt = window.getSelection(); } else if (document.getSelection) { txt = document.getSelection(); } else if (document.selection) { txt = document.selection.createRange().text; } return txt; } function openCloseOptions(evt) { var divOptions = getId('divOpt'); if (!divOptions) //Show options { divOptions = createElement('div', { id : 'divOpt', style : 'border-top:2px solid #5A91D8;position:relative; padding:5px;' }); getId('divDic').appendChild(divOptions); getId('optionsLink').style.visibility = 'hidden'; // color picker, the library doesn't work on Opera try { if (!window.divColorPicker) { window.divColorPicker = createElement('div', { id : 'optPicker', class : 'cp-small' }); window.colorPicker = ColorPicker( window.divColorPicker, function (hex, hsv, rgb) { getId('divDic').style.backgroundColor = hex; }); } window.colorPicker.setHex(GM_getValue('backgroundColor', '#EDF4FC')); divOptions.appendChild(window.divColorPicker); } catch (err) { divOptions.innerHTML += '

Error : Cannot load color picker (Known issue on Opera)

'; } //fields container divOptionsFields = createElement('p'); divOptions.appendChild(divOptionsFields); //from divOptionsFields.appendChild(createElement('span', null, null, 'From :')); divOptionsFields.appendChild(createElement('select', { id : 'optSelLangFrom' }, null, languagesGoogle)); getId('optSelLangFrom').value = GM_getValue('from') ? GM_getValue('from') : 'auto'; getId('optSelLangFrom').addEventListener('change', quickLookup, false); //to divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('span', null, null, ' To :')); divOptionsFields.appendChild(createElement('select', { id : 'optSelLangTo' }, null, languagesGoogle)); getId('optSelLangTo').value = GM_getValue('to') ? GM_getValue('to') : 'auto'; getId('optSelLangTo').addEventListener('change', quickLookup, false); //to2 divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('span', null, null, ' To 2 :')); divOptionsFields.appendChild(createElement('select', { id : 'optSelLangTo2' }, null, '' + languagesGoogle)); getId('optSelLangTo2').value = GM_getValue('to2') ? GM_getValue('to2') : 'Disabled'; getId('optSelLangTo2').addEventListener('change', quickLookup, false); //use text to speach divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('input', { id : 'checkTTS', type : 'checkbox' })); divOptionsFields.appendChild(createElement('span', null, null, 'Display Text To Speach')); getId('checkTTS').checked = GM_getValue('tts'); //hide details divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('input', { id : 'checkDetails', type : 'checkbox' })); divOptionsFields.appendChild(createElement('span', null, null, 'Hide details by default')); getId('checkDetails').checked = GM_getValue('details'); //font size divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('span', null, null, 'Font size :')); divOptionsFields.appendChild(createElement('select', { id : 'optFontSize' }, null, '')); getId('optFontSize').value = GM_getValue('fontsize') ? GM_getValue('fontsize') : 'small'; getId('optFontSize').addEventListener('change', quickLookup, false); //text color divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('span', null, null, 'Text color :')); divOptionsFields.appendChild(createElement('select', { id : 'optTextColor' }, null, '')); getId('optTextColor').value = GM_getValue('textcolor') ? GM_getValue('textcolor') : 'Gray'; getId('optTextColor').addEventListener('change', quickLookup, false); //use ctrl divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('input', { id : 'checkCtrl', type : 'checkbox' })); divOptionsFields.appendChild(createElement('span', null, null, 'Use Ctrl key')); getId('checkCtrl').checked = GM_getValue('ctrl'); //use alt divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('input', { id : 'checkAlt', type : 'checkbox' })); divOptionsFields.appendChild(createElement('span', null, null, 'Use Alt key')); getId('checkAlt').checked = GM_getValue('alt'); //save divOptionsFields.appendChild(createElement('br')); divOptionsFields.appendChild(createElement('a', { href : HREF_NO, class : "gootranslink" }, 'click saveOptions false', 'Save')); //reset divOptionsFields.appendChild(createElement('span', null, null, ' - ')); divOptionsFields.appendChild(createElement('a', { href : HREF_NO, class : "gootranslink" }, 'click resetOptions false', 'Reset')); //cancel divOptionsFields.appendChild(createElement('span', null, null, ' - ')); divOptionsFields.appendChild(createElement('a', { href : HREF_NO, class : "gootranslink" }, 'click openCloseOptions false', 'Cancel')); } else // Hide options { divOptions.parentNode.removeChild(divOptions); getId('optionsLink').style.visibility = 'visible'; } } function saveOptions(evt) { var backgroundColor = getId('divDic').style.backgroundColor; var from = getId('optSelLangFrom').value; var to = getId('optSelLangTo').value; var to2 = getId('optSelLangTo2').value; var tts = getId('checkTTS').checked; var details = getId('checkDetails').checked; var fontsize = getId('optFontSize').value; var textcolor = getId('optTextColor').value; var ctrl = getId('checkCtrl').checked; var alt = getId('checkAlt').checked; GM_setValue('backgroundColor', backgroundColor); GM_setValue('from', from); GM_setValue('to', to); GM_setValue('to2', to2); GM_setValue('tts', tts); GM_setValue('details', details); GM_setValue('fontsize', fontsize); GM_setValue('textcolor', textcolor); GM_setValue('ctrl', ctrl); GM_setValue('alt', alt); quickLookup(); getId('divDic').removeChild(getId('divOpt')); getId('optionsLink').style.visibility = 'visible'; } function resetOptions(evt) { GM_deleteValue('backgroundColor'); GM_deleteValue('from'); GM_deleteValue('to'); GM_deleteValue('to2'); GM_deleteValue('tts'); GM_deleteValue('fontsize'); GM_deleteValue('textcolor'); GM_deleteValue('ctrl'); GM_deleteValue('alt'); getId('divDic').parentNode.removeChild(getId('divDic')); } function css() { var style = createElement('style', { type : "text/css" }, null, "" + 'a.gootranslink:link {color: #0000FF !important; text-decoration: underline !important;}' + 'a.gootranslink:visited {color: #0000FF !important; text-decoration: underline !important;}' + 'a.gootranslink:hover {color: #0000FF !important; text-decoration: underline !important;}' + 'a.gootranslink:active {color: #0000FF !important; text-decoration: underline !important;}' + '.picker-wrapper,.slide-wrapper{position:relative;float:left}.picker-indicator,.slide-indicator{position:absolute;left:0;top:0;pointer-events:none}.picker,.slide{cursor:crosshair;float:left}.cp-default{background-color:gray;padding:12px;box-shadow:0 0 40px #000;border-radius:15px;float:left}.cp-default .picker{width:200px;height:200px}.cp-default .slide{width:30px;height:200px}.cp-default .slide-wrapper{margin-left:10px}.cp-default .picker-indicator{width:5px;height:5px;border:2px solid darkblue;-moz-border-radius:4px;-o-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;opacity:.5;-ms-filter:"alpha(opacity=50)";filter:alpha(opacity=50);filter:alpha(opacity=50);background-color:white}.cp-default .slide-indicator{width:100%;height:10px;left:-4px;opacity:.6;-ms-filter:"alpha(opacity=60)";filter:alpha(opacity=60);filter:alpha(opacity=60);border:4px solid lightblue;-moz-border-radius:4px;-o-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;background-color:white}.cp-small{padding:5px;background-color:white;float:left;border-radius:5px}.cp-small .picker{width:100px;height:100px}.cp-small .slide{width:15px;height:100px}.cp-small .slide-wrapper{margin-left:5px}.cp-small .picker-indicator{width:1px;height:1px;border:1px solid black;background-color:white}.cp-small .slide-indicator{width:100%;height:2px;left:0;background-color:black}.cp-fancy{padding:10px;background:-webkit-linear-gradient(top,#aaa 0,#222 100%);float:left;border:1px solid #999;box-shadow:inset 0 0 10px white}.cp-fancy .picker{width:200px;height:200px}.cp-fancy .slide{width:30px;height:200px}.cp-fancy .slide-wrapper{margin-left:10px}.cp-fancy .picker-indicator{width:24px;height:24px;background-image:url(http://cdn1.iconfinder.com/data/icons/fugue/bonus/icons-24/target.png)}.cp-fancy .slide-indicator{width:30px;height:31px;left:30px;background-image:url(http://cdn1.iconfinder.com/data/icons/bluecoral/Left.png)}.cp-normal{padding:10px;background-color:white;float:left;border:4px solid #d6d6d6;box-shadow:inset 0 0 10px white}.cp-normal .picker{width:200px;height:200px}.cp-normal .slide{width:30px;height:200px}.cp-normal .slide-wrapper{margin-left:10px}.cp-normal .picker-indicator{width:5px;height:5px;border:1px solid gray;opacity:.5;-ms-filter:"alpha(opacity=50)";filter:alpha(opacity=50);filter:alpha(opacity=50);background-color:white;pointer-events:none}.cp-normal .slide-indicator{width:100%;height:10px;left:-4px;opacity:.6;-ms-filter:"alpha(opacity=60)";filter:alpha(opacity=60);filter:alpha(opacity=60);border:4px solid gray;background-color:white;pointer-events:none}'); getTag('head')[0].appendChild(style); } /* * Short functions to replace the document.createElement document.getElementById and document.getElementsByTagName */ function createElement(type, attrArray, evtListener, html) { var node = document.createElement(type); for (var attr in attrArray) if (attrArray.hasOwnProperty(attr)) { node.setAttribute(attr, attrArray[attr]); } if (evtListener) { var a = evtListener.split(' '); node.addEventListener(a[0], eval(a[1]), eval(a[2])); } if (html) node.innerHTML = html; return node; } function getId(id, parent) { if (!parent) return document.getElementById(id); return parent.getElementById(id); } function getTag(name, parent) { if (!parent) return document.getElementsByTagName(name); return parent.getElementsByTagName(name); } /* * Drag and drop support adapted from http://www.hunlock.com/blogs/Javascript_Drag_and_Drop */ var savedTarget = null; // The target layer (effectively vidPane) var orgCursor = null; // The original mouse style so we can restore it var dragOK = false; // True if we're allowed to move the element under mouse var dragXoffset = 0; // How much we've moved the element on the horozontal var dragYoffset = 0; // How much we've moved the element on the verticle var didDrag = false; // Set to true when we do a drag function moveHandler(e) { if (e == null) return; // { e = window.event } if (e.button <= 1 && dragOK) { savedTarget.style.left = e.clientX - dragXoffset + 'px'; savedTarget.style.top = e.clientY - dragYoffset + 'px'; return false; } } function dragCleanup(e) { document.removeEventListener('mousemove', moveHandler, false); document.removeEventListener('mouseup', dragCleanup, false); savedTarget.style.cursor = orgCursor; dragOK = false; // Its been dragged now didDrag = true; } function dragHandler(e) { var htype = '-moz-grabbing'; if (e == null) return; // { e = window.event;} // htype='move';} var target = e.target; // != null ? e.target : e.srcElement; orgCursor = target.style.cursor; if (target.nodeName != 'DIV' && target.nodeName != 'P') return; if (target = clickedInsideID(target, 'divDic')) { savedTarget = target; target.style.cursor = htype; dragOK = true; dragXoffset = e.clientX - target.offsetLeft; dragYoffset = e.clientY - target.offsetTop; // Set the left before removing the right target.style.left = e.clientX - dragXoffset + 'px'; target.style.right = null; document.addEventListener('mousemove', moveHandler, false); document.addEventListener('mouseup', dragCleanup, false); return false; } } function clickedInsideID(target, id) { if (target.getAttribute('id') == id) return getId(id); if (target.parentNode) { while (target = target.parentNode) { try { if (target.getAttribute('id') == id) return getId(id); } catch (e) {} } } return null; } // End drag code /* * Images */ function images() { imgLookup = createElement('img', { border : 0 }); imgLookup.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABs0lEQVQ4jY2SP4viQBiHX0UQWz/AXb+VX8Iu/YqFhdhcd5BKEOTKC9jJFYrFgo3FIjYiCRauhTCQDMp4bJFklzCuLJLOWNj8rpDMJt7u7Q08xQzze953/hAR0el4QJLw8KR4fXkE/Wtch01zjP6gmxLsd9uPJafjAf1BF82WjmZLR61eRa1eVfNmS4cMxP8JksGk6FPB6XjAii1Qq1fBBYMMBL79+InvDIrbB0CzIpSmQHF0RnF0vkiTFxZX7A+6MOzwU0FxdEZKYJpj1fp1eO5KzF0JzYreF/iekzr77QMUhh2q1zDsUIULPQl6fXkEFww53cWKLWCaY3DBVMuaFWHuSsT7fM/5W5DTXYUMBGQgUJoCpelFst9tcc84DDuE7znQrAiFnrwIkuGY/W6rBIYdQgYC7RmHZkXwPQf3jL8JiCglISLKVCaqzfhZfc9RcMFwc/eMfGd9EWQbS+R0F9nGEtnGEpnKBJnKJFWxPNygPNygPePggqE942nBdTjG9xyUhxvVcqEnsWILrNjiTfCRJN9ZI99Zp8LxWsy73ztTmYCI6ObuGV/7Tym+/PqtICL6A7F/dNYyWabFAAAAAElFTkSuQmCC'; } if (typeof GM_deleteValue == 'undefined') { GM_addStyle = function (css) { var style = document.createElement('style'); style.textContent = css; document.getElementsByTagName('head')[0].appendChild(style); } GM_deleteValue = function (name) { localStorage.removeItem(name); } GM_getValue = function (name, defaultValue) { var value = localStorage.getItem(name); if (!value) return defaultValue; var type = value[0]; value = value.substring(1); switch (type) { case 'b': return value == 'true'; case 'n': return Number(value); default: return value; } } GM_log = function (message) { console.log(message); } GM_openInTab = function (url) { return window.open(url, "_blank"); } GM_registerMenuCommand = function (name, funk) { //todo } GM_setValue = function (name, value) { value = (typeof value)[0] + value; localStorage.setItem(name, value); } } /* * Cross browser support for GM functions * http://userscripts.org/topics/41177 */ function initCrossBrowserSupportForGmFunctions() { if (typeof GM_deleteValue == 'undefined') { GM_addStyle = function (css) { var style = document.createElement('style'); style.textContent = css; document.getElementsByTagName('head')[0].appendChild(style); } GM_deleteValue = function (name) { localStorage.removeItem(name); } GM_getValue = function (name, defaultValue) { var value = localStorage.getItem(name); if (!value) return defaultValue; var type = value[0]; value = value.substring(1); switch (type) { case 'b': return value == 'true'; case 'n': return Number(value); default: return value; } } GM_log = function (message) { console.log(message); } GM_openInTab = function (url) { return window.open(url, "_blank"); } GM_registerMenuCommand = function (name, funk) { //todo } GM_setValue = function (name, value) { value = (typeof value)[0] + value; localStorage.setItem(name, value); } } }