Index: chrome/browser/resources/options2/autofill_options_list.js |
diff --git a/chrome/browser/resources/options2/autofill_options_list.js b/chrome/browser/resources/options2/autofill_options_list.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c9ed67ce9ff07d3aa0aaa37fa7ba5d20b3c142fd |
--- /dev/null |
+++ b/chrome/browser/resources/options2/autofill_options_list.js |
@@ -0,0 +1,506 @@ |
+// Copyright (c) 2011 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.autofillOptions', function() { |
+ const DeletableItem = options.DeletableItem; |
+ const DeletableItemList = options.DeletableItemList; |
+ const InlineEditableItem = options.InlineEditableItem; |
+ const InlineEditableItemList = options.InlineEditableItemList; |
+ |
+ function AutofillEditProfileButton(guid, edit) { |
+ var editButtonEl = document.createElement('button'); |
+ editButtonEl.className = 'raw-button custom-appearance'; |
+ editButtonEl.textContent = |
+ templateData.autofillEditProfileButton; |
+ editButtonEl.onclick = function(e) { edit(guid); }; |
+ |
+ // Don't select the row when clicking the button. |
+ editButtonEl.onmousedown = function(e) { |
+ e.stopPropagation(); |
+ }; |
+ |
+ return editButtonEl; |
+ } |
+ |
+ /** |
+ * Creates a new address list item. |
+ * @param {Array} entry An array of the form [guid, label]. |
+ * @constructor |
+ * @extends {options.DeletableItem} |
+ */ |
+ function AddressListItem(entry) { |
+ var el = cr.doc.createElement('div'); |
+ el.guid = entry[0]; |
+ el.label = entry[1]; |
+ el.__proto__ = AddressListItem.prototype; |
+ el.decorate(); |
+ |
+ return el; |
+ } |
+ |
+ AddressListItem.prototype = { |
+ __proto__: DeletableItem.prototype, |
+ |
+ /** @inheritDoc */ |
+ decorate: function() { |
+ DeletableItem.prototype.decorate.call(this); |
+ |
+ // The stored label. |
+ var label = this.ownerDocument.createElement('div'); |
+ label.className = 'autofill-list-item'; |
+ label.textContent = this.label; |
+ this.contentElement.appendChild(label); |
+ |
+ // The 'Edit' button. |
+ var editButtonEl = new AutofillEditProfileButton( |
+ this.guid, |
+ AutofillOptions.loadAddressEditor); |
+ this.contentElement.appendChild(editButtonEl); |
+ }, |
+ }; |
+ |
+ /** |
+ * Creates a new credit card list item. |
+ * @param {Array} entry An array of the form [guid, label, icon]. |
+ * @constructor |
+ * @extends {options.DeletableItem} |
+ */ |
+ function CreditCardListItem(entry) { |
+ var el = cr.doc.createElement('div'); |
+ el.guid = entry[0]; |
+ el.label = entry[1]; |
+ el.icon = entry[2]; |
+ el.description = entry[3]; |
+ el.__proto__ = CreditCardListItem.prototype; |
+ el.decorate(); |
+ |
+ return el; |
+ } |
+ |
+ CreditCardListItem.prototype = { |
+ __proto__: DeletableItem.prototype, |
+ |
+ /** @inheritDoc */ |
+ decorate: function() { |
+ DeletableItem.prototype.decorate.call(this); |
+ |
+ // The stored label. |
+ var label = this.ownerDocument.createElement('div'); |
+ label.className = 'autofill-list-item'; |
+ label.textContent = this.label; |
+ this.contentElement.appendChild(label); |
+ |
+ // The credit card icon. |
+ var icon = this.ownerDocument.createElement('image'); |
+ icon.src = this.icon; |
+ icon.alt = this.description; |
+ this.contentElement.appendChild(icon); |
+ |
+ // The 'Edit' button. |
+ var editButtonEl = new AutofillEditProfileButton( |
+ this.guid, |
+ AutofillOptions.loadCreditCardEditor); |
+ this.contentElement.appendChild(editButtonEl); |
+ }, |
+ }; |
+ |
+ /** |
+ * Creates a new value list item. |
+ * @param {AutofillValuesList} list The parent list of this item. |
+ * @param {String} entry A string value. |
+ * @constructor |
+ * @extends {options.InlineEditableItem} |
+ */ |
+ function ValuesListItem(list, entry) { |
+ var el = cr.doc.createElement('div'); |
+ el.list = list; |
+ el.value = entry ? entry : ''; |
+ el.__proto__ = ValuesListItem.prototype; |
+ el.decorate(); |
+ |
+ return el; |
+ } |
+ |
+ ValuesListItem.prototype = { |
+ __proto__: InlineEditableItem.prototype, |
+ |
+ /** @inheritDoc */ |
+ decorate: function() { |
+ InlineEditableItem.prototype.decorate.call(this); |
+ |
+ // Note: This must be set prior to calling |createEditableTextCell|. |
+ this.isPlaceholder = !this.value; |
+ |
+ // The stored value. |
+ var cell = this.createEditableTextCell(this.value); |
+ this.contentElement.appendChild(cell); |
+ this.input = cell.querySelector('input'); |
+ |
+ if (this.isPlaceholder) { |
+ this.input.placeholder = this.list.getAttribute('placeholder'); |
+ this.deletable = false; |
+ } |
+ |
+ this.addEventListener('commitedit', this.onEditCommitted_); |
+ }, |
+ |
+ /** |
+ * @return This item's value. |
+ * @protected |
+ */ |
+ value_: function() { |
+ return this.input.value; |
+ }, |
+ |
+ /** |
+ * @param {Object} value The value to test. |
+ * @return true if the given value is non-empty. |
+ * @protected |
+ */ |
+ valueIsNonEmpty_: function(value) { |
+ return !!value; |
+ }, |
+ |
+ /** |
+ * @return true if value1 is logically equal to value2. |
+ */ |
+ valuesAreEqual_: function(value1, value2) { |
+ return value1 === value2; |
+ }, |
+ |
+ /** |
+ * Clears the item's value. |
+ * @protected |
+ */ |
+ clearValue_: function() { |
+ this.input.value = ''; |
+ }, |
+ |
+ /** |
+ * Called when committing an edit. |
+ * If this is an "Add ..." item, committing a non-empty value adds that |
+ * value to the end of the values list, but also leaves this "Add ..." item |
+ * in place. |
+ * @param {Event} e The end event. |
+ * @private |
+ */ |
+ onEditCommitted_: function(e) { |
+ var value = this.value_(); |
+ var i = this.list.items.indexOf(this); |
+ if (i < this.list.dataModel.length && |
+ this.valuesAreEqual_(value, this.list.dataModel.item(i))) { |
+ return; |
+ } |
+ |
+ var entries = this.list.dataModel.slice(); |
+ if (this.valueIsNonEmpty_(value) && |
+ !entries.some(this.valuesAreEqual_.bind(this, value))) { |
+ // Update with new value. |
+ if (this.isPlaceholder) { |
+ // It is important that updateIndex is done before validateAndSave. |
+ // Otherwise we can not be sure about AddRow index. |
+ this.list.dataModel.updateIndex(i); |
+ this.list.validateAndSave(i, 0, value); |
+ } else { |
+ this.list.validateAndSave(i, 1, value); |
+ } |
+ } else { |
+ // Reject empty values and duplicates. |
+ if (!this.isPlaceholder) |
+ this.list.dataModel.splice(i, 1); |
+ else |
+ this.clearValue_(); |
+ } |
+ }, |
+ }; |
+ |
+ /** |
+ * Creates a new name value list item. |
+ * @param {AutofillNameValuesList} list The parent list of this item. |
+ * @param {array} entry An array of [first, middle, last] names. |
+ * @constructor |
+ * @extends {options.ValuesListItem} |
+ */ |
+ function NameListItem(list, entry) { |
+ var el = cr.doc.createElement('div'); |
+ el.list = list; |
+ el.first = entry ? entry[0] : ''; |
+ el.middle = entry ? entry[1] : ''; |
+ el.last = entry ? entry[2] : ''; |
+ el.__proto__ = NameListItem.prototype; |
+ el.decorate(); |
+ |
+ return el; |
+ } |
+ |
+ NameListItem.prototype = { |
+ __proto__: ValuesListItem.prototype, |
+ |
+ /** @inheritDoc */ |
+ decorate: function() { |
+ InlineEditableItem.prototype.decorate.call(this); |
+ |
+ // Note: This must be set prior to calling |createEditableTextCell|. |
+ this.isPlaceholder = !this.first && !this.middle && !this.last; |
+ |
+ // The stored value. |
+ // For the simulated static "input element" to display correctly, the |
+ // value must not be empty. We use a space to force the UI to render |
+ // correctly when the value is logically empty. |
+ var cell = this.createEditableTextCell(this.first); |
+ this.contentElement.appendChild(cell); |
+ this.firstNameInput = cell.querySelector('input'); |
+ |
+ cell = this.createEditableTextCell(this.middle); |
+ this.contentElement.appendChild(cell); |
+ this.middleNameInput = cell.querySelector('input'); |
+ |
+ cell = this.createEditableTextCell(this.last); |
+ this.contentElement.appendChild(cell); |
+ this.lastNameInput = cell.querySelector('input'); |
+ |
+ if (this.isPlaceholder) { |
+ this.firstNameInput.placeholder = |
+ templateData.autofillAddFirstNamePlaceholder; |
+ this.middleNameInput.placeholder = |
+ templateData.autofillAddMiddleNamePlaceholder; |
+ this.lastNameInput.placeholder = |
+ templateData.autofillAddLastNamePlaceholder; |
+ this.deletable = false; |
+ } |
+ |
+ this.addEventListener('commitedit', this.onEditCommitted_); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ value_: function() { |
+ return [ this.firstNameInput.value, |
+ this.middleNameInput.value, |
+ this.lastNameInput.value ]; |
+ }, |
+ |
+ /** @inheritDoc */ |
+ valueIsNonEmpty_: function(value) { |
+ return value[0] || value[1] || value[2]; |
+ }, |
+ |
+ /** @inheritDoc */ |
+ valuesAreEqual_: function(value1, value2) { |
+ // First, check for null values. |
+ if (!value1 || !value2) |
+ return value1 == value2; |
+ |
+ return value1[0] === value2[0] && |
+ value1[1] === value2[1] && |
+ value1[2] === value2[2]; |
+ }, |
+ |
+ /** @inheritDoc */ |
+ clearValue_: function() { |
+ this.firstNameInput.value = ''; |
+ this.middleNameInput.value = ''; |
+ this.lastNameInput.value = ''; |
+ }, |
+ }; |
+ |
+ /** |
+ * Base class for shared implementation between address and credit card lists. |
+ * @constructor |
+ * @extends {options.DeletableItemList} |
+ */ |
+ var AutofillProfileList = cr.ui.define('list'); |
+ |
+ AutofillProfileList.prototype = { |
+ __proto__: DeletableItemList.prototype, |
+ |
+ decorate: function() { |
+ DeletableItemList.prototype.decorate.call(this); |
+ |
+ this.addEventListener('blur', this.onBlur_); |
+ }, |
+ |
+ /** |
+ * When the list loses focus, unselect all items in the list. |
+ * @private |
+ */ |
+ onBlur_: function() { |
+ this.selectionModel.unselectAll(); |
+ }, |
+ }; |
+ |
+ /** |
+ * Create a new address list. |
+ * @constructor |
+ * @extends {options.AutofillProfileList} |
+ */ |
+ var AutofillAddressList = cr.ui.define('list'); |
+ |
+ AutofillAddressList.prototype = { |
+ __proto__: AutofillProfileList.prototype, |
+ |
+ decorate: function() { |
+ AutofillProfileList.prototype.decorate.call(this); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ activateItemAtIndex: function(index) { |
+ AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ createItem: function(entry) { |
+ return new AddressListItem(entry); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ deleteItemAtIndex: function(index) { |
+ AutofillOptions.removeAddress(this.dataModel.item(index)[0]); |
+ }, |
+ }; |
+ |
+ /** |
+ * Create a new credit card list. |
+ * @constructor |
+ * @extends {options.DeletableItemList} |
+ */ |
+ var AutofillCreditCardList = cr.ui.define('list'); |
+ |
+ AutofillCreditCardList.prototype = { |
+ __proto__: AutofillProfileList.prototype, |
+ |
+ decorate: function() { |
+ AutofillProfileList.prototype.decorate.call(this); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ activateItemAtIndex: function(index) { |
+ AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ createItem: function(entry) { |
+ return new CreditCardListItem(entry); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ deleteItemAtIndex: function(index) { |
+ AutofillOptions.removeCreditCard(this.dataModel.item(index)[0]); |
+ }, |
+ }; |
+ |
+ /** |
+ * Create a new value list. |
+ * @constructor |
+ * @extends {options.InlineEditableItemList} |
+ */ |
+ var AutofillValuesList = cr.ui.define('list'); |
+ |
+ AutofillValuesList.prototype = { |
+ __proto__: InlineEditableItemList.prototype, |
+ |
+ /** @inheritDoc */ |
+ createItem: function(entry) { |
+ return new ValuesListItem(this, entry); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ deleteItemAtIndex: function(index) { |
+ this.dataModel.splice(index, 1); |
+ }, |
+ |
+ /** @inheritDoc */ |
+ shouldFocusPlaceholder: function() { |
+ return false; |
+ }, |
+ |
+ /** |
+ * Called when the list hierarchy as a whole loses or gains focus. |
+ * If the list was focused in response to a mouse click, call into the |
+ * superclass's implementation. If the list was focused in response to a |
+ * keyboard navigation, focus the first item. |
+ * If the list loses focus, unselect all the elements. |
+ * @param {Event} e The change event. |
+ * @private |
+ */ |
+ handleListFocusChange_: function(e) { |
+ // We check to see whether there is a selected item as a proxy for |
+ // distinguishing between mouse- and keyboard-originated focus events. |
+ var selectedItem = this.selectedItem; |
+ if (selectedItem) |
+ InlineEditableItemList.prototype.handleListFocusChange_.call(this, e); |
+ |
+ if (!e.newValue) { |
+ // When the list loses focus, unselect all the elements. |
+ this.selectionModel.unselectAll(); |
+ } else { |
+ // When the list gains focus, select the first item if nothing else is |
+ // selected. |
+ var firstItem = this.getListItemByIndex(0); |
+ if (!selectedItem && firstItem && e.newValue) |
+ firstItem.handleFocus_(); |
+ } |
+ }, |
+ |
+ /** |
+ * Called when a new list item should be validated; subclasses are |
+ * responsible for implementing if validation is required. |
+ * @param {number} index The index of the item that was inserted or changed. |
+ * @param {number} remove The number items to remove. |
+ * @param {string} value The value of the item to insert. |
+ */ |
+ validateAndSave: function(index, remove, value) { |
+ this.dataModel.splice(index, remove, value); |
+ }, |
+ }; |
+ |
+ /** |
+ * Create a new value list for phone number validation. |
+ * @constructor |
+ * @extends {options.AutofillValuesList} |
+ */ |
+ var AutofillNameValuesList = cr.ui.define('list'); |
+ |
+ AutofillNameValuesList.prototype = { |
+ __proto__: AutofillValuesList.prototype, |
+ |
+ /** @inheritDoc */ |
+ createItem: function(entry) { |
+ return new NameListItem(this, entry); |
+ }, |
+ }; |
+ |
+ /** |
+ * Create a new value list for phone number validation. |
+ * @constructor |
+ * @extends {options.AutofillValuesList} |
+ */ |
+ var AutofillPhoneValuesList = cr.ui.define('list'); |
+ |
+ AutofillPhoneValuesList.prototype = { |
+ __proto__: AutofillValuesList.prototype, |
+ |
+ /** @inheritDoc */ |
+ validateAndSave: function(index, remove, value) { |
+ var numbers = this.dataModel.slice(0, this.dataModel.length - 1); |
+ numbers.splice(index, remove, value); |
+ var info = new Array(); |
+ info[0] = index; |
+ info[1] = numbers; |
+ info[2] = $('country').value; |
+ chrome.send('validatePhoneNumbers', info); |
+ }, |
+ }; |
+ |
+ return { |
+ AddressListItem: AddressListItem, |
+ CreditCardListItem: CreditCardListItem, |
+ ValuesListItem: ValuesListItem, |
+ NameListItem: NameListItem, |
+ AutofillAddressList: AutofillAddressList, |
+ AutofillCreditCardList: AutofillCreditCardList, |
+ AutofillValuesList: AutofillValuesList, |
+ AutofillNameValuesList: AutofillNameValuesList, |
+ AutofillPhoneValuesList: AutofillPhoneValuesList, |
+ }; |
+}); |