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 OptionsPage = options.OptionsPage;
7 /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
9 // The GUID of the loaded address.
12 // The BCP 47 language code for the layout of input fields.
16 * AutofillEditAddressOverlay class
17 * Encapsulated handling of the 'Add Page' overlay page.
20 function AutofillEditAddressOverlay() {
21 OptionsPage.call(this, 'autofillEditAddress',
22 loadTimeData.getString('autofillEditAddressTitle'),
23 'autofill-edit-address-overlay');
26 cr.addSingletonGetter(AutofillEditAddressOverlay);
28 AutofillEditAddressOverlay.prototype = {
29 __proto__: OptionsPage.prototype,
32 * Initializes the page.
34 initializePage: function() {
35 OptionsPage.prototype.initializePage.call(this);
37 this.createMultiValueLists_();
40 $('autofill-edit-address-cancel-button').onclick = function(event) {
41 self.dismissOverlay_();
44 // TODO(jhawkins): Investigate other possible solutions.
45 $('autofill-edit-address-apply-button').onclick = function(event) {
46 // Blur active element to ensure that pending changes are committed.
47 if (document.activeElement)
48 document.activeElement.blur();
49 // Blurring is delayed for list elements. Queue save and close to
50 // ensure that pending changes have been applied.
51 setTimeout(function() {
52 self.pageDiv.querySelector('[field=phone]').doneValidating().then(
55 self.dismissOverlay_();
60 // Prevent 'blur' events on the OK and cancel buttons, which can trigger
61 // insertion of new placeholder elements. The addition of placeholders
62 // affects layout, which interferes with being able to click on the
64 $('autofill-edit-address-apply-button').onmousedown = function(event) {
65 event.preventDefault();
67 $('autofill-edit-address-cancel-button').onmousedown = function(event) {
68 event.preventDefault();
72 this.populateCountryList_();
73 this.rebuildInputFields_(
74 loadTimeData.getValue('autofillDefaultCountryComponents'));
76 loadTimeData.getString('autofillDefaultCountryLanguageCode');
77 this.connectInputEvents_();
78 this.setInputFields_({});
79 this.getCountrySelector_().onchange = function(event) {
80 self.countryChanged_();
85 * Specifically catch the situations in which the overlay is cancelled
86 * externally (e.g. by pressing <Esc>), so that the input fields and
87 * GUID can be properly cleared.
90 handleCancel: function() {
91 this.dismissOverlay_();
95 * Creates, decorates and initializes the multi-value lists for phone and
99 createMultiValueLists_: function() {
100 var list = this.pageDiv.querySelector('[field=phone]');
101 options.autofillOptions.AutofillPhoneValuesList.decorate(list);
102 list.autoExpands = true;
104 list = this.pageDiv.querySelector('[field=email]');
105 options.autofillOptions.AutofillValuesList.decorate(list);
106 list.autoExpands = true;
110 * Updates the data model for the |list| with the values from |entries|.
111 * @param {element} list The list to update.
112 * @param {Array} entries The list of items to be added to the list.
115 setMultiValueList_: function(list, entries) {
116 // Add special entry for adding new values.
117 var augmentedList = entries.slice();
118 augmentedList.push(null);
119 list.dataModel = new ArrayDataModel(augmentedList);
121 // Update the status of the 'OK' button.
122 this.inputFieldChanged_();
124 list.dataModel.addEventListener('splice',
125 this.inputFieldChanged_.bind(this));
126 list.dataModel.addEventListener('change',
127 this.inputFieldChanged_.bind(this));
131 * Clears any uncommitted input, resets the stored GUID and dismisses the
135 dismissOverlay_: function() {
136 this.setInputFields_({});
137 this.inputFieldChanged_();
139 this.languageCode = '';
140 OptionsPage.closeOverlay();
144 * Returns the country selector element.
145 * @return {element} The country selector.
148 getCountrySelector_: function() {
149 return this.pageDiv.querySelector('[field=country]');
153 * Returns all list elements.
154 * @return {NodeList} The list elements.
157 getLists_: function() {
158 return this.pageDiv.querySelectorAll('list[field]');
162 * Returns all text input elements.
163 * @return {NodeList} The text input elements.
166 getTextFields_: function() {
167 return this.pageDiv.querySelectorAll(
168 ':-webkit-any(textarea, input)[field]');
172 * Aggregates the values in the input fields into an object.
173 * @return {object} The mapping from field names to values.
176 getInputFields_: function() {
178 address['country'] = this.getCountrySelector_().value;
180 var lists = this.getLists_();
181 for (var i = 0; i < lists.length; i++) {
182 address[lists[i].getAttribute('field')] =
183 lists[i].dataModel.slice(0, lists[i].dataModel.length - 1);
186 var fields = this.getTextFields_();
187 for (var i = 0; i < fields.length; i++) {
188 address[fields[i].getAttribute('field')] = fields[i].value;
195 * Sets the value of each input field according to |address|.
196 * @param {object} address The object with values to use.
199 setInputFields_: function(address) {
200 this.getCountrySelector_().value = address['country'] || '';
202 var lists = this.getLists_();
203 for (var i = 0; i < lists.length; i++) {
204 this.setMultiValueList_(
205 lists[i], address[lists[i].getAttribute('field')] || []);
208 var fields = this.getTextFields_();
209 for (var i = 0; i < fields.length; i++) {
210 fields[i].value = address[fields[i].getAttribute('field')] || '';
215 * Aggregates the values in the input fields into an array and sends the
216 * array to the Autofill handler.
219 saveAddress_: function() {
220 var inputFields = this.getInputFields_();
221 var address = new Array();
223 address[argCounter++] = this.guid;
224 address[argCounter++] = inputFields['fullName'] || [];
225 address[argCounter++] = inputFields['companyName'] || '';
226 address[argCounter++] = inputFields['addrLines'] || '';
227 address[argCounter++] = inputFields['dependentLocality'] || '';
228 address[argCounter++] = inputFields['city'] || '';
229 address[argCounter++] = inputFields['state'] || '';
230 address[argCounter++] = inputFields['postalCode'] || '';
231 address[argCounter++] = inputFields['sortingCode'] || '';
232 address[argCounter++] = inputFields['country'] || '';
233 address[argCounter++] = inputFields['phone'] || [];
234 address[argCounter++] = inputFields['email'] || [];
235 address[argCounter++] = this.languageCode;
237 chrome.send('setAddress', address);
241 * Connects each input field to the inputFieldChanged_() method that enables
242 * or disables the 'Ok' button based on whether all the fields are empty or
246 connectInputEvents_: function() {
248 var fields = this.getTextFields_();
249 for (var i = 0; i < fields.length; i++) {
250 fields[i].oninput = function(event) { self.inputFieldChanged_(); };
255 * Disables the 'Ok' button if all of the fields are empty.
258 inputFieldChanged_: function() {
260 if (this.getCountrySelector_().value)
264 // Length of lists are tested for > 1 due to the "add" placeholder item
266 var lists = this.getLists_();
267 for (var i = 0; i < lists.length; i++) {
268 if (lists[i].items.length > 1) {
276 var fields = this.getTextFields_();
277 for (var i = 0; i < fields.length; i++) {
278 if (fields[i].value) {
285 $('autofill-edit-address-apply-button').disabled = disabled;
289 * Updates the address fields appropriately for the selected country.
292 countryChanged_: function() {
293 var countryCode = this.getCountrySelector_().value;
295 chrome.send('loadAddressEditorComponents', [countryCode]);
297 this.inputFieldChanged_();
301 * Populates the country <select> list.
304 populateCountryList_: function() {
305 var countryList = loadTimeData.getValue('autofillCountrySelectList');
307 // Add the countries to the country <select> list.
308 var countrySelect = this.getCountrySelector_();
309 // Add an empty option.
310 countrySelect.appendChild(new Option('', ''));
311 for (var i = 0; i < countryList.length; i++) {
312 var option = new Option(countryList[i].name,
313 countryList[i].value);
314 option.disabled = countryList[i].value == 'separator';
315 countrySelect.appendChild(option);
320 * Loads the address data from |address|, sets the input fields based on
321 * this data, and stores the GUID and language code of the address.
324 loadAddress_: function(address) {
325 this.rebuildInputFields_(address.components);
326 this.setInputFields_(address);
327 this.inputFieldChanged_();
328 this.connectInputEvents_();
329 this.guid = address.guid;
330 this.languageCode = address.languageCode;
334 * Takes a snapshot of the input values, clears the input values, loads the
335 * address input layout from |input.components|, restores the input values
336 * from snapshot, and stores the |input.languageCode| for the address.
339 loadAddressComponents_: function(input) {
340 var address = this.getInputFields_();
341 this.rebuildInputFields_(input.components);
342 this.setInputFields_(address);
343 this.inputFieldChanged_();
344 this.connectInputEvents_();
345 this.languageCode = input.languageCode;
349 * Clears address inputs and rebuilds the input fields according to
353 rebuildInputFields_: function(components) {
354 var content = $('autofill-edit-address-fields');
355 while (content.firstChild) {
356 content.removeChild(content.firstChild);
359 var customContainerElements = {'fullName': 'div'};
360 var customInputElements = {'fullName': 'list', 'addrLines': 'textarea'};
362 for (var i in components) {
363 var row = document.createElement('div');
364 row.classList.add('input-group', 'settings-row');
365 content.appendChild(row);
367 for (var j in components[i]) {
368 if (components[i][j].field == 'country')
371 var fieldContainer = document.createElement(
372 customContainerElements[components[i][j].field] || 'label');
373 row.appendChild(fieldContainer);
375 var fieldName = document.createElement('div');
376 fieldName.textContent = components[i][j].name;
377 fieldContainer.appendChild(fieldName);
379 var input = document.createElement(
380 customInputElements[components[i][j].field] || 'input');
381 input.setAttribute('field', components[i][j].field);
382 input.classList.add(components[i][j].length);
383 input.setAttribute('placeholder', components[i][j].placeholder || '');
384 fieldContainer.appendChild(input);
386 if (input.tagName == 'LIST') {
387 options.autofillOptions.AutofillValuesList.decorate(input);
388 input.autoExpands = true;
395 AutofillEditAddressOverlay.loadAddress = function(address) {
396 AutofillEditAddressOverlay.getInstance().loadAddress_(address);
399 AutofillEditAddressOverlay.loadAddressComponents = function(input) {
400 AutofillEditAddressOverlay.getInstance().loadAddressComponents_(input);
403 AutofillEditAddressOverlay.setTitle = function(title) {
404 $('autofill-address-title').textContent = title;
407 AutofillEditAddressOverlay.setValidatedPhoneNumbers = function(numbers) {
408 var instance = AutofillEditAddressOverlay.getInstance();
409 var phoneList = instance.pageDiv.querySelector('[field=phone]');
410 instance.setMultiValueList_(phoneList, numbers);
411 phoneList.didReceiveValidationResult();
416 AutofillEditAddressOverlay: AutofillEditAddressOverlay