1// Copyright (c) 2011 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
5cr.define('options.autofillOptions', function() {
6  const DeletableItem = options.DeletableItem;
7  const DeletableItemList = options.DeletableItemList;
8  const InlineEditableItem = options.InlineEditableItem;
9  const InlineEditableItemList = options.InlineEditableItemList;
10
11  /**
12   * Creates a new address list item.
13   * @param {Array} entry An array of the form [guid, label].
14   * @constructor
15   * @extends {options.DeletableItem}
16   */
17  function AddressListItem(entry) {
18    var el = cr.doc.createElement('div');
19    el.guid = entry[0];
20    el.label = entry[1];
21    el.__proto__ = AddressListItem.prototype;
22    el.decorate();
23
24    return el;
25  }
26
27  AddressListItem.prototype = {
28    __proto__: DeletableItem.prototype,
29
30    /** @inheritDoc */
31    decorate: function() {
32      DeletableItem.prototype.decorate.call(this);
33
34      // The stored label.
35      var label = this.ownerDocument.createElement('div');
36      label.className = 'autofill-list-item';
37      label.textContent = this.label;
38      this.contentElement.appendChild(label);
39    },
40  };
41
42  /**
43   * Creates a new credit card list item.
44   * @param {Array} entry An array of the form [guid, label, icon].
45   * @constructor
46   * @extends {options.DeletableItem}
47   */
48  function CreditCardListItem(entry) {
49    var el = cr.doc.createElement('div');
50    el.guid = entry[0];
51    el.label = entry[1];
52    el.icon = entry[2];
53    el.description = entry[3];
54    el.__proto__ = CreditCardListItem.prototype;
55    el.decorate();
56
57    return el;
58  }
59
60  CreditCardListItem.prototype = {
61    __proto__: DeletableItem.prototype,
62
63    /** @inheritDoc */
64    decorate: function() {
65      DeletableItem.prototype.decorate.call(this);
66
67      // The stored label.
68      var label = this.ownerDocument.createElement('div');
69      label.className = 'autofill-list-item';
70      label.textContent = this.label;
71      this.contentElement.appendChild(label);
72
73      // The credit card icon.
74      var icon = this.ownerDocument.createElement('image');
75      icon.src = this.icon;
76      icon.alt = this.description;
77      this.contentElement.appendChild(icon);
78    },
79  };
80
81  /**
82   * Creates a new value list item.
83   * @param {AutofillValuesList} list The parent list of this item.
84   * @param {String} entry A string value.
85   * @constructor
86   * @extends {options.InlineEditableItem}
87   */
88  function ValuesListItem(list, entry) {
89    var el = cr.doc.createElement('div');
90    el.list = list;
91    el.value = entry;
92    el.__proto__ = ValuesListItem.prototype;
93    el.decorate();
94
95    return el;
96  }
97
98  ValuesListItem.prototype = {
99    __proto__: InlineEditableItem.prototype,
100
101    /** @inheritDoc */
102    decorate: function() {
103      InlineEditableItem.prototype.decorate.call(this);
104
105      this.isPlaceholder = !this.value;
106
107      // The stored value.
108      var cell = this.createEditableTextCell(this.value);
109      this.contentElement.appendChild(cell);
110      this.input = cell.querySelector('input');
111
112      this.addEventListener('commitedit', this.onEditCommitted_);
113    },
114
115    /**
116     * Called when committing an edit.
117     * @param {Event} e The end event.
118     * @private
119     */
120    onEditCommitted_: function(e) {
121      var i = this.list.items.indexOf(this);
122      if (this.input.value == this.list.dataModel.item(i))
123        return;
124
125      if (this.input.value &&
126          this.list.dataModel.indexOf(this.input.value) == -1) {
127        // Update with new value.
128        this.list.dataModel.splice(i, 1, this.input.value);
129      } else {
130        // Reject empty values and duplicates.
131        this.list.dataModel.splice(i, 1);
132      }
133    },
134  };
135
136  /**
137   * Creates a new list item for the Add New Item row, which doesn't represent
138   * an actual entry in the values list but allows the user to add new
139   * values.
140   * @param {AutofillValuesList} entry The parent list of this item.
141   * @constructor
142   * @extends {cr.ui.ValuesListItem}
143   */
144  function ValuesAddRowListItem(list) {
145    var el = cr.doc.createElement('div');
146    el.list = list;
147    el.__proto__ = ValuesAddRowListItem.prototype;
148    el.decorate();
149
150    return el;
151  }
152
153  ValuesAddRowListItem.prototype = {
154    __proto__: ValuesListItem.prototype,
155
156    decorate: function() {
157      ValuesListItem.prototype.decorate.call(this);
158      this.input.value = '';
159      this.input.placeholder = this.list.getAttribute('placeholder');
160      this.deletable = false;
161    },
162
163    /**
164     * Called when committing an edit.  Committing a non-empty value adds it
165     * to the end of the values list, leaving this "AddRow" in place.
166     * @param {Event} e The end event.
167     * @extends {options.ValuesListItem}
168     * @private
169     */
170    onEditCommitted_: function(e) {
171      var i = this.list.items.indexOf(this);
172      if (i < 0 || i >= this.list.dataModel.length)
173        return;
174
175      if (this.input.value &&
176          this.list.dataModel.indexOf(this.input.value) == -1) {
177        this.list.dataModel.splice(i, 0, this.input.value);
178      } else {
179        this.input.value = '';
180        this.list.dataModel.updateIndex(i);
181      }
182    },
183  };
184
185  /**
186   * Create a new address list.
187   * @constructor
188   * @extends {options.DeletableItemList}
189   */
190  var AutofillAddressList = cr.ui.define('list');
191
192  AutofillAddressList.prototype = {
193    __proto__: DeletableItemList.prototype,
194
195    decorate: function() {
196      DeletableItemList.prototype.decorate.call(this);
197
198      this.addEventListener('blur', this.onBlur_);
199    },
200
201    /**
202     * When the list loses focus, unselect all items in the list.
203     * @private
204     */
205    onBlur_: function() {
206      this.selectionModel.unselectAll();
207    },
208
209    /** @inheritDoc */
210    createItem: function(entry) {
211      return new AddressListItem(entry);
212    },
213
214    /** @inheritDoc */
215    activateItemAtIndex: function(index) {
216      AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]);
217    },
218
219    /** @inheritDoc */
220    deleteItemAtIndex: function(index) {
221      AutofillOptions.removeAddress(this.dataModel.item(index)[0]);
222    },
223  };
224
225  /**
226   * Create a new credit card list.
227   * @constructor
228   * @extends {options.DeletableItemList}
229   */
230  var AutofillCreditCardList = cr.ui.define('list');
231
232  AutofillCreditCardList.prototype = {
233    __proto__: DeletableItemList.prototype,
234
235    decorate: function() {
236      DeletableItemList.prototype.decorate.call(this);
237
238      this.addEventListener('blur', this.onBlur_);
239    },
240
241    /**
242     * When the list loses focus, unselect all items in the list.
243     * @private
244     */
245    onBlur_: function() {
246      this.selectionModel.unselectAll();
247    },
248
249    /** @inheritDoc */
250    createItem: function(entry) {
251      return new CreditCardListItem(entry);
252    },
253
254    /** @inheritDoc */
255    activateItemAtIndex: function(index) {
256      AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]);
257    },
258
259    /** @inheritDoc */
260    deleteItemAtIndex: function(index) {
261      AutofillOptions.removeCreditCard(this.dataModel.item(index)[0]);
262    },
263  };
264
265  /**
266   * Create a new value list.
267   * @constructor
268   * @extends {options.InlineEditableItemList}
269   */
270  var AutofillValuesList = cr.ui.define('list');
271
272  AutofillValuesList.prototype = {
273    __proto__: InlineEditableItemList.prototype,
274
275    decorate: function() {
276      InlineEditableItemList.prototype.decorate.call(this);
277
278      var self = this;
279      function handleBlur(e) {
280        // When the blur event happens we do not know who is getting focus so we
281        // delay this a bit until we know if the new focus node is outside the
282        // list.
283        var doc = e.target.ownerDocument;
284        window.setTimeout(function() {
285          var activeElement = doc.activeElement;
286          if (!self.contains(activeElement))
287            self.selectionModel.unselectAll();
288        }, 50);
289      }
290
291      this.addEventListener('blur', handleBlur, true);
292    },
293
294    /** @inheritDoc */
295    createItem: function(entry) {
296      if (entry != null)
297        return new ValuesListItem(this, entry);
298      else
299        return new ValuesAddRowListItem(this);
300    },
301
302    /** @inheritDoc */
303    deleteItemAtIndex: function(index) {
304      this.dataModel.splice(index, 1);
305    },
306  };
307
308  return {
309    AddressListItem: AddressListItem,
310    CreditCardListItem: CreditCardListItem,
311    ValuesListItem: ValuesListItem,
312    ValuesAddRowListItem: ValuesAddRowListItem,
313    AutofillAddressList: AutofillAddressList,
314    AutofillCreditCardList: AutofillCreditCardList,
315    AutofillValuesList: AutofillValuesList,
316  };
317});
318