OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 // TODO(kochi): Generalize the notification as a component and put it | |
6 // in js/cr/ui/notification.js . | |
7 | |
8 cr.define('options', function() { | |
9 /** @const */ var OptionsPage = options.OptionsPage; | |
10 /** @const */ var LanguageList = options.LanguageList; | |
11 | |
12 // Some input methods like Chinese Pinyin have config pages. | |
13 // This is the map of the input method names to their config page names. | |
14 /** @const */ var INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME = { | |
15 'mozc': 'languageMozc', | |
16 'mozc-chewing': 'languageChewing', | |
17 'mozc-dv': 'languageMozc', | |
18 'mozc-hangul': 'languageHangul', | |
19 'mozc-jp': 'languageMozc', | |
20 'pinyin': 'languagePinyin', | |
21 'pinyin-dv': 'languagePinyin', | |
22 }; | |
23 | |
24 ///////////////////////////////////////////////////////////////////////////// | |
25 // LanguageOptions class: | |
26 | |
27 /** | |
28 * Encapsulated handling of ChromeOS language options page. | |
29 * @constructor | |
30 */ | |
31 function LanguageOptions(model) { | |
32 OptionsPage.call(this, 'languages', | |
33 loadTimeData.getString('languagePageTabTitle'), | |
34 'languagePage'); | |
35 } | |
36 | |
37 cr.addSingletonGetter(LanguageOptions); | |
38 | |
39 // Inherit LanguageOptions from OptionsPage. | |
40 LanguageOptions.prototype = { | |
41 __proto__: OptionsPage.prototype, | |
42 | |
43 /* For recording the prospective language (the next locale after relaunch). | |
44 * @type {?string} | |
45 * @private | |
46 */ | |
47 prospectiveUiLanguageCode_: null, | |
48 | |
49 /** | |
50 * Initializes LanguageOptions page. | |
51 * Calls base class implementation to start preference initialization. | |
52 */ | |
53 initializePage: function() { | |
54 OptionsPage.prototype.initializePage.call(this); | |
55 | |
56 var languageOptionsList = $('language-options-list'); | |
57 LanguageList.decorate(languageOptionsList); | |
58 | |
59 languageOptionsList.addEventListener('change', | |
60 this.handleLanguageOptionsListChange_.bind(this)); | |
61 languageOptionsList.addEventListener('save', | |
62 this.handleLanguageOptionsListSave_.bind(this)); | |
63 | |
64 this.prospectiveUiLanguageCode_ = | |
65 loadTimeData.getString('prospectiveUiLanguageCode'); | |
66 this.addEventListener('visibleChange', | |
67 this.handleVisibleChange_.bind(this)); | |
68 | |
69 if (cr.isChromeOS) { | |
70 $('chewing-confirm').onclick = $('hangul-confirm').onclick = | |
71 $('mozc-confirm').onclick = $('pinyin-confirm').onclick = | |
72 OptionsPage.closeOverlay.bind(OptionsPage); | |
73 | |
74 this.initializeInputMethodList_(); | |
75 this.initializeLanguageCodeToInputMethodIdsMap_(); | |
76 } | |
77 Preferences.getInstance().addEventListener(this.spellCheckDictionaryPref, | |
78 this.handleSpellCheckDictionaryPrefChange_.bind(this)); | |
79 | |
80 // Set up add button. | |
81 $('language-options-add-button').onclick = function(e) { | |
82 // Add the language without showing the overlay if it's specified in | |
83 // the URL hash (ex. lang_add=ja). Used for automated testing. | |
84 var match = document.location.hash.match(/\blang_add=([\w-]+)/); | |
85 if (match) { | |
86 var addLanguageCode = match[1]; | |
87 $('language-options-list').addLanguage(addLanguageCode); | |
88 } else { | |
89 OptionsPage.navigateToPage('addLanguage'); | |
90 } | |
91 }; | |
92 | |
93 if (cr.isChromeOS) { | |
94 // Listen to user clicks on the add language list. | |
95 var addLanguageList = $('add-language-overlay-language-list'); | |
96 addLanguageList.addEventListener('click', | |
97 this.handleAddLanguageListClick_.bind(this)); | |
98 } else { | |
99 // Listen to add language dialog ok button. | |
100 var addLanguageOkButton = $('add-language-overlay-ok-button'); | |
101 addLanguageOkButton.addEventListener('click', | |
102 this.handleAddLanguageOkButtonClick_.bind(this)); | |
103 | |
104 // Show experimental features if enabled. | |
105 if (loadTimeData.getBoolean('experimentalSpellCheckFeatures')) | |
106 $('auto-spell-correction-option').hidden = false; | |
107 | |
108 // Handle spell check enable/disable. | |
109 if (!cr.isMac) { | |
110 Preferences.getInstance().addEventListener( | |
111 this.enableSpellCheckPref, | |
112 this.updateEnableSpellCheck_.bind(this)); | |
113 | |
114 var spellCheckLanguageButton = getRequiredElement( | |
115 'language-options-spell-check-language-button'); | |
116 spellCheckLanguageButton.addEventListener( | |
117 'click', | |
118 this.handleSpellCheckLanguageButtonClick_.bind(this)); | |
119 } | |
120 } | |
121 | |
122 if (cr.isChromeOS) { | |
123 $('language-options-ui-restart-button').onclick = function() { | |
124 chrome.send('uiLanguageRestart'); | |
125 }; | |
126 } | |
127 | |
128 $('language-confirm').onclick = | |
129 OptionsPage.closeOverlay.bind(OptionsPage); | |
130 }, | |
131 | |
132 // The preference is a boolean that enables/disables spell checking. | |
133 enableSpellCheckPref: 'browser.enable_spellchecking', | |
134 // The preference is a CSV string that describes preload engines | |
135 // (i.e. active input methods). | |
136 preloadEnginesPref: 'settings.language.preload_engines', | |
137 // The list of preload engines, like ['mozc', 'pinyin']. | |
138 preloadEngines_: [], | |
139 // The preference is a string that describes the spell check | |
140 // dictionary language, like "en-US". | |
141 spellCheckDictionaryPref: 'spellcheck.dictionary', | |
142 spellCheckDictionary_: '', | |
143 // The map of language code to input method IDs, like: | |
144 // {'ja': ['mozc', 'mozc-jp'], 'zh-CN': ['pinyin'], ...} | |
145 languageCodeToInputMethodIdsMap_: {}, | |
146 | |
147 /** | |
148 * Initializes the input method list. | |
149 */ | |
150 initializeInputMethodList_: function() { | |
151 var inputMethodList = $('language-options-input-method-list'); | |
152 var inputMethodListData = loadTimeData.getValue('inputMethodList'); | |
153 var inputMethodPrototype = $('language-options-input-method-proto'); | |
154 | |
155 // Add all input methods, but make all of them invisible here. We'll | |
156 // change the visibility in handleLanguageOptionsListChange_() based | |
157 // on the selected language. Note that we only have less than 100 | |
158 // input methods, so creating DOM nodes at once here should be ok. | |
159 for (var i = 0; i < inputMethodListData.length; i++) { | |
160 var inputMethod = inputMethodListData[i]; | |
161 var element = inputMethodPrototype.cloneNode(true); | |
162 element.id = ''; | |
163 element.languageCodeSet = inputMethod.languageCodeSet; | |
164 var input = element.querySelectorAll('input')[0]; | |
165 input.inputMethodId = inputMethod.id; | |
166 var span = element.querySelectorAll('span')[0]; | |
167 span.textContent = inputMethod.displayName; | |
168 | |
169 // Listen to user clicks. | |
170 input.addEventListener('click', | |
171 this.handleCheckboxClick_.bind(this)); | |
172 | |
173 // Add the configure button if the config page is present for this | |
174 // input method. | |
175 if (inputMethod.id in INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME) { | |
176 var pageName = INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME[inputMethod.id]; | |
177 var button = this.createConfigureInputMethodButton_(inputMethod.id, | |
178 pageName); | |
179 element.appendChild(button); | |
180 } | |
181 inputMethodList.appendChild(element); | |
182 } | |
183 // Listen to pref change once the input method list is initialized. | |
184 Preferences.getInstance().addEventListener(this.preloadEnginesPref, | |
185 this.handlePreloadEnginesPrefChange_.bind(this)); | |
186 }, | |
187 | |
188 /** | |
189 * Creates a configure button for the given input method ID. | |
190 * @param {string} inputMethodId Input method ID (ex. "pinyin"). | |
191 * @param {string} pageName Name of the config page (ex. "languagePinyin"). | |
192 * @private | |
193 */ | |
194 createConfigureInputMethodButton_: function(inputMethodId, pageName) { | |
195 var button = document.createElement('button'); | |
196 button.textContent = loadTimeData.getString('configure'); | |
197 button.onclick = function(e) { | |
198 // Prevent the default action (i.e. changing the checked property | |
199 // of the checkbox). The button click here should not be handled | |
200 // as checkbox click. | |
201 e.preventDefault(); | |
202 chrome.send('inputMethodOptionsOpen', [inputMethodId]); | |
203 OptionsPage.navigateToPage(pageName); | |
204 }; | |
205 return button; | |
206 }, | |
207 | |
208 /** | |
209 * Handles OptionsPage's visible property change event. | |
210 * @param {Event} e Property change event. | |
211 * @private | |
212 */ | |
213 handleVisibleChange_: function(e) { | |
214 if (this.visible) { | |
215 $('language-options-list').redraw(); | |
216 chrome.send('languageOptionsOpen'); | |
217 } | |
218 }, | |
219 | |
220 /** | |
221 * Handles languageOptionsList's change event. | |
222 * @param {Event} e Change event. | |
223 * @private | |
224 */ | |
225 handleLanguageOptionsListChange_: function(e) { | |
226 var languageOptionsList = $('language-options-list'); | |
227 var languageCode = languageOptionsList.getSelectedLanguageCode(); | |
228 | |
229 // Select the language if it's specified in the URL hash (ex. lang=ja). | |
230 // Used for automated testing. | |
231 var match = document.location.hash.match(/\blang=([\w-]+)/); | |
232 if (match) { | |
233 var specifiedLanguageCode = match[1]; | |
234 if (languageOptionsList.selectLanguageByCode(specifiedLanguageCode)) { | |
235 languageCode = specifiedLanguageCode; | |
236 } | |
237 } | |
238 | |
239 if (cr.isWindows || cr.isChromeOS) | |
240 this.updateUiLanguageButton_(languageCode); | |
241 | |
242 if (!cr.isMac) { | |
243 this.updateSelectedLanguageName_(languageCode); | |
244 this.updateSpellCheckLanguageButton_(languageCode); | |
245 } | |
246 | |
247 if (cr.isChromeOS) | |
248 this.updateInputMethodList_(languageCode); | |
249 | |
250 this.updateLanguageListInAddLanguageOverlay_(); | |
251 }, | |
252 | |
253 /** | |
254 * Happens when a user changes back to the language they're currently using. | |
255 */ | |
256 currentLocaleWasReselected: function() { | |
257 this.updateUiLanguageButton_( | |
258 loadTimeData.getString('currentUiLanguageCode')); | |
259 }, | |
260 | |
261 /** | |
262 * Handles languageOptionsList's save event. | |
263 * @param {Event} e Save event. | |
264 * @private | |
265 */ | |
266 handleLanguageOptionsListSave_: function(e) { | |
267 if (cr.isChromeOS) { | |
268 // Sort the preload engines per the saved languages before save. | |
269 this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); | |
270 this.savePreloadEnginesPref_(); | |
271 } | |
272 }, | |
273 | |
274 /** | |
275 * Sorts preloadEngines_ by languageOptionsList's order. | |
276 * @param {Array} preloadEngines List of preload engines. | |
277 * @return {Array} Returns sorted preloadEngines. | |
278 * @private | |
279 */ | |
280 sortPreloadEngines_: function(preloadEngines) { | |
281 // For instance, suppose we have two languages and associated input | |
282 // methods: | |
283 // | |
284 // - Korean: hangul | |
285 // - Chinese: pinyin | |
286 // | |
287 // The preloadEngines preference should look like "hangul,pinyin". | |
288 // If the user reverse the order, the preference should be reorderd | |
289 // to "pinyin,hangul". | |
290 var languageOptionsList = $('language-options-list'); | |
291 var languageCodes = languageOptionsList.getLanguageCodes(); | |
292 | |
293 // Convert the list into a dictonary for simpler lookup. | |
294 var preloadEngineSet = {}; | |
295 for (var i = 0; i < preloadEngines.length; i++) { | |
296 preloadEngineSet[preloadEngines[i]] = true; | |
297 } | |
298 | |
299 // Create the new preload engine list per the language codes. | |
300 var newPreloadEngines = []; | |
301 for (var i = 0; i < languageCodes.length; i++) { | |
302 var languageCode = languageCodes[i]; | |
303 var inputMethodIds = this.languageCodeToInputMethodIdsMap_[ | |
304 languageCode]; | |
305 // Check if we have active input methods associated with the language. | |
306 for (var j = 0; j < inputMethodIds.length; j++) { | |
307 var inputMethodId = inputMethodIds[j]; | |
308 if (inputMethodId in preloadEngineSet) { | |
309 // If we have, add it to the new engine list. | |
310 newPreloadEngines.push(inputMethodId); | |
311 // And delete it from the set. This is necessary as one input | |
312 // method can be associated with more than one language thus | |
313 // we should avoid having duplicates in the new list. | |
314 delete preloadEngineSet[inputMethodId]; | |
315 } | |
316 } | |
317 } | |
318 | |
319 return newPreloadEngines; | |
320 }, | |
321 | |
322 /** | |
323 * Initializes the map of language code to input method IDs. | |
324 * @private | |
325 */ | |
326 initializeLanguageCodeToInputMethodIdsMap_: function() { | |
327 var inputMethodList = loadTimeData.getValue('inputMethodList'); | |
328 for (var i = 0; i < inputMethodList.length; i++) { | |
329 var inputMethod = inputMethodList[i]; | |
330 for (var languageCode in inputMethod.languageCodeSet) { | |
331 if (languageCode in this.languageCodeToInputMethodIdsMap_) { | |
332 this.languageCodeToInputMethodIdsMap_[languageCode].push( | |
333 inputMethod.id); | |
334 } else { | |
335 this.languageCodeToInputMethodIdsMap_[languageCode] = | |
336 [inputMethod.id]; | |
337 } | |
338 } | |
339 } | |
340 }, | |
341 | |
342 /** | |
343 * Updates the currently selected language name. | |
344 * @param {string} languageCode Language code (ex. "fr"). | |
345 * @private | |
346 */ | |
347 updateSelectedLanguageName_: function(languageCode) { | |
348 var languageInfo = LanguageList.getLanguageInfoFromLanguageCode( | |
349 languageCode); | |
350 var languageDisplayName = languageInfo.displayName; | |
351 var languageNativeDisplayName = languageInfo.nativeDisplayName; | |
352 var textDirection = languageInfo.textDirection; | |
353 | |
354 // If the native name is different, add it. | |
355 if (languageDisplayName != languageNativeDisplayName) { | |
356 languageDisplayName += ' - ' + languageNativeDisplayName; | |
357 } | |
358 | |
359 // Update the currently selected language name. | |
360 var languageName = $('language-options-language-name'); | |
361 languageName.textContent = languageDisplayName; | |
362 languageName.dir = textDirection; | |
363 }, | |
364 | |
365 /** | |
366 * Updates the UI language button. | |
367 * @param {string} languageCode Language code (ex. "fr"). | |
368 * @private | |
369 */ | |
370 updateUiLanguageButton_: function(languageCode) { | |
371 var uiLanguageButton = $('language-options-ui-language-button'); | |
372 var uiLanguageMessage = $('language-options-ui-language-message'); | |
373 var uiLanguageNotification = $('language-options-ui-notification-bar'); | |
374 | |
375 // Remove the event listener and add it back if useful. | |
376 uiLanguageButton.onclick = null; | |
377 | |
378 // Unhide the language button every time, as it could've been previously | |
379 // hidden by a language change. | |
380 uiLanguageButton.hidden = false; | |
381 | |
382 if (languageCode == this.prospectiveUiLanguageCode_) { | |
383 uiLanguageMessage.textContent = | |
384 loadTimeData.getString('is_displayed_in_this_language'); | |
385 showMutuallyExclusiveNodes( | |
386 [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); | |
387 } else if (languageCode in loadTimeData.getValue('uiLanguageCodeSet')) { | |
388 if (cr.isChromeOS && UIAccountTweaks.loggedInAsGuest()) { | |
389 // In the guest mode for ChromeOS, changing UI language does not make | |
390 // sense because it does not take effect after browser restart. | |
391 uiLanguageButton.hidden = true; | |
392 uiLanguageMessage.hidden = true; | |
393 } else { | |
394 uiLanguageButton.textContent = | |
395 loadTimeData.getString('display_in_this_language'); | |
396 showMutuallyExclusiveNodes( | |
397 [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 0); | |
398 uiLanguageButton.onclick = function(e) { | |
399 chrome.send('uiLanguageChange', [languageCode]); | |
400 }; | |
401 } | |
402 } else { | |
403 uiLanguageMessage.textContent = | |
404 loadTimeData.getString('cannot_be_displayed_in_this_language'); | |
405 showMutuallyExclusiveNodes( | |
406 [uiLanguageButton, uiLanguageMessage, uiLanguageNotification], 1); | |
407 } | |
408 }, | |
409 | |
410 /** | |
411 * Updates the spell check language button. | |
412 * @param {string} languageCode Language code (ex. "fr"). | |
413 * @private | |
414 */ | |
415 updateSpellCheckLanguageButton_: function(languageCode) { | |
416 var spellCheckLanguageButton = | |
417 $('language-options-spell-check-language-button'); | |
418 var spellCheckLanguageMessage = | |
419 $('language-options-spell-check-language-message'); | |
420 | |
421 if (languageCode == this.spellCheckDictionary_) { | |
422 spellCheckLanguageMessage.textContent = | |
423 loadTimeData.getString('is_used_for_spell_checking'); | |
424 showMutuallyExclusiveNodes( | |
425 [spellCheckLanguageButton, spellCheckLanguageMessage], 1); | |
426 } else if (languageCode in | |
427 loadTimeData.getValue('spellCheckLanguageCodeSet')) { | |
428 spellCheckLanguageButton.textContent = | |
429 loadTimeData.getString('use_this_for_spell_checking'); | |
430 showMutuallyExclusiveNodes( | |
431 [spellCheckLanguageButton, spellCheckLanguageMessage], 0); | |
432 spellCheckLanguageButton.languageCode = languageCode; | |
433 } else if (!languageCode) { | |
434 spellCheckLanguageButton.hidden = true; | |
435 spellCheckLanguageMessage.hidden = true; | |
436 } else { | |
437 spellCheckLanguageMessage.textContent = | |
438 loadTimeData.getString('cannot_be_used_for_spell_checking'); | |
439 showMutuallyExclusiveNodes( | |
440 [spellCheckLanguageButton, spellCheckLanguageMessage], 1); | |
441 } | |
442 }, | |
443 | |
444 /** | |
445 * Updates the input method list. | |
446 * @param {string} languageCode Language code (ex. "fr"). | |
447 * @private | |
448 */ | |
449 updateInputMethodList_: function(languageCode) { | |
450 // Give one of the checkboxes or buttons focus, if it's specified in the | |
451 // URL hash (ex. focus=mozc). Used for automated testing. | |
452 var focusInputMethodId = -1; | |
453 var match = document.location.hash.match(/\bfocus=([\w:-]+)\b/); | |
454 if (match) { | |
455 focusInputMethodId = match[1]; | |
456 } | |
457 // Change the visibility of the input method list. Input methods that | |
458 // matches |languageCode| will become visible. | |
459 var inputMethodList = $('language-options-input-method-list'); | |
460 var methods = inputMethodList.querySelectorAll('.input-method'); | |
461 for (var i = 0; i < methods.length; i++) { | |
462 var method = methods[i]; | |
463 if (languageCode in method.languageCodeSet) { | |
464 method.hidden = false; | |
465 var input = method.querySelectorAll('input')[0]; | |
466 // Give it focus if the ID matches. | |
467 if (input.inputMethodId == focusInputMethodId) { | |
468 input.focus(); | |
469 } | |
470 } else { | |
471 method.hidden = true; | |
472 } | |
473 } | |
474 | |
475 if (focusInputMethodId == 'add') { | |
476 $('language-options-add-button').focus(); | |
477 } | |
478 }, | |
479 | |
480 /** | |
481 * Updates the language list in the add language overlay. | |
482 * @param {string} languageCode Language code (ex. "fr"). | |
483 * @private | |
484 */ | |
485 updateLanguageListInAddLanguageOverlay_: function(languageCode) { | |
486 // Change the visibility of the language list in the add language | |
487 // overlay. Languages that are already active will become invisible, | |
488 // so that users don't add the same language twice. | |
489 var languageOptionsList = $('language-options-list'); | |
490 var languageCodes = languageOptionsList.getLanguageCodes(); | |
491 var languageCodeSet = {}; | |
492 for (var i = 0; i < languageCodes.length; i++) { | |
493 languageCodeSet[languageCodes[i]] = true; | |
494 } | |
495 var addLanguageList = $('add-language-overlay-language-list'); | |
496 var lis = addLanguageList.querySelectorAll('li'); | |
497 for (var i = 0; i < lis.length; i++) { | |
498 // The first child button knows the language code. | |
499 var button = lis[i].childNodes[0]; | |
500 if (button.languageCode in languageCodeSet) { | |
501 lis[i].style.display = 'none'; | |
502 } else { | |
503 lis[i].style.display = 'block'; | |
504 } | |
505 } | |
506 }, | |
507 | |
508 /** | |
509 * Handles preloadEnginesPref change. | |
510 * @param {Event} e Change event. | |
511 * @private | |
512 */ | |
513 handlePreloadEnginesPrefChange_: function(e) { | |
514 var value = e.value.value; | |
515 this.preloadEngines_ = this.filterBadPreloadEngines_(value.split(',')); | |
516 this.updateCheckboxesFromPreloadEngines_(); | |
517 $('language-options-list').updateDeletable(); | |
518 }, | |
519 | |
520 /** | |
521 * Handles input method checkbox's click event. | |
522 * @param {Event} e Click event. | |
523 * @private | |
524 */ | |
525 handleCheckboxClick_: function(e) { | |
526 var checkbox = e.target; | |
527 if (this.preloadEngines_.length == 1 && !checkbox.checked) { | |
528 // Don't allow disabling the last input method. | |
529 this.showNotification_( | |
530 loadTimeData.getString('please_add_another_input_method'), | |
531 loadTimeData.getString('ok_button')); | |
532 checkbox.checked = true; | |
533 return; | |
534 } | |
535 if (checkbox.checked) { | |
536 chrome.send('inputMethodEnable', [checkbox.inputMethodId]); | |
537 } else { | |
538 chrome.send('inputMethodDisable', [checkbox.inputMethodId]); | |
539 } | |
540 this.updatePreloadEnginesFromCheckboxes_(); | |
541 this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_); | |
542 this.savePreloadEnginesPref_(); | |
543 }, | |
544 | |
545 /** | |
546 * Handles add language list's click event. | |
547 * @param {Event} e Click event. | |
548 */ | |
549 handleAddLanguageListClick_: function(e) { | |
550 var languageOptionsList = $('language-options-list'); | |
551 var languageCode = e.target.languageCode; | |
552 // languageCode can be undefined, if click was made on some random | |
553 // place in the overlay, rather than a button. Ignore it. | |
554 if (!languageCode) { | |
555 return; | |
556 } | |
557 languageOptionsList.addLanguage(languageCode); | |
558 var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; | |
559 // Enable the first input method for the language added. | |
560 if (inputMethodIds && inputMethodIds[0] && | |
561 // Don't add the input method it's already present. This can | |
562 // happen if the same input method is shared among multiple | |
563 // languages (ex. English US keyboard is used for English US and | |
564 // Filipino). | |
565 this.preloadEngines_.indexOf(inputMethodIds[0]) == -1) { | |
566 this.preloadEngines_.push(inputMethodIds[0]); | |
567 this.updateCheckboxesFromPreloadEngines_(); | |
568 this.savePreloadEnginesPref_(); | |
569 } | |
570 OptionsPage.closeOverlay(); | |
571 }, | |
572 | |
573 /** | |
574 * Handles add language dialog ok button. | |
575 */ | |
576 handleAddLanguageOkButtonClick_: function() { | |
577 var languagesSelect = $('add-language-overlay-language-list'); | |
578 var selectedIndex = languagesSelect.selectedIndex; | |
579 if (selectedIndex >= 0) { | |
580 var selection = languagesSelect.options[selectedIndex]; | |
581 $('language-options-list').addLanguage(String(selection.value)); | |
582 OptionsPage.closeOverlay(); | |
583 } | |
584 }, | |
585 | |
586 /** | |
587 * Checks if languageCode is deletable or not. | |
588 * @param {String} languageCode the languageCode to check for deletability. | |
589 */ | |
590 languageIsDeletable: function(languageCode) { | |
591 // Don't allow removing the language if it's a UI language. | |
592 if (languageCode == this.prospectiveUiLanguageCode_) | |
593 return false; | |
594 return (!cr.isChromeOS || | |
595 this.canDeleteLanguage_(languageCode)); | |
596 }, | |
597 | |
598 /** | |
599 * Handles browse.enable_spellchecking change. | |
600 * @param {Event} e Change event. | |
601 * @private | |
602 */ | |
603 updateEnableSpellCheck_: function() { | |
604 var value = !$('enable-spell-check').checked; | |
605 | |
606 $('language-options-spell-check-language-button').disabled = value; | |
607 }, | |
608 | |
609 /** | |
610 * Handles spellCheckDictionaryPref change. | |
611 * @param {Event} e Change event. | |
612 * @private | |
613 */ | |
614 handleSpellCheckDictionaryPrefChange_: function(e) { | |
615 var languageCode = e.value.value; | |
616 this.spellCheckDictionary_ = languageCode; | |
617 var languageOptionsList = $('language-options-list'); | |
618 var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode(); | |
619 if (!cr.isMac) | |
620 this.updateSpellCheckLanguageButton_(selectedLanguageCode); | |
621 }, | |
622 | |
623 /** | |
624 * Handles spellCheckLanguageButton click. | |
625 * @param {Event} e Click event. | |
626 * @private | |
627 */ | |
628 handleSpellCheckLanguageButtonClick_: function(e) { | |
629 var languageCode = e.target.languageCode; | |
630 // Save the preference. | |
631 Preferences.setStringPref(this.spellCheckDictionaryPref, | |
632 languageCode); | |
633 chrome.send('spellCheckLanguageChange', [languageCode]); | |
634 }, | |
635 | |
636 /** | |
637 * Checks whether it's possible to remove the language specified by | |
638 * languageCode and returns true if possible. This function returns false | |
639 * if the removal causes the number of preload engines to be zero. | |
640 * | |
641 * @param {string} languageCode Language code (ex. "fr"). | |
642 * @return {boolean} Returns true on success. | |
643 * @private | |
644 */ | |
645 canDeleteLanguage_: function(languageCode) { | |
646 // First create the set of engines to be removed from input methods | |
647 // associated with the language code. | |
648 var enginesToBeRemovedSet = {}; | |
649 var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode]; | |
650 for (var i = 0; i < inputMethodIds.length; i++) { | |
651 enginesToBeRemovedSet[inputMethodIds[i]] = true; | |
652 } | |
653 | |
654 // Then eliminate engines that are also used for other active languages. | |
655 // For instance, if "xkb:us::eng" is used for both English and Filipino. | |
656 var languageCodes = $('language-options-list').getLanguageCodes(); | |
657 for (var i = 0; i < languageCodes.length; i++) { | |
658 // Skip the target language code. | |
659 if (languageCodes[i] == languageCode) { | |
660 continue; | |
661 } | |
662 // Check if input methods used in this language are included in | |
663 // enginesToBeRemovedSet. If so, eliminate these from the set, so | |
664 // we don't remove this time. | |
665 var inputMethodIdsForAnotherLanguage = | |
666 this.languageCodeToInputMethodIdsMap_[languageCodes[i]]; | |
667 for (var j = 0; j < inputMethodIdsForAnotherLanguage.length; j++) { | |
668 var inputMethodId = inputMethodIdsForAnotherLanguage[j]; | |
669 if (inputMethodId in enginesToBeRemovedSet) { | |
670 delete enginesToBeRemovedSet[inputMethodId]; | |
671 } | |
672 } | |
673 } | |
674 | |
675 // Update the preload engine list with the to-be-removed set. | |
676 var newPreloadEngines = []; | |
677 for (var i = 0; i < this.preloadEngines_.length; i++) { | |
678 if (!(this.preloadEngines_[i] in enginesToBeRemovedSet)) { | |
679 newPreloadEngines.push(this.preloadEngines_[i]); | |
680 } | |
681 } | |
682 // Don't allow this operation if it causes the number of preload | |
683 // engines to be zero. | |
684 return (newPreloadEngines.length > 0); | |
685 }, | |
686 | |
687 /** | |
688 * Saves the preload engines preference. | |
689 * @private | |
690 */ | |
691 savePreloadEnginesPref_: function() { | |
692 Preferences.setStringPref(this.preloadEnginesPref, | |
693 this.preloadEngines_.join(',')); | |
694 }, | |
695 | |
696 /** | |
697 * Updates the checkboxes in the input method list from the preload | |
698 * engines preference. | |
699 * @private | |
700 */ | |
701 updateCheckboxesFromPreloadEngines_: function() { | |
702 // Convert the list into a dictonary for simpler lookup. | |
703 var dictionary = {}; | |
704 for (var i = 0; i < this.preloadEngines_.length; i++) { | |
705 dictionary[this.preloadEngines_[i]] = true; | |
706 } | |
707 | |
708 var inputMethodList = $('language-options-input-method-list'); | |
709 var checkboxes = inputMethodList.querySelectorAll('input'); | |
710 for (var i = 0; i < checkboxes.length; i++) { | |
711 checkboxes[i].checked = (checkboxes[i].inputMethodId in dictionary); | |
712 } | |
713 }, | |
714 | |
715 /** | |
716 * Updates the preload engines preference from the checkboxes in the | |
717 * input method list. | |
718 * @private | |
719 */ | |
720 updatePreloadEnginesFromCheckboxes_: function() { | |
721 this.preloadEngines_ = []; | |
722 var inputMethodList = $('language-options-input-method-list'); | |
723 var checkboxes = inputMethodList.querySelectorAll('input'); | |
724 for (var i = 0; i < checkboxes.length; i++) { | |
725 if (checkboxes[i].checked) { | |
726 this.preloadEngines_.push(checkboxes[i].inputMethodId); | |
727 } | |
728 } | |
729 var languageOptionsList = $('language-options-list'); | |
730 languageOptionsList.updateDeletable(); | |
731 }, | |
732 | |
733 /** | |
734 * Filters bad preload engines in case bad preload engines are | |
735 * stored in the preference. Removes duplicates as well. | |
736 * @param {Array} preloadEngines List of preload engines. | |
737 * @private | |
738 */ | |
739 filterBadPreloadEngines_: function(preloadEngines) { | |
740 // Convert the list into a dictonary for simpler lookup. | |
741 var dictionary = {}; | |
742 var list = loadTimeData.getValue('inputMethodList'); | |
743 for (var i = 0; i < list.length; i++) { | |
744 dictionary[list[i].id] = true; | |
745 } | |
746 | |
747 var filteredPreloadEngines = []; | |
748 var seen = {}; | |
749 for (var i = 0; i < preloadEngines.length; i++) { | |
750 // Check if the preload engine is present in the | |
751 // dictionary, and not duplicate. Otherwise, skip it. | |
752 if (preloadEngines[i] in dictionary && !(preloadEngines[i] in seen)) { | |
753 filteredPreloadEngines.push(preloadEngines[i]); | |
754 seen[preloadEngines[i]] = true; | |
755 } | |
756 } | |
757 return filteredPreloadEngines; | |
758 }, | |
759 | |
760 // TODO(kochi): This is an adapted copy from new_tab.js. | |
761 // If this will go as final UI, refactor this to share the component with | |
762 // new new tab page. | |
763 /** | |
764 * Shows notification | |
765 * @private | |
766 */ | |
767 notificationTimeout_: null, | |
768 showNotification_: function(text, actionText, opt_delay) { | |
769 var notificationElement = $('notification'); | |
770 var actionLink = notificationElement.querySelector('.link-color'); | |
771 var delay = opt_delay || 10000; | |
772 | |
773 function show() { | |
774 window.clearTimeout(this.notificationTimeout_); | |
775 notificationElement.classList.add('show'); | |
776 document.body.classList.add('notification-shown'); | |
777 } | |
778 | |
779 function hide() { | |
780 window.clearTimeout(this.notificationTimeout_); | |
781 notificationElement.classList.remove('show'); | |
782 document.body.classList.remove('notification-shown'); | |
783 // Prevent tabbing to the hidden link. | |
784 actionLink.tabIndex = -1; | |
785 // Setting tabIndex to -1 only prevents future tabbing to it. If, | |
786 // however, the user switches window or a tab and then moves back to | |
787 // this tab the element may gain focus. We therefore make sure that we | |
788 // blur the element so that the element focus is not restored when | |
789 // coming back to this window. | |
790 actionLink.blur(); | |
791 } | |
792 | |
793 function delayedHide() { | |
794 this.notificationTimeout_ = window.setTimeout(hide, delay); | |
795 } | |
796 | |
797 notificationElement.firstElementChild.textContent = text; | |
798 actionLink.textContent = actionText; | |
799 | |
800 actionLink.onclick = hide; | |
801 actionLink.onkeydown = function(e) { | |
802 if (e.keyIdentifier == 'Enter') { | |
803 hide(); | |
804 } | |
805 }; | |
806 notificationElement.onmouseover = show; | |
807 notificationElement.onmouseout = delayedHide; | |
808 actionLink.onfocus = show; | |
809 actionLink.onblur = delayedHide; | |
810 // Enable tabbing to the link now that it is shown. | |
811 actionLink.tabIndex = 0; | |
812 | |
813 show(); | |
814 delayedHide(); | |
815 } | |
816 }; | |
817 | |
818 /** | |
819 * Shows the node at |index| in |nodes|, hides all others. | |
820 * @param {Array<HTMLElement>} nodes The nodes to be shown or hidden. | |
821 * @param {number} index The index of |nodes| to show. | |
822 */ | |
823 function showMutuallyExclusiveNodes(nodes, index) { | |
824 assert(index >= 0 && index < nodes.length); | |
825 for (var i = 0; i < nodes.length; ++i) { | |
826 assert(nodes[i] instanceof HTMLElement); // TODO(dbeam): Ignore null? | |
827 nodes[i].hidden = i != index; | |
828 } | |
829 } | |
830 | |
831 /** | |
832 * Chrome callback for when the UI language preference is saved. | |
833 * @param {string} languageCode The newly selected language to use. | |
834 */ | |
835 LanguageOptions.uiLanguageSaved = function(languageCode) { | |
836 this.prospectiveUiLanguageCode_ = languageCode; | |
837 | |
838 // If the user is no longer on the same language code, ignore. | |
839 if ($('language-options-list').getSelectedLanguageCode() != languageCode) | |
840 return; | |
841 | |
842 // Special case for when a user changes to a different language, and changes | |
843 // back to the same language without having restarted Chrome or logged | |
844 // in/out of ChromeOS. | |
845 if (languageCode == loadTimeData.getString('currentUiLanguageCode')) { | |
846 LanguageOptions.getInstance().currentLocaleWasReselected(); | |
847 return; | |
848 } | |
849 | |
850 // Otherwise, show a notification telling the user that their changes will | |
851 // only take effect after restart. | |
852 showMutuallyExclusiveNodes([$('language-options-ui-language-button'), | |
853 $('language-options-ui-notification-bar')], 1); | |
854 }; | |
855 | |
856 // Export | |
857 return { | |
858 LanguageOptions: LanguageOptions | |
859 }; | |
860 }); | |
OLD | NEW |