Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(39)

Side by Side Diff: chrome/browser/resources/settings/languages_page/languages.js

Issue 1902893003: MD Settings: simplify language model and data binding (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: update comment based on other CL feedback Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview 'settings-languages' provides convenient access to 6 * @fileoverview 'settings-languages' provides convenient access to
7 * Chrome's language and input method settings. 7 * Chrome's language and input method settings.
8 * 8 *
9 * Instances of this element have a 'languages' property, which reflects the 9 * Instances of this element have a 'languages' property, which reflects the
10 * current language settings. The 'languages' property is read-only, meaning 10 * current language settings. The 'languages' property is read-only, meaning
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 * 'languages' property updates like any other Polymer property. 56 * 'languages' property updates like any other Polymer property.
57 * @implements {LanguageHelper} 57 * @implements {LanguageHelper}
58 */ 58 */
59 SettingsLanguagesSingletonElement = Polymer({ 59 SettingsLanguagesSingletonElement = Polymer({
60 is: 'settings-languages-singleton', 60 is: 'settings-languages-singleton',
61 61
62 behaviors: [PrefsBehavior], 62 behaviors: [PrefsBehavior],
63 63
64 properties: { 64 properties: {
65 /** 65 /**
66 * @type {LanguagesModel|undefined} 66 * @type {!LanguagesModel|undefined}
67 */ 67 */
68 languages: { 68 languages: {
69 type: Object, 69 type: Object,
70 notify: true, 70 notify: true,
71 }, 71 },
72 72
73 /** 73 /**
74 * Object containing all preferences. 74 * Object containing all preferences.
75 */ 75 */
76 prefs: { 76 prefs: {
77 type: Object, 77 type: Object,
78 notify: true, 78 notify: true,
79 }, 79 },
80 80
81 /** 81 /**
82 * PromiseResolver to be resolved when the singleton has been initialized. 82 * PromiseResolver to be resolved when the singleton has been initialized.
83 * @private {!PromiseResolver} 83 * @private {!PromiseResolver}
84 */ 84 */
85 resolver_: { 85 resolver_: {
86 type: Object, 86 type: Object,
87 value: function() { 87 value: function() {
88 return new PromiseResolver(); 88 return new PromiseResolver();
89 }, 89 },
90 }, 90 },
91
92 /** @type {!LanguageSettingsPrivate} */
93 languageSettingsPrivate: Object,
91 }, 94 },
92 95
93 /** 96 /**
94 * Hash map of languages.supportedLanguages using language codes as keys for 97 * Hash map of supported languages by language codes for fast lookup.
95 * fast lookup. 98 * @private {!Map<string, !chrome.languageSettingsPrivate.Language>}
96 * @private {!Object<!chrome.languageSettingsPrivate.Language>}
97 */ 99 */
98 supportedLanguageMap_: {}, 100 supportedLanguageMap_: new Map(),
99 101
100 /** 102 /**
101 * Hash map of languages.enabledLanguages using language codes as keys for 103 * Hash set of enabled language codes for membership testing.
102 * fast lookup. 104 * @private {!Set<string>}
103 * @private {!Object<!LanguageInfo>}
104 */ 105 */
105 enabledLanguageMap_: {}, 106 enabledLanguageSet_: new Set(),
106 107
107 observers: [ 108 observers: [
108 'preferredLanguagesPrefChanged_(' + 109 'preferredLanguagesPrefChanged_(' +
109 'prefs.' + preferredLanguagesPrefName + '.value, ' + 110 'prefs.' + preferredLanguagesPrefName + '.value, ' +
110 'languages)', 111 'languages)',
111 'spellCheckDictionariesPrefChanged_(' + 112 'spellCheckDictionariesPrefChanged_(' +
112 'prefs.spellcheck.dictionaries.value.*, ' + 113 'prefs.spellcheck.dictionaries.value.*, ' +
113 'languages)', 114 'languages)',
114 'translateLanguagesPrefChanged_(' + 115 'translateLanguagesPrefChanged_(' +
115 'prefs.translate_blocked_languages.value.*,' + 116 'prefs.translate_blocked_languages.value.*,' +
116 'languages)', 117 'languages)',
118 'prospectiveUILanguageChanged_(' +
119 'prefs.intl.app_locale.value,' +
120 'languages)',
117 ], 121 ],
118 122
119 /** @override */ 123 /** @override */
120 created: function() { 124 created: function() {
121 this.languageSettingsPrivate = 125 this.languageSettingsPrivate =
122 languageSettings.languageSettingsPrivateApiForTest || 126 languageSettings.languageSettingsPrivateApiForTest ||
123 /** @type {!LanguageSettingsPrivate} */(chrome.languageSettingsPrivate); 127 /** @type {!LanguageSettingsPrivate} */(chrome.languageSettingsPrivate);
124 128
125 var promises = [ 129 var promises = [
126 // Wait until prefs are initialized before creating the model, so we can 130 // Wait until prefs are initialized before creating the model, so we can
(...skipping 15 matching lines...) Expand all
142 this.createModel_(results[1], results[2]); 146 this.createModel_(results[1], results[2]);
143 this.resolver_.resolve(); 147 this.resolver_.resolve();
144 }.bind(this)); 148 }.bind(this));
145 }, 149 },
146 150
147 /** 151 /**
148 * Updates the list of enabled languages from the preferred languages pref. 152 * Updates the list of enabled languages from the preferred languages pref.
149 * @private 153 * @private
150 */ 154 */
151 preferredLanguagesPrefChanged_: function() { 155 preferredLanguagesPrefChanged_: function() {
152 var enabledLanguages = 156 var enabledLanguageStates =
153 this.getEnabledLanguages_(this.languages.translateTarget); 157 this.getEnabledLanguageStates_(this.languages.translateTarget);
154 158
155 // Reset the enabled language map before updating 159 // Recreate the enabled language set before updating languages.enabled.
156 // languages.enabledLanguages. 160 this.enabledLanguageSet_.clear();
157 this.enabledLanguageMap_ = {}; 161 for (var languageState of enabledLanguageStates)
158 for (var i = 0; i < enabledLanguages.length; i++) { 162 this.enabledLanguageSet_.add(languageState.language.code);
159 var languageInfo = enabledLanguages[i]; 163
160 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo; 164 this.set('languages.enabled', enabledLanguageStates);
161 } 165 this.updateRemovableLanguages_();
162 this.set('languages.enabledLanguages', enabledLanguages);
163 }, 166 },
164 167
165 /** 168 /**
166 * Updates the spellCheckEnabled state of each enabled language. 169 * Updates the spellCheckEnabled state of each enabled language.
167 * @private 170 * @private
168 */ 171 */
169 spellCheckDictionariesPrefChanged_: function() { 172 spellCheckDictionariesPrefChanged_: function() {
170 var spellCheckMap = this.makeMapFromArray_(/** @type {!Array<string>} */( 173 var spellCheckSet = this.makeSetFromArray_(/** @type {!Array<string>} */(
171 this.getPref('spellcheck.dictionaries').value)); 174 this.getPref('spellcheck.dictionaries').value));
172 for (var i = 0; i < this.languages.enabledLanguages.length; i++) { 175 for (var i = 0; i < this.languages.enabled.length; i++) {
173 var languageCode = this.languages.enabledLanguages[i].language.code; 176 var languageState = this.languages.enabled[i];
174 this.set('languages.enabledLanguages.' + i + '.state.spellCheckEnabled', 177 this.set('languages.enabled.' + i + '.spellCheckEnabled',
175 !!spellCheckMap[languageCode]); 178 !!spellCheckSet.has(languageState.language.code));
176 } 179 }
177 }, 180 },
178 181
179 /** @private */ 182 /** @private */
180 translateLanguagesPrefChanged_: function() { 183 translateLanguagesPrefChanged_: function() {
181 var translateBlockedPref = this.getPref('translate_blocked_languages'); 184 var translateBlockedPref = this.getPref('translate_blocked_languages');
182 var translateBlockedMap = this.makeMapFromArray_( 185 var translateBlockedSet = this.makeSetFromArray_(
183 /** @type {!Array<string>} */(translateBlockedPref.value)); 186 /** @type {!Array<string>} */(translateBlockedPref.value));
184 187
185 for (var i = 0; i < this.languages.enabledLanguages.length; i++) { 188 for (var i = 0; i < this.languages.enabled.length; i++) {
186 var translateCode = this.convertLanguageCodeForTranslate( 189 var translateCode = this.convertLanguageCodeForTranslate(
187 this.languages.enabledLanguages[i].language.code); 190 this.languages.enabled[i].language.code);
188 this.set( 191 this.set(
189 'languages.enabledLanguages.' + i + '.state.translateEnabled', 192 'languages.enabled.' + i + '.translateEnabled',
190 !translateBlockedMap[translateCode]); 193 !translateBlockedSet.has(translateCode));
191 } 194 }
192 }, 195 },
193 196
197 /** @private */
198 prospectiveUILanguageChanged_: function() {
199 this.updateRemovableLanguages_();
200 },
201
194 /** 202 /**
195 * Constructs the languages model. 203 * Constructs the languages model.
196 * @param {!Array<!chrome.languageSettingsPrivate.Language>} 204 * @param {!Array<!chrome.languageSettingsPrivate.Language>}
197 * supportedLanguages 205 * supportedLanguages
198 * @param {string} translateTarget Language code of the default translate 206 * @param {string} translateTarget Language code of the default translate
199 * target language. 207 * target language.
200 * @private 208 * @private
201 */ 209 */
202 createModel_: function(supportedLanguages, translateTarget) { 210 createModel_: function(supportedLanguages, translateTarget) {
203 // Populate the hash map of supported languages. 211 // Populate the hash map of supported languages.
204 for (var i = 0; i < supportedLanguages.length; i++) { 212 for (var language of supportedLanguages) {
205 var language = supportedLanguages[i];
206 language.supportsUI = !!language.supportsUI; 213 language.supportsUI = !!language.supportsUI;
207 language.supportsTranslate = !!language.supportsTranslate; 214 language.supportsTranslate = !!language.supportsTranslate;
208 language.supportsSpellcheck = !!language.supportsSpellcheck; 215 language.supportsSpellcheck = !!language.supportsSpellcheck;
209 this.supportedLanguageMap_[language.code] = language; 216 this.supportedLanguageMap_.set(language.code, language);
210 } 217 }
211 218
212 // Create a list of enabled language info from the supported languages. 219 // Create a list of enabled languages from the supported languages.
213 var enabledLanguages = this.getEnabledLanguages_(translateTarget); 220 var enabledLanguageStates = this.getEnabledLanguageStates_(translateTarget);
214 // Populate the hash map of enabled languages. 221 // Populate the hash set of enabled languages.
215 for (var i = 0; i < enabledLanguages.length; i++) { 222 for (var languageState of enabledLanguageStates)
216 var languageInfo = enabledLanguages[i]; 223 this.enabledLanguageSet_.add(languageState.language.code);
217 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo;
218 }
219 224
220 // Initialize the Polymer languages model. 225 // Initialize the Polymer languages model.
221 this.languages = /** @type {!LanguagesModel} */({ 226 this.languages = /** @type {!LanguagesModel} */({
222 supportedLanguages: supportedLanguages, 227 supported: supportedLanguages,
223 enabledLanguages: enabledLanguages, 228 enabled: enabledLanguageStates,
224 translateTarget: translateTarget, 229 translateTarget: translateTarget,
225 }); 230 });
226 }, 231 },
227 232
228 /** 233 /**
229 * Returns a list of LanguageInfos for each enabled language in the supported 234 * Returns a list of LanguageStates for each enabled language in the supported
230 * languages list. 235 * languages list.
231 * @param {string} translateTarget Language code of the default translate 236 * @param {string} translateTarget Language code of the default translate
232 * target language. 237 * target language.
233 * @return {!Array<!LanguageInfo>} 238 * @return {!Array<!LanguageState>}
234 * @private 239 * @private
235 */ 240 */
236 getEnabledLanguages_: function(translateTarget) { 241 getEnabledLanguageStates_: function(translateTarget) {
237 assert(CrSettingsPrefs.isInitialized); 242 assert(CrSettingsPrefs.isInitialized);
238 243
239 var pref = this.getPref(preferredLanguagesPrefName); 244 var pref = this.getPref(preferredLanguagesPrefName);
240 var enabledLanguageCodes = pref.value.split(','); 245 var enabledLanguageCodes = pref.value.split(',');
241 var enabledLanguages = /** @type {!Array<!LanguageInfo>} */ [];
242
243 var spellCheckPref = this.getPref('spellcheck.dictionaries'); 246 var spellCheckPref = this.getPref('spellcheck.dictionaries');
244 var spellCheckMap = this.makeMapFromArray_(/** @type {!Array<string>} */( 247 var spellCheckSet = this.makeSetFromArray_(/** @type {!Array<string>} */(
245 spellCheckPref.value)); 248 spellCheckPref.value));
246 249
247 var translateBlockedPref = this.getPref('translate_blocked_languages'); 250 var translateBlockedPref = this.getPref('translate_blocked_languages');
248 var translateBlockedMap = this.makeMapFromArray_( 251 var translateBlockedSet = this.makeSetFromArray_(
249 /** @type {!Array<string>} */(translateBlockedPref.value)); 252 /** @type {!Array<string>} */(translateBlockedPref.value));
250 253
254 var enabledLanguageStates = [];
251 for (var i = 0; i < enabledLanguageCodes.length; i++) { 255 for (var i = 0; i < enabledLanguageCodes.length; i++) {
252 var code = enabledLanguageCodes[i]; 256 var code = enabledLanguageCodes[i];
253 var language = this.supportedLanguageMap_[code]; 257 var language = this.supportedLanguageMap_.get(code);
254 // Skip unsupported languages. 258 // Skip unsupported languages.
255 if (!language) 259 if (!language)
256 continue; 260 continue;
257 var state = /** @type {LanguageState} */({}); 261 var languageState = /** @type {LanguageState} */({});
258 state.spellCheckEnabled = !!spellCheckMap[code]; 262 languageState.language = language;
263 languageState.spellCheckEnabled = !!spellCheckSet.has(code);
259 // Translate is considered disabled if this language maps to any translate 264 // Translate is considered disabled if this language maps to any translate
260 // language that is blocked. 265 // language that is blocked.
261 var translateCode = this.convertLanguageCodeForTranslate(code); 266 var translateCode = this.convertLanguageCodeForTranslate(code);
262 state.translateEnabled = !!language.supportsTranslate && 267 languageState.translateEnabled = !!language.supportsTranslate &&
263 !translateBlockedMap[translateCode] && 268 !translateBlockedSet.has(translateCode) &&
264 translateCode != translateTarget; 269 translateCode != translateTarget;
265 enabledLanguages.push(/** @type {LanguageInfo} */( 270 enabledLanguageStates.push(languageState);
266 {language: language, state: state}));
267 } 271 }
268 return enabledLanguages; 272 return enabledLanguageStates;
269 }, 273 },
270 274
271 /** 275 /**
272 * Creates an object whose keys are the elements of the list. 276 * Updates the |removable| property of the enabled language states based
273 * @param {!Array<string>} list 277 * on what other languages are enabled.
274 * @return {!Object<boolean>}
275 * @private 278 * @private
276 */ 279 */
277 makeMapFromArray_: function(list) { 280 updateRemovableLanguages_: function() {
278 var map = {}; 281 assert(this.languages);
279 for (var i = 0; i < list.length; i++) 282 for (var i = 0; i < this.languages.enabled.length; i++) {
280 map[list[i]] = true; 283 var languageState = this.languages.enabled[i];
281 return map; 284 this.set('languages.enabled.' + i + '.removable',
285 this.canDisableLanguage(languageState.language.code));
286 }
287 },
288
289 /**
290 * Creates a Set from the elements of the arary.
291 * @param {!Array<T>} list
292 * @return {!Set<T>}
293 * @template T
294 * @private
295 */
296 makeSetFromArray_: function(list) {
297 var set = new Set();
298 for (var item of list)
299 set.add(item);
300 return set;
282 }, 301 },
283 302
284 // LanguageHelper implementation. 303 // LanguageHelper implementation.
285 // TODO(michaelpg): replace duplicate docs with @override once b/24294625 304 // TODO(michaelpg): replace duplicate docs with @override once b/24294625
286 // is fixed. 305 // is fixed.
287 306
288 /** @return {!Promise} */ 307 /** @return {!Promise} */
289 whenReady: function() { 308 whenReady: function() {
290 return this.resolver_.promise; 309 return this.resolver_.promise;
291 }, 310 },
292 311
293 <if expr="chromeos or is_win">
294 /** 312 /**
295 * Sets the prospective UI language to the chosen language. This won't affect 313 * Sets the prospective UI language to the chosen language. This won't affect
296 * the actual UI language until a restart. 314 * the actual UI language until a restart.
297 * @param {string} languageCode 315 * @param {string} languageCode
298 */ 316 */
299 setUILanguage: function(languageCode) { 317 setUILanguage: function(languageCode) {
318 assert(cr.isChromeOS || cr.isWindows);
300 chrome.send('setUILanguage', [languageCode]); 319 chrome.send('setUILanguage', [languageCode]);
301 }, 320 },
302 321
303 /** Resets the prospective UI language back to the actual UI language. */ 322 /** Resets the prospective UI language back to the actual UI language. */
304 resetUILanguage: function() { 323 resetUILanguage: function() {
324 assert(cr.isChromeOS || cr.isWindows);
305 chrome.send('setUILanguage', [navigator.language]); 325 chrome.send('setUILanguage', [navigator.language]);
306 }, 326 },
307 </if>
308 327
309 /** 328 /**
310 * Returns the "prospective" UI language, i.e. the one to be used on next 329 * Returns the "prospective" UI language, i.e. the one to be used on next
311 * restart. If the pref is not set, the current UI language is also the 330 * restart. If the pref is not set, the current UI language is also the
312 * "prospective" language. 331 * "prospective" language.
313 * @return {string} Language code of the prospective UI language. 332 * @return {string} Language code of the prospective UI language.
314 */ 333 */
315 getProspectiveUILanguage: function() { 334 getProspectiveUILanguage: function() {
316 return /** @type {string} */(this.getPref('intl.app_locale').value) || 335 return /** @type {string} */(this.getPref('intl.app_locale').value) ||
317 navigator.language; 336 navigator.language;
318 }, 337 },
319 338
320 /** 339 /**
321 * @param {string} languageCode 340 * @param {string} languageCode
322 * @return {boolean} True if the language is enabled. 341 * @return {boolean} True if the language is enabled.
323 */ 342 */
324 isLanguageEnabled: function(languageCode) { 343 isLanguageEnabled: function(languageCode) {
325 return !!this.enabledLanguageMap_[languageCode]; 344 return this.enabledLanguageSet_.has(languageCode);
326 }, 345 },
327 346
328 /** 347 /**
329 * Enables the language, making it available for spell check and input. 348 * Enables the language, making it available for spell check and input.
330 * @param {string} languageCode 349 * @param {string} languageCode
331 */ 350 */
332 enableLanguage: function(languageCode) { 351 enableLanguage: function(languageCode) {
333 if (!CrSettingsPrefs.isInitialized) 352 if (!CrSettingsPrefs.isInitialized)
334 return; 353 return;
335 354
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 * @return {boolean} 390 * @return {boolean}
372 */ 391 */
373 canDisableLanguage: function(languageCode) { 392 canDisableLanguage: function(languageCode) {
374 // Cannot disable the prospective UI language. 393 // Cannot disable the prospective UI language.
375 if ((cr.isChromeOS || cr.isWindows) && 394 if ((cr.isChromeOS || cr.isWindows) &&
376 languageCode == this.getProspectiveUILanguage()) { 395 languageCode == this.getProspectiveUILanguage()) {
377 return false; 396 return false;
378 } 397 }
379 398
380 // Cannot disable the only enabled language. 399 // Cannot disable the only enabled language.
381 if (this.languages.enabledLanguages.length == 1) 400 if (this.languages.enabled.length == 1)
382 return false; 401 return false;
383 402
384 return true; 403 return true;
385 }, 404 },
386 405
387 /** 406 /**
388 * Enables translate for the given language by removing the translate 407 * Enables translate for the given language by removing the translate
389 * language from the blocked languages preference. 408 * language from the blocked languages preference.
390 * @param {string} languageCode 409 * @param {string} languageCode
391 */ 410 */
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 return kTranslateLanguageSynonyms[main]; 461 return kTranslateLanguageSynonyms[main];
443 462
444 return main; 463 return main;
445 }, 464 },
446 465
447 /** 466 /**
448 * @param {string} languageCode 467 * @param {string} languageCode
449 * @return {!chrome.languageSettingsPrivate.Language|undefined} 468 * @return {!chrome.languageSettingsPrivate.Language|undefined}
450 */ 469 */
451 getLanguage: function(languageCode) { 470 getLanguage: function(languageCode) {
452 return this.supportedLanguageMap_[languageCode]; 471 return this.supportedLanguageMap_.get(languageCode);
453 }, 472 },
454 }); 473 });
455 })(); 474 })();
456 475
457 /** 476 /**
458 * A reference to the singleton under the guise of a LanguageHelper 477 * A reference to the singleton under the guise of a LanguageHelper
459 * implementation. This provides a limited API but implies the singleton 478 * implementation. This provides a limited API but implies the singleton
460 * should not be used directly for data binding. 479 * should not be used directly for data binding.
461 */ 480 */
462 var LanguageHelperImpl = SettingsLanguagesSingletonElement; 481 var LanguageHelperImpl = SettingsLanguagesSingletonElement;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 * manually sending a change notification for our 'languages' property (since 518 * manually sending a change notification for our 'languages' property (since
500 * it's the same object as the singleton's property, but isn't bound by 519 * it's the same object as the singleton's property, but isn't bound by
501 * Polymer). 520 * Polymer).
502 * @private 521 * @private
503 */ 522 */
504 singletonLanguagesChanged_: function(e) { 523 singletonLanguagesChanged_: function(e) {
505 // Forward the change notification to the host. 524 // Forward the change notification to the host.
506 this.fire(e.type, e.detail, {bubbles: false}); 525 this.fire(e.type, e.detail, {bubbles: false});
507 }, 526 },
508 }); 527 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698