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