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