Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview 'cr-settings-languages' provides convenient access to | |
| 7 * Chrome's language and input method settings. | |
| 8 * | |
| 9 * Instances of this element have a 'languages' property, which reflects the | |
| 10 * current language settings. The 'languages' property is read-only, meaning | |
| 11 * hosts using this element cannot change it directly. Instead, changes to | |
| 12 * language settings should be made using this element's public functions. | |
| 13 * | |
| 14 * Use two-way binding syntax to propagate changes from child to host, so that | |
| 15 * changes made internally to 'languages' propagate to your host element: | |
| 16 * | |
| 17 * <template> | |
| 18 * <cr-settings-languages languages="{{languages}}"> | |
| 19 * </cr-settings-languages> | |
| 20 * <div>[[languages.someProperty]]</div> | |
| 21 * </template> | |
| 22 * | |
| 23 * @group Chrome Settings Elements | |
| 24 * @element cr-settings-languages | |
| 25 */ | |
| 26 | |
| 27 /** @typedef {{spellCheckEnabled: boolean}} */ | |
| 28 var LanguageState; | |
| 29 | |
| 30 /** | |
| 31 * @typedef {{language: !chrome.languageSettingsPrivate.Language, | |
| 32 * state: !LanguageState}} | |
| 33 */ | |
| 34 var LanguageInfo; | |
| 35 | |
| 36 /** | |
| 37 * supportedLanguages: an array of languages, ordered alphabetically. | |
| 38 * enabledLanguages: an array of enabled language info and state, ordered by | |
| 39 * preference. | |
| 40 * @typedef {{ | |
| 41 * supportedLanguages: !Array<!chrome.languageSettingsPrivate.Language>, | |
| 42 * enabledLanguages: !Array<!LanguageInfo> | |
| 43 * }} | |
| 44 */ | |
| 45 var LanguagesModel; | |
| 46 | |
| 47 (function() { | |
| 48 'use strict'; | |
| 49 | |
| 50 /** | |
| 51 * This element has a reference to the singleton, exposing the singleton's | |
| 52 * language model to the host of this element as the 'languages' property. | |
| 53 */ | |
| 54 Polymer({ | |
| 55 is: 'cr-settings-languages', | |
| 56 | |
| 57 properties: { | |
| 58 /** | |
| 59 * Singleton element created at startup which provides the languages model. | |
| 60 * @type {!Element} | |
| 61 */ | |
| 62 singleton_: { | |
| 63 type: Object, | |
| 64 value: document.createElement('cr-settings-languages-singleton'), | |
| 65 }, | |
| 66 | |
| 67 /** | |
| 68 * A reference to the languages model from the singleton, exposed as a | |
| 69 * read-only property so hosts can bind to it, but not change it. | |
| 70 * @type {LanguagesModel|undefined} | |
| 71 */ | |
| 72 languages: { | |
| 73 type: Object, | |
| 74 notify: true, | |
| 75 readOnly: true, | |
| 76 }, | |
| 77 }, | |
| 78 | |
| 79 ready: function() { | |
| 80 // Set the 'languages' property to reference the singleton's model. | |
| 81 this._setLanguages(this.singleton_.languages); | |
| 82 // Listen for changes to the singleton's languages property, so we know | |
| 83 // when to notify hosts of changes to (our reference to) the property. | |
| 84 this.listen( | |
| 85 this.singleton_, 'languages-changed', 'singletonLanguagesChanged_'); | |
| 86 }, | |
| 87 | |
| 88 /** | |
| 89 * Takes changes reported by the singleton and forwards them to the host, | |
| 90 * manually sending a change notification for our 'languages' property (since | |
| 91 * it's the same object as the singleton's property, but isn't bound by | |
| 92 * Polymer). | |
| 93 * @private | |
| 94 */ | |
| 95 singletonLanguagesChanged_: function(e) { | |
| 96 // Forward the change notification to the host. | |
| 97 this.fire(e.type, e.detail, {bubbles: false}); | |
| 98 }, | |
| 99 | |
| 100 // Forward public methods to the singleton. | |
| 101 | |
| 102 /** @param {string} languageCode */ | |
| 103 setUILanguage: function(languageCode) { | |
| 104 if (cr.isWindows || cr.isChromeOS) | |
| 105 this.singleton_.setUILanguage(languageCode); | |
| 106 }, | |
| 107 | |
| 108 /** @param {string} languageCode */ | |
| 109 enableLanguage: function(languageCode) { | |
| 110 this.singleton_.enableLanguage(languageCode); | |
| 111 }, | |
| 112 | |
| 113 /** @param {string} languageCode */ | |
| 114 disableLanguage: function(languageCode) { | |
| 115 this.singleton_.disableLanguage(languageCode); | |
| 116 }, | |
| 117 | |
| 118 /** | |
| 119 * @param {string} languageCode | |
| 120 * @return {boolean} | |
| 121 */ | |
| 122 isEnabled: function(languageCode) { | |
| 123 return this.singleton_.isEnabled(languageCode); | |
| 124 }, | |
| 125 | |
| 126 /** | |
| 127 * @param {string} languageCode | |
| 128 * @param {boolean} enable | |
| 129 */ | |
| 130 toggleSpellCheck: function(languageCode, enable) { | |
| 131 this.singleton_.toggleSpellCheck(languageCode, enable); | |
| 132 }, | |
| 133 }); | |
| 134 | |
| 135 var preferredLanguagesPrefName = cr.isChromeOS ? | |
| 136 'settings.language.preferred_languages' : 'intl.accept_languages'; | |
| 137 | |
| 138 /** | |
| 139 * Singleton element created when cr-settings-languages is registered. | |
| 140 * Generates the languages model on start-up, and updates it whenever Chrome's | |
| 141 * pref store and other settings change. These updates propagate to each | |
| 142 * <cr-settings-language> instance so that their 'languages' property updates | |
| 143 * like any other Polymer property. | |
| 144 */ | |
| 145 Polymer({ | |
| 146 is: 'cr-settings-languages-singleton', | |
| 147 | |
| 148 properties: { | |
| 149 /** | |
| 150 * @type {LanguagesModel|undefined} | |
| 151 */ | |
| 152 languages: { | |
| 153 type: Object, | |
| 154 notify: true, | |
| 155 }, | |
| 156 | |
| 157 /** | |
| 158 * Object containing all preferences. | |
| 159 */ | |
| 160 prefs: { | |
| 161 type: Object, | |
| 162 notify: true, | |
| 163 }, | |
| 164 }, | |
| 165 | |
| 166 /** | |
| 167 * Hash map of languages.supportedLanguages using language codes as keys for | |
| 168 * fast lookup. | |
| 169 * @private {!Object<!chrome.languageSettingsPrivate.Language>} | |
| 170 */ | |
| 171 supportedLanguageMap_: {}, | |
| 172 | |
| 173 /** | |
| 174 * Hash map of languages.enabledLanguages using language codes as keys for | |
| 175 * fast lookup. | |
| 176 * @private {!Object<!LanguageInfo>} | |
| 177 */ | |
| 178 enabledLanguageMap_: {}, | |
| 179 | |
| 180 observers: [ | |
| 181 'preferredLanguagesPrefChanged_(prefs.' + | |
| 182 preferredLanguagesPrefName + '.value)', | |
| 183 'spellCheckDictionariesPrefChanged_(prefs.spellcheck.dictionaries.value.*)', | |
| 184 ], | |
| 185 | |
| 186 /** @override */ | |
| 187 created: function() { | |
| 188 chrome.languageSettingsPrivate.getLanguageList(function(languageList) { | |
| 189 // Wait until prefs are initialized before creating the model, so we can | |
| 190 // include information about enabled languages. | |
| 191 CrSettingsPrefs.initialized.then(function() { | |
| 192 this.createModel_(languageList); | |
| 193 this.initialized_ = true; | |
| 194 }.bind(this)); | |
| 195 }.bind(this)); | |
| 196 }, | |
| 197 | |
| 198 /** | |
| 199 * Constructs the languages model from the given language list. | |
| 200 * @param {!Array<!chrome.languageSettingsPrivate.Language>} | |
| 201 * supportedLanguages | |
| 202 */ | |
| 203 createModel_: function(supportedLanguages) { | |
| 204 // Populate the hash map of supported languages. | |
| 205 for (var i = 0; i < supportedLanguages.length; i++) { | |
| 206 this.supportedLanguageMap_[supportedLanguages[i].code] = | |
| 207 supportedLanguages[i]; | |
| 208 } | |
| 209 | |
| 210 // Create a list of enabled language info from the supported languages. | |
| 211 var enabledLanguages = this.getEnabledLanguages_(); | |
| 212 // Populate the hash map of enabled languages. | |
| 213 for (var i = 0; i < enabledLanguages.length; i++) { | |
| 214 var languageInfo = enabledLanguages[i]; | |
| 215 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo; | |
| 216 } | |
| 217 | |
| 218 // Initialize the Polymer languages model. | |
| 219 this.languages = { | |
| 220 supportedLanguages: supportedLanguages, | |
| 221 enabledLanguages: enabledLanguages, | |
| 222 }; | |
| 223 }, | |
| 224 | |
| 225 /** | |
| 226 * Returns a list of LanguageInfos for each enabled language in the supported | |
| 227 * languages list. | |
| 228 * @return {!Array<!LanguageInfo>} | |
| 229 * @private | |
| 230 */ | |
| 231 getEnabledLanguages_: function() { | |
| 232 assert(CrSettingsPrefs.isInitialized); | |
| 233 | |
| 234 var pref = this.getPref_(preferredLanguagesPrefName); | |
| 235 var enabledLanguageCodes = pref.value.split(','); | |
| 236 var enabledLanguages = []; | |
| 237 var spellCheckMap = this.getSpellCheckMap_(); | |
| 238 for (var i = 0; i < enabledLanguageCodes.length; i++) { | |
| 239 var code = enabledLanguageCodes[i]; | |
| 240 var language = this.supportedLanguageMap_[code]; | |
| 241 if (!language) | |
| 242 continue; | |
| 243 var state = {spellCheckEnabled: !!spellCheckMap[code]}; | |
| 244 enabledLanguages.push({language: language, state: state}); | |
| 245 } | |
| 246 return enabledLanguages; | |
| 247 }, | |
| 248 | |
| 249 /** | |
| 250 * Creates a map whose keys are languages enabled for spell check. | |
| 251 * @return {!Object<boolean>} | |
| 252 */ | |
| 253 getSpellCheckMap_: function() { | |
| 254 assert(CrSettingsPrefs.isInitialized); | |
| 255 | |
| 256 var spellCheckCodes = this.getPref_('spellcheck.dictionaries').value; | |
| 257 var spellCheckMap = {}; | |
| 258 for (var i = 0; i < spellCheckCodes.length; i++) | |
| 259 spellCheckMap[spellCheckCodes[i]] = true; | |
| 260 return spellCheckMap; | |
| 261 }, | |
| 262 | |
| 263 /** | |
| 264 * Updates the list of enabled languages from the preferred languages pref. | |
| 265 * @private | |
| 266 * */ | |
| 267 preferredLanguagesPrefChanged_: function() { | |
| 268 if (!this.initialized_) | |
| 269 return; | |
| 270 | |
| 271 var enabledLanguages = this.getEnabledLanguages_(); | |
| 272 // Reset the enabled language map. Do this before notifying of the change | |
| 273 // via languages.enabledLanguages. | |
| 274 this.enabledLanguageMap_ = {}; | |
| 275 for (var i = 0; i < enabledLanguages.length; i++) { | |
| 276 var languageInfo = enabledLanguages[i]; | |
| 277 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo; | |
| 278 } | |
| 279 this.set('languages.enabledLanguages', enabledLanguages); | |
| 280 }, | |
| 281 | |
| 282 /** | |
| 283 * Updates the spellCheckEnabled state of each enabled language. | |
| 284 * @private | |
| 285 */ | |
| 286 spellCheckDictionariesPrefChanged_: function() { | |
| 287 if (!this.initialized_) | |
| 288 return; | |
| 289 | |
| 290 var spellCheckMap = this.getSpellCheckMap_(); | |
| 291 for (var i = 0; i < this.languages.enabledLanguages.length; i++) { | |
| 292 var languageCode = this.languages.enabledLanguages[i].language.code; | |
| 293 this.set('languages.enabledLanguages.' + i + '.state.spellCheckEnabled', | |
| 294 !!spellCheckMap[languageCode]); | |
| 295 } | |
| 296 }, | |
| 297 | |
| 298 /** | |
| 299 * Gets the pref at the given key. Asserts if the pref is not found. | |
| 300 * @param {string} key | |
| 301 * @return {!chrome.settingsPrivate.PrefObject} | |
| 302 */ | |
| 303 getPref_: function(key) { | |
| 304 var pref = /** @type {!chrome.settingsPrivate.PrefObject} */( | |
| 305 this.get(key, this.prefs)); | |
| 306 assert(pref != undefined, 'Pref is missing: ' + key); | |
|
Dan Beam
2015/09/25 01:50:50
nit: typeof pref != 'undefined' or pref !== undefi
michaelpg
2015/09/25 02:08:44
Done.
| |
| 307 return pref; | |
| 308 }, | |
| 309 | |
| 310 /** | |
| 311 * Sets the value of the pref at the given key. Asserts if the pref is not | |
| 312 * found. | |
| 313 * @param {string} key | |
| 314 * @param {*} value | |
| 315 */ | |
| 316 setPrefValue_: function(key, value) { | |
| 317 this.getPref_(key); | |
| 318 this.set('prefs.' + key + '.value', value); | |
| 319 }, | |
| 320 | |
| 321 /** | |
| 322 * Windows and Chrome OS only: Sets the prospective UI language to the chosen | |
| 323 * language. This dosen't affect the actual UI language until a restart. | |
| 324 * @param {string} languageCode | |
| 325 */ | |
| 326 setUILanguage: function(languageCode) { | |
| 327 chrome.send('setUILanguage', [languageCode]); | |
| 328 }, | |
| 329 | |
| 330 /** | |
| 331 * Enables the language, making it available for spell check and input. | |
| 332 * @param {string} languageCode | |
| 333 */ | |
| 334 enableLanguage: function(languageCode) { | |
| 335 if (!CrSettingsPrefs.isInitialized) | |
| 336 return; | |
| 337 | |
| 338 var languageCodes = this.getPref_(preferredLanguagesPrefName).value; | |
| 339 var index = languageCodes.split(',').indexOf(languageCode); | |
| 340 if (index > -1) | |
| 341 return; | |
| 342 this.setPrefValue_(preferredLanguagesPrefName, | |
| 343 languageCodes + ',' + languageCode); | |
| 344 }, | |
| 345 | |
| 346 /** | |
| 347 * Disables the language. | |
| 348 * @param {string} languageCode | |
| 349 */ | |
| 350 disableLanguage: function(languageCode) { | |
| 351 if (!CrSettingsPrefs.isInitialized) | |
| 352 return; | |
| 353 | |
| 354 // Cannot disable the UI language. | |
| 355 var appLocale = this.getPref_('intl.app_locale').value || | |
| 356 navigator.language; | |
| 357 assert(languageCode != appLocale); | |
| 358 | |
| 359 // Cannot disable the only enabled language. | |
| 360 var pref = this.getPref_(preferredLanguagesPrefName); | |
| 361 var languageCodes = pref.value.split(','); | |
| 362 assert(languageCodes.length > 1); | |
| 363 | |
| 364 // Remove the language from spell check. | |
| 365 this.arrayDelete('prefs.spellcheck.dictionaries.value', languageCode); | |
| 366 | |
| 367 var languageIndex = languageCodes.indexOf(languageCode); | |
| 368 if (languageIndex == -1) | |
| 369 return; | |
| 370 languageCodes.splice(languageIndex, 1); | |
| 371 this.setPrefValue_(preferredLanguagesPrefName, languageCodes.join(',')); | |
| 372 }, | |
| 373 | |
| 374 /** | |
| 375 * @param {string} languageCode | |
| 376 * @return {boolean} True if the language is enabled. | |
| 377 */ | |
| 378 isEnabled: function(languageCode) { | |
| 379 return !!this.enabledLanguageMap_[languageCode]; | |
| 380 }, | |
| 381 | |
| 382 /** | |
| 383 * Enables or disables spell check for the given language. | |
| 384 * @param {string} languageCode | |
| 385 * @param {boolean} enable | |
| 386 */ | |
| 387 toggleSpellCheck: function(languageCode, enable) { | |
| 388 if (!this.initialized_) | |
| 389 return; | |
| 390 | |
| 391 if (enable) { | |
| 392 var spellCheckPref = this.getPref_('spellcheck.dictionaries'); | |
| 393 if (spellCheckPref.value.indexOf(languageCode) == -1) | |
| 394 this.push('prefs.spellcheck.dictionaries.value', languageCode); | |
| 395 } else { | |
| 396 this.arrayDelete('prefs.spellcheck.dictionaries.value', languageCode); | |
| 397 } | |
| 398 }, | |
| 399 }); | |
| 400 })(); | |
| OLD | NEW |