Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / options / autofill_edit_address_overlay.js
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.
4
5 cr.define('options', function() {
6   /** @const */ var OptionsPage = options.OptionsPage;
7   /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
8
9   // The GUID of the loaded address.
10   var guid;
11
12   // The BCP 47 language code for the layout of input fields.
13   var languageCode;
14
15   /**
16    * AutofillEditAddressOverlay class
17    * Encapsulated handling of the 'Add Page' overlay page.
18    * @class
19    */
20   function AutofillEditAddressOverlay() {
21     OptionsPage.call(this, 'autofillEditAddress',
22                      loadTimeData.getString('autofillEditAddressTitle'),
23                      'autofill-edit-address-overlay');
24   }
25
26   cr.addSingletonGetter(AutofillEditAddressOverlay);
27
28   AutofillEditAddressOverlay.prototype = {
29     __proto__: OptionsPage.prototype,
30
31     /**
32      * Initializes the page.
33      */
34     initializePage: function() {
35       OptionsPage.prototype.initializePage.call(this);
36
37       this.createMultiValueLists_();
38
39       var self = this;
40       $('autofill-edit-address-cancel-button').onclick = function(event) {
41         self.dismissOverlay_();
42       };
43
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(
53               function() {
54                 self.saveAddress_();
55                 self.dismissOverlay_();
56               });
57         }, 0);
58       };
59
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
63       // buttons.
64       $('autofill-edit-address-apply-button').onmousedown = function(event) {
65         event.preventDefault();
66       };
67       $('autofill-edit-address-cancel-button').onmousedown = function(event) {
68         event.preventDefault();
69       };
70
71       this.guid = '';
72       this.populateCountryList_();
73       this.rebuildInputFields_(
74           loadTimeData.getValue('autofillDefaultCountryComponents'));
75       this.languageCode =
76           loadTimeData.getString('autofillDefaultCountryLanguageCode');
77       this.connectInputEvents_();
78       this.setInputFields_({});
79       this.getCountrySelector_().onchange = function(event) {
80         self.countryChanged_();
81       };
82     },
83
84     /**
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.
88     * @override
89     */
90     handleCancel: function() {
91       this.dismissOverlay_();
92     },
93
94     /**
95      * Creates, decorates and initializes the multi-value lists for phone and
96      * email.
97      * @private
98      */
99     createMultiValueLists_: function() {
100       var list = this.pageDiv.querySelector('[field=phone]');
101       options.autofillOptions.AutofillPhoneValuesList.decorate(list);
102       list.autoExpands = true;
103
104       list = this.pageDiv.querySelector('[field=email]');
105       options.autofillOptions.AutofillValuesList.decorate(list);
106       list.autoExpands = true;
107     },
108
109     /**
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.
113      * @private
114      */
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);
120
121       // Update the status of the 'OK' button.
122       this.inputFieldChanged_();
123
124       list.dataModel.addEventListener('splice',
125                                       this.inputFieldChanged_.bind(this));
126       list.dataModel.addEventListener('change',
127                                       this.inputFieldChanged_.bind(this));
128     },
129
130     /**
131      * Clears any uncommitted input, resets the stored GUID and dismisses the
132      * overlay.
133      * @private
134      */
135     dismissOverlay_: function() {
136       this.setInputFields_({});
137       this.inputFieldChanged_();
138       this.guid = '';
139       this.languageCode = '';
140       OptionsPage.closeOverlay();
141     },
142
143     /**
144      * Returns the country selector element.
145      * @return {element} The country selector.
146      * @private
147      */
148     getCountrySelector_: function() {
149       return this.pageDiv.querySelector('[field=country]');
150     },
151
152     /**
153      * Returns all list elements.
154      * @return {NodeList} The list elements.
155      * @private
156      */
157     getLists_: function() {
158       return this.pageDiv.querySelectorAll('list[field]');
159     },
160
161     /**
162      * Returns all text input elements.
163      * @return {NodeList} The text input elements.
164      * @private
165      */
166     getTextFields_: function() {
167       return this.pageDiv.querySelectorAll(
168           ':-webkit-any(textarea, input)[field]');
169     },
170
171     /**
172      * Aggregates the values in the input fields into an object.
173      * @return {object} The mapping from field names to values.
174      * @private
175      */
176     getInputFields_: function() {
177       var address = {};
178       address['country'] = this.getCountrySelector_().value;
179
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);
184       }
185
186       var fields = this.getTextFields_();
187       for (var i = 0; i < fields.length; i++) {
188         address[fields[i].getAttribute('field')] = fields[i].value;
189       }
190
191       return address;
192     },
193
194     /**
195      * Sets the value of each input field according to |address|.
196      * @param {object} address The object with values to use.
197      * @private
198      */
199     setInputFields_: function(address) {
200       this.getCountrySelector_().value = address['country'] || '';
201
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')] || []);
206       }
207
208       var fields = this.getTextFields_();
209       for (var i = 0; i < fields.length; i++) {
210         fields[i].value = address[fields[i].getAttribute('field')] || '';
211       }
212     },
213
214     /**
215      * Aggregates the values in the input fields into an array and sends the
216      * array to the Autofill handler.
217      * @private
218      */
219     saveAddress_: function() {
220       var inputFields = this.getInputFields_();
221       var address = new Array();
222       var argCounter = 0;
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;
236
237       chrome.send('setAddress', address);
238     },
239
240     /**
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
243      * not.
244      * @private
245      */
246     connectInputEvents_: function() {
247       var self = this;
248       var fields = this.getTextFields_();
249       for (var i = 0; i < fields.length; i++) {
250         fields[i].oninput = function(event) { self.inputFieldChanged_(); };
251       }
252     },
253
254     /**
255      * Disables the 'Ok' button if all of the fields are empty.
256      * @private
257      */
258     inputFieldChanged_: function() {
259       var disabled = true;
260       if (this.getCountrySelector_().value)
261         disabled = false;
262
263       if (disabled) {
264         // Length of lists are tested for > 1 due to the "add" placeholder item
265         // in the list.
266         var lists = this.getLists_();
267         for (var i = 0; i < lists.length; i++) {
268           if (lists[i].items.length > 1) {
269             disabled = false;
270             break;
271           }
272         }
273       }
274
275       if (disabled) {
276         var fields = this.getTextFields_();
277         for (var i = 0; i < fields.length; i++) {
278           if (fields[i].value) {
279             disabled = false;
280             break;
281           }
282         }
283       }
284
285       $('autofill-edit-address-apply-button').disabled = disabled;
286     },
287
288     /**
289      * Updates the address fields appropriately for the selected country.
290      * @private
291      */
292     countryChanged_: function() {
293       var countryCode = this.getCountrySelector_().value;
294       if (countryCode)
295         chrome.send('loadAddressEditorComponents', [countryCode]);
296       else
297         this.inputFieldChanged_();
298     },
299
300     /**
301      * Populates the country <select> list.
302      * @private
303      */
304     populateCountryList_: function() {
305       var countryList = loadTimeData.getValue('autofillCountrySelectList');
306
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);
316       }
317     },
318
319     /**
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.
322      * @private
323      */
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;
331     },
332
333     /**
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.
337      * @private
338      */
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;
346     },
347
348     /**
349      * Clears address inputs and rebuilds the input fields according to
350      * |components|.
351      * @private
352      */
353     rebuildInputFields_: function(components) {
354       var content = $('autofill-edit-address-fields');
355       while (content.firstChild) {
356         content.removeChild(content.firstChild);
357       }
358
359       var customContainerElements = {'fullName': 'div'};
360       var customInputElements = {'fullName': 'list', 'addrLines': 'textarea'};
361
362       for (var i in components) {
363         var row = document.createElement('div');
364         row.classList.add('input-group', 'settings-row');
365         content.appendChild(row);
366
367         for (var j in components[i]) {
368           if (components[i][j].field == 'country')
369             continue;
370
371           var fieldContainer = document.createElement(
372               customContainerElements[components[i][j].field] || 'label');
373           row.appendChild(fieldContainer);
374
375           var fieldName = document.createElement('div');
376           fieldName.textContent = components[i][j].name;
377           fieldContainer.appendChild(fieldName);
378
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);
385
386           if (input.tagName == 'LIST') {
387             options.autofillOptions.AutofillValuesList.decorate(input);
388             input.autoExpands = true;
389           }
390         }
391       }
392     },
393   };
394
395   AutofillEditAddressOverlay.loadAddress = function(address) {
396     AutofillEditAddressOverlay.getInstance().loadAddress_(address);
397   };
398
399   AutofillEditAddressOverlay.loadAddressComponents = function(input) {
400     AutofillEditAddressOverlay.getInstance().loadAddressComponents_(input);
401   };
402
403   AutofillEditAddressOverlay.setTitle = function(title) {
404     $('autofill-address-title').textContent = title;
405   };
406
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();
412   };
413
414   // Export
415   return {
416     AutofillEditAddressOverlay: AutofillEditAddressOverlay
417   };
418 });