diff --git a/apps/keyboard/js/imes/jskanji/jskanji.js b/apps/keyboard/js/imes/jskanji/jskanji.js index 86a3a3dfe339..6eae4aab7ee9 100644 --- a/apps/keyboard/js/imes/jskanji/jskanji.js +++ b/apps/keyboard/js/imes/jskanji/jskanji.js @@ -369,7 +369,7 @@ } }; - this.activate = function ime_activate(language, suggestions, state) { + this.activate = function ime_activate(language, state, options) { var inputType = state.type; debug('Activate. Input type: ' + inputType); var layout = IMELayouts.JP; diff --git a/apps/keyboard/js/imes/jspinyin/jspinyin.js b/apps/keyboard/js/imes/jspinyin/jspinyin.js index 981867de6b0d..cc5974dbf7e4 100644 --- a/apps/keyboard/js/imes/jspinyin/jspinyin.js +++ b/apps/keyboard/js/imes/jspinyin/jspinyin.js @@ -291,7 +291,7 @@ IMEngineBase.prototype = { /** * Notifies when the IM is shown */ - activate: function engineBase_activate(language, suggestions, state) { + activate: function engineBase_activate(language, state, options) { } }; @@ -700,9 +700,9 @@ IMEngine.prototype = { /** * Override */ - activate: function engine_activate(language, suggestions, state) { + activate: function engine_activate(language, state, options) { var inputType = state.type; - IMEngineBase.prototype.activate.call(this, language, suggestions, state); + IMEngineBase.prototype.activate.call(this, language, state, options); debug('Activate. Input type: ' + inputType); PinyinDecoderService.flushCache(null); var keyboard = this._inputTraditionalChinese ? diff --git a/apps/keyboard/js/imes/latin/latin.js b/apps/keyboard/js/imes/latin/latin.js index dbe13ee498e9..ef24269427fd 100644 --- a/apps/keyboard/js/imes/latin/latin.js +++ b/apps/keyboard/js/imes/latin/latin.js @@ -4,13 +4,15 @@ 'use strict'; /* - * This latin input method provides three forms of input assistance: + * This latin input method provides four forms of input assistance: * * 1) word suggestions * - * 2) auto capitalization + * 2) auto correction * - * 3) punctuation assistance by converting space space to period space + * 3) auto capitalization + * + * 4) punctuation assistance by converting space space to period space * and by transposing space followed by punctuation. * * These input modifications are controlled by the type and inputmode @@ -44,14 +46,20 @@ var inputMode; // The inputmode we're using: see getInputMode() var capitalizing; // Are we auto-capitalizing for this activation? var suggesting; // Are we offering suggestions for this activation? + var correcting; // Are we auto-correcting user input? var punctuating; // Are we offering punctuation assistance? var inputText; // The input text var cursor; // The insertion position var selection; // The end of the selection, if there is one, or 0 - var lastKeyWasSpace; // Was the last key a space? + var lastSpaceTimestamp; // If the last key was a space, this is the timestamp var layoutParams; // Parameters passed to setLayoutParams var idleTimer; // Used by deactivate var suggestionsTimer; // Used by updateSuggestions; + var autoCorrection; // Correction to make if next input is space + var revertTo; // Revert to this on backspace after autocorrect + var revertFrom; // Revert away from this on backspace + var justAutoCorrected; // Was last change an auto correction? + var correctionDisabled; // Temporarily diabled after reverting? // Terminate the worker when the keyboard is inactive for this long. const workerTimeout = 30000; // 30 seconds of idle time @@ -69,13 +77,23 @@ const QUESTION = 63; const EXCLAMATION = 33; const COMMA = 44; + const COLON = 58; + const SEMICOLON = 59; const WS = /^\s+$/; // all whitespace characters const UC = /^[A-ZÀ-ÖØ-Þ]+$/; // all uppercase latin characters - const LETTER = /^[a-zA-ZÀ-ÖØ-öø-ÿ]+$/; // all latin letters const DOUBLE_SPACE_TIME = 700; // ms between spaces to convert to ". " + // Don't offer to autocorrect unless we're reasonably certain that the + // user wants this correction. The first suggested word must be at least + // this much more highly weighted than the second suggested word. + // XXX: this seems too low, but we get a root word and the root with suffix + // that have similar weights, and should probably auto correct on one. + // Maybe the prediction engine should weight on the length of the word so + // that we can raise this to 1.25 or something. + const AUTO_CORRECT_THRESHOLD = 1.05; + // keyboard.js calls this to pass us the interface object we need // to communicate with it function init(interfaceObject) { @@ -115,7 +133,7 @@ // This gets called whenever the keyboard pops up to tell us everything // we need to provide useful typing assistance. - function activate(lang, suggestionsEnabled, state) { + function activate(lang, state, options) { language = lang; inputMode = getInputMode(state.type, state.inputmode); inputText = state.value; @@ -128,14 +146,15 @@ // Figure out what kind of input assistance we're providing for this // activation. capitalizing = punctuating = (inputMode === 'latin-prose'); - suggesting = (suggestionsEnabled && inputMode !== 'verbatim'); + suggesting = (options.suggest && inputMode !== 'verbatim'); + correcting = (options.correct && inputMode !== 'verbatim'); // If we are going to offer suggestions, set up the worker thread. - if (suggesting) + if (suggesting || correcting) setupSuggestionsWorker(); // Reset the double space flag - lastKeyWasSpace = 0; + lastSpaceTimestamp = 0; // Start off with the correct capitalization and suggestions updateCapitalization(); @@ -172,13 +191,13 @@ worker.onmessage = function(e) { switch (e.data.cmd) { case 'log': - console.log.apply(console, e.data.args); + console.log.apply(console, e.data.message); break; case 'unknownLanguage': - console.error('No dictionary for language', e.data.args[0]); + console.error('No dictionary for language', e.data.language); break; case 'predictions': - keyboard.sendCandidates(e.data.args); + handleSuggestions(e.data.input, e.data.suggestions); break; } }; @@ -189,17 +208,98 @@ worker.postMessage({ cmd: 'setLanguage', args: [language]}); } + /* + * The keyboard calls this method to tell us about user input. + * + * What we do with the input depends on various things: + * + * - whether we are suggesting, correcting, punctuating and/or capitalizing: + * these are controlled by settings, input mode and input type + * + * - whether there is a selected region in the input field + * + * - whether there is an auto-correction ready (when input is space + * or punctuation). + * + * - whether the last action was an autocorrection (when input is backspace) + * + * - the cursor position (affects suggestions, capitalization, etc.) + * + * If there is a selection just handle simple insertions and deletions + * with no extra behavior. (I think) + * + * If input is a space or punctuation: + * + * If there is a autocorrection ready, and we are correcting and + * the cursor is at the end of a word, make the correction + * + * If the previous character is a space, fix the punctuation. Note that + * this only works for a subset of the punctuation characters. + * + * Otherwise just insert it. + * + * If we're correcting and corrections are temporarily diabled, turn them + * back on. + * + * If input is a backspace: + * + * If we just did an auto-correction, revert it and turn off corrections + * until the next space or punctuation character. + * + * If we just inserted a suggested word that the user selected, revert + * the insertion, but don't disable autocorrect. + * + * Should we undo punctuation corrections this way, too? + * + * Otherwise, just delete the character before the cursor + * + * For any other input character, just insert it. + * + * Reset the backspace reversion state + * + * Update the capitalization state, if we're capitalizing + */ function click(keycode, repeat) { - if (punctuating && handlePunctuation(keycode)) { - // nothing to do here: handlePunctuation did it for us + // If the key is anything other than a backspace, forget about any + // previous changes that we would otherwise revert. + if (keycode !== BACKSPACE) { + revertTo = revertFrom = ''; + justAutoCorrected = false; + } + + if (selection) { + // If there is selected text, don't do anything fancy here. + handleKey(keycode); } else { - // Update our internal model of the input text and cursor position - updateState(keycode); - // Generate the key event - keyboard.sendKey(keycode); + switch (keycode) { + case SPACE: + case RETURN: + case PERIOD: + case QUESTION: + case EXCLAMATION: + case COMMA: + case COLON: + case SEMICOLON: + // These keys may trigger word or punctuation corrections + handleCorrections(keycode); + correctionDisabled = false; + break; + + case BACKSPACE: + handleBackspace(); + break; + + default: + handleKey(keycode); + } } + // If there was a potential auto correction, we either used it in + // handleCorrections() above or it is now out of date, so clear it + // so it doesn't get used later + autoCorrection = null; + // And update the keyboard capitalization state, if necessary updateCapitalization(); @@ -210,23 +310,251 @@ if (keycode === SPACE || keycode === RETURN) { keyboard.setLayoutPage(LAYOUT_PAGE_DEFAULT); } + + lastSpaceTimestamp = (keycode === SPACE) ? Date.now() : 0; } - // If the user selections one of the suggestions offered by this input method + // Handle any key (including backspace) and do the right thing even if + // there is a selection in the text field. This method does not perform + // auto-correction or auto-punctuation. + function handleKey(keycode) { + // First, update our internal state + if (keycode === BACKSPACE) { + if (selection) { + // backspace while a region is selected erases the selection + // and leaves the cursor at the selection start + inputText = inputText.substring(0, cursor) + + inputText.substring(selection); + selection = 0; + } else if (cursor > 0) { + cursor--; + inputText = inputText.substring(0, cursor) + + inputText.substring(cursor + 1); + } + } else { + if (selection) { + inputText = + inputText.substring(0, cursor) + + String.fromCharCode(keycode) + + inputText.substring(selection); + selection = 0; + } else { + inputText = + inputText.substring(0, cursor) + + String.fromCharCode(keycode) + + inputText.substring(cursor); + } + cursor++; + } + + // Generate the key event + keyboard.sendKey(keycode); + } + + // If we just did auto correction or auto punctuation, then backspace + // should undo it. Otherwise it is just an ordinary backspace. + function handleBackspace() { + // If we made a correction and haven't changed it at all yet, + // then revert it. + var len = revertFrom ? revertFrom.length : 0; + if (len && cursor > len && + inputText.substring(cursor - len, cursor) === revertFrom) { + + // Revert the content of the text field + for (var i = 0; i < len; i++) + keyboard.sendKey(BACKSPACE); + keyboard.sendString(revertTo); + + // Revert our internal state + inputText = + inputText.substring(0, cursor - len) + + revertTo + + inputText.substring(cursor); + cursor -= len - revertTo.length; + + // If the change we just reverted was an auto-correction then + // temporarily disable auto correction until the next space + if (justAutoCorrected) { + correctionDisabled = true; + } + + revertFrom = revertTo = ''; + justAutoCorrected = false; + } + else { + handleKey(BACKSPACE); + } + } + + // This function is called when the user types space, return or a punctuation + // character. It performs auto correction or auto punctuation or just + // inserts the character. + function handleCorrections(keycode) { + if (correcting && autoCorrection && !correctionDisabled && atWordEnd()) { + autoCorrect(keycode); + } + else if (punctuating && cursor >= 2 && + isWhiteSpace(inputText[cursor - 1]) && + !isWhiteSpace(inputText[cursor - 2])) + { + autoPunctuate(keycode); + } + else { + handleKey(keycode); + } + } + + // Perform an autocorrection. Assumes that all pre-conditions for + // auto-correction have been met. + function autoCorrect(keycode) { + // Get the word before the cursor + var currentWord = wordBeforeCursor(); + var currentWordLength = currentWord.length; + + // Figure out the auto correction text + var newWord = autoCorrection; // Atart with suggested word + newWord += String.fromCharCode(keycode); // and add the user's input. + if (keycode !== SPACE && keycode !== RETURN) // If not whitespace + newWord += ' '; // add a space. + + // Backspace over the current word in the text field + for (var i = 0; i < currentWordLength; i++) + keyboard.sendKey(BACKSPACE); + + // And send the correction to the textfield + keyboard.sendString(newWord); + + // Now update our internal state to match. + inputText = + inputText.substring(0, cursor - currentWordLength) + + newWord + + inputText.substring(cursor); + + // Update the cursor position, too. + cursor = cursor - currentWordLength + newWord.length; + + // Remember the change we just made so we can revert it if the + // user types backspace + revertTo = currentWord; + revertFrom = newWord; + justAutoCorrected = true; + } + + // Auto punctuate, converting space punctuation to punctuation space + // or converting space space to period space if the two spaces were + // close enough together. Assumes that pre-conditions for auto punctuation + // have been met. + function autoPunctuate(keycode) { + switch (keycode) { + case SPACE: + if (Date.now() - lastSpaceTimestamp < DOUBLE_SPACE_TIME) + fixPunctuation(PERIOD); + else + handleKey(keycode); + break; + + case PERIOD: + case QUESTION: + case EXCLAMATION: + case COMMA: + fixPunctuation(keycode); + break; + + default: + // colon and semicolon don't auto-punctuate because they're + // used after spaces for smileys. + handleKey(keycode); + break; + } + + // In both the space space and the space period case we call this function + function fixPunctuation(keycode) { + keyboard.sendKey(BACKSPACE); + keyboard.sendKey(keycode); + keyboard.sendKey(SPACE); + + var newtext = String.fromCharCode(keycode) + ' '; + + inputText = inputText.substring(0, cursor - 1) + + newtext + + inputText.substring(cursor); + cursor++; + + // Remember this change so we can revert it on backspace + revertTo = ' '; + revertFrom = newtext; + justAutoCorrected = false; + } + } + + + // When the worker thread sends us a batch of suggestions, deal + // with them here. + function handleSuggestions(input, suggestions) { + if (suggestions.length === 0) { // If no suggestions + keyboard.sendCandidates(suggestions); // Clear any displayed suggestions + return; // We're done + } + + // Check that the word before the cursor has not changed since + // we requested these suggestions. If the user has typed faster + // than we could offer suggestions, ignore these. + if (wordBeforeCursor() !== input) { + keyboard.sendCandidates([]); // Clear any displayed suggestions + return; + } + + // Figure out if the first suggestion is good enough to offer as + // an autocorrection. We define "good enough" as significantly better + // than the second best suggestion. And significance is defined by + // a tuneable constant. + var significant = + suggestions.length === 1 || + suggestions[0][1] / suggestions[1][1] > AUTO_CORRECT_THRESHOLD; + + // Loop through the suggestions discarding the weights, and checking + // to see if the user's current input is one of the words. We don't + // want to autocorrect a valid word. Also, if the input begins with + // a capital letter, capitalize the suggestions + var lcinput = input.toLowerCase(); + var inputStartsWithCapital = isUpperCase(input[0]); + var inputIsWord = false; + for (var i = 0; i < suggestions.length; i++) { + suggestions[i] = suggestions[i][0]; + if (lcinput === suggestions[i].toLowerCase()) + inputIsWord = true; + if (inputStartsWithCapital) + suggestions[i] = + suggestions[i][0].toUpperCase() + suggestions[i].substring(1); + } + + // If we're going to use the first suggestion as an auto-correction + // then we have to tell the renderer to highlight it and we have to + // ensure that the raw input is also listed as a suggestion. If the + // input is the same as the first suggestion, don't auto-correct it. + if (correcting && !correctionDisabled && significant && !inputIsWord) { + // Remember the word to use if the next character is a space. + autoCorrection = suggestions[0]; + // Make sure the user also has their actual input as a choice + // XXX: should this be highlighted in some special way? + // XXX: or should we just have a x icon to dismiss the autocorrection? + suggestions.push(input); + // Mark the auto-correction so the renderer can highlight it + suggestions[0] = '*' + suggestions[0]; + } + + keyboard.sendCandidates(suggestions); + } + + // If the user selects one of the suggestions offered by this input method // the keyboard calls this method to tell us it has been selected. // We have to backspace over the current word, insert this new word, and // update our internal state to match. function select(word) { - // Find the position of the first letter of the current word - for (var firstletter = cursor - 1; firstletter >= 0; firstletter--) { - if (!LETTER.test(inputText[firstletter])) { - break; - } - } - firstletter++; + var oldWord = wordBeforeCursor(); // Send backspaces - for (var i = 0, n = cursor - firstletter; i < n; i++) + for (var i = 0, n = oldWord.length; i < n; i++) keyboard.sendKey(BACKSPACE); // Send the word @@ -237,12 +565,12 @@ // Update internal state inputText = - inputText.substring(0, firstletter) + + inputText.substring(0, cursor - oldWord.length) + word + ' ' + inputText.substring(cursor); - cursor = firstletter + word.length + 1; + cursor += word.length - oldWord.length + 1; // Clear the suggestions keyboard.sendCandidates([]); @@ -260,7 +588,7 @@ function updateSuggestions(repeat) { // If the user hasn't enabled suggestions, or if they're not appropriate // for this input type, or are turned off by the input mode, do nothing - if (!suggesting) + if (!suggesting && ! correcting) return; // If we deferred suggestions because of a key repeat, clear that timer @@ -277,113 +605,12 @@ // If we're not at the end of a word, we want to clear any suggestions // that might already be there if (!atWordEnd()) { - keyboard.sendCandidates([]); + if (suggesting) + keyboard.sendCandidates([]); return; } - // Otherwise, find the word we're at the end of and ask for completions - for (var firstletter = cursor - 1; firstletter >= 0; firstletter--) { - if (!LETTER.test(inputText[firstletter])) { - break; - } - } - firstletter++; - - // firstletter is now the position of the start of the word and cursor is - // the end of the word - var word = inputText.substring(firstletter, cursor); - - worker.postMessage({cmd: 'predict', args: [word]}); - } - - // This function handles two special punctuation cases. If the user - // types two spaces sufficiently close together we convert them to - // period space. And if the user types nonspace space punctuation, - // we transpose the space and the punctuation. (This supports adding - // a punctuation mark after a word suggestion since suggestions - // automatically insert a space.) This method returns true if it - // handles the keycode and returns false otherwise. - function handlePunctuation(keycode) { - if (!punctuating || selection) - return false; - - // In both the space space and the space period case we call this function - function fixPunctuation(keycode) { - keyboard.sendKey(BACKSPACE); - keyboard.sendKey(keycode); - keyboard.sendKey(SPACE); - - inputText = inputText.substring(0, cursor - 1) + - String.fromCharCode(keycode) + - ' ' + - inputText.substring(cursor); - cursor++; - } - - switch (keycode) { - case SPACE: - var now = Date.now(); - - if (lastKeyWasSpace && - (now - lastKeyWasSpace) < DOUBLE_SPACE_TIME && - cursor >= 2 && - !isWhiteSpace(inputText[cursor - 2])) - { - fixPunctuation(PERIOD); - lastKeyWasSpace = 0; - return true; - } - - lastKeyWasSpace = now; - return false; - - case PERIOD: - case QUESTION: - case EXCLAMATION: - case COMMA: - lastKeyWasSpace = 0; - if (cursor >= 2 && - isWhiteSpace(inputText[cursor - 1]) && - !isWhiteSpace(inputText[cursor - 2])) { - fixPunctuation(keycode); - return true; - } - return false; - - default: - lastKeyWasSpace = 0; - return false; - } - } - - function updateState(keycode) { - if (keycode === BACKSPACE) { - if (selection) { - // backspace while a region is selected erases the selection - // and leaves the cursor at the selection start - inputText = inputText.substring(0, cursor) + - inputText.substring(selection); - selection = 0; - } else if (cursor > 0) { - cursor--; - inputText = inputText.substring(0, cursor) + - inputText.substring(cursor + 1); - } - } else { - if (selection) { - inputText = - inputText.substring(0, cursor) + - String.fromCharCode(keycode) + - inputText.substring(selection); - selection = 0; - } else { - inputText = - inputText.substring(0, cursor) + - String.fromCharCode(keycode) + - inputText.substring(cursor); - } - cursor++; - } + worker.postMessage({cmd: 'predict', args: [wordBeforeCursor()]}); } function updateCapitalization() { @@ -450,8 +677,23 @@ return false; // We're at the end of a word if the cursor is not at the start and - // the character before the cursor is a letter - return cursor > 0 && LETTER.test(inputText[cursor - 1]); + // the character before the cursor is not whitespace + return cursor > 0 && !WS.test(inputText[cursor - 1]); + } + + // Get the word before the cursor + function wordBeforeCursor() { + // Otherwise, find the word we're at the end of and ask for completions + for (var firstletter = cursor - 1; firstletter >= 0; firstletter--) { + if (WS.test(inputText[firstletter])) { + break; + } + } + firstletter++; + + // firstletter is now the position of the start of the word and cursor is + // the end of the word + return inputText.substring(firstletter, cursor); } function atSentenceStart() { diff --git a/apps/keyboard/js/imes/latin/predictions.js b/apps/keyboard/js/imes/latin/predictions.js index c4adf30ec0af..347db8d1907e 100644 --- a/apps/keyboard/js/imes/latin/predictions.js +++ b/apps/keyboard/js/imes/latin/predictions.js @@ -277,7 +277,7 @@ var Predictions = function() { } // Record the suggestion and move to the next best candidate if (!(prefix in _suggestions_index)) { - _suggestions.push(prefix); + _suggestions.push([prefix, cand.freq]); _suggestions_index[prefix] = true; } } diff --git a/apps/keyboard/js/imes/latin/worker.js b/apps/keyboard/js/imes/latin/worker.js index ae0fc93b190f..dc8db21ff709 100644 --- a/apps/keyboard/js/imes/latin/worker.js +++ b/apps/keyboard/js/imes/latin/worker.js @@ -38,7 +38,7 @@ self.onmessage = function(e) { // Send console messages back to the main thread with this method function log(msg) { - postMessage({cmd: 'log', args: [msg]}); + postMessage({cmd: 'log', message: msg}); } // Track our current language so we don't load dictionaries more often @@ -66,7 +66,7 @@ var Commands = { // if (!xhr.response || xhr.response.byteLength === 0) { log('error loading dictionary'); - postMessage({ cmd: 'unknownLanguage', args: [language] }); + postMessage({ cmd: 'unknownLanguage', language: language }); } else { Predictions.setDictionary(xhr.response); @@ -81,11 +81,11 @@ var Commands = { predict: function predict(prefix) { try { var words = Predictions.predict(prefix); - postMessage({ cmd: 'predictions', args: words }); + postMessage({ cmd: 'predictions', input: prefix, suggestions: words }); } catch (e) { log('Exception in predictions.js: ' + JSON.stringify(e)); - postMessage({cmd: 'predictions', args: [] }); + postMessage({cmd: 'predictions', input: prefix, suggestions: [] }); } } }; diff --git a/apps/keyboard/js/keyboard.js b/apps/keyboard/js/keyboard.js index 586e72bc5acc..9332b632432a 100644 --- a/apps/keyboard/js/keyboard.js +++ b/apps/keyboard/js/keyboard.js @@ -232,6 +232,7 @@ const specialCodes = [ // These values are initialized with user settings var userLanguage; var suggestionsEnabled; +var correctionsEnabled; var clickEnabled; var vibrationEnabled; var enabledKeyboardGroups; @@ -279,11 +280,12 @@ window.addEventListener('load', getKeyboardSettings); function getKeyboardSettings() { // Before we can initialize the keyboard we need to know the current - // value of all keyboard-related settings. These are two of the settings - // we want to query, with the default value we'll use + // value of all keyboard-related settings. These are the settings + // we want to query, with the default values we'll use if the query fails var settingsQuery = { 'language.current': 'en-US', 'keyboard.wordsuggestion': true, + 'keyboard.autocorrect': true, 'keyboard.vibration': false, 'keyboard.clicksound': false, 'ring.enabled': true @@ -299,6 +301,7 @@ function getKeyboardSettings() { // Copy settings values to the corresponding global variables. userLanguage = values['language.current']; suggestionsEnabled = values['keyboard.wordsuggestion']; + correctionsEnabled = values['keyboard.autocorrect']; vibrationEnabled = values['keyboard.vibration']; clickEnabled = values['keyboard.clicksound']; isSoundEnabled = values['ring.enabled']; @@ -337,6 +340,13 @@ function initKeyboard() { suggestionsEnabled = e.settingValue; }); + navigator.mozSettings.addObserver('keyboard.autocorrect', function(e) { + // The keyboard won't be displayed when this setting changes, so we + // don't need to tell the keyboard about the new value right away. + // We pass the value to the input method when the keyboard is displayed + correctionsEnabled = e.settingValue; + }); + navigator.mozSettings.addObserver('keyboard.vibration', function(e) { vibrationEnabled = e.settingValue; }); @@ -1388,7 +1398,10 @@ function showKeyboard(state) { resetKeyboard(); if (inputMethod.activate) { - inputMethod.activate(userLanguage, suggestionsEnabled, state); + inputMethod.activate(userLanguage, state, { + suggest: suggestionsEnabled, + correct: correctionsEnabled + }); } if (!inputMethod.displaysCandidates || diff --git a/apps/keyboard/js/render.js b/apps/keyboard/js/render.js index 496061a70dcd..0b9470dba132 100644 --- a/apps/keyboard/js/render.js +++ b/apps/keyboard/js/render.js @@ -261,6 +261,10 @@ const IMERender = (function() { var span = document.createElement('span'); span.dataset.selection = true; if (typeof candidate === 'string') { + if (candidate[0] === '*') { // it is an autocorrection candidate + candidate = candidate.substring(1); + span.classList.add('autocorrect'); + } span.dataset.data = span.textContent = candidate; } else { diff --git a/apps/keyboard/style/keyboard.css b/apps/keyboard/style/keyboard.css index a751d1180652..8108db24a439 100644 --- a/apps/keyboard/style/keyboard.css +++ b/apps/keyboard/style/keyboard.css @@ -583,7 +583,7 @@ bubble above the key when you tap and hold. */ border-right: 0.1rem solid #808098; font-size: 3.2rem; line-height: 6rem; - min-width: 6rem; + min-width: 5rem; display: inline-block; height: 6.4rem; padding: 0 1rem; @@ -602,6 +602,11 @@ bubble above the key when you tap and hold. */ margin: 0.3rem; } +#keyboard-candidate-panel.latin span.autocorrect { + background: #0ac; /* XXX: gradient like above? */ +} + + #keyboard-candidate-panel-toggle-button { border-left: 0.1rem solid #e8e8ff; border-right: 0.1rem solid #808098; diff --git a/apps/keyboard/test/unit/latin_test.js b/apps/keyboard/test/unit/latin_test.js index 58e21ff3f745..1dcd33d10ef7 100644 --- a/apps/keyboard/test/unit/latin_test.js +++ b/apps/keyboard/test/unit/latin_test.js @@ -270,13 +270,13 @@ suite('latin input method capitalization and punctuation', function() { // reset the output state reset(); // activate the IM - im.activate('en', false, { + im.activate('en', { type: type, inputmode: mode, value: state.value, selectionStart: state.cursor, selectionEnd: state.cursor - }); + },{suggest: false, correct: false}); // Send the input one character at a time, converting // the input to uppercase if the IM has set uppercase @@ -357,13 +357,13 @@ suite("latin input method word suggestions", function() { suggestionsExpected = expected; reset(); - im.activate(language, enabled, { + im.activate(language, { type: type, inputmode: mode, value: state.value, selectionStart: state.cursor, selectionEnd: state.se || state.cursor - }); + }, { suggest: enabled, correct: false}); // Send some input and see if we get completions im.click('t'.charCodeAt(0)); diff --git a/apps/settings/index.html b/apps/settings/index.html index 08916314b4c8..0dee75a3d7e2 100644 --- a/apps/settings/index.html +++ b/apps/settings/index.html @@ -1440,6 +1440,13 @@

Keyboard

Click sound +
  • + + Auto correction +