// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // TODO(kochi): Generalize the notification as a component and put it // in js/cr/ui/notification.js . cr.define('options', function() { /** @const */ var OptionsPage = options.OptionsPage; /** @const */ var LanguageList = options.LanguageList; // Some input methods like Chinese Pinyin have config pages. // This is the map of the input method names to their config page names. /** @const */ var INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME = { 'mozc': 'languageMozc', 'mozc-chewing': 'languageChewing', 'mozc-dv': 'languageMozc', 'mozc-hangul': 'languageHangul', 'mozc-jp': 'languageMozc', 'pinyin': 'languagePinyin', 'pinyin-dv': 'languagePinyin', }; ///////////////////////////////////////////////////////////////////////////// // LanguageOptions class: /** * Encapsulated handling of ChromeOS language options page. * @constructor */ function LanguageOptions(model) { OptionsPage.call(this, 'languages', loadTimeData.getString('languagePageTabTitle'), 'languagePage'); } cr.addSingletonGetter(LanguageOptions); // Inherit LanguageOptions from OptionsPage. LanguageOptions.prototype = { __proto__: OptionsPage.prototype, /* For recording the prospective language (the next locale after relaunch). * @type {?string} * @private */ prospectiveUiLanguageCode_: null, /** * Initializes LanguageOptions page. * Calls base class implementation to start preference initialization. */ initializePage: function() { OptionsPage.prototype.initializePage.call(this); var languageOptionsList = $('language-options-list'); LanguageList.decorate(languageOptionsList); languageOptionsList.addEventListener('change', this.handleLanguageOptionsListChange_.bind(this)); languageOptionsList.addEventListener('save', this.handleLanguageOptionsListSave_.bind(this)); this.prospectiveUiLanguageCode_ = loadTimeData.getString('prospectiveUiLanguageCode'); this.addEventListener('visibleChange', this.handleVisibleChange_.bind(this)); if (cr.isChromeOS) { $('chewing-confirm').onclick = $('hangul-confirm').onclick = $('mozc-confirm').onclick = $('pinyin-confirm').onclick = OptionsPage.closeOverlay.bind(OptionsPage); this.initializeInputMethodList_(); this.initializeLanguageCodeToInputMethodIdsMap_(); } Preferences.getInstance().addEventListener(this.spellCheckDictionaryPref, this.handleSpellCheckDictionaryPrefChange_.bind(this)); // Set up add button. $('language-options-add-button').onclick = function(e) { // Add the language without showing the overlay if it's specified in // the URL hash (ex. lang_add=ja). Used for automated testing. var match = document.location.hash.match(/\blang_add=([\w-]+)/); if (match) { var addLanguageCode = match[1]; $('language-options-list').addLanguage(addLanguageCode); } else { OptionsPage.navigateToPage('addLanguage'); } }; if (cr.isChromeOS) { // Listen to user clicks on the add language list. var addLanguageList = $('add-language-overlay-language-list'); addLanguageList.addEventListener( 'click', this.handleAddLanguageListClick_.bind(this)); $('language-options-extension-ime-button').addEventListener( 'click', this.handleExtensionImeButtonClick_.bind(this)); } else { // Listen to add language dialog ok button. var addLanguageOkButton = $('add-language-overlay-ok-button'); addLanguageOkButton.addEventListener( 'click', this.handleAddLanguageOkButtonClick_.bind(this)); // Show experimental features if enabled. if (loadTimeData.getBoolean('enableSpellingAutoCorrect')) $('auto-spell-correction-option').hidden = false; // Handle spell check enable/disable. if (!cr.isMac) { Preferences.getInstance().addEventListener( this.enableSpellCheckPref, this.updateEnableSpellCheck_.bind(this)); var spellCheckLanguageButton = getRequiredElement( 'language-options-spell-check-language-button'); spellCheckLanguageButton.addEventListener( 'click', this.handleSpellCheckLanguageButtonClick_.bind(this)); } } if (cr.isChromeOS) { $('language-options-ui-restart-button').onclick = function() { chrome.send('uiLanguageRestart'); }; } $('language-confirm').onclick = OptionsPage.closeOverlay.bind(OptionsPage); }, // The preference is a boolean that enables/disables spell checking. enableSpellCheckPref: 'browser.enable_spellchecking', // The preference is a CSV string that describes preload engines // (i.e. active input methods). preloadEnginesPref: 'settings.language.preload_engines', // The list of preload engines, like ['mozc', 'pinyin']. preloadEngines_: [], // The preference that lists the extension IMEs that are filtered out of // the language menu. filteredExtensionImesPref: 'settings.language.filtered_extension_imes', // The list of extension IMEs that are filtered out of the language menu. filteredExtensionImes_: [], // The preference is a string that describes the spell check // dictionary language, like "en-US". spellCheckDictionaryPref: 'spellcheck.dictionary', spellCheckDictionary_: '', // The map of language code to input method IDs, like: // {'ja': ['mozc', 'mozc-jp'], 'zh-CN': ['pinyin'], ...} languageCodeToInputMethodIdsMap_: {}, /** * Initializes the input method list. */ initializeInputMethodList_: function() { var inputMethodList = $('language-options-input-method-list'); var inputMethodListData = loadTimeData.getValue('inputMethodList'); var inputMethodPrototype = $('language-options-input-method-proto'); // Add all input methods, but make all of them invisible here. We'll // change the visibility in handleLanguageOptionsListChange_() based // on the selected language. Note that we only have less than 100 // input methods, so creating DOM nodes at once here should be ok. for (var i = 0; i < inputMethodListData.length; i++) { var inputMethod = inputMethodListData[i]; var element = inputMethodPrototype.cloneNode(true); element.id = ''; element.languageCodeSet = inputMethod.languageCodeSet; var input = element.querySelectorAll('input')[0]; input.inputMethodId = inputMethod.id; var span = element.querySelectorAll('span')[0]; span.textContent = inputMethod.displayName; // Listen to user clicks. input.addEventListener('click', this.handleCheckboxClick_.bind(this)); // Add the configure button if the config page is present for this // input method. if (inputMethod.id in INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME) { var pageName = INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME[inputMethod.id]; var button = this.createConfigureInputMethodButton_(inputMethod.id, pageName); element.appendChild(button); } inputMethodList.appendChild(element); } var extensionImeList = loadTimeData.getValue('extensionImeList'); for (var i = 0; i < extensionImeList.length; i++) { var inputMethod = extensionImeList[i]; var element = inputMethodPrototype.cloneNode(true); element.id = ''; element.languageCodeSet = {}; var input = element.querySelectorAll('input')[0]; input.inputMethodId = inputMethod.id; var span = element.querySelectorAll('span')[0]; span.textContent = inputMethod.displayName; input.addEventListener('click', this.handleExtensionCheckboxClick_.bind(this)); inputMethodList.appendChild(element); } // Listen to pref change once the input method list is initialized. Preferences.getInstance().addEventListener( this.preloadEnginesPref, this.handlePreloadEnginesPrefChange_.bind(this)); Preferences.getInstance().addEventListener( this.filteredExtensionImesPref, this.handleFilteredExtensionsPrefChange_.bind(this)); }, /** * Creates a configure button for the given input method ID. * @param {string} inputMethodId Input method ID (ex. "pinyin"). * @param {string} pageName Name of the config page (ex. "languagePinyin"). * @private */ createConfigureInputMethodButton_: function(inputMethodId, pageName) { var button = document.createElement('button'); button.textContent = loadTimeData.getString('configure'); button.onclick = function(e) { // Prevent the default action (i.e. changing the checked property // of the checkbox). The button click here should not be handled // as checkbox click. e.preventDefault(); chrome.send('inputMethodOptionsOpen', [inputMethodId]); OptionsPage.navigateToPage(pageName); }; return button; }, /** * Handles OptionsPage's visible property change event. * @param {Event} e Property change event. * @private */ handleVisibleChange_: function(e) { if (this.visible) { $('language-options-list').redraw(); chrome.send('languageOptionsOpen'); } }, /** * Handles languageOptionsList's change event. * @param {Event} e Change event. * @private */ handleLanguageOptionsListChange_: function(e) { var languageOptionsList = $('language-options-list'); var languageCode = languageOptionsList.getSelectedLanguageCode(); // If there's no selection, just return. if (!languageCode) return; // Select the language if it's specified in the URL hash (ex. lang=ja). // Used for automated testing. var match = document.location.hash.match(/\blang=([\w-]+)/); if (match) { var specifiedLanguageCode = match[1]; if (languageOptionsList.selectLanguageByCode(specifiedLanguageCode)) { languageCode = specifiedLanguageCode; } } if (cr.isWindows || cr.isChromeOS) this.updateUiLanguageButton_(languageCode); if (!cr.isMac) { this.updateSelectedLanguageName_(languageCode); this.updateSpellCheckLanguageButton_(languageCode); } if (cr.isChromeOS) this.updateInputMethodList_(languageCode); this.updateLanguageListInAddLanguageOverlay_(); }, /** * Happens when a user changes back to the language they're currently using. */ currentLocaleWasReselected: function() { this.updateUiLanguageButton_( loadTimeData.getString('currentUiLanguageCode')); }, /** * Handles languageOptionsList's save event. * @param {Event} e Save event. * @private */ handleLanguageOptionsListSave_: function(e) { if (cr.isChromeOS) { // Sort the preload engines per the saved languages before save. this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); this.savePreloadEnginesPref_(); } }, /** * Sorts preloadEngines_ by languageOptionsList's order. * @param {Array} preloadEngines List of preload engines. * @return {Array} Returns sorted preloadEngines. * @private */ sortPreloadEngines_: function(preloadEngines) { // For instance, suppose we have two languages and associated input // methods: // // - Korean: hangul // - Chinese: pinyin // // The preloadEngines preference should look like "hangul,pinyin". // If the user reverse the order, the preference should be reorderd // to "pinyin,hangul". var languageOptionsList = $('language-options-list'); var languageCodes = languageOptionsList.getLanguageCodes(); // Convert the list into a dictonary for simpler lookup. var preloadEngineSet = {}; for (var i = 0; i < preloadEngines.length; i++) { preloadEngineSet[preloadEngines[i]] = true; } // Create the new preload engine list per the language codes. var newPreloadEngines = []; for (var i = 0; i < languageCodes.length; i++) { var languageCode = languageCodes[i]; var inputMethodIds = this.languageCodeToInputMethodIdsMap_[ languageCode]; // Check if we have active input methods associated with the language. for (var j = 0; j < inputMethodIds.length; j++) { var inputMethodId = inputMethodIds[j]; if (inputMethodId in preloadEngineSet) { // If we have, add it to the new engine list. newPreloadEngines.push(inputMethodId); // And delete it from the set. This is necessary as one input // method can be associated with more than one language thus // we should avoid having duplicates in the new list. delete preloadEngineSet[inputMethodId]; } } } return newPreloadEngines; }, /** * Initializes the map of language code to input method IDs. * @private */ initializeLanguageCodeToInputMethodIdsMap_: function() { var inputMethodList = loadTimeData.getValue('inputMethodList'); for (var i = 0; i < inputMethodList.length; i++) { var inputMethod = inputMethodList[i]; for (var languageCode in inputMethod.languageCodeSet) { if (languageCode in this.languageCodeToInputMethodIdsMap_) { this.languageCodeToInputMethodIdsMap_[languageCode].push( inputMethod.id); } else { this.languageCodeToInputMethodIdsMap_[languageCode] = [inputMethod.id]; } } } }, /** * Updates the currently selected language name. * @param {string} languageCode Language code (ex. "fr"). * @private */ updateSelectedLanguageName_: function(languageCode) { var languageInfo = LanguageList.getLanguageInfoFromLanguageCode( languageCode); var languageDisplayName = languageInfo.displayName; var languageNativeDisplayName = languageInfo.nativeDisplayName; var textDirection = languageInfo.textDirection; // If the native name is different, add it. if (languageDisplayName != languageNativeDisplayName) { languageDisplayName += ' - ' + languageNativeDisplayName; } // Update the currently selected language name. var languageName = $('language-options-language-name'); languageName.textContent = languageDisplayName; languageName.dir = textDirection; }, /** * Updates the UI language button. * @param {string} languageCode Language code (ex. "fr"). * @private */ updateUiLanguageButton_: function(languageCode) { var uiLanguageButton = $('language-options-ui-language-button'); var uiLanguageMessage = $('language-options-ui-language-message'); var uiLanguageNotification = $('language-options-ui-notification-bar'); // Remove the event listener and add it back if useful. uiLanguageButton.onclick = null; // Unhide the language button every time, as it could've been previously // hidden by a language change. uiLanguageButton.hidden = false; if (languageCode == this.prospectiveUiLanguageCode_) { uiLanguageMessage.textContent = loadTimeData.getString('is_displayed_in_this_language'); showMutuallyExclusiveNodes( [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); } else if (languageCode in loadTimeData.getValue('uiLanguageCodeSet')) { if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) { // In the guest mode for ChromeOS, changing UI language does not make // sense because it does not take effect after browser restart. uiLanguageButton.hidden = true; uiLanguageMessage.hidden = true; } else { uiLanguageButton.textContent = loadTimeData.getString('display_in_this_language'); showMutuallyExclusiveNodes( [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 0); uiLanguageButton.onclick = function(e) { chrome.send('uiLanguageChange', [languageCode]); }; } } else { uiLanguageMessage.textContent = loadTimeData.getString('cannot_be_displayed_in_this_language'); showMutuallyExclusiveNodes( [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); } }, /** * Updates the spell check language button. * @param {string} languageCode Language code (ex. "fr"). * @private */ updateSpellCheckLanguageButton_: function(languageCode) { var spellCheckLanguageButton = $('language-options-spell-check-language-button'); var spellCheckLanguageMessage = $('language-options-spell-check-language-message'); if (languageCode == this.spellCheckDictionary_) { spellCheckLanguageMessage.textContent = loadTimeData.getString('is_used_for_spell_checking'); showMutuallyExclusiveNodes( [spellCheckLanguageButton, spellCheckLanguageMessage], 1); } else if (languageCode in loadTimeData.getValue('spellCheckLanguageCodeSet')) { spellCheckLanguageButton.textContent = loadTimeData.getString('use_this_for_spell_checking'); showMutuallyExclusiveNodes( [spellCheckLanguageButton, spellCheckLanguageMessage], 0); spellCheckLanguageButton.languageCode = languageCode; } else if (!languageCode) { spellCheckLanguageButton.hidden = true; spellCheckLanguageMessage.hidden = true; } else { spellCheckLanguageMessage.textContent = loadTimeData.getString('cannot_be_used_for_spell_checking'); showMutuallyExclusiveNodes( [spellCheckLanguageButton, spellCheckLanguageMessage], 1); } }, /** * Updates the input method list. * @param {string} languageCode Language code (ex. "fr"). * @private */ updateInputMethodList_: function(languageCode) { // Give one of the checkboxes or buttons focus, if it's specified in the // URL hash (ex. focus=mozc). Used for automated testing. var focusInputMethodId = -1; var match = document.location.hash.match(/\bfocus=([\w:-]+)\b/); if (match) { focusInputMethodId = match[1]; } // Change the visibility of the input method list. Input methods that // matches |languageCode| will become visible. var inputMethodList = $('language-options-input-method-list'); var methods = inputMethodList.querySelectorAll('.input-method'); for (var i = 0; i < methods.length; i++) { var method = methods[i]; if (languageCode in method.languageCodeSet) { method.hidden = false; var input = method.querySelectorAll('input')[0]; // Give it focus if the ID matches. if (input.inputMethodId == focusInputMethodId) { input.focus(); } } else { method.hidden = true; } } if (focusInputMethodId == 'add') { $('language-options-add-button').focus(); } }, /** * Updates the language list in the add language overlay. * @param {string} languageCode Language code (ex. "fr"). * @private */ updateLanguageListInAddLanguageOverlay_: function(languageCode) { // Change the visibility of the language list in the add language // overlay. Languages that are already active will become invisible, // so that users don't add the same language twice. var languageOptionsList = $('language-options-list'); var languageCodes = languageOptionsList.getLanguageCodes(); var languageCodeSet = {}; for (var i = 0; i < languageCodes.length; i++) { languageCodeSet[languageCodes[i]] = true; } var addLanguageList = $('add-language-overlay-language-list'); var lis = addLanguageList.querySelectorAll('li'); for (var i = 0; i < lis.length; i++) { // The first child button knows the language code. var button = lis[i].childNodes[0]; if (button.languageCode in languageCodeSet) { lis[i].style.display = 'none'; } else { lis[i].style.display = 'block'; } } }, /** * Handles preloadEnginesPref change. * @param {Event} e Change event. * @private */ handlePreloadEnginesPrefChange_: function(e) { var value = e.value.value; this.preloadEngines_ = this.filterBadPreloadEngines_(value.split(',')); this.updateCheckboxesFromPreloadEngines_(); $('language-options-list').updateDeletable(); }, /** * Handles filteredExtensionImesPref change. * @param {Event} e Change event. * @private */ handleFilteredExtensionsPrefChange_: function(e) { var value = e.value.value; this.filteredExtensionImes_ = value.split(','); this.updateCheckboxesFromFilteredExtensions_(); }, /** * Handles input method checkbox's click event. * @param {Event} e Click event. * @private */ handleCheckboxClick_: function(e) { var checkbox = e.target; if (this.preloadEngines_.length == 1 && !checkbox.checked) { // Don't allow disabling the last input method. this.showNotification_( loadTimeData.getString('please_add_another_input_method'), loadTimeData.getString('ok_button')); checkbox.checked = true; return; } if (checkbox.checked) { chrome.send('inputMethodEnable', [checkbox.inputMethodId]); } else { chrome.send('inputMethodDisable', [checkbox.inputMethodId]); } this.updatePreloadEnginesFromCheckboxes_(); this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); this.savePreloadEnginesPref_(); }, /** * Handles extension input method checkbox's click event. * @param {Event} e Click event. * @private */ handleExtensionCheckboxClick_: function(e) { var checkbox = e.target; this.updateFilteredExtensionsFromCheckboxes_(); this.saveFilteredExtensionPref_(); }, /** * Handles add language list's click event. * @param {Event} e Click event. */ handleAddLanguageListClick_: function(e) { var languageOptionsList = $('language-options-list'); var languageCode = e.target.languageCode; // languageCode can be undefined, if click was made on some random // place in the overlay, rather than a button. Ignore it. if (!languageCode) { return; } languageOptionsList.addLanguage(languageCode); var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; // Enable the first input method for the language added. if (inputMethodIds && inputMethodIds[0] && // Don't add the input method it's already present. This can // happen if the same input method is shared among multiple // languages (ex. English US keyboard is used for English US and // Filipino). this.preloadEngines_.indexOf(inputMethodIds[0]) == -1) { this.preloadEngines_.push(inputMethodIds[0]); this.updateCheckboxesFromPreloadEngines_(); this.savePreloadEnginesPref_(); } OptionsPage.closeOverlay(); }, /** * Handles extension IME button. */ handleExtensionImeButtonClick_: function() { $('language-options-list').clearSelection(); var languageName = $('language-options-language-name'); languageName.textContent = loadTimeData.getString('extension_ime_label'); var uiLanguageMessage = $('language-options-ui-language-message'); uiLanguageMessage.textContent = loadTimeData.getString('extension_ime_description'); var uiLanguageButton = $('language-options-ui-language-button'); uiLanguageButton.onclick = null; uiLanguageButton.hidden = true; this.updateSpellCheckLanguageButton_(); // Hide all input method checkboxes that aren't extension IMEs. var inputMethodList = $('language-options-input-method-list'); var methods = inputMethodList.querySelectorAll('.input-method'); for (var i = 0; i < methods.length; i++) { var method = methods[i]; var input = method.querySelectorAll('input')[0]; // Give it focus if the ID matches. if (input.inputMethodId.match(/^_ext_ime_/)) method.hidden = false; else method.hidden = true; } }, handleAddLanguageOkButtonClick_: function() { var languagesSelect = $('add-language-overlay-language-list'); var selectedIndex = languagesSelect.selectedIndex; if (selectedIndex >= 0) { var selection = languagesSelect.options[selectedIndex]; $('language-options-list').addLanguage(String(selection.value)); OptionsPage.closeOverlay(); } }, /** * Checks if languageCode is deletable or not. * @param {String} languageCode the languageCode to check for deletability. */ languageIsDeletable: function(languageCode) { // Don't allow removing the language if it's a UI language. if (languageCode == this.prospectiveUiLanguageCode_) return false; return (!cr.isChromeOS || this.canDeleteLanguage_(languageCode)); }, /** * Handles browse.enable_spellchecking change. * @param {Event} e Change event. * @private */ updateEnableSpellCheck_: function() { var value = !$('enable-spell-check').checked; $('language-options-spell-check-language-button').disabled = value; }, /** * Handles spellCheckDictionaryPref change. * @param {Event} e Change event. * @private */ handleSpellCheckDictionaryPrefChange_: function(e) { var languageCode = e.value.value; this.spellCheckDictionary_ = languageCode; var languageOptionsList = $('language-options-list'); var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode(); if (!cr.isMac) this.updateSpellCheckLanguageButton_(selectedLanguageCode); }, /** * Handles spellCheckLanguageButton click. * @param {Event} e Click event. * @private */ handleSpellCheckLanguageButtonClick_: function(e) { var languageCode = e.target.languageCode; // Save the preference. Preferences.setStringPref(this.spellCheckDictionaryPref, languageCode, true); chrome.send('spellCheckLanguageChange', [languageCode]); }, /** * Checks whether it's possible to remove the language specified by * languageCode and returns true if possible. This function returns false * if the removal causes the number of preload engines to be zero. * * @param {string} languageCode Language code (ex. "fr"). * @return {boolean} Returns true on success. * @private */ canDeleteLanguage_: function(languageCode) { // First create the set of engines to be removed from input methods // associated with the language code. var enginesToBeRemovedSet = {}; var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; for (var i = 0; i < inputMethodIds.length; i++) { enginesToBeRemovedSet[inputMethodIds[i]] = true; } // Then eliminate engines that are also used for other active languages. // For instance, if "xkb:us::eng" is used for both English and Filipino. var languageCodes = $('language-options-list').getLanguageCodes(); for (var i = 0; i < languageCodes.length; i++) { // Skip the target language code. if (languageCodes[i] == languageCode) { continue; } // Check if input methods used in this language are included in // enginesToBeRemovedSet. If so, eliminate these from the set, so // we don't remove this time. var inputMethodIdsForAnotherLanguage = this.languageCodeToInputMethodIdsMap_[languageCodes[i]]; for (var j = 0; j < inputMethodIdsForAnotherLanguage.length; j++) { var inputMethodId = inputMethodIdsForAnotherLanguage[j]; if (inputMethodId in enginesToBeRemovedSet) { delete enginesToBeRemovedSet[inputMethodId]; } } } // Update the preload engine list with the to-be-removed set. var newPreloadEngines = []; for (var i = 0; i < this.preloadEngines_.length; i++) { if (!(this.preloadEngines_[i] in enginesToBeRemovedSet)) { newPreloadEngines.push(this.preloadEngines_[i]); } } // Don't allow this operation if it causes the number of preload // engines to be zero. return (newPreloadEngines.length > 0); }, /** * Saves the filtered extension preference. * @private */ saveFilteredExtensionPref_: function() { Preferences.setStringPref(this.filteredExtensionImesPref, this.filteredExtensionImes_.join(','), true); }, /** * Updates the checkboxes in the input method list from the filtered * extensions preference. * @private */ updateCheckboxesFromFilteredExtensions_: function() { // Convert the list into a dictonary for simpler lookup. var dictionary = {}; for (var i = 0; i < this.filteredExtensionImes_.length; i++) dictionary[this.filteredExtensionImes_[i]] = true; var inputMethodList = $('language-options-input-method-list'); var checkboxes = inputMethodList.querySelectorAll('input'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].inputMethodId.match(/^_ext_ime_/)) checkboxes[i].checked = !(checkboxes[i].inputMethodId in dictionary); } }, /** * Updates the filtered extensions preference from the checkboxes in the * input method list. * @private */ updateFilteredExtensionsFromCheckboxes_: function() { this.filteredExtensionImes_ = []; var inputMethodList = $('language-options-input-method-list'); var checkboxes = inputMethodList.querySelectorAll('input'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].inputMethodId.match(/^_ext_ime_/)) { if (!checkboxes[i].checked) this.filteredExtensionImes_.push(checkboxes[i].inputMethodId); } } }, /** * Saves the preload engines preference. * @private */ savePreloadEnginesPref_: function() { Preferences.setStringPref(this.preloadEnginesPref, this.preloadEngines_.join(','), true); }, /** * Updates the checkboxes in the input method list from the preload * engines preference. * @private */ updateCheckboxesFromPreloadEngines_: function() { // Convert the list into a dictonary for simpler lookup. var dictionary = {}; for (var i = 0; i < this.preloadEngines_.length; i++) { dictionary[this.preloadEngines_[i]] = true; } var inputMethodList = $('language-options-input-method-list'); var checkboxes = inputMethodList.querySelectorAll('input'); for (var i = 0; i < checkboxes.length; i++) { if (!checkboxes[i].inputMethodId.match(/^_ext_ime_/)) checkboxes[i].checked = (checkboxes[i].inputMethodId in dictionary); } }, /** * Updates the preload engines preference from the checkboxes in the * input method list. * @private */ updatePreloadEnginesFromCheckboxes_: function() { this.preloadEngines_ = []; var inputMethodList = $('language-options-input-method-list'); var checkboxes = inputMethodList.querySelectorAll('input'); for (var i = 0; i < checkboxes.length; i++) { if (!checkboxes[i].inputMethodId.match(/^_ext_ime_/)) { if (checkboxes[i].checked) this.preloadEngines_.push(checkboxes[i].inputMethodId); } } var languageOptionsList = $('language-options-list'); languageOptionsList.updateDeletable(); }, /** * Filters bad preload engines in case bad preload engines are * stored in the preference. Removes duplicates as well. * @param {Array} preloadEngines List of preload engines. * @private */ filterBadPreloadEngines_: function(preloadEngines) { // Convert the list into a dictonary for simpler lookup. var dictionary = {}; var list = loadTimeData.getValue('inputMethodList'); for (var i = 0; i < list.length; i++) { dictionary[list[i].id] = true; } var filteredPreloadEngines = []; var seen = {}; for (var i = 0; i < preloadEngines.length; i++) { // Check if the preload engine is present in the // dictionary, and not duplicate. Otherwise, skip it. if (preloadEngines[i] in dictionary && !(preloadEngines[i] in seen)) { filteredPreloadEngines.push(preloadEngines[i]); seen[preloadEngines[i]] = true; } } return filteredPreloadEngines; }, // TODO(kochi): This is an adapted copy from new_tab.js. // If this will go as final UI, refactor this to share the component with // new new tab page. /** * Shows notification * @private */ notificationTimeout_: null, showNotification_: function(text, actionText, opt_delay) { var notificationElement = $('notification'); var actionLink = notificationElement.querySelector('.link-color'); var delay = opt_delay || 10000; function show() { window.clearTimeout(this.notificationTimeout_); notificationElement.classList.add('show'); document.body.classList.add('notification-shown'); } function hide() { window.clearTimeout(this.notificationTimeout_); notificationElement.classList.remove('show'); document.body.classList.remove('notification-shown'); // Prevent tabbing to the hidden link. actionLink.tabIndex = -1; // Setting tabIndex to -1 only prevents future tabbing to it. If, // however, the user switches window or a tab and then moves back to // this tab the element may gain focus. We therefore make sure that we // blur the element so that the element focus is not restored when // coming back to this window. actionLink.blur(); } function delayedHide() { this.notificationTimeout_ = window.setTimeout(hide, delay); } notificationElement.firstElementChild.textContent = text; actionLink.textContent = actionText; actionLink.onclick = hide; actionLink.onkeydown = function(e) { if (e.keyIdentifier == 'Enter') { hide(); } }; notificationElement.onmouseover = show; notificationElement.onmouseout = delayedHide; actionLink.onfocus = show; actionLink.onblur = delayedHide; // Enable tabbing to the link now that it is shown. actionLink.tabIndex = 0; show(); delayedHide(); } }; /** * Shows the node at |index| in |nodes|, hides all others. * @param {Array} nodes The nodes to be shown or hidden. * @param {number} index The index of |nodes| to show. */ function showMutuallyExclusiveNodes(nodes, index) { assert(index >= 0 && index < nodes.length); for (var i = 0; i < nodes.length; ++i) { assert(nodes[i] instanceof HTMLElement); // TODO(dbeam): Ignore null? nodes[i].hidden = i != index; } } /** * Chrome callback for when the UI language preference is saved. * @param {string} languageCode The newly selected language to use. */ LanguageOptions.uiLanguageSaved = function(languageCode) { this.prospectiveUiLanguageCode_ = languageCode; // If the user is no longer on the same language code, ignore. if ($('language-options-list').getSelectedLanguageCode() != languageCode) return; // Special case for when a user changes to a different language, and changes // back to the same language without having restarted Chrome or logged // in/out of ChromeOS. if (languageCode == loadTimeData.getString('currentUiLanguageCode')) { LanguageOptions.getInstance().currentLocaleWasReselected(); return; } // Otherwise, show a notification telling the user that their changes will // only take effect after restart. showMutuallyExclusiveNodes([$('language-options-ui-language-button'), $('language-options-ui-notification-bar')], 1); }; // Export return { LanguageOptions: LanguageOptions }; });