blob: 7928445d2a3160153b41b6095fecb0c267392450 [file] [log] [blame]
michaelpg790ff8a2015-09-03 03:27:221// 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/**
tommycli5bcd3c1b2015-10-07 23:25:546 * @fileoverview 'settings-languages-page' is the settings page
michaelpg790ff8a2015-09-03 03:27:227 * for language and input method settings.
michaelpg790ff8a2015-09-03 03:27:228 */
michaelpgc5bbdca2017-01-18 02:59:379cr.exportPath('settings');
10
11/**
dpapad00039d92018-01-08 22:01:4812 * @type {number} Millisecond delay that can be used when closing an action
michaelpgc5bbdca2017-01-18 02:59:3713 * menu to keep it briefly on-screen.
14 */
15settings.kMenuCloseDelay = 100;
16
Renjie Liub350ff322017-09-05 05:02:3617/**
18 * Name of the language setting is shown uma histogram.
19 * @type {string}
20 */
21const LANGUAGE_SETTING_IS_SHOWN_UMA_NAME = 'Translate.LanguageSettingsIsShown';
22
michaelpg0f479fec2015-09-25 04:03:4923(function() {
24'use strict';
25
michaelpg790ff8a2015-09-03 03:27:2226Polymer({
tommycli5bcd3c1b2015-10-07 23:25:5427 is: 'settings-languages-page',
michaelpg790ff8a2015-09-03 03:27:2228
29 properties: {
30 /**
31 * Preferences state.
32 */
33 prefs: {
34 type: Object,
35 notify: true,
36 },
37
michaelpg790ff8a2015-09-03 03:27:2238 /**
michaelpg0f479fec2015-09-25 04:03:4939 * Read-only reference to the languages model provided by the
tommycli5bcd3c1b2015-10-07 23:25:5440 * 'settings-languages' instance.
michaelpgabbd180d2016-04-23 02:42:3541 * @type {!LanguagesModel|undefined}
michaelpg790ff8a2015-09-03 03:27:2242 */
michaelpg0f479fec2015-09-25 04:03:4943 languages: {
44 type: Object,
45 notify: true,
michaelpg790ff8a2015-09-03 03:27:2246 },
dschuyler825c18d42016-02-11 02:51:0547
michaelpg75252ef72016-08-19 01:47:3948 /** @type {!LanguageHelper} */
49 languageHelper: Object,
50
dbeam141ae1f2017-06-19 18:28:3551 // <if expr="not is_macosx">
dschuyler825c18d42016-02-11 02:51:0552 /** @private */
michaelpg44d9dc82016-12-08 02:04:0053 spellCheckSecondaryText_: {
dschuyler825c18d42016-02-11 02:51:0554 type: String,
michaelpg44d9dc82016-12-08 02:04:0055 value: '',
Marc-Antoine Courteau311a7cec2017-12-14 21:18:4956 computed: 'getSpellCheckSecondaryText_(languages.enabled.*, ' +
Marc-Antoine Courteau41f4c372017-12-18 19:33:0757 'languages.forcedSpellCheckLanguages.*, ' +
58 'prefs.browser.enable_spellchecking.*)',
Marc-Antoine Courteau311a7cec2017-12-14 21:18:4959 },
60
61 /** @private */
62 spellCheckLanguages_: {
63 type: Array,
64 value: function() {
65 return [];
66 },
dschuyler825c18d42016-02-11 02:51:0567 },
Marc-Antoine Courteau41f4c372017-12-18 19:33:0768
69 /** @private */
70 spellCheckDisabled_: {
71 type: Boolean,
72 value: false,
73 },
dbeam141ae1f2017-06-19 18:28:3574 // </if>
michaelpg4d04ee92016-04-08 19:56:2875
76 /**
77 * The language to display the details for.
michaelpgabbd180d2016-04-23 02:42:3578 * @type {!LanguageState|undefined}
michaelpg4d04ee92016-04-08 19:56:2879 * @private
80 */
81 detailLanguage_: Object,
michaelpg9a670d42016-08-25 02:06:3782
Renjie Liub350ff322017-09-05 05:02:3683 /**
84 * Whether the language settings list is opened.
85 * @private
86 */
87 languagesOpened_: {
88 type: Boolean,
89 observer: 'onLanguagesOpenedChanged_',
90 },
91
michaelpg9a670d42016-08-25 02:06:3792 /** @private */
93 showAddLanguagesDialog_: Boolean,
dpapada4375e02017-04-11 04:07:4994
95 /** @private {!Map<string, string>} */
96 focusConfig_: {
97 type: Object,
98 value: function() {
dpapad00039d92018-01-08 22:01:4899 const map = new Map();
dbeam141ae1f2017-06-19 18:28:35100 // <if expr="not is_macosx">
scottchenc31781b2017-07-10 19:52:42101 if (settings.routes.EDIT_DICTIONARY) {
102 map.set(
103 settings.routes.EDIT_DICTIONARY.path,
dpapadfc810ef2018-07-09 23:44:15104 '#spellCheckCollapse .subpage-arrow button');
scottchenc31781b2017-07-10 19:52:42105 }
dbeam141ae1f2017-06-19 18:28:35106 // </if>
107 // <if expr="chromeos">
scottchenc31781b2017-07-10 19:52:42108 if (settings.routes.INPUT_METHODS) {
109 map.set(
110 settings.routes.INPUT_METHODS.path,
dpapadfc810ef2018-07-09 23:44:15111 '#inputMethodsCollapse .subpage-arrow button');
scottchenc31781b2017-07-10 19:52:42112 }
dbeam141ae1f2017-06-19 18:28:35113 // </if>
dpapada4375e02017-04-11 04:07:49114 return map;
115 },
116 },
michaelpg45acdc7a2016-04-17 01:27:11117 },
michaelpga49c98b2015-12-04 20:27:25118
Marc-Antoine Courteau75c1ff32018-02-02 02:22:43119 // <if expr="not is_macosx">
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49120 observers: [
121 'updateSpellcheckLanguages_(languages.enabled.*, ' +
122 'languages.forcedSpellCheckLanguages.*)',
Marc-Antoine Courteau41f4c372017-12-18 19:33:07123 'updateSpellcheckEnabled_(prefs.browser.enable_spellchecking.*)',
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49124 ],
Esmael El-Moslimany3f225c82018-03-29 23:27:32125
126 /**
127 * Checks if there are any errors downloading the spell check dictionary. This
128 * is used for showing/hiding error messages, spell check toggle and retry.
129 * button.
130 * @param {number} downloadDictionaryFailureCount
131 * @param {number} threshold
132 * @return {boolean}
133 * @private
134 */
135 errorsGreaterThan_: function(downloadDictionaryFailureCount, threshold) {
136 return downloadDictionaryFailureCount > threshold;
137 },
Marc-Antoine Courteau75c1ff32018-02-02 02:22:43138 // </if>
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49139
michaelpg790ff8a2015-09-03 03:27:22140 /**
michaelpg9a670d42016-08-25 02:06:37141 * Stamps and opens the Add Languages dialog, registering a listener to
142 * disable the dialog's dom-if again on close.
hcarmona60836482016-12-08 22:42:07143 * @param {!Event} e
michaelpg790ff8a2015-09-03 03:27:22144 * @private
145 */
hcarmona60836482016-12-08 22:42:07146 onAddLanguagesTap_: function(e) {
147 e.preventDefault();
michaelpg9a670d42016-08-25 02:06:37148 this.showAddLanguagesDialog_ = true;
dpapad2bd0fd442018-06-15 17:09:02149 },
150
151 /** @private */
152 onAddLanguagesDialogClose_: function() {
153 this.showAddLanguagesDialog_ = false;
154 cr.ui.focusWithoutInk(assert(this.$.addLanguages));
michaelpg790ff8a2015-09-03 03:27:22155 },
156
157 /**
michaelpg4fed88702017-02-02 20:57:19158 * Used to determine which "Move" buttons to show for ordering enabled
159 * languages.
160 * @param {number} n
michaelpg4fed88702017-02-02 20:57:19161 * @return {boolean} True if |language| is at the |n|th index in the list of
162 * enabled languages.
mahmadi04d3fdc62016-06-28 14:43:24163 * @private
164 */
dpapadf7345e7e2018-06-26 22:46:33165 isNthLanguage_: function(n) {
166 if (this.languages == undefined || this.detailLanguage_ == undefined)
167 return false;
168
dpapad00039d92018-01-08 22:01:48169 const compareLanguage = assert(this.languages.enabled[n]);
dpapadf7345e7e2018-06-26 22:46:33170 return this.detailLanguage_.language == compareLanguage.language;
mahmadi04d3fdc62016-06-28 14:43:24171 },
172
173 /**
michaelpg4fed88702017-02-02 20:57:19174 * @return {boolean} True if the "Move to top" option for |language| should be
175 * visible.
michaelpge11da422016-09-29 00:36:40176 * @private
177 */
dpapadf7345e7e2018-06-26 22:46:33178 showMoveUp_: function() {
michaelpg4fed88702017-02-02 20:57:19179 // "Move up" is a no-op for the top language, and redundant with
180 // "Move to top" for the 2nd language.
dpapadf7345e7e2018-06-26 22:46:33181 return !this.isNthLanguage_(0) && !this.isNthLanguage_(1);
michaelpge11da422016-09-29 00:36:40182 },
183
184 /**
michaelpg4fed88702017-02-02 20:57:19185 * @return {boolean} True if the "Move down" option for |language| should be
186 * visible.
mahmadi04d3fdc62016-06-28 14:43:24187 * @private
188 */
dpapadf7345e7e2018-06-26 22:46:33189 showMoveDown_: function() {
190 return this.languages != undefined &&
191 !this.isNthLanguage_(this.languages.enabled.length - 1);
mahmadi04d3fdc62016-06-28 14:43:24192 },
193
194 /**
195 * @param {!Object} change Polymer change object for languages.enabled.*.
196 * @return {boolean} True if there are less than 2 languages.
197 */
198 isHelpTextHidden_: function(change) {
dpapadf7345e7e2018-06-26 22:46:33199 return this.languages != undefined && this.languages.enabled.length <= 1;
mahmadi04d3fdc62016-06-28 14:43:24200 },
201
dbeam141ae1f2017-06-19 18:28:35202 // <if expr="chromeos">
dpapadeb8e8df2017-04-19 17:45:26203 /**
204 * Applies Chrome OS session tweaks to the menu.
205 * @param {!CrActionMenuElement} menu
206 * @private
207 */
208 tweakMenuForCrOS_: function(menu) {
209 // In a CrOS multi-user session, the primary user controls the UI language.
210 // TODO(michaelpg): The language selection should not be hidden, but should
211 // show a policy indicator. crbug.com/648498
212 if (this.isSecondaryUser_())
213 menu.querySelector('#uiLanguageItem').hidden = true;
214
215 // The UI language choice doesn't persist for guests.
Wenzhao Zangc7402e82018-10-23 17:49:17216 if (loadTimeData.getBoolean('isGuest') &&
217 !loadTimeData.getBoolean('isDemoSession')) {
dpapadeb8e8df2017-04-19 17:45:26218 menu.querySelector('#uiLanguageItem').hidden = true;
Wenzhao Zangc7402e82018-10-23 17:49:17219 }
dpapadeb8e8df2017-04-19 17:45:26220 },
221
222 /**
223 * Opens the Manage Input Methods page.
224 * @private
225 */
226 onManageInputMethodsTap_: function() {
scottchenc31781b2017-07-10 19:52:42227 settings.navigateTo(settings.routes.INPUT_METHODS);
dpapadeb8e8df2017-04-19 17:45:26228 },
229
230 /**
231 * Handler for tap and <Enter> events on an input method on the main page,
232 * which sets it as the current input method.
233 * @param {!{model: !{item: !chrome.languageSettingsPrivate.InputMethod},
234 * target: !{tagName: string},
235 * type: string,
236 * key: (string|undefined)}} e
237 */
238 onInputMethodTap_: function(e) {
Dave Schuylerede4e782017-07-11 01:04:03239 // Taps on the button are handled in onInputMethodOptionsTap_.
240 // TODO(dschuyler): The row has two operations that are not clearly
241 // delineated. crbug.com/740691
242 if (e.target.tagName == 'BUTTON')
dpapadeb8e8df2017-04-19 17:45:26243 return;
244
245 // Ignore key presses other than <Enter>.
246 if (e.type == 'keypress' && e.key != 'Enter')
247 return;
248
249 // Set the input method.
250 this.languageHelper.setCurrentInputMethod(e.model.item.id);
251 },
252
253 /**
254 * Opens the input method extension's options page in a new tab (or focuses
255 * an existing instance of the IME's options).
256 * @param {!{model: !{item: chrome.languageSettingsPrivate.InputMethod}}} e
257 * @private
258 */
259 onInputMethodOptionsTap_: function(e) {
260 this.languageHelper.openInputMethodOptions(e.model.item.id);
261 },
dbeam141ae1f2017-06-19 18:28:35262 // </if>
dpapadeb8e8df2017-04-19 17:45:26263
dbeam141ae1f2017-06-19 18:28:35264 // <if expr="chromeos or is_win">
dpapadeb8e8df2017-04-19 17:45:26265 /**
266 * @return {boolean} True for a secondary user in a multi-profile session.
267 * @private
268 */
269 isSecondaryUser_: function() {
270 return cr.isChromeOS && loadTimeData.getBoolean('isSecondaryUser');
271 },
272
273 /**
274 * @param {string} languageCode The language code identifying a language.
275 * @param {string} prospectiveUILanguage The prospective UI language.
276 * @return {boolean} True if the prospective UI language is set to
277 * |languageCode| but requires a restart to take effect.
278 * @private
279 */
280 isRestartRequired_: function(languageCode, prospectiveUILanguage) {
281 return prospectiveUILanguage == languageCode &&
282 this.languageHelper.requiresRestart();
283 },
284
mahmadi04d3fdc62016-06-28 14:43:24285 /**
michaelpgc3b245a42016-09-22 03:20:56286 * @param {!LanguageState} languageState
287 * @param {string} prospectiveUILanguage The chosen UI language.
michaelpg4cb21262016-12-14 02:46:48288 * @return {boolean} True if the given language cannot be set as the
michaelpgc3b245a42016-09-22 03:20:56289 * prospective UI language by the user.
290 * @private
291 */
292 disableUILanguageCheckbox_: function(languageState, prospectiveUILanguage) {
dpapad4e55d032018-09-12 18:08:12293 if (this.detailLanguage_ === undefined)
294 return true;
295
michaelpgc3b245a42016-09-22 03:20:56296 // UI language setting belongs to the primary user.
297 if (this.isSecondaryUser_())
298 return true;
299
michaelpg4cb21262016-12-14 02:46:48300 // If the language cannot be a UI language, we can't set it as the
michaelpgc3b245a42016-09-22 03:20:56301 // prospective UI language.
302 if (!languageState.language.supportsUI)
303 return true;
304
michaelpg4cb21262016-12-14 02:46:48305 // Unchecking the currently chosen language doesn't make much sense.
306 if (languageState.language.code == prospectiveUILanguage)
michaelpgc3b245a42016-09-22 03:20:56307 return true;
michaelpgc3b245a42016-09-22 03:20:56308
Alexander Hendrichca7c8b12018-10-13 09:52:08309 // Check if the language is prohibited by the current "AllowedUILocales"
Alexander Hendrich55d0b0d92018-05-08 11:29:10310 // policy.
Alexander Hendrichca7c8b12018-10-13 09:52:08311 if (languageState.language.isProhibitedUILocale)
Alexander Hendrich55d0b0d92018-05-08 11:29:10312 return true;
313
michaelpg4cb21262016-12-14 02:46:48314 // Otherwise, the prospective language can be changed to this language.
michaelpgc3b245a42016-09-22 03:20:56315 return false;
316 },
317
318 /**
michaelpgc3b245a42016-09-22 03:20:56319 * Handler for changes to the UI language checkbox.
Scott Chen5ad171e2018-05-08 20:29:17320 * @param {!{target: !Element}} e
michaelpgc3b245a42016-09-22 03:20:56321 * @private
322 */
323 onUILanguageChange_: function(e) {
michaelpg4cb21262016-12-14 02:46:48324 // We don't support unchecking this checkbox. TODO(michaelpg): Ask for a
325 // simpler widget.
326 assert(e.target.checked);
327 this.languageHelper.setProspectiveUILanguage(
328 this.detailLanguage_.language.code);
329
michaelpgc5bbdca2017-01-18 02:59:37330 this.closeMenuSoon_();
michaelpgc3b245a42016-09-22 03:20:56331 },
dbeam141ae1f2017-06-19 18:28:35332 // </if>
michaelpgc3b245a42016-09-22 03:20:56333
michaelpgc5bbdca2017-01-18 02:59:37334 /**
michaelpgc3b245a42016-09-22 03:20:56335 * @param {!chrome.languageSettingsPrivate.Language} language
336 * @param {string} targetLanguageCode The default translate target language.
337 * @return {boolean} True if the translate checkbox should be disabled.
338 * @private
339 */
340 disableTranslateCheckbox_: function(language, targetLanguageCode) {
dpapadf7345e7e2018-06-26 22:46:33341 if (language == undefined || !language.supportsTranslate)
michaelpgc3b245a42016-09-22 03:20:56342 return true;
343
344 return this.languageHelper.convertLanguageCodeForTranslate(language.code) ==
345 targetLanguageCode;
346 },
347
348 /**
349 * Handler for changes to the translate checkbox.
Scott Chen5ad171e2018-05-08 20:29:17350 * @param {!{target: !Element}} e
michaelpgc3b245a42016-09-22 03:20:56351 * @private
352 */
353 onTranslateCheckboxChange_: function(e) {
354 if (e.target.checked) {
355 this.languageHelper.enableTranslateLanguage(
356 this.detailLanguage_.language.code);
357 } else {
358 this.languageHelper.disableTranslateLanguage(
359 this.detailLanguage_.language.code);
360 }
michaelpgc5bbdca2017-01-18 02:59:37361 this.closeMenuSoon_();
michaelpge11da422016-09-29 00:36:40362 },
363
364 /**
365 * Returns "complex" if the menu includes checkboxes, which should change the
366 * spacing of items and show a separator in the menu.
367 * @param {boolean} translateEnabled
368 * @return {string}
369 */
370 getMenuClass_: function(translateEnabled) {
371 if (translateEnabled || cr.isChromeOS || cr.isWindows)
372 return 'complex';
373 return '';
374 },
375
376 /**
377 * Moves the language to the top of the list.
378 * @private
379 */
380 onMoveToTopTap_: function() {
dbeam141ae1f2017-06-19 18:28:35381 /** @type {!CrActionMenuElement} */ (this.$.menu.get()).close();
michaelpge11da422016-09-29 00:36:40382 this.languageHelper.moveLanguageToFront(this.detailLanguage_.language.code);
michaelpgc3b245a42016-09-22 03:20:56383 },
384
385 /**
mahmadi04d3fdc62016-06-28 14:43:24386 * Moves the language up in the list.
mahmadi04d3fdc62016-06-28 14:43:24387 * @private
388 */
michaelpgb44ce492016-08-27 10:39:01389 onMoveUpTap_: function() {
dbeam141ae1f2017-06-19 18:28:35390 /** @type {!CrActionMenuElement} */ (this.$.menu.get()).close();
Claudio Magni72414d982017-11-07 06:29:48391 this.languageHelper.moveLanguage(
392 this.detailLanguage_.language.code, true /* upDirection */);
mahmadi04d3fdc62016-06-28 14:43:24393 },
394
395 /**
396 * Moves the language down in the list.
mahmadi04d3fdc62016-06-28 14:43:24397 * @private
398 */
michaelpgb44ce492016-08-27 10:39:01399 onMoveDownTap_: function() {
dbeam141ae1f2017-06-19 18:28:35400 /** @type {!CrActionMenuElement} */ (this.$.menu.get()).close();
Claudio Magni72414d982017-11-07 06:29:48401 this.languageHelper.moveLanguage(
402 this.detailLanguage_.language.code, false /* upDirection */);
mahmadi04d3fdc62016-06-28 14:43:24403 },
404
405 /**
michaelpg9a670d42016-08-25 02:06:37406 * Disables the language.
michaelpg9a670d42016-08-25 02:06:37407 * @private
408 */
michaelpgb44ce492016-08-27 10:39:01409 onRemoveLanguageTap_: function() {
dbeam141ae1f2017-06-19 18:28:35410 /** @type {!CrActionMenuElement} */ (this.$.menu.get()).close();
michaelpgb44ce492016-08-27 10:39:01411 this.languageHelper.disableLanguage(this.detailLanguage_.language.code);
michaelpg9a670d42016-08-25 02:06:37412 },
413
dbeam141ae1f2017-06-19 18:28:35414 // <if expr="chromeos or is_win">
michaelpg9a670d42016-08-25 02:06:37415 /**
dpapadeb8e8df2017-04-19 17:45:26416 * Checks whether the prospective UI language (the pref that indicates what
417 * language to use in Chrome) matches the current language. This pref is used
418 * only on Chrome OS and Windows; we don't control the UI language elsewhere.
419 * @param {string} languageCode The language code identifying a language.
420 * @param {string} prospectiveUILanguage The prospective UI language.
421 * @return {boolean} True if the given language matches the prospective UI
422 * pref (which may be different from the actual UI language).
michaelpgd700e562016-04-24 00:41:59423 * @private
424 */
dpapadeb8e8df2017-04-19 17:45:26425 isProspectiveUILanguage_: function(languageCode, prospectiveUILanguage) {
426 return languageCode == prospectiveUILanguage;
michaelpgd700e562016-04-24 00:41:59427 },
428
dbeam141ae1f2017-06-19 18:28:35429 /**
430 * @param {string} prospectiveUILanguage
431 * @return {string}
432 * @private
433 */
dpapadeb8e8df2017-04-19 17:45:26434 getProspectiveUILanguageName_: function(prospectiveUILanguage) {
435 return this.languageHelper.getLanguage(prospectiveUILanguage).displayName;
michaelpgd700e562016-04-24 00:41:59436 },
dbeam141ae1f2017-06-19 18:28:35437 // </if>
michaelpgd700e562016-04-24 00:41:59438
439 /**
dpapadeb8e8df2017-04-19 17:45:26440 * @return {string}
michaelpgd700e562016-04-24 00:41:59441 * @private
442 */
dpapadeb8e8df2017-04-19 17:45:26443 getLanguageListTwoLine_: function() {
444 return cr.isChromeOS || cr.isWindows ? 'two-line' : '';
michaelpgd700e562016-04-24 00:41:59445 },
446
dbeam141ae1f2017-06-19 18:28:35447 // <if expr="not is_macosx">
michaelpgd700e562016-04-24 00:41:59448 /**
michaelpg9fefdaf2016-06-13 22:33:11449 * Returns the secondary text for the spell check subsection based on the
450 * enabled spell check languages, listing at most 2 languages.
451 * @return {string}
452 * @private
453 */
454 getSpellCheckSecondaryText_: function() {
dpapadf7345e7e2018-06-26 22:46:33455 if (this.languages == undefined || this.prefs == undefined)
456 return '';
457
Marc-Antoine Courteau401867a2018-06-08 16:25:04458 if (this.getSpellCheckDisabledByPolicy_())
Marc-Antoine Courteau41f4c372017-12-18 19:33:07459 return loadTimeData.getString('spellCheckDisabled');
dpapad00039d92018-01-08 22:01:48460 const enabledSpellCheckLanguages =
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49461 this.getSpellCheckLanguages_().filter(function(languageState) {
462 return (languageState.spellCheckEnabled || languageState.isManaged) &&
dbeam141ae1f2017-06-19 18:28:35463 languageState.language.supportsSpellcheck;
michaelpg9fefdaf2016-06-13 22:33:11464 });
465 switch (enabledSpellCheckLanguages.length) {
466 case 0:
467 return '';
468 case 1:
469 return enabledSpellCheckLanguages[0].language.displayName;
470 case 2:
471 return loadTimeData.getStringF(
472 'spellCheckSummaryTwoLanguages',
473 enabledSpellCheckLanguages[0].language.displayName,
474 enabledSpellCheckLanguages[1].language.displayName);
475 case 3:
476 // "foo, bar, and 1 other"
477 return loadTimeData.getStringF(
478 'spellCheckSummaryThreeLanguages',
479 enabledSpellCheckLanguages[0].language.displayName,
480 enabledSpellCheckLanguages[1].language.displayName);
481 default:
482 // "foo, bar, and [N-2] others"
483 return loadTimeData.getStringF(
484 'spellCheckSummaryMultipleLanguages',
485 enabledSpellCheckLanguages[0].language.displayName,
486 enabledSpellCheckLanguages[1].language.displayName,
487 (enabledSpellCheckLanguages.length - 2).toLocaleString());
488 }
489 },
490
491 /**
Marc-Antoine Courteau41f4c372017-12-18 19:33:07492 * Returns whether spellcheck is disabled by policy or not.
493 * @return {boolean}
494 * @private
495 */
Marc-Antoine Courteau401867a2018-06-08 16:25:04496 getSpellCheckDisabledByPolicy_: function() {
dpapad00039d92018-01-08 22:01:48497 const pref = /** @type {!chrome.settingsPrivate.PrefObject} */ (
Marc-Antoine Courteau41f4c372017-12-18 19:33:07498 this.get('browser.enable_spellchecking', this.prefs));
Marc-Antoine Courteau401867a2018-06-08 16:25:04499 return pref.enforcement == chrome.settingsPrivate.Enforcement.ENFORCED &&
500 pref.value === false;
Marc-Antoine Courteau41f4c372017-12-18 19:33:07501 },
502
503 /**
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49504 * Returns an array of enabled languages, plus spellcheck languages that are
505 * forced by policy.
506 * @return {!Array<!LanguageState|!ForcedLanguageState>}
507 * @private
508 */
509 getSpellCheckLanguages_: function() {
510 return this.languages.enabled.concat(
Esmael El-Moslimany3f225c82018-03-29 23:27:32511 this.languages.forcedSpellCheckLanguages);
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49512 },
513
514 /** @private */
515 updateSpellcheckLanguages_: function() {
dpapadf7345e7e2018-06-26 22:46:33516 if (this.languages == undefined)
517 return;
518
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49519 this.set('spellCheckLanguages_', this.getSpellCheckLanguages_());
520
521 // Notify Polymer of subproperties that might have changed on the items in
522 // the spellCheckLanguages_ array, to make sure the UI updates. Polymer
523 // would otherwise not notice the changes in the subproperties, as some of
524 // them are references to those from |this.languages.enabled|. It would be
525 // possible to |this.linkPaths()| objects from |this.languages.enabled| to
526 // |this.spellCheckLanguages_|, but that would require complex housekeeping
527 // to |this.unlinkPaths()| as |this.languages.enabled| changes.
dpapad00039d92018-01-08 22:01:48528 for (let i = 0; i < this.spellCheckLanguages_.length; i++) {
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49529 this.notifyPath(`spellCheckLanguages_.${i}.isManaged`);
530 this.notifyPath(`spellCheckLanguages_.${i}.spellCheckEnabled`);
Esmael El-Moslimany3f225c82018-03-29 23:27:32531 this.notifyPath(
532 `spellCheckLanguages_.${i}.downloadDictionaryFailureCount`);
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49533 }
534 },
535
Marc-Antoine Courteau41f4c372017-12-18 19:33:07536 /** @private */
537 updateSpellcheckEnabled_: function() {
dpapadf7345e7e2018-06-26 22:46:33538 if (this.prefs == undefined)
539 return;
540
Marc-Antoine Courteau401867a2018-06-08 16:25:04541 this.set('spellCheckDisabled_', this.getSpellCheckDisabledByPolicy_());
Marc-Antoine Courteau41f4c372017-12-18 19:33:07542
543 // If the spellcheck section was expanded, close it.
544 if (this.spellCheckDisabled_)
545 this.set('spellCheckOpened_', false);
546 },
547
Marc-Antoine Courteau311a7cec2017-12-14 21:18:49548 /**
michaelpg53fc07e2015-10-27 20:18:16549 * Opens the Custom Dictionary page.
550 * @private
551 */
552 onEditDictionaryTap_: function() {
scottchenc31781b2017-07-10 19:52:42553 settings.navigateTo(settings.routes.EDIT_DICTIONARY);
michaelpg53fc07e2015-10-27 20:18:16554 },
michaelpg53fc07e2015-10-27 20:18:16555
michaelpgaf3a9282015-10-01 00:07:33556 /**
dpapadeb8e8df2017-04-19 17:45:26557 * Handler for enabling or disabling spell check.
558 * @param {!{target: Element, model: !{item: !LanguageState}}} e
michaelpgaf3a9282015-10-01 00:07:33559 */
dpapadeb8e8df2017-04-19 17:45:26560 onSpellCheckChange_: function(e) {
dpapad00039d92018-01-08 22:01:48561 const item = e.model.item;
dpapadeb8e8df2017-04-19 17:45:26562 if (!item.language.supportsSpellcheck)
563 return;
michaelpg790ff8a2015-09-03 03:27:22564
dbeam141ae1f2017-06-19 18:28:35565 this.languageHelper.toggleSpellCheck(
566 item.language.code, !item.spellCheckEnabled);
michaelpg04f42db22016-11-29 02:18:14567 },
michaelpg4d04ee92016-04-08 19:56:28568
569 /**
Esmael El-Moslimany3f225c82018-03-29 23:27:32570 * Handler to initiate another attempt at downloading the spell check
571 * dictionary for a specified language.
572 * @param {!{target: Element, model: !{item: !LanguageState}}} e
573 */
574 onRetryDictionaryDownloadClick_: function(e) {
575 assert(this.errorsGreaterThan_(
576 e.model.item.downloadDictionaryFailureCount, 0));
577 this.languageHelper.retryDownloadDictionary(e.model.item.language.code);
578 },
579
580 /**
581 * Handler for clicking on the name of the language. The action taken must
582 * match the control that is available.
583 * @param {!{target: Element, model: !{item: !LanguageState}}} e
584 */
585 onSpellCheckNameClick_: function(e) {
586 assert(!this.isSpellCheckNameClickDisabled_(e.model.item));
587 this.onSpellCheckChange_(e);
588 },
589
590 /**
591 * Name only supports clicking when language is not managed, supports
592 * spellcheck, and the dictionary has been downloaded with no errors.
593 * @param {!LanguageState|!ForcedLanguageState} item
594 * @return {boolean}
595 * @private
596 */
597 isSpellCheckNameClickDisabled_: function(item) {
598 return item.isManaged || !item.language.supportsSpellcheck ||
599 item.downloadDictionaryFailureCount > 0;
600 },
601
602 /**
michaelpg44d9dc82016-12-08 02:04:00603 * @return {string}
604 * @private
605 */
606 getSpellCheckListTwoLine_: function() {
607 return this.spellCheckSecondaryText_.length ? 'two-line' : '';
608 },
dbeam141ae1f2017-06-19 18:28:35609 // </if>
michaelpg44d9dc82016-12-08 02:04:00610
611 /**
michaelpg4d04ee92016-04-08 19:56:28612 * Returns either the "selected" class, if the language matches the
613 * prospective UI language, or an empty string. Languages can only be
614 * selected on Chrome OS and Windows.
615 * @param {string} languageCode The language code identifying a language.
616 * @param {string} prospectiveUILanguage The prospective UI language.
617 * @return {string} The class name for the language item.
618 * @private
619 */
michaelpg4cb21262016-12-14 02:46:48620 getLanguageItemClass_: function(languageCode, prospectiveUILanguage) {
michaelpgc3b245a42016-09-22 03:20:56621 if ((cr.isChromeOS || cr.isWindows) &&
michaelpg4cb21262016-12-14 02:46:48622 languageCode == prospectiveUILanguage) {
michaelpgc3b245a42016-09-22 03:20:56623 return 'selected';
michaelpgabbd180d2016-04-23 02:42:35624 }
michaelpgc3b245a42016-09-22 03:20:56625 return '';
626 },
dbeamed6888382016-06-25 01:33:15627
dbeam141ae1f2017-06-19 18:28:35628 // <if expr="chromeos">
michaelpg790ff8a2015-09-03 03:27:22629 /**
630 * @param {string} id The input method ID.
631 * @param {string} currentId The ID of the currently enabled input method.
632 * @return {boolean} True if the IDs match.
633 * @private
634 */
635 isCurrentInputMethod_: function(id, currentId) {
636 assert(cr.isChromeOS);
637 return id == currentId;
638 },
michaelpg53fc07e2015-10-27 20:18:16639
640 /**
michaelpg4d04ee92016-04-08 19:56:28641 * @param {string} id The input method ID.
642 * @param {string} currentId The ID of the currently enabled input method.
643 * @return {string} The class for the input method item.
644 * @private
645 */
646 getInputMethodItemClass_: function(id, currentId) {
michaelpgabbd180d2016-04-23 02:42:35647 assert(cr.isChromeOS);
michaelpg4d04ee92016-04-08 19:56:28648 return this.isCurrentInputMethod_(id, currentId) ? 'selected' : '';
649 },
michaelpg4d04ee92016-04-08 19:56:28650
michaelpgd700e562016-04-24 00:41:59651 getInputMethodName_: function(id) {
652 assert(cr.isChromeOS);
dpapad00039d92018-01-08 22:01:48653 const inputMethod =
dbeam141ae1f2017-06-19 18:28:35654 this.languages.inputMethods.enabled.find(function(inputMethod) {
michaelpgd700e562016-04-24 00:41:59655 return inputMethod.id == id;
656 });
657 return inputMethod ? inputMethod.displayName : '';
658 },
dbeam141ae1f2017-06-19 18:28:35659 // </if>
michaelpgd700e562016-04-24 00:41:59660
michaelpg4d04ee92016-04-08 19:56:28661 /**
michaelpgb44ce492016-08-27 10:39:01662 * @param {!Event} e
663 * @private
664 */
dpapad46b5ca12016-10-15 02:11:42665 onDotsTap_: function(e) {
michaelpg4cb21262016-12-14 02:46:48666 // Set a copy of the LanguageState object since it is not data-bound to the
667 // languages model directly.
dbeam141ae1f2017-06-19 18:28:35668 this.detailLanguage_ = /** @type {!LanguageState} */ (Object.assign(
michaelpg4cb21262016-12-14 02:46:48669 {},
dbeam141ae1f2017-06-19 18:28:35670 /** @type {!{model: !{item: !LanguageState}}} */ (e).model.item));
michaelpgb44ce492016-08-27 10:39:01671
672 // Ensure the template has been stamped.
dpapad00039d92018-01-08 22:01:48673 let menu = /** @type {?CrActionMenuElement} */ (this.$.menu.getIfExists());
michaelpgc3b245a42016-09-22 03:20:56674 if (!menu) {
dbeam141ae1f2017-06-19 18:28:35675 menu = /** @type {!CrActionMenuElement} */ (this.$.menu.get());
676 // <if expr="chromeos">
dpapadeb8e8df2017-04-19 17:45:26677 this.tweakMenuForCrOS_(menu);
dbeam141ae1f2017-06-19 18:28:35678 // </if>
michaelpgb44ce492016-08-27 10:39:01679 }
michaelpgc3b245a42016-09-22 03:20:56680
dpapad46b5ca12016-10-15 02:11:42681 menu.showAt(/** @type {!Element} */ (e.target));
michaelpgc3b245a42016-09-22 03:20:56682 },
683
684 /**
Renjie Liub350ff322017-09-05 05:02:36685 * @param {boolean} newVal The new value of languagesOpened_.
686 * @param {boolean} oldVal The old value of languagesOpened_.
687 * @private
688 */
689 onLanguagesOpenedChanged_: function(newVal, oldVal) {
690 if (!oldVal && newVal) {
691 chrome.send(
692 'metricsHandler:recordBooleanHistogram',
693 [LANGUAGE_SETTING_IS_SHOWN_UMA_NAME, true]);
694 }
695 },
696
697 /**
michaelpgc5bbdca2017-01-18 02:59:37698 * Closes the shared action menu after a short delay, so when a checkbox is
dpapad04f68ff82018-02-17 03:35:28699 * clicked it can be seen to change state before disappearing.
michaelpgc5bbdca2017-01-18 02:59:37700 * @private
701 */
702 closeMenuSoon_: function() {
dpapad00039d92018-01-08 22:01:48703 const menu = /** @type {!CrActionMenuElement} */ (this.$.menu.get());
michaelpgc5bbdca2017-01-18 02:59:37704 setTimeout(function() {
705 if (menu.open)
706 menu.close();
707 }, settings.kMenuCloseDelay);
708 },
709
dbeam141ae1f2017-06-19 18:28:35710 // <if expr="chromeos or is_win">
michaelpgc5bbdca2017-01-18 02:59:37711 /**
michaelpgc3b245a42016-09-22 03:20:56712 * Handler for the restart button.
713 * @private
714 */
715 onRestartTap_: function() {
dbeam141ae1f2017-06-19 18:28:35716 // <if expr="chromeos">
michaelpgc3b245a42016-09-22 03:20:56717 settings.LifetimeBrowserProxyImpl.getInstance().signOutAndRestart();
dbeam141ae1f2017-06-19 18:28:35718 // </if>
719 // <if expr="is_win">
michaelpgc3b245a42016-09-22 03:20:56720 settings.LifetimeBrowserProxyImpl.getInstance().restart();
dbeam141ae1f2017-06-19 18:28:35721 // </if>
michaelpgb44ce492016-08-27 10:39:01722 },
dbeam141ae1f2017-06-19 18:28:35723 // </if>
michaelpg10719a72016-12-07 09:05:21724
725 /**
726 * Toggles the expand button within the element being listened to.
727 * @param {!Event} e
728 * @private
729 */
730 toggleExpandButton_: function(e) {
731 // The expand button handles toggling itself.
dpapad00039d92018-01-08 22:01:48732 const expandButtonTag = 'CR-EXPAND-BUTTON';
michaelpg10719a72016-12-07 09:05:21733 if (e.target.tagName == expandButtonTag)
734 return;
735
Marc-Antoine Courteau41f4c372017-12-18 19:33:07736 if (!e.currentTarget.hasAttribute('actionable'))
737 return;
738
michaelpg10719a72016-12-07 09:05:21739 /** @type {!CrExpandButtonElement} */
dpapad00039d92018-01-08 22:01:48740 const expandButton = e.currentTarget.querySelector(expandButtonTag);
michaelpg10719a72016-12-07 09:05:21741 assert(expandButton);
742 expandButton.expanded = !expandButton.expanded;
743 },
michaelpg790ff8a2015-09-03 03:27:22744});
michaelpg0f479fec2015-09-25 04:03:49745})();