| 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(typeof pref != 'undefined', 'Pref is missing: ' + key); |
| 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 |