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', function() {
6 /** @const */ var Page
= cr
.ui
.pageManager
.Page
;
7 /** @const */ var PageManager
= cr
.ui
.pageManager
.PageManager
;
8 /** @const */ var ArrayDataModel
= cr
.ui
.ArrayDataModel
;
11 * AutofillEditAddressOverlay class
12 * Encapsulated handling of the 'Add Page' overlay page.
14 * @extends {cr.ui.pageManager.Page}
16 function AutofillEditAddressOverlay() {
17 Page
.call(this, 'autofillEditAddress',
18 loadTimeData
.getString('autofillEditAddressTitle'),
19 'autofill-edit-address-overlay');
22 cr
.addSingletonGetter(AutofillEditAddressOverlay
);
24 AutofillEditAddressOverlay
.prototype = {
25 __proto__
: Page
.prototype,
28 * The GUID of the loaded address.
34 * The BCP 47 language code for the layout of input fields.
40 * The saved field values for the address. For example, if the user changes
41 * from United States to Switzerland, then the State field will be hidden
42 * and its value will be stored here. If the user changes back to United
43 * States, then the State field will be restored to its previous value, as
44 * stored in this object.
47 savedFieldValues_
: {},
50 initializePage: function() {
51 Page
.prototype.initializePage
.call(this);
53 this.createMultiValueLists_();
56 $('autofill-edit-address-cancel-button').onclick = function(event
) {
57 self
.dismissOverlay_();
60 // TODO(jhawkins): Investigate other possible solutions.
61 $('autofill-edit-address-apply-button').onclick = function(event
) {
62 // Blur active element to ensure that pending changes are committed.
63 if (document
.activeElement
)
64 document
.activeElement
.blur();
65 // Blurring is delayed for list elements. Queue save and close to
66 // ensure that pending changes have been applied.
67 setTimeout(function() {
68 self
.pageDiv
.querySelector('[field=phone]').doneValidating().then(
71 self
.dismissOverlay_();
76 // Prevent 'blur' events on the OK and cancel buttons, which can trigger
77 // insertion of new placeholder elements. The addition of placeholders
78 // affects layout, which interferes with being able to click on the
80 $('autofill-edit-address-apply-button').onmousedown = function(event
) {
81 event
.preventDefault();
83 $('autofill-edit-address-cancel-button').onmousedown = function(event
) {
84 event
.preventDefault();
88 this.populateCountryList_();
89 this.rebuildInputFields_(
90 loadTimeData
.getValue('autofillDefaultCountryComponents'));
92 loadTimeData
.getString('autofillDefaultCountryLanguageCode');
93 this.connectInputEvents_();
94 this.setInputFields_({});
95 this.getCountrySwitcher_().onchange = function(event
) {
96 self
.countryChanged_();
101 * Specifically catch the situations in which the overlay is cancelled
102 * externally (e.g. by pressing <Esc>), so that the input fields and
103 * GUID can be properly cleared.
106 handleCancel: function() {
107 this.dismissOverlay_();
111 * Creates, decorates and initializes the multi-value lists for phone and
115 createMultiValueLists_: function() {
116 var list
= this.pageDiv
.querySelector('[field=phone]');
117 options
.autofillOptions
.AutofillPhoneValuesList
.decorate(list
);
118 list
.autoExpands
= true;
120 list
= this.pageDiv
.querySelector('[field=email]');
121 options
.autofillOptions
.AutofillValuesList
.decorate(list
);
122 list
.autoExpands
= true;
126 * Updates the data model for the |list| with the values from |entries|.
127 * @param {cr.ui.List} list The list to update.
128 * @param {Array} entries The list of items to be added to the list.
131 setMultiValueList_: function(list
, entries
) {
132 // Add special entry for adding new values.
133 var augmentedList
= entries
.slice();
134 augmentedList
.push(null);
135 list
.dataModel
= new ArrayDataModel(augmentedList
);
137 // Update the status of the 'OK' button.
138 this.inputFieldChanged_();
140 list
.dataModel
.addEventListener('splice',
141 this.inputFieldChanged_
.bind(this));
142 list
.dataModel
.addEventListener('change',
143 this.inputFieldChanged_
.bind(this));
147 * Clears any uncommitted input, resets the stored GUID and dismisses the
151 dismissOverlay_: function() {
152 this.setInputFields_({});
153 this.inputFieldChanged_();
155 this.languageCode_
= '';
156 this.savedInputFields_
= {};
157 PageManager
.closeOverlay();
161 * @return {Element} The element used to switch countries.
164 getCountrySwitcher_: function() {
165 return this.pageDiv
.querySelector('[field=country]');
169 * Returns all list elements.
170 * @return {!NodeList} The list elements.
173 getLists_: function() {
174 return this.pageDiv
.querySelectorAll('list[field]');
178 * Returns all text input elements.
179 * @return {!NodeList} The text input elements.
182 getTextFields_: function() {
183 return this.pageDiv
.querySelectorAll('textarea[field], input[field]');
187 * Creates a map from type => value for all text fields.
188 * @return {Object} The mapping from field names to values.
191 getInputFields_: function() {
192 var address
= {country
: this.getCountrySwitcher_().value
};
194 var lists
= this.getLists_();
195 for (var i
= 0; i
< lists
.length
; i
++) {
196 address
[lists
[i
].getAttribute('field')] =
197 lists
[i
].dataModel
.slice(0, lists
[i
].dataModel
.length
- 1);
200 var fields
= this.getTextFields_();
201 for (var i
= 0; i
< fields
.length
; i
++) {
202 address
[fields
[i
].getAttribute('field')] = fields
[i
].value
;
209 * Sets the value of each input field according to |address|.
210 * @param {Object} address The object with values to use.
213 setInputFields_: function(address
) {
214 this.getCountrySwitcher_().value
= address
.country
|| '';
216 var lists
= this.getLists_();
217 for (var i
= 0; i
< lists
.length
; i
++) {
218 this.setMultiValueList_(
219 lists
[i
], address
[lists
[i
].getAttribute('field')] || []);
222 var fields
= this.getTextFields_();
223 for (var i
= 0; i
< fields
.length
; i
++) {
224 fields
[i
].value
= address
[fields
[i
].getAttribute('field')] || '';
229 * Aggregates the values in the input fields into an array and sends the
230 * array to the Autofill handler.
233 saveAddress_: function() {
234 var inputFields
= this.getInputFields_();
237 inputFields
['fullName'] || [],
238 inputFields
['companyName'] || '',
239 inputFields
['addrLines'] || '',
240 inputFields
['dependentLocality'] || '',
241 inputFields
['city'] || '',
242 inputFields
['state'] || '',
243 inputFields
['postalCode'] || '',
244 inputFields
['sortingCode'] || '',
245 inputFields
['country'] || loadTimeData
.getString('defaultCountryCode'),
246 inputFields
['phone'] || [],
247 inputFields
['email'] || [],
250 chrome
.send('setAddress', address
);
252 // If the GUID is empty, this form is being used to add a new address,
253 // rather than edit an existing one.
254 if (!this.guid_
.length
) {
255 chrome
.send('coreOptionsUserMetricsAction',
256 ['Options_AutofillAddressAdded']);
261 * Connects each input field to the inputFieldChanged_() method that enables
262 * or disables the 'Ok' button based on whether all the fields are empty or
266 connectInputEvents_: function() {
267 var fields
= this.getTextFields_();
268 for (var i
= 0; i
< fields
.length
; i
++) {
269 fields
[i
].oninput
= this.inputFieldChanged_
.bind(this);
274 * Disables the 'Ok' button if all of the fields are empty.
277 inputFieldChanged_: function() {
278 var disabled
= !this.getCountrySwitcher_().value
;
280 // Length of lists are tested for > 1 due to the "add" placeholder item
282 var lists
= this.getLists_();
283 for (var i
= 0; i
< lists
.length
; i
++) {
284 if (lists
[i
].items
.length
> 1) {
292 var fields
= this.getTextFields_();
293 for (var i
= 0; i
< fields
.length
; i
++) {
294 if (fields
[i
].value
) {
301 $('autofill-edit-address-apply-button').disabled
= disabled
;
305 * Updates the address fields appropriately for the selected country.
308 countryChanged_: function() {
309 var countryCode
= this.getCountrySwitcher_().value
;
311 chrome
.send('loadAddressEditorComponents', [countryCode
]);
313 this.inputFieldChanged_();
317 * Populates the country <select> list.
320 populateCountryList_: function() {
321 var countryList
= loadTimeData
.getValue('autofillCountrySelectList');
323 // Add the countries to the country <select> list.
324 var countrySelect
= this.getCountrySwitcher_();
325 // Add an empty option.
326 countrySelect
.appendChild(new Option('', ''));
327 for (var i
= 0; i
< countryList
.length
; i
++) {
328 var option
= new Option(countryList
[i
].name
,
329 countryList
[i
].value
);
330 option
.disabled
= countryList
[i
].value
== 'separator';
331 countrySelect
.appendChild(option
);
336 * Called to prepare the overlay when a new address is being added.
339 prepForNewAddress_: function() {
340 // Focus the first element.
341 this.pageDiv
.querySelector('input').focus();
345 * Loads the address data from |address|, sets the input fields based on
346 * this data, and stores the GUID and language code of the address.
347 * @param {!Object} address Lots of info about an address from the browser.
350 loadAddress_: function(address
) {
351 this.rebuildInputFields_(address
.components
);
352 this.setInputFields_(address
);
353 this.inputFieldChanged_();
354 this.connectInputEvents_();
355 this.guid_
= address
.guid
;
356 this.languageCode_
= address
.languageCode
;
360 * Takes a snapshot of the input values, clears the input values, loads the
361 * address input layout from |input.components|, restores the input values
362 * from snapshot, and stores the |input.languageCode| for the address.
363 * @param {{languageCode: string, components: Array<Array<Object>>}} input
364 * Info about how to layout inputs fields in this dialog.
367 loadAddressComponents_: function(input
) {
368 var inputFields
= this.getInputFields_();
369 for (var fieldName
in inputFields
) {
370 if (inputFields
.hasOwnProperty(fieldName
))
371 this.savedFieldValues_
[fieldName
] = inputFields
[fieldName
];
373 this.rebuildInputFields_(input
.components
);
374 this.setInputFields_(this.savedFieldValues_
);
375 this.inputFieldChanged_();
376 this.connectInputEvents_();
377 this.languageCode_
= input
.languageCode
;
381 * Clears address inputs and rebuilds the input fields according to
383 * @param {Array<Array<Object>>} components A list of information about
387 rebuildInputFields_: function(components
) {
388 var content
= $('autofill-edit-address-fields');
389 content
.innerHTML
= '';
391 var customContainerElements
= {fullName
: 'div'};
392 var customInputElements
= {fullName
: 'list', addrLines
: 'textarea'};
394 for (var i
in components
) {
395 var row
= document
.createElement('div');
396 row
.classList
.add('input-group', 'settings-row');
397 content
.appendChild(row
);
399 for (var j
in components
[i
]) {
400 if (components
[i
][j
].field
== 'country')
403 var fieldContainer
= document
.createElement(
404 customContainerElements
[components
[i
][j
].field
] || 'label');
405 row
.appendChild(fieldContainer
);
407 var fieldName
= document
.createElement('div');
408 fieldName
.textContent
= components
[i
][j
].name
;
409 fieldContainer
.appendChild(fieldName
);
411 var input
= document
.createElement(
412 customInputElements
[components
[i
][j
].field
] || 'input');
413 input
.setAttribute('field', components
[i
][j
].field
);
414 input
.classList
.add(components
[i
][j
].length
);
415 input
.setAttribute('placeholder', components
[i
][j
].placeholder
|| '');
416 fieldContainer
.appendChild(input
);
418 if (input
.tagName
== 'LIST') {
419 options
.autofillOptions
.AutofillValuesList
.decorate(input
);
420 input
.autoExpands
= true;
427 AutofillEditAddressOverlay
.prepForNewAddress = function() {
428 AutofillEditAddressOverlay
.getInstance().prepForNewAddress_();
431 AutofillEditAddressOverlay
.loadAddress = function(address
) {
432 AutofillEditAddressOverlay
.getInstance().loadAddress_(address
);
435 AutofillEditAddressOverlay
.loadAddressComponents = function(input
) {
436 AutofillEditAddressOverlay
.getInstance().loadAddressComponents_(input
);
439 AutofillEditAddressOverlay
.setTitle = function(title
) {
440 $('autofill-address-title').textContent
= title
;
443 AutofillEditAddressOverlay
.setValidatedPhoneNumbers = function(numbers
) {
444 var instance
= AutofillEditAddressOverlay
.getInstance();
445 var phoneList
= instance
.pageDiv
.querySelector('[field=phone]');
446 instance
.setMultiValueList_(assertInstanceof(phoneList
, cr
.ui
.List
),
448 phoneList
.didReceiveValidationResult();
453 AutofillEditAddressOverlay
: AutofillEditAddressOverlay