summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-06 22:36:05 +0000
committerdhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-06 22:36:05 +0000
commitd778644fc02d5ac61a642a1eb3fadf43933c28b4 (patch)
tree0758435ce437d375d6d5bf0e3ded087b211d1770
parent0fb012aabaf28f0dad93d68c76faef78bcd44ac3 (diff)
downloadchromium_src-d778644fc02d5ac61a642a1eb3fadf43933c28b4.zip
chromium_src-d778644fc02d5ac61a642a1eb3fadf43933c28b4.tar.gz
chromium_src-d778644fc02d5ac61a642a1eb3fadf43933c28b4.tar.bz2
Autofill extend profiles to include multi-valued fields, part 4 (UI)
Adds multi-valued editing support for Autofill address information. Name, phone, fax, and email fields have been converted to editable lists that allow the user to store variants for a given address. BUG=65625 TEST=Manual: add, update, and delete multi-value items for name, phone, fax, and email. Review URL: http://codereview.chromium.org/6735065 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@80710 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd12
-rw-r--r--chrome/browser/resources/options/autofill_edit_address_overlay.html28
-rw-r--r--chrome/browser/resources/options/autofill_edit_address_overlay.js92
-rw-r--r--chrome/browser/resources/options/autofill_options_list.js152
-rw-r--r--chrome/browser/resources/options/autofill_overlay.css47
-rw-r--r--chrome/browser/ui/webui/options/autofill_options_handler.cc63
6 files changed, 347 insertions, 47 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 04c8df5a..2297e1e 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7196,6 +7196,18 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_AUTOFILL_DIALOG_FAX" desc="The label of the Fax entry.">
Fax
</message>
+ <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_NAME" desc="The placeholder text of the name field.">
+ Add new name
+ </message>
+ <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_PHONE" desc="The placeholder text of the phone field.">
+ Add new phone
+ </message>
+ <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_FAX" desc="The placeholder text of the fax field.">
+ Add new fax
+ </message>
+ <message name="IDS_AUTOFILL_DIALOG_ADD_NEW_EMAIL" desc="The placeholder text of the email field.">
+ Add new email
+ </message>
<message name="IDS_AUTOFILL_DIALOG_NAME_ON_CARD" desc="The label of the Name on card entry.">
Name on card
</message>
diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.html b/chrome/browser/resources/options/autofill_edit_address_overlay.html
index 0dbee9f..2579302 100644
--- a/chrome/browser/resources/options/autofill_edit_address_overlay.html
+++ b/chrome/browser/resources/options/autofill_edit_address_overlay.html
@@ -4,7 +4,8 @@
<div class="input">
<label>
<div><span i18n-content="fullNameLabel"></span></div>
- <input type="text" class="autofill-form" id="full-name">
+ <list id="full-name-list"
+ i18n-values="placeholder:addNewNamePlaceholder"></list>
</label>
</div>
<div class="input">
@@ -60,31 +61,36 @@
<div class="table">
<div class="row">
<div class="input cell">
- <label for="phone">
+ <label for="phone-list">
<span i18n-content="phoneLabel"></span>
</label>
</div>
<div class="input cell">
- <label for="fax">
+ <label for="fax-list">
<span i18n-content="faxLabel"></span>
</label>
</div>
+ <div class="input cell">
+ <label for="email-list">
+ <span i18n-content="emailLabel"></span>
+ </label>
+ </div>
</div>
<div class="row">
<div class="input cell">
- <input type="text" class="autofill-form" id="phone">
+ <list id="phone-list"
+ i18n-values="placeholder:addNewPhonePlaceholder"></list>
+ </div>
+ <div class="input cell">
+ <list id="fax-list"
+ i18n-values="placeholder:addNewFaxPlaceholder"></list>
</div>
<div class="input cell">
- <input type="text" class="autofill-form" id="fax">
+ <list id="email-list"
+ i18n-values="placeholder:addNewEmailPlaceholder"></list>
</div>
</div>
</div>
- <div class="input">
- <label>
- <div><span i18n-content="emailLabel"></span></div>
- <input type="text" class="autofill-form" id="email">
- </label>
- </div>
</div>
<div class="action-area button-strip">
<button type="reset" id="autofill-edit-address-cancel-button"
diff --git a/chrome/browser/resources/options/autofill_edit_address_overlay.js b/chrome/browser/resources/options/autofill_edit_address_overlay.js
index c1ce694..e682759 100644
--- a/chrome/browser/resources/options/autofill_edit_address_overlay.js
+++ b/chrome/browser/resources/options/autofill_edit_address_overlay.js
@@ -4,6 +4,7 @@
cr.define('options', function() {
const OptionsPage = options.OptionsPage;
+ const ArrayDataModel = cr.ui.ArrayDataModel;
// The GUID of the loaded address.
var guid;
@@ -30,6 +31,8 @@ cr.define('options', function() {
initializePage: function() {
OptionsPage.prototype.initializePage.call(this);
+ this.createMultiValueLists_();
+
var self = this;
$('autofill-edit-address-cancel-button').onclick = function(event) {
self.dismissOverlay_();
@@ -46,6 +49,51 @@ cr.define('options', function() {
},
/**
+ * Creates, decorates and initializes the multi-value lists for full name,
+ * phone, fax, and email.
+ * @private
+ */
+ createMultiValueLists_: function() {
+ var list = $('full-name-list');
+ options.autofillOptions.AutofillValuesList.decorate(list);
+ list.autoExpands = true;
+
+ list = $('phone-list');
+ options.autofillOptions.AutofillValuesList.decorate(list);
+ list.autoExpands = true;
+
+ list = $('fax-list');
+ options.autofillOptions.AutofillValuesList.decorate(list);
+ list.autoExpands = true;
+
+ list = $('email-list');
+ options.autofillOptions.AutofillValuesList.decorate(list);
+ list.autoExpands = true;
+ },
+
+ /**
+ * Updates the data model for the list named |listName| with the values from
+ * |entries|.
+ * @param {String} listName The id of the list.
+ * @param {Array} entries The list of items to be added to the list.
+ */
+ setMultiValueList_: function(listName, entries) {
+ // Add data entries, filtering null or empty strings.
+ var list = $(listName);
+ list.dataModel = new ArrayDataModel(
+ entries.filter(function(i) {return i}));
+
+ // Add special entry for adding new values.
+ list.dataModel.splice(list.dataModel.length, 0, null);
+
+ var self = this;
+ list.dataModel.addEventListener(
+ 'splice', function(event) { self.inputFieldChanged_(); });
+ list.dataModel.addEventListener(
+ 'change', function(event) { self.inputFieldChanged_(); });
+ },
+
+ /**
* Clears any uncommitted input, resets the stored GUID and dismisses the
* overlay.
* @private
@@ -64,7 +112,8 @@ cr.define('options', function() {
saveAddress_: function() {
var address = new Array();
address[0] = this.guid;
- address[1] = $('full-name').value;
+ var list = $('full-name-list');
+ address[1] = list.dataModel.slice(0, list.dataModel.length - 1);
address[2] = $('company-name').value;
address[3] = $('addr-line-1').value;
address[4] = $('addr-line-2').value;
@@ -72,9 +121,12 @@ cr.define('options', function() {
address[6] = $('state').value;
address[7] = $('postal-code').value;
address[8] = $('country').value;
- address[9] = $('phone').value;
- address[10] = $('fax').value;
- address[11] = $('email').value;
+ list = $('phone-list');
+ address[9] = list.dataModel.slice(0, list.dataModel.length - 1);
+ list = $('fax-list');
+ address[10] = list.dataModel.slice(0, list.dataModel.length - 1);
+ list = $('email-list');
+ address[11] = list.dataModel.slice(0, list.dataModel.length - 1);
chrome.send('setAddress', address);
},
@@ -87,10 +139,9 @@ cr.define('options', function() {
*/
connectInputEvents_: function() {
var self = this;
- $('full-name').oninput = $('company-name').oninput =
- $('addr-line-1').oninput = $('addr-line-2').oninput = $('city').oninput =
- $('state').oninput = $('postal-code').oninput = $('phone').oninput =
- $('fax').oninput = $('email').oninput = function(event) {
+ $('company-name').oninput = $('addr-line-1').oninput =
+ $('addr-line-2').oninput = $('city').oninput = $('state').oninput =
+ $('postal-code').oninput = function(event) {
self.inputFieldChanged_();
}
@@ -105,12 +156,15 @@ cr.define('options', function() {
* @private
*/
inputFieldChanged_: function() {
+ // Length of lists are tested for <= 1 due to the "add" placeholder item
+ // in the list.
var disabled =
- !$('full-name').value && !$('company-name').value &&
+ $('full-name-list').items.length <= 1 &&
+ !$('company-name').value &&
!$('addr-line-1').value && !$('addr-line-2').value &&
!$('city').value && !$('state').value && !$('postal-code').value &&
- !$('country').value && !$('phone').value && !$('fax').value &&
- !$('email').value;
+ !$('country').value && $('phone-list').items.length <= 1 &&
+ $('fax-list').items.length <= 1 && $('email-list').items.length <= 1;
$('autofill-edit-address-apply-button').disabled = disabled;
},
@@ -186,7 +240,7 @@ cr.define('options', function() {
* @private
*/
clearInputFields_: function() {
- $('full-name').value = '';
+ this.setMultiValueList_('full-name-list', []);
$('company-name').value = '';
$('addr-line-1').value = '';
$('addr-line-2').value = '';
@@ -194,9 +248,9 @@ cr.define('options', function() {
$('state').value = '';
$('postal-code').value = '';
$('country').value = '';
- $('phone').value = '';
- $('fax').value = '';
- $('email').value = '';
+ this.setMultiValueList_('phone-list', []);
+ this.setMultiValueList_('fax-list', []);
+ this.setMultiValueList_('email-list', []);
this.countryChanged_();
},
@@ -217,7 +271,7 @@ cr.define('options', function() {
* @private
*/
setInputFields_: function(address) {
- $('full-name').value = address['fullName'];
+ this.setMultiValueList_('full-name-list', address['fullName']);
$('company-name').value = address['companyName'];
$('addr-line-1').value = address['addrLine1'];
$('addr-line-2').value = address['addrLine2'];
@@ -225,9 +279,9 @@ cr.define('options', function() {
$('state').value = address['state'];
$('postal-code').value = address['postalCode'];
$('country').value = address['country'];
- $('phone').value = address['phone'];
- $('fax').value = address['fax'];
- $('email').value = address['email'];
+ this.setMultiValueList_('phone-list', address['phone']);
+ this.setMultiValueList_('fax-list', address['fax']);
+ this.setMultiValueList_('email-list', address['email']);
this.countryChanged_();
},
diff --git a/chrome/browser/resources/options/autofill_options_list.js b/chrome/browser/resources/options/autofill_options_list.js
index b90bbb7..8638726 100644
--- a/chrome/browser/resources/options/autofill_options_list.js
+++ b/chrome/browser/resources/options/autofill_options_list.js
@@ -5,6 +5,8 @@
cr.define('options.autofillOptions', function() {
const DeletableItem = options.DeletableItem;
const DeletableItemList = options.DeletableItemList;
+ const InlineEditableItem = options.InlineEditableItem;
+ const InlineEditableItemList = options.InlineEditableItemList;
/**
* Creates a new address list item.
@@ -75,6 +77,110 @@ cr.define('options.autofillOptions', function() {
};
/**
+ * 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;
+ el.__proto__ = ValuesListItem.prototype;
+ el.decorate();
+
+ return el;
+ }
+
+ ValuesListItem.prototype = {
+ __proto__: InlineEditableItem.prototype,
+
+ /** @inheritDoc */
+ decorate: function() {
+ InlineEditableItem.prototype.decorate.call(this);
+
+ this.isPlaceholder = !this.value;
+
+ // The stored value.
+ var cell = this.createEditableTextCell(this.value);
+ this.contentElement.appendChild(cell);
+ this.input = cell.querySelector('input');
+
+ this.addEventListener('commitedit', this.onEditCommitted_);
+ },
+
+ /**
+ * Called when committing an edit.
+ * @param {Event} e The end event.
+ * @private
+ */
+ onEditCommitted_: function(e) {
+ var i = this.list.items.indexOf(this);
+ if (this.input.value == this.list.dataModel.item(i))
+ return;
+
+ if (this.input.value &&
+ this.list.dataModel.indexOf(this.input.value) == -1) {
+ // Update with new value.
+ this.list.dataModel.splice(i, 1, this.input.value);
+ } else {
+ // Reject empty values and duplicates.
+ this.list.dataModel.splice(i, 1);
+ }
+ },
+ };
+
+ /**
+ * Creates a new list item for the Add New Item row, which doesn't represent
+ * an actual entry in the values list but allows the user to add new
+ * values.
+ * @param {AutofillValuesList} entry The parent list of this item.
+ * @constructor
+ * @extends {cr.ui.ValuesListItem}
+ */
+ function ValuesAddRowListItem(list) {
+ var el = cr.doc.createElement('div');
+ el.list = list;
+ el.__proto__ = ValuesAddRowListItem.prototype;
+ el.decorate();
+
+ return el;
+ }
+
+ ValuesAddRowListItem.prototype = {
+ __proto__: ValuesListItem.prototype,
+
+ decorate: function() {
+ ValuesListItem.prototype.decorate.call(this);
+ this.input.value = '';
+ this.input.placeholder = this.list.getAttribute('placeholder');
+ this.deletable = false;
+ },
+
+ /**
+ * Called when committing an edit. Committing a non-empty value adds it
+ * to the end of the values list, leaving this "AddRow" in place.
+ * @param {Event} e The end event.
+ * @extends {options.ValuesListItem}
+ * @private
+ */
+ onEditCommitted_: function(e) {
+ var i = this.list.items.indexOf(this);
+ if (i < 0 || i >= this.list.dataModel.length)
+ return;
+
+ if (this.input.value &&
+ this.list.dataModel.indexOf(this.input.value) == -1) {
+ this.list.dataModel.splice(i, 0, this.input.value);
+ } else {
+ this.input.value = '';
+ this.list.dataModel.updateIndex(i);
+ }
+ },
+ };
+
+ /**
* Create a new address list.
* @constructor
* @extends {options.DeletableItemList}
@@ -154,10 +260,56 @@ cr.define('options.autofillOptions', function() {
},
};
+ /**
+ * Create a new value list.
+ * @constructor
+ * @extends {options.InlineEditableItemList}
+ */
+ var AutofillValuesList = cr.ui.define('list');
+
+ AutofillValuesList.prototype = {
+ __proto__: InlineEditableItemList.prototype,
+
+ decorate: function() {
+ InlineEditableItemList.prototype.decorate.call(this);
+
+ var self = this;
+ function handleBlur(e) {
+ // When the blur event happens we do not know who is getting focus so we
+ // delay this a bit until we know if the new focus node is outside the
+ // list.
+ var doc = e.target.ownerDocument;
+ window.setTimeout(function() {
+ var activeElement = doc.activeElement;
+ if (!self.contains(activeElement))
+ self.selectionModel.unselectAll();
+ }, 50);
+ }
+
+ this.addEventListener('blur', handleBlur, true);
+ },
+
+ /** @inheritDoc */
+ createItem: function(entry) {
+ if (entry != null)
+ return new ValuesListItem(this, entry);
+ else
+ return new ValuesAddRowListItem(this);
+ },
+
+ /** @inheritDoc */
+ deleteItemAtIndex: function(index) {
+ this.dataModel.splice(index, 1);
+ },
+ };
+
return {
AddressListItem: AddressListItem,
CreditCardListItem: CreditCardListItem,
+ ValuesListItem: ValuesListItem,
+ ValuesAddRowListItem: ValuesAddRowListItem,
AutofillAddressList: AutofillAddressList,
AutofillCreditCardList: AutofillCreditCardList,
+ AutofillValuesList: AutofillValuesList,
};
});
diff --git a/chrome/browser/resources/options/autofill_overlay.css b/chrome/browser/resources/options/autofill_overlay.css
index 32b8e27..c9a707a 100644
--- a/chrome/browser/resources/options/autofill_overlay.css
+++ b/chrome/browser/resources/options/autofill_overlay.css
@@ -1,4 +1,7 @@
-#autofill-edit-address-overlay,
+#autofill-edit-address-overlay {
+ width: 510px;
+}
+
#autofill-edit-credit-card-overlay {
width: 500px;
}
@@ -9,7 +12,6 @@ div.table {
div.cell {
display: table-cell;
- max-width: 151px;
}
div.row {
@@ -20,11 +22,46 @@ div.input {
padding: 2px;
}
-input.autofill-form {
- width: 90%;
- max-width: 151px;
+/* Pad |city|, |state|, and |postal-code| to line up nicely with |phone-list|
+ * |fax-list| and |email-list|. */
+#city, #state, #postal-code {
+ margin: 0;
+ -webkit-margin-start: 2px;
+ -webkit-margin-end: 4px;
}
#country {
max-width: 450px;
}
+
+#autofill-edit-address-overlay list {
+ /* Min height is a multiple of the list item height (32) */
+ min-height: 32px;
+ width: 156px;
+}
+
+#autofill-edit-address-overlay list div.static-text {
+ -webkit-box-flex: 1;
+ -webkit-border-radius: 2px;
+ -webkit-padding-start: 4px;
+ -webkit-padding-end: 4px;
+ border: 1px solid darkGray;
+ width: 141px;
+}
+
+#autofill-edit-address-overlay list#full-name-list {
+ width: 216px;
+}
+
+#autofill-edit-address-overlay list#full-name-list div.static-text {
+ width: 201px;
+}
+
+#autofill-edit-address-overlay list#full-name-list input {
+ width: 211px;
+}
+
+/* Adjust the close button placement. */
+#autofill-edit-address-overlay list .raw-button {
+ padding: 1px 10px;
+}
diff --git a/chrome/browser/ui/webui/options/autofill_options_handler.cc b/chrome/browser/ui/webui/options/autofill_options_handler.cc
index e991a221..8bf7709 100644
--- a/chrome/browser/ui/webui/options/autofill_options_handler.cc
+++ b/chrome/browser/ui/webui/options/autofill_options_handler.cc
@@ -68,6 +68,31 @@ DictionaryValue* GetCountryData() {
return country_data;
}
+// Get the multi-valued element for |type| and return it in |ListValue| form.
+void GetValueList(const AutofillProfile& profile,
+ AutofillFieldType type,
+ scoped_ptr<ListValue>* list) {
+ std::vector<string16> values;
+ profile.GetMultiInfo(type, &values);
+ list->reset(new ListValue);
+ for (size_t i = 0; i < values.size(); ++i) {
+ (*list)->Set(i, Value::CreateStringValue(values[i]));
+ }
+}
+
+// Set the multi-valued element for |type| from input |list| values.
+void SetValueList(const ListValue* list,
+ AutofillFieldType type,
+ AutofillProfile* profile) {
+ std::vector<string16> values(list->GetSize());
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ string16 value;
+ if (list->GetString(i, &value))
+ values[i] = value;
+ }
+ profile->SetMultiInfo(type, values);
+}
+
} // namespace
AutofillOptionsHandler::AutofillOptionsHandler()
@@ -168,6 +193,14 @@ void AutofillOptionsHandler::SetAddressOverlayStrings(
l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_FAX));
localized_strings->SetString("emailLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_EMAIL));
+ localized_strings->SetString("addNewNamePlaceholder",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_NAME));
+ localized_strings->SetString("addNewPhonePlaceholder",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_PHONE));
+ localized_strings->SetString("addNewFaxPlaceholder",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_FAX));
+ localized_strings->SetString("addNewEmailPlaceholder",
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_ADD_NEW_EMAIL));
std::string app_locale = AutofillCountry::ApplicationLocale();
std::string default_country_code =
@@ -266,7 +299,9 @@ void AutofillOptionsHandler::LoadAddressEditor(const ListValue* args) {
DictionaryValue address;
address.SetString("guid", profile->guid());
- address.SetString("fullName", profile->GetInfo(NAME_FULL));
+ scoped_ptr<ListValue> list;
+ GetValueList(*profile, NAME_FULL, &list);
+ address.Set("fullName", list.release());
address.SetString("companyName", profile->GetInfo(COMPANY_NAME));
address.SetString("addrLine1", profile->GetInfo(ADDRESS_HOME_LINE1));
address.SetString("addrLine2", profile->GetInfo(ADDRESS_HOME_LINE2));
@@ -274,9 +309,12 @@ void AutofillOptionsHandler::LoadAddressEditor(const ListValue* args) {
address.SetString("state", profile->GetInfo(ADDRESS_HOME_STATE));
address.SetString("postalCode", profile->GetInfo(ADDRESS_HOME_ZIP));
address.SetString("country", profile->CountryCode());
- address.SetString("phone", profile->GetInfo(PHONE_HOME_WHOLE_NUMBER));
- address.SetString("fax", profile->GetInfo(PHONE_FAX_WHOLE_NUMBER));
- address.SetString("email", profile->GetInfo(EMAIL_ADDRESS));
+ GetValueList(*profile, PHONE_HOME_WHOLE_NUMBER, &list);
+ address.Set("phone", list.release());
+ GetValueList(*profile, PHONE_FAX_WHOLE_NUMBER, &list);
+ address.Set("fax", list.release());
+ GetValueList(*profile, EMAIL_ADDRESS, &list);
+ address.Set("email", list.release());
web_ui_->CallJavascriptFunction("AutofillOptions.editAddress", address);
}
@@ -330,8 +368,9 @@ void AutofillOptionsHandler::SetAddress(const ListValue* args) {
std::string country_code;
string16 value;
- if (args->GetString(1, &value))
- profile.SetInfo(NAME_FULL, value);
+ ListValue* list_value;
+ if (args->GetList(1, &list_value))
+ SetValueList(list_value, NAME_FULL, &profile);
if (args->GetString(2, &value))
profile.SetInfo(COMPANY_NAME, value);
if (args->GetString(3, &value))
@@ -346,12 +385,12 @@ void AutofillOptionsHandler::SetAddress(const ListValue* args) {
profile.SetInfo(ADDRESS_HOME_ZIP, value);
if (args->GetString(8, &country_code))
profile.SetCountryCode(country_code);
- if (args->GetString(9, &value))
- profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, value);
- if (args->GetString(10, &value))
- profile.SetInfo(PHONE_FAX_WHOLE_NUMBER, value);
- if (args->GetString(11, &value))
- profile.SetInfo(EMAIL_ADDRESS, value);
+ if (args->GetList(9, &list_value))
+ SetValueList(list_value, PHONE_HOME_WHOLE_NUMBER, &profile);
+ if (args->GetList(10, &list_value))
+ SetValueList(list_value, PHONE_FAX_WHOLE_NUMBER, &profile);
+ if (args->GetList(11, &list_value))
+ SetValueList(list_value, EMAIL_ADDRESS, &profile);
if (!guid::IsValidGUID(profile.guid())) {
profile.set_guid(guid::GenerateGUID());