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.
5 cr.define('options.autofillOptions', function() {
6 /** @const */ var DeletableItem = options.DeletableItem;
7 /** @const */ var DeletableItemList = options.DeletableItemList;
8 /** @const */ var InlineEditableItem = options.InlineEditableItem;
9 /** @const */ var InlineEditableItemList = options.InlineEditableItemList;
11 function AutofillEditProfileButton(guid, edit) {
12 var editButtonEl = document.createElement('button');
13 editButtonEl.className = 'list-inline-button custom-appearance';
14 editButtonEl.textContent =
15 loadTimeData.getString('autofillEditProfileButton');
16 editButtonEl.onclick = function(e) { edit(guid); };
18 editButtonEl.onmousedown = function(e) {
19 // Don't select the row when clicking the button.
21 // Don't focus on the button when clicking it.
29 * Creates a new address list item.
30 * @param {Array} entry An array of the form [guid, label].
32 * @extends {options.DeletableItem}
34 function AddressListItem(entry) {
35 var el = cr.doc.createElement('div');
38 el.__proto__ = AddressListItem.prototype;
44 AddressListItem.prototype = {
45 __proto__: DeletableItem.prototype,
48 decorate: function() {
49 DeletableItem.prototype.decorate.call(this);
52 var label = this.ownerDocument.createElement('div');
53 label.className = 'autofill-list-item';
54 label.textContent = this.label;
55 this.contentElement.appendChild(label);
58 var editButtonEl = new AutofillEditProfileButton(
60 AutofillOptions.loadAddressEditor);
61 this.contentElement.appendChild(editButtonEl);
66 * Creates a new credit card list item.
67 * @param {Array} entry An array of the form [guid, label, icon].
69 * @extends {options.DeletableItem}
71 function CreditCardListItem(entry) {
72 var el = cr.doc.createElement('div');
76 el.description = entry[3];
77 el.__proto__ = CreditCardListItem.prototype;
83 CreditCardListItem.prototype = {
84 __proto__: DeletableItem.prototype,
87 decorate: function() {
88 DeletableItem.prototype.decorate.call(this);
91 var label = this.ownerDocument.createElement('div');
92 label.className = 'autofill-list-item';
93 label.textContent = this.label;
94 this.contentElement.appendChild(label);
96 // The credit card icon.
97 var icon = this.ownerDocument.createElement('img');
99 icon.alt = this.description;
100 this.contentElement.appendChild(icon);
102 // The 'Edit' button.
103 var editButtonEl = new AutofillEditProfileButton(
105 AutofillOptions.loadCreditCardEditor);
106 this.contentElement.appendChild(editButtonEl);
111 * Creates a new value list item.
112 * @param {AutofillValuesList} list The parent list of this item.
113 * @param {string} entry A string value.
115 * @extends {options.InlineEditableItem}
117 function ValuesListItem(list, entry) {
118 var el = cr.doc.createElement('div');
120 el.value = entry ? entry : '';
121 el.__proto__ = ValuesListItem.prototype;
127 ValuesListItem.prototype = {
128 __proto__: InlineEditableItem.prototype,
131 decorate: function() {
132 InlineEditableItem.prototype.decorate.call(this);
134 // Note: This must be set prior to calling |createEditableTextCell|.
135 this.isPlaceholder = !this.value;
138 var cell = this.createEditableTextCell(this.value);
139 this.contentElement.appendChild(cell);
140 this.input = cell.querySelector('input');
142 if (this.isPlaceholder) {
143 this.input.placeholder = this.list.getAttribute('placeholder');
144 this.deletable = false;
147 this.addEventListener('commitedit', this.onEditCommitted_);
151 * @return {string} This item's value.
155 return this.input.value;
159 * @param {Object} value The value to test.
160 * @return {boolean} True if the given value is non-empty.
163 valueIsNonEmpty_: function(value) {
168 * @return {boolean} True if value1 is logically equal to value2.
170 valuesAreEqual_: function(value1, value2) {
171 return value1 === value2;
175 * Clears the item's value.
178 clearValue_: function() {
179 this.input.value = '';
183 * Called when committing an edit.
184 * If this is an "Add ..." item, committing a non-empty value adds that
185 * value to the end of the values list, but also leaves this "Add ..." item
187 * @param {Event} e The end event.
190 onEditCommitted_: function(e) {
191 var value = this.value_();
192 var i = this.list.items.indexOf(this);
193 if (i < this.list.dataModel.length &&
194 this.valuesAreEqual_(value, this.list.dataModel.item(i))) {
198 var entries = this.list.dataModel.slice();
199 if (this.valueIsNonEmpty_(value) &&
200 !entries.some(this.valuesAreEqual_.bind(this, value))) {
201 // Update with new value.
202 if (this.isPlaceholder) {
203 // It is important that updateIndex is done before validateAndSave.
204 // Otherwise we can not be sure about AddRow index.
205 this.list.dataModel.updateIndex(i);
206 this.list.validateAndSave(i, 0, value);
208 this.list.validateAndSave(i, 1, value);
211 // Reject empty values and duplicates.
212 if (!this.isPlaceholder)
213 this.list.dataModel.splice(i, 1);
221 * Creates a new name value list item.
222 * @param {AutofillNameValuesList} list The parent list of this item.
223 * @param {array} entry An array of [first, middle, last] names.
225 * @extends {options.ValuesListItem}
227 function NameListItem(list, entry) {
228 var el = cr.doc.createElement('div');
230 el.first = entry ? entry[0] : '';
231 el.middle = entry ? entry[1] : '';
232 el.last = entry ? entry[2] : '';
233 el.__proto__ = NameListItem.prototype;
239 NameListItem.prototype = {
240 __proto__: ValuesListItem.prototype,
243 decorate: function() {
244 InlineEditableItem.prototype.decorate.call(this);
246 // Note: This must be set prior to calling |createEditableTextCell|.
247 this.isPlaceholder = !this.first && !this.middle && !this.last;
250 // For the simulated static "input element" to display correctly, the
251 // value must not be empty. We use a space to force the UI to render
252 // correctly when the value is logically empty.
253 var cell = this.createEditableTextCell(this.first);
254 this.contentElement.appendChild(cell);
255 this.firstNameInput = cell.querySelector('input');
257 cell = this.createEditableTextCell(this.middle);
258 this.contentElement.appendChild(cell);
259 this.middleNameInput = cell.querySelector('input');
261 cell = this.createEditableTextCell(this.last);
262 this.contentElement.appendChild(cell);
263 this.lastNameInput = cell.querySelector('input');
265 if (this.isPlaceholder) {
266 this.firstNameInput.placeholder =
267 loadTimeData.getString('autofillAddFirstNamePlaceholder');
268 this.middleNameInput.placeholder =
269 loadTimeData.getString('autofillAddMiddleNamePlaceholder');
270 this.lastNameInput.placeholder =
271 loadTimeData.getString('autofillAddLastNamePlaceholder');
272 this.deletable = false;
275 this.addEventListener('commitedit', this.onEditCommitted_);
280 return [this.firstNameInput.value,
281 this.middleNameInput.value,
282 this.lastNameInput.value];
286 valueIsNonEmpty_: function(value) {
287 return value[0] || value[1] || value[2];
291 valuesAreEqual_: function(value1, value2) {
292 // First, check for null values.
293 if (!value1 || !value2)
294 return value1 == value2;
296 return value1[0] === value2[0] &&
297 value1[1] === value2[1] &&
298 value1[2] === value2[2];
302 clearValue_: function() {
303 this.firstNameInput.value = '';
304 this.middleNameInput.value = '';
305 this.lastNameInput.value = '';
310 * Base class for shared implementation between address and credit card lists.
312 * @extends {options.DeletableItemList}
314 var AutofillProfileList = cr.ui.define('list');
316 AutofillProfileList.prototype = {
317 __proto__: DeletableItemList.prototype,
319 decorate: function() {
320 DeletableItemList.prototype.decorate.call(this);
322 this.addEventListener('blur', this.onBlur_);
326 * When the list loses focus, unselect all items in the list.
329 onBlur_: function() {
330 this.selectionModel.unselectAll();
335 * Create a new address list.
337 * @extends {options.AutofillProfileList}
339 var AutofillAddressList = cr.ui.define('list');
341 AutofillAddressList.prototype = {
342 __proto__: AutofillProfileList.prototype,
344 decorate: function() {
345 AutofillProfileList.prototype.decorate.call(this);
349 activateItemAtIndex: function(index) {
350 AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]);
354 createItem: function(entry) {
355 return new AddressListItem(entry);
359 deleteItemAtIndex: function(index) {
360 AutofillOptions.removeData(this.dataModel.item(index)[0]);
365 * Create a new credit card list.
367 * @extends {options.DeletableItemList}
369 var AutofillCreditCardList = cr.ui.define('list');
371 AutofillCreditCardList.prototype = {
372 __proto__: AutofillProfileList.prototype,
374 decorate: function() {
375 AutofillProfileList.prototype.decorate.call(this);
379 activateItemAtIndex: function(index) {
380 AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]);
384 createItem: function(entry) {
385 return new CreditCardListItem(entry);
389 deleteItemAtIndex: function(index) {
390 AutofillOptions.removeData(this.dataModel.item(index)[0]);
395 * Create a new value list.
397 * @extends {options.InlineEditableItemList}
399 var AutofillValuesList = cr.ui.define('list');
401 AutofillValuesList.prototype = {
402 __proto__: InlineEditableItemList.prototype,
405 createItem: function(entry) {
406 return new ValuesListItem(this, entry);
410 deleteItemAtIndex: function(index) {
411 this.dataModel.splice(index, 1);
415 shouldFocusPlaceholder: function() {
420 * Called when the list hierarchy as a whole loses or gains focus.
421 * If the list was focused in response to a mouse click, call into the
422 * superclass's implementation. If the list was focused in response to a
423 * keyboard navigation, focus the first item.
424 * If the list loses focus, unselect all the elements.
425 * @param {Event} e The change event.
428 handleListFocusChange_: function(e) {
429 // We check to see whether there is a selected item as a proxy for
430 // distinguishing between mouse- and keyboard-originated focus events.
431 var selectedItem = this.selectedItem;
433 InlineEditableItemList.prototype.handleListFocusChange_.call(this, e);
436 // When the list loses focus, unselect all the elements.
437 this.selectionModel.unselectAll();
439 // When the list gains focus, select the first item if nothing else is
441 var firstItem = this.getListItemByIndex(0);
442 if (!selectedItem && firstItem && e.newValue)
443 firstItem.handleFocus_();
448 * Called when a new list item should be validated; subclasses are
449 * responsible for implementing if validation is required.
450 * @param {number} index The index of the item that was inserted or changed.
451 * @param {number} remove The number items to remove.
452 * @param {string} value The value of the item to insert.
454 validateAndSave: function(index, remove, value) {
455 this.dataModel.splice(index, remove, value);
460 * Create a new value list for phone number validation.
462 * @extends {options.AutofillValuesList}
464 var AutofillNameValuesList = cr.ui.define('list');
466 AutofillNameValuesList.prototype = {
467 __proto__: AutofillValuesList.prototype,
470 createItem: function(entry) {
471 return new NameListItem(this, entry);
476 * Create a new value list for phone number validation.
478 * @extends {options.AutofillValuesList}
480 var AutofillPhoneValuesList = cr.ui.define('list');
482 AutofillPhoneValuesList.prototype = {
483 __proto__: AutofillValuesList.prototype,
486 validateAndSave: function(index, remove, value) {
487 var numbers = this.dataModel.slice(0, this.dataModel.length - 1);
488 numbers.splice(index, remove, value);
489 var info = new Array();
492 info[2] = $('country').value;
493 chrome.send('validatePhoneNumbers', info);
498 AddressListItem: AddressListItem,
499 CreditCardListItem: CreditCardListItem,
500 ValuesListItem: ValuesListItem,
501 NameListItem: NameListItem,
502 AutofillAddressList: AutofillAddressList,
503 AutofillCreditCardList: AutofillCreditCardList,
504 AutofillValuesList: AutofillValuesList,
505 AutofillNameValuesList: AutofillNameValuesList,
506 AutofillPhoneValuesList: AutofillPhoneValuesList,