blob: c36c98a4df0a94d89c5e5bbc9c8f52961b3e1c5b [file] [log] [blame]
// Copyright (c) 2012 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 */ var DeletableItem = options.DeletableItem;
/** @const */ var DeletableItemList = options.DeletableItemList;
/** @const */ var InlineEditableItem = options.InlineEditableItem;
/** @const */ var InlineEditableItemList = options.InlineEditableItemList;
function AutofillEditProfileButton(guid, edit) {
var editButtonEl = document.createElement('button');
editButtonEl.className = 'list-inline-button custom-appearance';
editButtonEl.textContent =
loadTimeData.getString('autofillEditProfileButton');
editButtonEl.onclick = function(e) { edit(guid); };
editButtonEl.onmousedown = function(e) {
// Don't select the row when clicking the button.
e.stopPropagation();
// Don't focus on the button when clicking it.
e.preventDefault();
};
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,
/** @override */
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,
/** @override */
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,
/** @override */
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 {string} This item's value.
* @protected
*/
value_: function() {
return this.input.value;
},
/**
* @param {Object} value The value to test.
* @return {boolean} True if the given value is non-empty.
* @protected
*/
valueIsNonEmpty_: function(value) {
return !!value;
},
/**
* @return {boolean} 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,
/** @override */
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 =
loadTimeData.getString('autofillAddFirstNamePlaceholder');
this.middleNameInput.placeholder =
loadTimeData.getString('autofillAddMiddleNamePlaceholder');
this.lastNameInput.placeholder =
loadTimeData.getString('autofillAddLastNamePlaceholder');
this.deletable = false;
}
this.addEventListener('commitedit', this.onEditCommitted_);
},
/** @override */
value_: function() {
return [this.firstNameInput.value,
this.middleNameInput.value,
this.lastNameInput.value];
},
/** @override */
valueIsNonEmpty_: function(value) {
return value[0] || value[1] || value[2];
},
/** @override */
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];
},
/** @override */
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);
},
/** @override */
activateItemAtIndex: function(index) {
AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]);
},
/** @override */
createItem: function(entry) {
return new AddressListItem(entry);
},
/** @override */
deleteItemAtIndex: function(index) {
AutofillOptions.removeData(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);
},
/** @override */
activateItemAtIndex: function(index) {
AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]);
},
/** @override */
createItem: function(entry) {
return new CreditCardListItem(entry);
},
/** @override */
deleteItemAtIndex: function(index) {
AutofillOptions.removeData(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,
/** @override */
createItem: function(entry) {
return new ValuesListItem(this, entry);
},
/** @override */
deleteItemAtIndex: function(index) {
this.dataModel.splice(index, 1);
},
/** @override */
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,
/** @override */
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,
/** @override */
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,
};
});