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_: { | |
|
stevenjb
2015/09/23 22:27:54
I like 'singleton' here.
michaelpg
2015/09/24 02:15:19
Acknowledged.
| |
| 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: { | |
|
stevenjb
2015/09/23 22:27:54
Heh, so, FWIW, I was / would be fine with this bei
michaelpg
2015/09/24 02:15:20
Acknowledged.
| |
| 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 preferredLanguagesPath; | |
| 136 if (cr.isChromeOS) | |
| 137 preferredLanguagesPath = 'prefs.settings.language.preferred_languages.value'; | |
| 138 else | |
| 139 preferredLanguagesPath = 'prefs.intl.accept_languages.value'; | |
|
stevenjb
2015/09/23 22:27:54
nit: I know that it would add a tiny bit of comple
Dan Beam
2015/09/23 23:28:17
can haz ternary?
michaelpg
2015/09/24 02:15:20
Done.
michaelpg
2015/09/24 02:15:20
Done.
| |
| 140 | |
| 141 /** | |
| 142 * Singleton element created when cr-settings-languages is registered. | |
| 143 * Generates the languages model on start-up, and updates it whenever Chrome's | |
| 144 * pref store and other settings change. These updates propagate to each | |
| 145 * <cr-settings-language> instance so that their 'languages' property updates | |
| 146 * like any other Polymer property. | |
| 147 */ | |
| 148 Polymer({ | |
| 149 is: 'cr-settings-languages-singleton', | |
| 150 | |
| 151 properties: { | |
| 152 /** | |
| 153 * @type {LanguagesModel|undefined} | |
| 154 */ | |
| 155 languages: { | |
|
stevenjb
2015/09/23 22:27:54
Same here, but it definitely makes sense to me to
michaelpg
2015/09/24 02:15:19
yep. If we don't match property names, we'd have t
| |
| 156 type: Object, | |
| 157 notify: true, | |
| 158 }, | |
| 159 | |
| 160 /** | |
| 161 * Object containing all preferences. | |
| 162 */ | |
| 163 prefs: { | |
| 164 type: Object, | |
| 165 notify: true, | |
| 166 }, | |
| 167 }, | |
| 168 | |
| 169 /** | |
| 170 * Hash map of languages.supportedLanguages using language codes as keys for | |
| 171 * fast lookup. | |
| 172 * @private {!Object<!chrome.languageSettingsPrivate.Language>} | |
| 173 */ | |
| 174 supportedLanguageMap_: {}, | |
| 175 | |
| 176 /** | |
| 177 * Hash map of languages.enabledLanguages using language codes as keys for | |
| 178 * fast lookup. | |
| 179 * @private {!Object<!LanguageInfo>} | |
| 180 */ | |
| 181 enabledLanguageMap_: {}, | |
| 182 | |
| 183 observers: [ | |
| 184 'preferredLanguagesPrefChanged_(' + preferredLanguagesPath + ')', | |
| 185 'spellCheckDictionariesPrefChanged_(prefs.spellcheck.dictionaries.value.*)', | |
| 186 ], | |
| 187 | |
| 188 /** @override */ | |
| 189 created: function() { | |
| 190 chrome.languageSettingsPrivate.getLanguageList(function(languageList) { | |
| 191 // Wait until prefs are initialized before creating the model, so we can | |
| 192 // include information about enabled languages. | |
| 193 CrSettingsPrefs.initialized.then(function() { | |
| 194 this.createModel_(languageList); | |
| 195 this.initialized_ = true; | |
| 196 }.bind(this)); | |
| 197 }.bind(this)); | |
| 198 }, | |
| 199 | |
| 200 /** | |
| 201 * Constructs the languages model from the given language list. | |
| 202 * @param {!Array<!chrome.languageSettingsPrivate.Language>} | |
| 203 * supportedLanguages | |
| 204 */ | |
| 205 createModel_: function(supportedLanguages) { | |
| 206 // Populate the hash map of supported languages. | |
| 207 for (var i = 0; i < supportedLanguages.length; i++) { | |
| 208 this.supportedLanguageMap_[supportedLanguages[i].code] = | |
| 209 supportedLanguages[i]; | |
| 210 } | |
| 211 | |
| 212 // Create a list of enabled language info from the supported languages. | |
| 213 var enabledLanguages = this.getEnabledLanguages_(); | |
| 214 // Populate the hash map of enabled languages. | |
| 215 for (var i = 0; i < enabledLanguages.length; i++) { | |
| 216 var languageInfo = enabledLanguages[i]; | |
| 217 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo; | |
| 218 } | |
| 219 | |
| 220 // Initialize the Polymer languages model. | |
| 221 this.languages = { | |
| 222 supportedLanguages: supportedLanguages, | |
| 223 enabledLanguages: enabledLanguages, | |
| 224 }; | |
| 225 }, | |
| 226 | |
| 227 /** | |
| 228 * Returns a list of LanguageInfos for each enabled language in the supported | |
| 229 * languages list. | |
| 230 * @private | |
| 231 * @return {!Array<!LanguageInfo>} | |
| 232 */ | |
| 233 getEnabledLanguages_: function() { | |
| 234 assert(CrSettingsPrefs.isInitialized); | |
| 235 | |
| 236 var languageCodes = this.get(preferredLanguagesPath).split(','); | |
|
stevenjb
2015/09/23 22:27:54
What do you think of:
getPref_: function(path) {
michaelpg
2015/09/24 02:15:20
Done. It's not bad, but I'm not sure about the pre
stevenjb
2015/09/24 15:46:06
Yeah, I've been trying to think of a nice way we c
stevenjb
2015/09/24 15:46:06
Yeah, I've been trying to think of a nice way we c
michaelpg
2015/09/24 17:21:11
Yeah, a behavior would be a good way to make this
| |
| 237 var enabledLanguages = []; | |
| 238 var spellCheckMap = this.getSpellCheckMap_(); | |
| 239 for (var i = 0; i < languageCodes.length; i++) { | |
| 240 var code = languageCodes[i]; | |
| 241 var language = this.supportedLanguageMap_[code]; | |
| 242 if (!language) | |
| 243 continue; | |
| 244 var state = {spellCheckEnabled: !!spellCheckMap[code]}; | |
|
Dan Beam
2015/09/23 23:28:17
why does this need to be a full dictionary?
michaelpg
2015/09/24 02:15:19
other properties are a'comin, like translateEnable
| |
| 245 enabledLanguages.push({language: language, state: state}); | |
| 246 } | |
| 247 return enabledLanguages; | |
| 248 }, | |
| 249 | |
| 250 /** | |
| 251 * Creates a map whose keys are languages enabled for spell check. | |
| 252 * @return {!Object<boolean>} | |
| 253 */ | |
| 254 getSpellCheckMap_: function() { | |
| 255 assert(CrSettingsPrefs.isInitialized); | |
| 256 | |
| 257 var spellCheckPref = /** @type {chrome.settingsPrivate.PrefObject} */( | |
| 258 this.get('prefs.spellcheck.dictionaries')); | |
| 259 var spellCheckCodes = spellCheckPref.value; | |
| 260 var spellCheckMap = {}; | |
| 261 for (var i = 0; i < spellCheckCodes.length; i++) | |
| 262 spellCheckMap[spellCheckCodes[i]] = true; | |
| 263 return spellCheckMap; | |
| 264 }, | |
| 265 | |
| 266 /** | |
| 267 * Updates the list of enabled languages from the preferred languages pref. | |
| 268 * @private | |
| 269 * */ | |
| 270 preferredLanguagesPrefChanged_: function() { | |
| 271 if (!this.initialized_) | |
| 272 return; | |
| 273 | |
| 274 var enabledLanguages = this.getEnabledLanguages_(); | |
| 275 // Reset the enabled language map. Do this before notifying of the change | |
| 276 // via languages.enabledLanguages. | |
| 277 this.enabledLanguageMap_ = {}; | |
| 278 for (var i = 0; i < enabledLanguages.length; i++) { | |
| 279 var languageInfo = enabledLanguages[i]; | |
| 280 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo; | |
| 281 } | |
| 282 this.set('languages.enabledLanguages', enabledLanguages); | |
| 283 }, | |
| 284 | |
| 285 /** | |
| 286 * Updates the spellCheckEnabled state of each enabled language. | |
| 287 * @private | |
| 288 */ | |
| 289 spellCheckDictionariesPrefChanged_: function() { | |
| 290 if (!this.initialized_) | |
| 291 return; | |
| 292 | |
| 293 var spellCheckMap = this.getSpellCheckMap_(); | |
| 294 for (var i = 0; i < this.languages.enabledLanguages.length; i++) { | |
| 295 var languageCode = this.languages.enabledLanguages[i].language.code; | |
| 296 this.set('languages.enabledLanguages.' + i + '.state.spellCheckEnabled', | |
| 297 !!spellCheckMap[languageCode]); | |
| 298 } | |
| 299 }, | |
| 300 | |
| 301 /** | |
| 302 * Windows and Chrome OS only: Sets the prospective UI language to the chosen | |
| 303 * language. This dosen't affect the actual UI language until a restart. | |
| 304 * @param {string} languageCode | |
| 305 */ | |
| 306 setUILanguage: function(languageCode) { | |
| 307 chrome.send('setUILanguage', [languageCode]); | |
| 308 }, | |
| 309 | |
| 310 /** | |
| 311 * Enables the language, making it available for spell check and input. | |
| 312 * @param {string} languageCode | |
| 313 */ | |
| 314 enableLanguage: function(languageCode) { | |
| 315 if (!CrSettingsPrefs.isInitialized) | |
| 316 return; | |
| 317 | |
| 318 var languageCodes = this.get(preferredLanguagesPath); | |
| 319 var index = languageCodes.split(',').indexOf(languageCode); | |
| 320 if (index > -1) | |
| 321 return; | |
| 322 this.set(preferredLanguagesPath, languageCodes + ',' + languageCode); | |
| 323 }, | |
| 324 | |
| 325 /** | |
| 326 * Disables the language. | |
| 327 * @param {string} languageCode | |
| 328 */ | |
| 329 disableLanguage: function(languageCode) { | |
| 330 if (!CrSettingsPrefs.isInitialized) | |
| 331 return; | |
| 332 | |
| 333 // Don't disable the UI language. | |
| 334 var appLocale = this.get('prefs.intl.app_locale.value') || | |
| 335 navigator.language; | |
| 336 if (languageCode == appLocale) | |
|
Dan Beam
2015/09/23 23:28:17
wait, how does this happen? can this be assert(la
michaelpg
2015/09/24 02:15:20
Done.
| |
| 337 return; | |
| 338 | |
| 339 // Don't disable the only enabled language. | |
|
Dan Beam
2015/09/23 23:28:17
same question: don't we prevent this in the UI (so
michaelpg
2015/09/24 02:15:20
Done.
| |
| 340 var languageCodes = this.get(preferredLanguagesPath).split(','); | |
| 341 if (languageCodes.length == 1) | |
| 342 return; | |
| 343 | |
| 344 // Remove the language from spell check. | |
| 345 this.arrayDelete('prefs.spellcheck.dictionaries.value', languageCode); | |
| 346 | |
| 347 var languageIndex = languageCodes.indexOf(languageCode); | |
| 348 if (languageIndex == -1) | |
| 349 return; | |
| 350 languageCodes.splice(languageIndex, 1); | |
| 351 this.set(preferredLanguagesPath, languageCodes.join(',')); | |
| 352 }, | |
| 353 | |
| 354 /** | |
| 355 * @param {string} languageCode | |
| 356 * @return {boolean} True if the language is enabled. | |
| 357 */ | |
| 358 isEnabled: function(languageCode) { | |
| 359 return !!this.enabledLanguageMap_[languageCode]; | |
| 360 }, | |
| 361 | |
| 362 /** | |
| 363 * Enables or disables spell check for the given language. | |
| 364 * @param {string} languageCode | |
| 365 * @param {boolean} enable | |
| 366 */ | |
| 367 toggleSpellCheck: function(languageCode, enable) { | |
| 368 if (!this.initialized_) | |
| 369 return; | |
| 370 | |
| 371 var spellCheckPref = /** @type {chrome.settingsPrivate.PrefObject} */( | |
| 372 this.get('prefs.spellcheck.dictionaries')); | |
| 373 if (enable) { | |
| 374 if (spellCheckPref.value.indexOf(languageCode) == -1) | |
| 375 this.push('prefs.spellcheck.dictionaries.value', languageCode); | |
| 376 } else { | |
| 377 // TODO: need update externs before committing | |
|
Dan Beam
2015/09/23 23:28:17
TODO(michaelpg): or actually do
michaelpg
2015/09/24 02:15:20
actually done (after four tries): https://coderevi
| |
| 378 this.arrayDelete('prefs.spellcheck.dictionaries.value', languageCode); | |
| 379 } | |
| 380 }, | |
| 381 }); | |
| 382 })(); | |
| OLD | NEW |