// ==UserScript== // @name Duolingo input language switcher // @namespace https://www.duolingo.com/IVrL9 // @author T1mL3arn // @match https://www.duolingo.com/* // @version 2.0.2 // @description This script allows you to type letters appropriate for current task without changing keyboard's layout // @run-at document-start // @grant none // @icon https://www.androidpolice.com/wp-content/uploads/2014/03/nexusae0_Duolingo-Thumb.png // @license GPLv3 // @homepageURL https://github.com/T1mL3arn/Duolingo-input-language-switcher // @supportURL https://greasyfork.org/en/scripts/37693-duolingo-input-language-switcher/feedback // @downloadURL none // ==/UserScript== // Generated by Haxe 4.0.5 (function ($global) { "use strict"; var Main = function() { this.document = window.document; this.console = window.console; this.initLanguages(); if(this.document.readyState == "interactive" || this.document.readyState == "complete") { this.onready(); } else { this.document.addEventListener("DOMContentLoaded",$bind(this,this.onready)); } }; Main.main = function() { new Main(); }; Main.prototype = { initLanguages: function() { this.keyCodes = ["Backquote","Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","Digit0","Minus","Equal","Backslash","KeyQ","KeyW","KeyE","KeyR","KeyT","KeyY","KeyU","KeyI","KeyO","KeyP","BracketLeft","BracketRight","KeyA","KeyS","KeyD","KeyF","KeyG","KeyH","KeyJ","KeyK","KeyL","Semicolon","Quote","KeyZ","KeyX","KeyC","KeyV","KeyB","KeyN","KeyM","Comma","Period","Slash"]; this.languages = { }; this.languages.ru = "ё1234567890-=\\йцукенгшщзхъфывапролджэячсмитьбю.Ё!\"№;%:?*()_+/ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ,"; this.languages.en = "`1234567890-=\\qwertyuiop[]asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+|QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>?"; var len = this.languages.ru.length; var _g = 0; var _g1 = Reflect.fields(this.languages); while(_g < _g1.length) { var f = _g1[_g]; ++_g; var act = this.languages[f].length; if(act != len) { this.console.error("LangString test failed: expected len " + len + "; actual len " + act + "; lang name " + f); this.console.error(this.languages[f]); return; } if(act != this.keyCodes.length * 2) { this.console.error("KeyCodes and LangString test failed: expected lang string len " + this.keyCodes.length * 2 + "; actual len " + act + "; lang name " + f); return; } } } ,onready: function(e) { this.document.removeEventListener("DOMContentLoaded",$bind(this,this.onready)); this.console.log("Duolingo input switcher is ready"); window.document.body.addEventListener("keydown",$bind(this,this.onKeyDown)); } ,onKeyDown: function(e) { if(e.ctrlKey) { return; } var pressedKeyCodeIndex = this.keyCodes.indexOf(e.code); if(pressedKeyCodeIndex == -1) { return; } var inputElt = e.target; switch(inputElt.dataset.test) { case "challenge-listen-input":case "challenge-listentap-input":case "challenge-name-input": this.setLanguagePair("en","ru"); break; case "challenge-text-input": this.setLanguagePair("en","ru"); break; case "challenge-translate-input": this.nativeLanguage = "ru"; this.foreignLanguage = "en"; var lang = inputElt.getAttribute("lang"); if(lang == this.nativeLanguage) { this.setLanguagePair("ru","en"); } else if(lang == this.foreignLanguage) { this.setLanguagePair("en","ru"); } break; default: return; } e.preventDefault(); this.replaceLetter(this.getLanguageLetter(this.targetLanguage,pressedKeyCodeIndex,e.shiftKey),inputElt); this.callReactOnChange(inputElt); } ,setLanguagePair: function(target,source) { this.targetLanguage = target; this.sourceLanguage = source; } ,getLanguageLetter: function(language,letterIndex,isUppercase) { var letters = this.languages[language]; if(isUppercase) { return letters.charAt(letterIndex + this.keyCodes.length); } else { return letters.charAt(letterIndex); } } ,replaceLetter: function(letter,inputElt) { var start = inputElt.selectionStart; var end = inputElt.selectionEnd; var phrase = inputElt.value; phrase = phrase.substring(0,start) + letter + phrase.substring(end); inputElt.value = phrase; inputElt.setSelectionRange(start + 1,start + 1); } ,callReactOnChange: function(elt) { var _g = 0; var _g1 = Reflect.fields(elt); while(_g < _g1.length) { var fieldName = _g1[_g]; ++_g; if(fieldName.indexOf("__reactEventHandlers") != -1) { Reflect.field(elt,fieldName).onChange({ target : elt}); } } } }; var Reflect = function() { }; Reflect.field = function(o,field) { try { return o[field]; } catch( e ) { return null; } }; Reflect.fields = function(o) { var a = []; if(o != null) { var hasOwnProperty = Object.prototype.hasOwnProperty; for( var f in o ) { if(f != "__id__" && f != "hx__closures__" && hasOwnProperty.call(o,f)) { a.push(f); } } } return a; }; var $_; function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; } $global.$haxeUID |= 0; Main.main(); })(typeof window != "undefined" ? window : typeof global != "undefined" ? global : typeof self != "undefined" ? self : this);