| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| cr.define('options.search_engines', function() { |
| /** @const */ var ControlledSettingIndicator = |
| options.ControlledSettingIndicator; |
| /** @const */ var InlineEditableItemList = options.InlineEditableItemList; |
| /** @const */ var InlineEditableItem = options.InlineEditableItem; |
| /** @const */ var ListSelectionController = cr.ui.ListSelectionController; |
| |
| /** |
| * Creates a new search engine list item. |
| * @param {Object} searchEnigne The search engine this represents. |
| * @constructor |
| * @extends {cr.ui.ListItem} |
| */ |
| function SearchEngineListItem(searchEngine) { |
| var el = cr.doc.createElement('div'); |
| el.searchEngine_ = searchEngine; |
| SearchEngineListItem.decorate(el); |
| return el; |
| } |
| |
| /** |
| * Decorates an element as a search engine list item. |
| * @param {!HTMLElement} el The element to decorate. |
| */ |
| SearchEngineListItem.decorate = function(el) { |
| el.__proto__ = SearchEngineListItem.prototype; |
| el.decorate(); |
| }; |
| |
| SearchEngineListItem.prototype = { |
| __proto__: InlineEditableItem.prototype, |
| |
| /** |
| * Input field for editing the engine name. |
| * @type {HTMLElement} |
| * @private |
| */ |
| nameField_: null, |
| |
| /** |
| * Input field for editing the engine keyword. |
| * @type {HTMLElement} |
| * @private |
| */ |
| keywordField_: null, |
| |
| /** |
| * Input field for editing the engine url. |
| * @type {HTMLElement} |
| * @private |
| */ |
| urlField_: null, |
| |
| /** |
| * Whether or not an input validation request is currently outstanding. |
| * @type {boolean} |
| * @private |
| */ |
| waitingForValidation_: false, |
| |
| /** |
| * Whether or not the current set of input is known to be valid. |
| * @type {boolean} |
| * @private |
| */ |
| currentlyValid_: false, |
| |
| /** @override */ |
| decorate: function() { |
| InlineEditableItem.prototype.decorate.call(this); |
| |
| var engine = this.searchEngine_; |
| |
| if (engine.modelIndex == '-1') { |
| this.isPlaceholder = true; |
| engine.name = ''; |
| engine.keyword = ''; |
| engine.url = ''; |
| } |
| |
| this.currentlyValid_ = !this.isPlaceholder; |
| |
| if (engine.default) |
| this.classList.add('default'); |
| |
| this.deletable = engine.canBeRemoved; |
| |
| // Construct the name column. |
| var nameColEl = this.ownerDocument.createElement('div'); |
| nameColEl.className = 'name-column'; |
| nameColEl.classList.add('weakrtl'); |
| this.contentElement.appendChild(nameColEl); |
| |
| // Add the favicon. |
| var faviconDivEl = this.ownerDocument.createElement('div'); |
| faviconDivEl.className = 'favicon'; |
| if (!this.isPlaceholder) { |
| faviconDivEl.style.backgroundImage = imageset( |
| 'chrome://favicon/size/16@scalefactorx/iconurl/' + engine.iconURL); |
| } |
| nameColEl.appendChild(faviconDivEl); |
| |
| var nameEl = this.createEditableTextCell(engine.displayName); |
| nameEl.classList.add('weakrtl'); |
| nameColEl.appendChild(nameEl); |
| |
| // Then the keyword column. |
| var keywordEl = this.createEditableTextCell(engine.keyword); |
| keywordEl.className = 'keyword-column'; |
| keywordEl.classList.add('weakrtl'); |
| this.contentElement.appendChild(keywordEl); |
| |
| // And the URL column. |
| var urlEl = this.createEditableTextCell(engine.url); |
| // Extensions should not display a URL column. |
| if (!engine.isExtension) { |
| var urlWithButtonEl = this.ownerDocument.createElement('div'); |
| urlWithButtonEl.appendChild(urlEl); |
| urlWithButtonEl.className = 'url-column'; |
| urlWithButtonEl.classList.add('weakrtl'); |
| this.contentElement.appendChild(urlWithButtonEl); |
| // Add the Make Default button. Temporary until drag-and-drop |
| // re-ordering is implemented. When this is removed, remove the extra |
| // div above. |
| if (engine.canBeDefault) { |
| var makeDefaultButtonEl = this.ownerDocument.createElement('button'); |
| makeDefaultButtonEl.className = |
| 'custom-appearance list-inline-button'; |
| makeDefaultButtonEl.textContent = |
| loadTimeData.getString('makeDefaultSearchEngineButton'); |
| makeDefaultButtonEl.onclick = function(e) { |
| chrome.send('managerSetDefaultSearchEngine', [engine.modelIndex]); |
| }; |
| makeDefaultButtonEl.onmousedown = function(e) { |
| // Don't select the row when clicking the button. |
| e.stopPropagation(); |
| // Don't focus on the button. |
| e.preventDefault(); |
| }; |
| urlWithButtonEl.appendChild(makeDefaultButtonEl); |
| } |
| } |
| |
| // Do final adjustment to the input fields. |
| this.nameField_ = nameEl.querySelector('input'); |
| // The editable field uses the raw name, not the display name. |
| this.nameField_.value = engine.name; |
| this.keywordField_ = keywordEl.querySelector('input'); |
| this.urlField_ = urlEl.querySelector('input'); |
| |
| if (engine.urlLocked) |
| this.urlField_.disabled = true; |
| |
| if (engine.isExtension) |
| this.nameField_.disabled = true; |
| |
| if (this.isPlaceholder) { |
| this.nameField_.placeholder = |
| loadTimeData.getString('searchEngineTableNamePlaceholder'); |
| this.keywordField_.placeholder = |
| loadTimeData.getString('searchEngineTableKeywordPlaceholder'); |
| this.urlField_.placeholder = |
| loadTimeData.getString('searchEngineTableURLPlaceholder'); |
| } |
| |
| var fields = [this.nameField_, this.keywordField_, this.urlField_]; |
| for (var i = 0; i < fields.length; i++) { |
| fields[i].oninput = this.startFieldValidation_.bind(this); |
| } |
| |
| // Listen for edit events. |
| if (engine.canBeEdited) { |
| this.addEventListener('edit', this.onEditStarted_.bind(this)); |
| this.addEventListener('canceledit', this.onEditCancelled_.bind(this)); |
| this.addEventListener('commitedit', this.onEditCommitted_.bind(this)); |
| } else { |
| this.editable = false; |
| this.querySelector('.row-delete-button').hidden = true; |
| var indicator = ControlledSettingIndicator(); |
| indicator.setAttribute('setting', 'search-engine'); |
| // Create a synthetic pref change event decorated as |
| // CoreOptionsHandler::CreateValueForPref() does. |
| var event = new cr.Event(this.contentType); |
| event.value = { controlledBy: 'policy' }; |
| indicator.handlePrefChange(event); |
| this.appendChild(indicator); |
| } |
| }, |
| |
| /** @override */ |
| get currentInputIsValid() { |
| return !this.waitingForValidation_ && this.currentlyValid_; |
| }, |
| |
| /** @override */ |
| get hasBeenEdited() { |
| var engine = this.searchEngine_; |
| return this.nameField_.value != engine.name || |
| this.keywordField_.value != engine.keyword || |
| this.urlField_.value != engine.url; |
| }, |
| |
| /** |
| * Called when entering edit mode; starts an edit session in the model. |
| * @param {Event} e The edit event. |
| * @private |
| */ |
| onEditStarted_: function(e) { |
| var editIndex = this.searchEngine_.modelIndex; |
| chrome.send('editSearchEngine', [String(editIndex)]); |
| this.startFieldValidation_(); |
| }, |
| |
| /** |
| * Called when committing an edit; updates the model. |
| * @param {Event} e The end event. |
| * @private |
| */ |
| onEditCommitted_: function(e) { |
| chrome.send('searchEngineEditCompleted', this.getInputFieldValues_()); |
| }, |
| |
| /** |
| * Called when cancelling an edit; informs the model and resets the control |
| * states. |
| * @param {Event} e The cancel event. |
| * @private |
| */ |
| onEditCancelled_: function() { |
| chrome.send('searchEngineEditCancelled'); |
| |
| // The name field has been automatically set to match the display name, |
| // but it should use the raw name instead. |
| this.nameField_.value = this.searchEngine_.name; |
| this.currentlyValid_ = !this.isPlaceholder; |
| }, |
| |
| /** |
| * Returns the input field values as an array suitable for passing to |
| * chrome.send. The order of the array is important. |
| * @private |
| * @return {array} The current input field values. |
| */ |
| getInputFieldValues_: function() { |
| return [this.nameField_.value, |
| this.keywordField_.value, |
| this.urlField_.value]; |
| }, |
| |
| /** |
| * Begins the process of asynchronously validing the input fields. |
| * @private |
| */ |
| startFieldValidation_: function() { |
| this.waitingForValidation_ = true; |
| var args = this.getInputFieldValues_(); |
| args.push(this.searchEngine_.modelIndex); |
| chrome.send('checkSearchEngineInfoValidity', args); |
| }, |
| |
| /** |
| * Callback for the completion of an input validition check. |
| * @param {Object} validity A dictionary of validitation results. |
| */ |
| validationComplete: function(validity) { |
| this.waitingForValidation_ = false; |
| // TODO(stuartmorgan): Implement the full validation UI with |
| // checkmark/exclamation mark icons and tooltips showing the errors. |
| if (validity.name) { |
| this.nameField_.setCustomValidity(''); |
| } else { |
| this.nameField_.setCustomValidity( |
| loadTimeData.getString('editSearchEngineInvalidTitleToolTip')); |
| } |
| |
| if (validity.keyword) { |
| this.keywordField_.setCustomValidity(''); |
| } else { |
| this.keywordField_.setCustomValidity( |
| loadTimeData.getString('editSearchEngineInvalidKeywordToolTip')); |
| } |
| |
| if (validity.url) { |
| this.urlField_.setCustomValidity(''); |
| } else { |
| this.urlField_.setCustomValidity( |
| loadTimeData.getString('editSearchEngineInvalidURLToolTip')); |
| } |
| |
| this.currentlyValid_ = validity.name && validity.keyword && validity.url; |
| }, |
| }; |
| |
| var SearchEngineList = cr.ui.define('list'); |
| |
| SearchEngineList.prototype = { |
| __proto__: InlineEditableItemList.prototype, |
| |
| /** @override */ |
| createItem: function(searchEngine) { |
| return new SearchEngineListItem(searchEngine); |
| }, |
| |
| /** @override */ |
| deleteItemAtIndex: function(index) { |
| var modelIndex = this.dataModel.item(index).modelIndex; |
| chrome.send('removeSearchEngine', [String(modelIndex)]); |
| }, |
| |
| /** |
| * Passes the results of an input validation check to the requesting row |
| * if it's still being edited. |
| * @param {number} modelIndex The model index of the item that was checked. |
| * @param {Object} validity A dictionary of validitation results. |
| */ |
| validationComplete: function(validity, modelIndex) { |
| // If it's not still being edited, it no longer matters. |
| var currentSelection = this.selectedItem; |
| if (!currentSelection) |
| return; |
| var listItem = this.getListItem(currentSelection); |
| if (listItem.editing && currentSelection.modelIndex == modelIndex) |
| listItem.validationComplete(validity); |
| }, |
| }; |
| |
| // Export |
| return { |
| SearchEngineList: SearchEngineList |
| }; |
| |
| }); |
| |