Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / options / search_engine_manager_engine_list.js
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 cr.define('options.search_engines', function() {
6   /** @const */ var ControlledSettingIndicator =
7                     options.ControlledSettingIndicator;
8   /** @const */ var InlineEditableItemList = options.InlineEditableItemList;
9   /** @const */ var InlineEditableItem = options.InlineEditableItem;
10   /** @const */ var ListSelectionController = cr.ui.ListSelectionController;
11
12   /**
13    * Creates a new search engine list item.
14    * @param {Object} searchEnigne The search engine this represents.
15    * @constructor
16    * @extends {cr.ui.ListItem}
17    */
18   function SearchEngineListItem(searchEngine) {
19     var el = cr.doc.createElement('div');
20     el.searchEngine_ = searchEngine;
21     SearchEngineListItem.decorate(el);
22     return el;
23   }
24
25   /**
26    * Decorates an element as a search engine list item.
27    * @param {!HTMLElement} el The element to decorate.
28    */
29   SearchEngineListItem.decorate = function(el) {
30     el.__proto__ = SearchEngineListItem.prototype;
31     el.decorate();
32   };
33
34   SearchEngineListItem.prototype = {
35     __proto__: InlineEditableItem.prototype,
36
37     /**
38      * Input field for editing the engine name.
39      * @type {HTMLElement}
40      * @private
41      */
42     nameField_: null,
43
44     /**
45      * Input field for editing the engine keyword.
46      * @type {HTMLElement}
47      * @private
48      */
49     keywordField_: null,
50
51     /**
52      * Input field for editing the engine url.
53      * @type {HTMLElement}
54      * @private
55      */
56     urlField_: null,
57
58     /**
59      * Whether or not an input validation request is currently outstanding.
60      * @type {boolean}
61      * @private
62      */
63     waitingForValidation_: false,
64
65     /**
66      * Whether or not the current set of input is known to be valid.
67      * @type {boolean}
68      * @private
69      */
70     currentlyValid_: false,
71
72     /** @override */
73     decorate: function() {
74       InlineEditableItem.prototype.decorate.call(this);
75
76       var engine = this.searchEngine_;
77
78       if (engine.modelIndex == '-1') {
79         this.isPlaceholder = true;
80         engine.name = '';
81         engine.keyword = '';
82         engine.url = '';
83       }
84
85       this.currentlyValid_ = !this.isPlaceholder;
86
87       if (engine.default)
88         this.classList.add('default');
89
90       this.deletable = engine.canBeRemoved;
91
92       // Construct the name column.
93       var nameColEl = this.ownerDocument.createElement('div');
94       nameColEl.className = 'name-column';
95       nameColEl.classList.add('weakrtl');
96       this.contentElement.appendChild(nameColEl);
97
98       // Add the favicon.
99       var faviconDivEl = this.ownerDocument.createElement('div');
100       faviconDivEl.className = 'favicon';
101       if (!this.isPlaceholder) {
102         faviconDivEl.style.backgroundImage = imageset(
103             'chrome://favicon/size/16@scalefactorx/iconurl/' + engine.iconURL);
104       }
105       nameColEl.appendChild(faviconDivEl);
106
107       var nameEl = this.createEditableTextCell(engine.displayName);
108       nameEl.classList.add('weakrtl');
109       nameColEl.appendChild(nameEl);
110
111       // Then the keyword column.
112       var keywordEl = this.createEditableTextCell(engine.keyword);
113       keywordEl.className = 'keyword-column';
114       keywordEl.classList.add('weakrtl');
115       this.contentElement.appendChild(keywordEl);
116
117       // And the URL column.
118       var urlEl = this.createEditableTextCell(engine.url);
119       // Extensions should not display a URL column.
120       if (!engine.isExtension) {
121         var urlWithButtonEl = this.ownerDocument.createElement('div');
122         urlWithButtonEl.appendChild(urlEl);
123         urlWithButtonEl.className = 'url-column';
124         urlWithButtonEl.classList.add('weakrtl');
125         this.contentElement.appendChild(urlWithButtonEl);
126         // Add the Make Default button. Temporary until drag-and-drop
127         // re-ordering is implemented. When this is removed, remove the extra
128         // div above.
129         if (engine.canBeDefault) {
130           var makeDefaultButtonEl = this.ownerDocument.createElement('button');
131           makeDefaultButtonEl.className =
132               'custom-appearance list-inline-button';
133           makeDefaultButtonEl.textContent =
134               loadTimeData.getString('makeDefaultSearchEngineButton');
135           makeDefaultButtonEl.onclick = function(e) {
136             chrome.send('managerSetDefaultSearchEngine', [engine.modelIndex]);
137           };
138           makeDefaultButtonEl.onmousedown = function(e) {
139             // Don't select the row when clicking the button.
140             e.stopPropagation();
141             // Don't focus on the button.
142             e.preventDefault();
143           };
144           urlWithButtonEl.appendChild(makeDefaultButtonEl);
145         }
146       }
147
148       // Do final adjustment to the input fields.
149       this.nameField_ = nameEl.querySelector('input');
150       // The editable field uses the raw name, not the display name.
151       this.nameField_.value = engine.name;
152       this.keywordField_ = keywordEl.querySelector('input');
153       this.urlField_ = urlEl.querySelector('input');
154
155       if (engine.urlLocked)
156         this.urlField_.disabled = true;
157
158       if (engine.isExtension)
159         this.nameField_.disabled = true;
160
161       if (this.isPlaceholder) {
162         this.nameField_.placeholder =
163             loadTimeData.getString('searchEngineTableNamePlaceholder');
164         this.keywordField_.placeholder =
165             loadTimeData.getString('searchEngineTableKeywordPlaceholder');
166         this.urlField_.placeholder =
167             loadTimeData.getString('searchEngineTableURLPlaceholder');
168       }
169
170       var fields = [this.nameField_, this.keywordField_, this.urlField_];
171         for (var i = 0; i < fields.length; i++) {
172         fields[i].oninput = this.startFieldValidation_.bind(this);
173       }
174
175       // Listen for edit events.
176       if (engine.canBeEdited) {
177         this.addEventListener('edit', this.onEditStarted_.bind(this));
178         this.addEventListener('canceledit', this.onEditCancelled_.bind(this));
179         this.addEventListener('commitedit', this.onEditCommitted_.bind(this));
180       } else {
181         this.editable = false;
182         this.querySelector('.row-delete-button').hidden = true;
183         var indicator = ControlledSettingIndicator();
184         indicator.setAttribute('setting', 'search-engine');
185         // Create a synthetic pref change event decorated as
186         // CoreOptionsHandler::CreateValueForPref() does.
187         var event = new Event(this.contentType);
188         if (engine.extension) {
189           event.value = { controlledBy: 'extension',
190                           extension: engine.extension };
191         } else {
192           event.value = { controlledBy: 'policy' };
193         }
194         indicator.handlePrefChange(event);
195         this.appendChild(indicator);
196       }
197     },
198
199     /** @override */
200     get currentInputIsValid() {
201       return !this.waitingForValidation_ && this.currentlyValid_;
202     },
203
204     /** @override */
205     get hasBeenEdited() {
206       var engine = this.searchEngine_;
207       return this.nameField_.value != engine.name ||
208              this.keywordField_.value != engine.keyword ||
209              this.urlField_.value != engine.url;
210     },
211
212     /**
213      * Called when entering edit mode; starts an edit session in the model.
214      * @param {Event} e The edit event.
215      * @private
216      */
217     onEditStarted_: function(e) {
218       var editIndex = this.searchEngine_.modelIndex;
219       chrome.send('editSearchEngine', [String(editIndex)]);
220       this.startFieldValidation_();
221     },
222
223     /**
224      * Called when committing an edit; updates the model.
225      * @param {Event} e The end event.
226      * @private
227      */
228     onEditCommitted_: function(e) {
229       chrome.send('searchEngineEditCompleted', this.getInputFieldValues_());
230     },
231
232     /**
233      * Called when cancelling an edit; informs the model and resets the control
234      * states.
235      * @param {Event} e The cancel event.
236      * @private
237      */
238     onEditCancelled_: function() {
239       chrome.send('searchEngineEditCancelled');
240
241       // The name field has been automatically set to match the display name,
242       // but it should use the raw name instead.
243       this.nameField_.value = this.searchEngine_.name;
244       this.currentlyValid_ = !this.isPlaceholder;
245     },
246
247     /**
248      * Returns the input field values as an array suitable for passing to
249      * chrome.send. The order of the array is important.
250      * @private
251      * @return {array} The current input field values.
252      */
253     getInputFieldValues_: function() {
254       return [this.nameField_.value,
255               this.keywordField_.value,
256               this.urlField_.value];
257     },
258
259     /**
260      * Begins the process of asynchronously validing the input fields.
261      * @private
262      */
263     startFieldValidation_: function() {
264       this.waitingForValidation_ = true;
265       var args = this.getInputFieldValues_();
266       args.push(this.searchEngine_.modelIndex);
267       chrome.send('checkSearchEngineInfoValidity', args);
268     },
269
270     /**
271      * Callback for the completion of an input validition check.
272      * @param {Object} validity A dictionary of validitation results.
273      */
274     validationComplete: function(validity) {
275       this.waitingForValidation_ = false;
276       // TODO(stuartmorgan): Implement the full validation UI with
277       // checkmark/exclamation mark icons and tooltips showing the errors.
278       if (validity.name) {
279         this.nameField_.setCustomValidity('');
280       } else {
281         this.nameField_.setCustomValidity(
282             loadTimeData.getString('editSearchEngineInvalidTitleToolTip'));
283       }
284
285       if (validity.keyword) {
286         this.keywordField_.setCustomValidity('');
287       } else {
288         this.keywordField_.setCustomValidity(
289             loadTimeData.getString('editSearchEngineInvalidKeywordToolTip'));
290       }
291
292       if (validity.url) {
293         this.urlField_.setCustomValidity('');
294       } else {
295         this.urlField_.setCustomValidity(
296             loadTimeData.getString('editSearchEngineInvalidURLToolTip'));
297       }
298
299       this.currentlyValid_ = validity.name && validity.keyword && validity.url;
300     },
301   };
302
303   var SearchEngineList = cr.ui.define('list');
304
305   SearchEngineList.prototype = {
306     __proto__: InlineEditableItemList.prototype,
307
308     /** @override */
309     createItem: function(searchEngine) {
310       return new SearchEngineListItem(searchEngine);
311     },
312
313     /** @override */
314     deleteItemAtIndex: function(index) {
315       var modelIndex = this.dataModel.item(index).modelIndex;
316       chrome.send('removeSearchEngine', [String(modelIndex)]);
317     },
318
319     /**
320      * Passes the results of an input validation check to the requesting row
321      * if it's still being edited.
322      * @param {number} modelIndex The model index of the item that was checked.
323      * @param {Object} validity A dictionary of validitation results.
324      */
325     validationComplete: function(validity, modelIndex) {
326       // If it's not still being edited, it no longer matters.
327       var currentSelection = this.selectedItem;
328       if (!currentSelection)
329         return;
330       var listItem = this.getListItem(currentSelection);
331       if (listItem.editing && currentSelection.modelIndex == modelIndex)
332         listItem.validationComplete(validity);
333     },
334   };
335
336   // Export
337   return {
338     SearchEngineList: SearchEngineList
339   };
340
341 });
342