OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 InlineEditableItemList = options.InlineEditableItemList; | |
7 const InlineEditableItem = options.InlineEditableItem; | |
8 const 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 this.contentElement.appendChild(nameColEl); | |
94 | |
95 // Add the favicon. | |
96 var faviconDivEl = this.ownerDocument.createElement('div'); | |
97 faviconDivEl.className = 'favicon'; | |
98 var imgEl = this.ownerDocument.createElement('img'); | |
99 imgEl.src = 'chrome://favicon/iconurl/' + engine['iconURL']; | |
100 faviconDivEl.appendChild(imgEl); | |
101 nameColEl.appendChild(faviconDivEl); | |
102 | |
103 var nameEl = this.createEditableTextCell(engine['displayName']); | |
104 nameColEl.appendChild(nameEl); | |
105 | |
106 // Then the keyword column. | |
107 var keywordEl = this.createEditableTextCell(engine['keyword']); | |
108 keywordEl.className = 'keyword-column'; | |
109 this.contentElement.appendChild(keywordEl); | |
110 | |
111 // And the URL column. | |
112 var urlEl = this.createEditableTextCell(engine['url']); | |
113 var urlWithButtonEl = this.ownerDocument.createElement('div'); | |
114 urlWithButtonEl.appendChild(urlEl); | |
115 urlWithButtonEl.className = 'url-column'; | |
116 this.contentElement.appendChild(urlWithButtonEl); | |
117 // Add the Make Default button. Temporary until drag-and-drop re-ordering | |
118 // is implemented. When this is removed, remove the extra div above. | |
119 if (engine['canBeDefault']) { | |
120 var makeDefaultButtonEl = this.ownerDocument.createElement('button'); | |
121 makeDefaultButtonEl.className = "raw-button"; | |
122 makeDefaultButtonEl.textContent = | |
123 templateData.makeDefaultSearchEngineButton; | |
124 makeDefaultButtonEl.onclick = function(e) { | |
125 chrome.send('managerSetDefaultSearchEngine', [engine['modelIndex']]); | |
126 }; | |
127 // Don't select the row when clicking the button. | |
128 makeDefaultButtonEl.onmousedown = function(e) { | |
129 e.stopPropagation(); | |
130 }; | |
131 urlWithButtonEl.appendChild(makeDefaultButtonEl); | |
132 } | |
133 | |
134 // Do final adjustment to the input fields. | |
135 this.nameField_ = nameEl.querySelector('input'); | |
136 // The editable field uses the raw name, not the display name. | |
137 this.nameField_.value = engine['name']; | |
138 this.keywordField_ = keywordEl.querySelector('input'); | |
139 this.urlField_ = urlEl.querySelector('input'); | |
140 | |
141 if (engine['urlLocked']) | |
142 this.urlField_.disabled = true; | |
143 | |
144 if (this.isPlaceholder) { | |
145 this.nameField_.placeholder = | |
146 localStrings.getString('searchEngineTableNamePlaceholder'); | |
147 this.keywordField_.placeholder = | |
148 localStrings.getString('searchEngineTableKeywordPlaceholder'); | |
149 this.urlField_.placeholder = | |
150 localStrings.getString('searchEngineTableURLPlaceholder'); | |
151 } | |
152 | |
153 var fields = [ this.nameField_, this.keywordField_, this.urlField_ ]; | |
154 for (var i = 0; i < fields.length; i++) { | |
155 fields[i].oninput = this.startFieldValidation_.bind(this); | |
156 } | |
157 | |
158 // Listen for edit events. | |
159 this.addEventListener('edit', this.onEditStarted_.bind(this)); | |
160 this.addEventListener('canceledit', this.onEditCancelled_.bind(this)); | |
161 this.addEventListener('commitedit', this.onEditCommitted_.bind(this)); | |
162 }, | |
163 | |
164 /** @inheritDoc */ | |
165 get currentInputIsValid() { | |
166 return !this.waitingForValidation_ && this.currentlyValid_; | |
167 }, | |
168 | |
169 /** @inheritDoc */ | |
170 get hasBeenEdited() { | |
171 var engine = this.searchEngine_; | |
172 return this.nameField_.value != engine['name'] || | |
173 this.keywordField_.value != engine['keyword'] || | |
174 this.urlField_.value != engine['url']; | |
175 }, | |
176 | |
177 /** | |
178 * Called when entering edit mode; starts an edit session in the model. | |
179 * @param {Event} e The edit event. | |
180 * @private | |
181 */ | |
182 onEditStarted_: function(e) { | |
183 var editIndex = this.searchEngine_['modelIndex']; | |
184 chrome.send('editSearchEngine', [String(editIndex)]); | |
185 this.startFieldValidation_(); | |
186 }, | |
187 | |
188 /** | |
189 * Called when committing an edit; updates the model. | |
190 * @param {Event} e The end event. | |
191 * @private | |
192 */ | |
193 onEditCommitted_: function(e) { | |
194 chrome.send('searchEngineEditCompleted', this.getInputFieldValues_()); | |
195 }, | |
196 | |
197 /** | |
198 * Called when cancelling an edit; informs the model and resets the control | |
199 * states. | |
200 * @param {Event} e The cancel event. | |
201 * @private | |
202 */ | |
203 onEditCancelled_: function() { | |
204 chrome.send('searchEngineEditCancelled'); | |
205 | |
206 // The name field has been automatically set to match the display name, | |
207 // but it should use the raw name instead. | |
208 this.nameField_.value = this.searchEngine_['name']; | |
209 this.currentlyValid_ = !this.isPlaceholder; | |
210 }, | |
211 | |
212 /** | |
213 * Returns the input field values as an array suitable for passing to | |
214 * chrome.send. The order of the array is important. | |
215 * @private | |
216 * @return {array} The current input field values. | |
217 */ | |
218 getInputFieldValues_: function() { | |
219 return [ this.nameField_.value, | |
220 this.keywordField_.value, | |
221 this.urlField_.value ]; | |
222 }, | |
223 | |
224 /** | |
225 * Begins the process of asynchronously validing the input fields. | |
226 * @private | |
227 */ | |
228 startFieldValidation_: function() { | |
229 this.waitingForValidation_ = true; | |
230 var args = this.getInputFieldValues_(); | |
231 args.push(this.searchEngine_['modelIndex']); | |
232 chrome.send('checkSearchEngineInfoValidity', args); | |
233 }, | |
234 | |
235 /** | |
236 * Callback for the completion of an input validition check. | |
237 * @param {Object} validity A dictionary of validitation results. | |
238 */ | |
239 validationComplete: function(validity) { | |
240 this.waitingForValidation_ = false; | |
241 // TODO(stuartmorgan): Implement the full validation UI with | |
242 // checkmark/exclamation mark icons and tooltips showing the errors. | |
243 if (validity['name']) { | |
244 this.nameField_.setCustomValidity(''); | |
245 } else { | |
246 this.nameField_.setCustomValidity( | |
247 templateData.editSearchEngineInvalidTitleToolTip); | |
248 } | |
249 | |
250 if (validity['keyword']) { | |
251 this.keywordField_.setCustomValidity(''); | |
252 } else { | |
253 this.keywordField_.setCustomValidity( | |
254 templateData.editSearchEngineInvalidKeywordToolTip); | |
255 } | |
256 | |
257 if (validity['url']) { | |
258 this.urlField_.setCustomValidity(''); | |
259 } else { | |
260 this.urlField_.setCustomValidity( | |
261 templateData.editSearchEngineInvalidURLToolTip); | |
262 } | |
263 | |
264 this.currentlyValid_ = validity['name'] && validity['keyword'] && | |
265 validity['url']; | |
266 }, | |
267 }; | |
268 | |
269 var SearchEngineList = cr.ui.define('list'); | |
270 | |
271 SearchEngineList.prototype = { | |
272 __proto__: InlineEditableItemList.prototype, | |
273 | |
274 /** @inheritDoc */ | |
275 createItem: function(searchEngine) { | |
276 return new SearchEngineListItem(searchEngine); | |
277 }, | |
278 | |
279 /** @inheritDoc */ | |
280 deleteItemAtIndex: function(index) { | |
281 var modelIndex = this.dataModel.item(index)['modelIndex'] | |
282 chrome.send('removeSearchEngine', [String(modelIndex)]); | |
283 }, | |
284 | |
285 /** | |
286 * Passes the results of an input validation check to the requesting row | |
287 * if it's still being edited. | |
288 * @param {number} modelIndex The model index of the item that was checked. | |
289 * @param {Object} validity A dictionary of validitation results. | |
290 */ | |
291 validationComplete: function(validity, modelIndex) { | |
292 // If it's not still being edited, it no longer matters. | |
293 var currentSelection = this.selectedItem; | |
294 if (!currentSelection) | |
295 return; | |
296 var listItem = this.getListItem(currentSelection); | |
297 if (listItem.editing && currentSelection['modelIndex'] == modelIndex) | |
298 listItem.validationComplete(validity); | |
299 }, | |
300 }; | |
301 | |
302 // Export | |
303 return { | |
304 SearchEngineList: SearchEngineList | |
305 }; | |
306 | |
307 }); | |
308 | |
OLD | NEW |