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

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

Issue 1351623008: MD Settings: Languages model for language pages (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@SingletonPrefs
Patch Set: stevenjb model feedback Created 5 years, 3 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
(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-model' provides convenient access to
7 * Chrome's language and input method settings.
8 *
9 * A singleton element, 'cr-settings-language-model-private', provides a
10 * read-only model object with information about languages and input methods.
11 * The singleton updates this model whenever Chrome's pref store and other
12 * settings change, and also provides an API for changing these settings.
13 *
14 * To access the model's object and the singleton's API, add an instance of
15 * cr-settings-languages-model to your local DOM:
16 *
17 * <template>
18 * <cr-settings-languages-model model="{{model}}">
19 * </cr-settings-languages-model>
20 * <div>[[model.someProperty]]</div>
21 * </template>
22 *
23 * This element exposes the model and the API, so it can be used like any other
24 * Polymer element without having to actually create duplicate models.
25 *
26 * Internally, this element communicates with the singleton to propagate
27 * changes for data binding. Because each instance of this element has a
28 * reference to the same singleton, each element can add a listener for the
29 * singleton's 'model-changed' event. Once triggered, each element then forwards
30 * this event on to its host. From the perspective of the host, the element
31 * emits 'model-changed' events just like any other property change events.
32 *
33 * This data binding is one-way, child-to-host: the model property is read-only.
stevenjb 2015/09/22 16:47:33 This seems to contradict 'model="{{model}}"' above
michaelpg 2015/09/23 21:27:27 Yes, as described by https://www.polymer-project.o
34 * Any changes should be made through this element's public functions.
35 *
36 * @group Chrome Settings Elements
37 * @element cr-settings-languages-model
38 */
39
40 /**
41 * @typedef {{spellCheckEnabled: boolean}}
42 */
43 var LanguageState;
44
45 /**
46 * @typedef {{language: !chrome.languageSettingsPrivate.Language,
47 * state: !LanguageState}}
48 */
49 var LanguageInfo;
50
51 /**
52 * The model includes:
53 * supportedLanguages: an array of languages, ordered alphabetically.
54 * enabledLanguages: an array of enabled language info and state, ordered by
55 * preference.
56 * @typedef {{
57 * supportedLanguages: !Array<!chrome.languageSettingsPrivate.Language>,
58 * enabledLanguages: !Array<!LanguageInfo>
59 * }}
60 */
61 var LanguagesModel;
62
63 (function() {
64 'use strict';
65
66 /**
67 * This element has a reference to the singleton, exposing its model to other
68 * Polymer objects.
69 * */
70 Polymer({
71 is: 'cr-settings-languages-model',
72
73 properties: {
74 /**
75 * Singleton element created at startup which provides the model shared by
76 * all instances of cr-settings-language-model.
77 * @type {!Element}
78 */
79 privateModel_: {
80 type: Object,
81 value: document.createElement('cr-settings-languages-model-private'),
82 },
83
84 /**
85 * The singleton's private model, exposed as a property so hosts can bind
stevenjb 2015/09/22 16:47:33 This is super confusing, why is the above member n
86 * to it.
87 * @type {LanguagesModel|undefined}
88 */
89 model: {
90 type: Object,
91 notify: true,
92 readOnly: true,
93 },
94 },
95
96 ready: function() {
97 // Set the 'model' property to reference the singleton's model.
98 this._setModel(this.privateModel_.model);
99 // Have to manually notify the host of changes to this model property
100 // by listening for changes to the singleton's model property.
101 this.listen(this.privateModel_, 'model-changed', 'privateModelChanged_');
102 },
103
104 /**
105 * Forwards changes to the model reported by the singleton to the host.
106 * From the host's perspective, our 'model' property will have changed.
107 * @private
108 */
109 privateModelChanged_: function(e) {
110 // Forward the change notification to the host.
111 this.fire(e.type, e.detail, {bubbles: false});
112 },
113
114 // Forward public methods to the singleton.
michaelpg 2015/09/22 04:03:15 Obviously forwarding all these methods is ugly. Bu
115
116 /** @param {string} languageCode */
117 setUILanguage: function(languageCode) {
118 this.privateModel_.setUILanguage(languageCode);
119 },
120
121 /** @param {string} languageCode */
122 enableLanguage: function(languageCode) {
123 this.privateModel_.enableLanguage(languageCode);
124 },
125
126 /** @param {string} languageCode */
127 disableLanguage: function(languageCode) {
128 this.privateModel_.disableLanguage(languageCode);
129 },
130
131 /**
132 * @param {string} languageCode
133 * @return {boolean}
134 */
135 isEnabled: function(languageCode) {
136 return this.privateModel_.isEnabled(languageCode);
137 },
138
139 /**
140 * @param {string} languageCode
141 * @param {boolean} enable
142 */
143 toggleSpellCheck: function(languageCode, enable) {
144 this.privateModel_.toggleSpellCheck(languageCode, enable);
145 },
146 });
147
148 var preferredLanguagesPath;
149 if (cr.isChromeOS)
150 preferredLanguagesPath = 'prefs.settings.language.preferred_languages.value';
151 else
152 preferredLanguagesPath = 'prefs.intl.accept_languages.value';
153
154 /**
155 * Privately used element that contains, listens to and updates the shared
156 * languages model.
157 */
158 Polymer({
159 is: 'cr-settings-languages-model-private',
160
161 properties: {
162 /**
163 * @type {LanguagesModel|undefined}
164 */
165 model: {
166 type: Object,
167 notify: true,
168 },
169
170 /**
171 * Object containing all preferences, for use by Polymer controls.
172 */
173 prefs: {
174 type: Object,
175 notify: true,
176 },
177 },
178
179 /**
180 * Hash map of model.supportedLanguages using language codes as keys for
181 * fast lookup. Not data bound.
182 * @private {!Object<!chrome.languageSettingsPrivate.Language>}
183 */
184 supportedLanguageMap_: {},
185
186 /**
187 * Hash maps of model.enabledLanguages using language codes as keys for
188 * fast lookup. Not data bound.
189 * @private {!Object<!LanguageInfo>}
190 */
191 enabledLanguageMap_: {},
192
193 observers: [
194 'preferredLanguagesPrefChanged_(' + preferredLanguagesPath + ')',
195 'spellCheckDictionariesPrefChanged_(prefs.spellcheck.dictionaries.value.*)',
196 ],
197
198 /** @override */
199 created: function() {
200 chrome.languageSettingsPrivate.getLanguageList(function(languageList) {
201 // Wait until prefs are initialized before creating the model, so we can
202 // include information about enabled languages.
203 CrSettingsPrefs.initialized.then(function() {
204 this.createModel_(languageList);
205 this.initialized_ = true;
206 }.bind(this));
207 }.bind(this));
208 },
209
210 /**
211 * Constructs the model with the given language list.
212 * @param {!Array<!chrome.languageSettingsPrivate.Language>}
213 * supportedLanguages
214 */
215 createModel_: function(supportedLanguages) {
216 // Populate the hash map of supported languages.
217 for (var i = 0; i < supportedLanguages.length; i++) {
218 this.supportedLanguageMap_[supportedLanguages[i].code] =
219 supportedLanguages[i];
220 }
221
222 // Create a list of enabled language info from the supported languages.
223 var enabledLanguages = this.getEnabledLanguages_();
224 // Populate the hash map of enabled languages.
225 for (var i = 0; i < enabledLanguages.length; i++) {
226 var languageInfo = enabledLanguages[i];
227 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo;
228 }
229
230 // Initialize the Polymer model.
231 this.model = {
232 supportedLanguages: supportedLanguages,
233 enabledLanguages: enabledLanguages,
234 };
235 },
236
237 /**
238 * Returns a list of LanguageInfos for each enabled language in the supported
239 * languages list.
240 * @private
241 * @return {!Array<!LanguageInfo>}
242 */
243 getEnabledLanguages_: function() {
244 assert(CrSettingsPrefs.isInitialized);
245
246 var languageCodes = this.get(preferredLanguagesPath).split(',');
247 var enabledLanguages = [];
248 var spellCheckMap = this.getSpellCheckMap_();
249 for (var i = 0; i < languageCodes.length; i++) {
250 var code = languageCodes[i];
251 var language = this.supportedLanguageMap_[code];
252 if (!language)
253 continue;
254 var state = {spellCheckEnabled: !!spellCheckMap[code]};
255 enabledLanguages.push({language: language, state: state});
256 }
257 return enabledLanguages;
258 },
259
260 /**
261 * Creates a map whose keys are languages enabled for spell check.
262 * @return {!Object<boolean>}
263 */
264 getSpellCheckMap_: function() {
265 assert(CrSettingsPrefs.isInitialized);
266
267 var spellCheckPref = /** @type {chrome.settingsPrivate.PrefObject} */(
268 this.get('prefs.spellcheck.dictionaries'));
269 var spellCheckCodes = spellCheckPref.value;
270 var spellCheckMap = {};
271 for (var i = 0; i < spellCheckCodes.length; i++)
272 spellCheckMap[spellCheckCodes[i]] = true;
273 return spellCheckMap;
274 },
275
276 /** @private */
277 preferredLanguagesPrefChanged_: function() {
278 if (!this.initialized_)
279 return;
280
281 var enabledLanguages = this.getEnabledLanguages_();
282 // Reset the enabled language map. Do this before notifying of the change
283 // via model.enabledLanguages.
284 this.enabledLanguageMap_ = {};
285 for (var i = 0; i < enabledLanguages.length; i++) {
286 var languageInfo = enabledLanguages[i];
287 this.enabledLanguageMap_[languageInfo.language.code] = languageInfo;
288 }
289 this.set('model.enabledLanguages', enabledLanguages);
290 },
291
292 /**
293 * Updates the spellCheckEnabled state of each enabled language.
294 * @private
295 */
296 spellCheckDictionariesPrefChanged_: function() {
297 if (!this.initialized_)
298 return;
299
300 var spellCheckMap = this.getSpellCheckMap_();
301 for (var i = 0; i < this.model.enabledLanguages.length; i++) {
302 this.set('model.enabledLanguages.' + i + '.state.spellCheckEnabled',
303 !!spellCheckMap[this.model.enabledLanguages[i].language.code]);
304 }
305 },
306
307 /**
308 * Windows and Chrome OS only: Sets the prospective UI language to the chosen
309 * language. This dosen't affect the actual UI language until a restart.
310 * @param {string} languageCode
311 */
312 setUILanguage: function(languageCode) {
313 // Set the prospective UI language. This won't take effect until a restart.
314 chrome.send('setUILanguage', [languageCode]);
315 },
316
317 /**
318 * Enables the language, making it available for spell check and input.
319 * @param {string} languageCode
320 */
321 enableLanguage: function(languageCode) {
322 if (!CrSettingsPrefs.isInitialized)
323 return;
324
325 var languageCodes = this.get(preferredLanguagesPath);
326 var index = languageCodes.split(',').indexOf(languageCode);
327 if (index > -1)
328 return;
329 this.set(preferredLanguagesPath, languageCodes + ',' + languageCode);
330 },
331
332 /**
333 * Disables the language.
334 * @param {string} languageCode
335 */
336 disableLanguage: function(languageCode) {
337 if (!CrSettingsPrefs.isInitialized)
338 return;
339
340 // Don't disable the UI language.
341 var appLocale = this.get('prefs.intl.app_locale.value') ||
342 navigator.language;
343 if (languageCode == appLocale)
344 return;
345
346 var languageCodes = this.get(preferredLanguagesPath).split(',');
347 // Don't disable the only enabled language.
348 if (languageCodes.length == 1)
349 return;
350
351 // Remove the language from spell check.
352 var spellCheckIndex =
353 this.get('prefs.spellcheck.dictionaries.value').indexOf(languageCode);
354 if (spellCheckIndex != -1)
355 this.splice('prefs.spellcheck.dictionaries.value', spellCheckIndex, 1);
356
357 var languageIndex = languageCodes.indexOf(languageCode);
358 if (languageIndex == -1)
359 return;
360 languageCodes.splice(languageIndex, 1);
361 this.set(preferredLanguagesPath, languageCodes.join(','));
362 },
363
364 /**
365 * @param {string} languageCode
366 * @return {boolean} True if the language is enabled.
367 */
368 isEnabled: function(languageCode) {
369 return !!this.enabledLanguageMap_[languageCode];
370 },
371
372 /**
373 * Enables or disables spell check for the given language.
374 * @param {string} languageCode
375 * @param {boolean} enable
376 */
377 toggleSpellCheck: function(languageCode, enable) {
378 if (!this.initialized_)
379 return;
380
381 var spellCheckPref = /** @type {chrome.settingsPrivate.PrefObject} */(
382 this.get('prefs.spellcheck.dictionaries'));
383 if (enable) {
384 if (spellCheckPref.value.indexOf(languageCode) == -1)
385 this.push('prefs.spellcheck.dictionaries.value', languageCode);
386 } else {
387 // TODO: need update externs before committing
388 this.arrayDelete('prefs.spellcheck.dictionaries.value', languageCode);
389 }
390 },
391 });
392 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698