blob: bef31067cb65ce7db8093b0fb0ba924496245c8b [file] [log] [blame]
// Copyright 2016 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.
/**
* @fileoverview 'settings-manage-input-methods-page' is a sub-page for enabling
* and disabling input methods. Input methods are grouped by base languages to
* avoid showing duplicate or ambiguous input methods.
*
* @group Chrome Settings Elements
* @element settings-manage-input-methods-page
*/
Polymer({
is: 'settings-manage-input-methods-page',
properties: {
/** @type {!LanguagesModel|undefined} */
languages: {
type: Object,
notify: true,
},
/** @type {!LanguageHelper} */
languageHelper: Object,
/**
* List of enabled languages with the input methods to show.
* @private {!Array<
* !{language: !chrome.languageSettingsPrivate.Language,
* inputMethods: !Array<!chrome.languageSettingsPrivate.InputMethod>
* }>}
*/
languageList_: {
type: Array,
value: function() {
return [];
},
},
},
observers: [
'availableInputMethodsChanged_(languages.enabled.*,' +
'languages.inputMethods.supported.*)',
'enabledInputMethodsChanged_(languages.inputMethods.enabled.*)',
],
/** @private */
availableInputMethodsChanged_: function() {
this.populateLanguageList_();
},
/** @private */
enabledInputMethodsChanged_: function() {
this.populateLanguageList_();
},
/**
* Handler for an input method checkbox.
* @param {!{model: !{item: chrome.languageSettingsPrivate.InputMethod},
* target: !PaperCheckboxElement}} e
* @private
*/
onCheckboxChange_: function(e) {
// TODO(michaelpg): Show confirmation dialog for 3rd-party IMEs.
const id = e.model.item.id;
if (e.target.checked)
this.languageHelper.addInputMethod(id);
else
this.languageHelper.removeInputMethod(id);
},
/**
* Returns true if the input method can be removed.
* @param {!chrome.languageSettingsPrivate.InputMethod} targetInputMethod
* @param {!Object} change Polymer change object (provided in the HTML so this
* gets called whenever languages.inputMethods.enabled.* changes).
* @return {boolean}
* @private
*/
enableInputMethodCheckbox_: function(targetInputMethod, change) {
if (!targetInputMethod.enabled)
return true;
// Third-party IMEs can always be removed.
if (!this.languageHelper.isComponentIme(targetInputMethod))
return true;
// Can be removed as long as there is another component IME.
return this.languages.inputMethods.enabled.some(function(inputMethod) {
return inputMethod != targetInputMethod &&
this.languageHelper.isComponentIme(inputMethod);
}, this);
},
/**
* Creates the list of languages and their input methods as the data source
* for the view.
* @private
*/
populateLanguageList_: function() {
const languageList = [];
// Languages that have already been listed further up.
const /** !Set<string> */ usedLanguages = new Set();
// Add languages in preference order. However, if there are multiple
// enabled variants of the same base language, group them all as the base
// language instead of showing each variant individually. This prevents us
// from displaying duplicate input methods under different variants.
for (let i = 0; i < this.languages.enabled.length; i++) {
const languageState = this.languages.enabled[i];
// Skip the language if we have already included it or its base language.
if (usedLanguages.has(languageState.language.code))
continue;
const baseLanguageCode = this.languageHelper.getLanguageCodeWithoutRegion(
languageState.language.code);
if (usedLanguages.has(baseLanguageCode))
continue;
// Find the other languages further down in the preferred languages list
// which also use this language's base language code.
const languageFamilyCodes = [languageState.language.code];
for (let j = i + 1; j < this.languages.enabled.length; j++) {
const otherCode = this.languages.enabled[j].language.code;
if (this.languageHelper.getLanguageCodeWithoutRegion(otherCode) ==
baseLanguageCode) {
languageFamilyCodes.push(this.languages.enabled[j].language.code);
}
}
const combinedInputMethods =
this.getInputMethodsForLanguages(languageFamilyCodes);
// Skip the language if it has no new input methods.
if (!combinedInputMethods.length)
continue;
// Add the language or base language.
let displayLanguage = languageState.language;
if (languageFamilyCodes.length > 1) {
const baseLanguage = this.languageHelper.getLanguage(baseLanguageCode);
if (baseLanguage)
displayLanguage = baseLanguage;
}
languageList.push({
language: displayLanguage,
inputMethods: combinedInputMethods,
});
for (let k = 0; k < languageFamilyCodes.length; k++)
usedLanguages.add(languageFamilyCodes[k]);
}
this.languageList_ = languageList;
this.notifyInputMethodsChanged_();
},
/**
* Returns the input methods that support any of the given languages.
* @param {!Array<string>} languageCodes
* @return {!Array<!chrome.languageSettingsPrivate.InputMethod>}
* @private
*/
getInputMethodsForLanguages: function(languageCodes) {
// Input methods that have already been listed for this language.
const /** !Set<string> */ usedInputMethods = new Set();
/** @type {!Array<chrome.languageSettingsPrivate.InputMethod>} */
const combinedInputMethods = [];
for (let i = 0; i < languageCodes.length; i++) {
const inputMethods =
this.languageHelper.getInputMethodsForLanguage(languageCodes[i]);
// Get the language's unused input methods and mark them as used.
const newInputMethods = inputMethods.filter(function(inputMethod) {
if (usedInputMethods.has(inputMethod.id))
return false;
usedInputMethods.add(inputMethod.id);
return true;
});
[].push.apply(combinedInputMethods, newInputMethods);
}
return combinedInputMethods;
},
// TODO(Polymer/polymer#3603): We have to notify Polymer of properties that
// may have changed on nested objects, even when the outer property itself
// is set to a new array.
// TODO(michaelpg): Test this behavior.
/** @private */
notifyInputMethodsChanged_: function() {
for (let i = 0; i < this.languageList_.length; i++) {
for (let j = 0; j < this.languageList_[i].inputMethods.length; j++) {
this.notifyPath(
'languageList_.' + i + '.inputMethods.' + j + '.enabled',
this.languageList_[i].inputMethods[j].enabled);
}
}
},
});