OLD | NEW |
(Empty) | |
| 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 |
| 5 cr.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 function AutofillEditProfileButton(guid, edit) { |
| 12 var editButtonEl = document.createElement('button'); |
| 13 editButtonEl.className = 'raw-button custom-appearance'; |
| 14 editButtonEl.textContent = |
| 15 templateData.autofillEditProfileButton; |
| 16 editButtonEl.onclick = function(e) { edit(guid); }; |
| 17 |
| 18 // Don't select the row when clicking the button. |
| 19 editButtonEl.onmousedown = function(e) { |
| 20 e.stopPropagation(); |
| 21 }; |
| 22 |
| 23 return editButtonEl; |
| 24 } |
| 25 |
| 26 /** |
| 27 * Creates a new address list item. |
| 28 * @param {Array} entry An array of the form [guid, label]. |
| 29 * @constructor |
| 30 * @extends {options.DeletableItem} |
| 31 */ |
| 32 function AddressListItem(entry) { |
| 33 var el = cr.doc.createElement('div'); |
| 34 el.guid = entry[0]; |
| 35 el.label = entry[1]; |
| 36 el.__proto__ = AddressListItem.prototype; |
| 37 el.decorate(); |
| 38 |
| 39 return el; |
| 40 } |
| 41 |
| 42 AddressListItem.prototype = { |
| 43 __proto__: DeletableItem.prototype, |
| 44 |
| 45 /** @inheritDoc */ |
| 46 decorate: function() { |
| 47 DeletableItem.prototype.decorate.call(this); |
| 48 |
| 49 // The stored label. |
| 50 var label = this.ownerDocument.createElement('div'); |
| 51 label.className = 'autofill-list-item'; |
| 52 label.textContent = this.label; |
| 53 this.contentElement.appendChild(label); |
| 54 |
| 55 // The 'Edit' button. |
| 56 var editButtonEl = new AutofillEditProfileButton( |
| 57 this.guid, |
| 58 AutofillOptions.loadAddressEditor); |
| 59 this.contentElement.appendChild(editButtonEl); |
| 60 }, |
| 61 }; |
| 62 |
| 63 /** |
| 64 * Creates a new credit card list item. |
| 65 * @param {Array} entry An array of the form [guid, label, icon]. |
| 66 * @constructor |
| 67 * @extends {options.DeletableItem} |
| 68 */ |
| 69 function CreditCardListItem(entry) { |
| 70 var el = cr.doc.createElement('div'); |
| 71 el.guid = entry[0]; |
| 72 el.label = entry[1]; |
| 73 el.icon = entry[2]; |
| 74 el.description = entry[3]; |
| 75 el.__proto__ = CreditCardListItem.prototype; |
| 76 el.decorate(); |
| 77 |
| 78 return el; |
| 79 } |
| 80 |
| 81 CreditCardListItem.prototype = { |
| 82 __proto__: DeletableItem.prototype, |
| 83 |
| 84 /** @inheritDoc */ |
| 85 decorate: function() { |
| 86 DeletableItem.prototype.decorate.call(this); |
| 87 |
| 88 // The stored label. |
| 89 var label = this.ownerDocument.createElement('div'); |
| 90 label.className = 'autofill-list-item'; |
| 91 label.textContent = this.label; |
| 92 this.contentElement.appendChild(label); |
| 93 |
| 94 // The credit card icon. |
| 95 var icon = this.ownerDocument.createElement('image'); |
| 96 icon.src = this.icon; |
| 97 icon.alt = this.description; |
| 98 this.contentElement.appendChild(icon); |
| 99 |
| 100 // The 'Edit' button. |
| 101 var editButtonEl = new AutofillEditProfileButton( |
| 102 this.guid, |
| 103 AutofillOptions.loadCreditCardEditor); |
| 104 this.contentElement.appendChild(editButtonEl); |
| 105 }, |
| 106 }; |
| 107 |
| 108 /** |
| 109 * Creates a new value list item. |
| 110 * @param {AutofillValuesList} list The parent list of this item. |
| 111 * @param {String} entry A string value. |
| 112 * @constructor |
| 113 * @extends {options.InlineEditableItem} |
| 114 */ |
| 115 function ValuesListItem(list, entry) { |
| 116 var el = cr.doc.createElement('div'); |
| 117 el.list = list; |
| 118 el.value = entry ? entry : ''; |
| 119 el.__proto__ = ValuesListItem.prototype; |
| 120 el.decorate(); |
| 121 |
| 122 return el; |
| 123 } |
| 124 |
| 125 ValuesListItem.prototype = { |
| 126 __proto__: InlineEditableItem.prototype, |
| 127 |
| 128 /** @inheritDoc */ |
| 129 decorate: function() { |
| 130 InlineEditableItem.prototype.decorate.call(this); |
| 131 |
| 132 // Note: This must be set prior to calling |createEditableTextCell|. |
| 133 this.isPlaceholder = !this.value; |
| 134 |
| 135 // The stored value. |
| 136 var cell = this.createEditableTextCell(this.value); |
| 137 this.contentElement.appendChild(cell); |
| 138 this.input = cell.querySelector('input'); |
| 139 |
| 140 if (this.isPlaceholder) { |
| 141 this.input.placeholder = this.list.getAttribute('placeholder'); |
| 142 this.deletable = false; |
| 143 } |
| 144 |
| 145 this.addEventListener('commitedit', this.onEditCommitted_); |
| 146 }, |
| 147 |
| 148 /** |
| 149 * @return This item's value. |
| 150 * @protected |
| 151 */ |
| 152 value_: function() { |
| 153 return this.input.value; |
| 154 }, |
| 155 |
| 156 /** |
| 157 * @param {Object} value The value to test. |
| 158 * @return true if the given value is non-empty. |
| 159 * @protected |
| 160 */ |
| 161 valueIsNonEmpty_: function(value) { |
| 162 return !!value; |
| 163 }, |
| 164 |
| 165 /** |
| 166 * @return true if value1 is logically equal to value2. |
| 167 */ |
| 168 valuesAreEqual_: function(value1, value2) { |
| 169 return value1 === value2; |
| 170 }, |
| 171 |
| 172 /** |
| 173 * Clears the item's value. |
| 174 * @protected |
| 175 */ |
| 176 clearValue_: function() { |
| 177 this.input.value = ''; |
| 178 }, |
| 179 |
| 180 /** |
| 181 * Called when committing an edit. |
| 182 * If this is an "Add ..." item, committing a non-empty value adds that |
| 183 * value to the end of the values list, but also leaves this "Add ..." item |
| 184 * in place. |
| 185 * @param {Event} e The end event. |
| 186 * @private |
| 187 */ |
| 188 onEditCommitted_: function(e) { |
| 189 var value = this.value_(); |
| 190 var i = this.list.items.indexOf(this); |
| 191 if (i < this.list.dataModel.length && |
| 192 this.valuesAreEqual_(value, this.list.dataModel.item(i))) { |
| 193 return; |
| 194 } |
| 195 |
| 196 var entries = this.list.dataModel.slice(); |
| 197 if (this.valueIsNonEmpty_(value) && |
| 198 !entries.some(this.valuesAreEqual_.bind(this, value))) { |
| 199 // Update with new value. |
| 200 if (this.isPlaceholder) { |
| 201 // It is important that updateIndex is done before validateAndSave. |
| 202 // Otherwise we can not be sure about AddRow index. |
| 203 this.list.dataModel.updateIndex(i); |
| 204 this.list.validateAndSave(i, 0, value); |
| 205 } else { |
| 206 this.list.validateAndSave(i, 1, value); |
| 207 } |
| 208 } else { |
| 209 // Reject empty values and duplicates. |
| 210 if (!this.isPlaceholder) |
| 211 this.list.dataModel.splice(i, 1); |
| 212 else |
| 213 this.clearValue_(); |
| 214 } |
| 215 }, |
| 216 }; |
| 217 |
| 218 /** |
| 219 * Creates a new name value list item. |
| 220 * @param {AutofillNameValuesList} list The parent list of this item. |
| 221 * @param {array} entry An array of [first, middle, last] names. |
| 222 * @constructor |
| 223 * @extends {options.ValuesListItem} |
| 224 */ |
| 225 function NameListItem(list, entry) { |
| 226 var el = cr.doc.createElement('div'); |
| 227 el.list = list; |
| 228 el.first = entry ? entry[0] : ''; |
| 229 el.middle = entry ? entry[1] : ''; |
| 230 el.last = entry ? entry[2] : ''; |
| 231 el.__proto__ = NameListItem.prototype; |
| 232 el.decorate(); |
| 233 |
| 234 return el; |
| 235 } |
| 236 |
| 237 NameListItem.prototype = { |
| 238 __proto__: ValuesListItem.prototype, |
| 239 |
| 240 /** @inheritDoc */ |
| 241 decorate: function() { |
| 242 InlineEditableItem.prototype.decorate.call(this); |
| 243 |
| 244 // Note: This must be set prior to calling |createEditableTextCell|. |
| 245 this.isPlaceholder = !this.first && !this.middle && !this.last; |
| 246 |
| 247 // The stored value. |
| 248 // For the simulated static "input element" to display correctly, the |
| 249 // value must not be empty. We use a space to force the UI to render |
| 250 // correctly when the value is logically empty. |
| 251 var cell = this.createEditableTextCell(this.first); |
| 252 this.contentElement.appendChild(cell); |
| 253 this.firstNameInput = cell.querySelector('input'); |
| 254 |
| 255 cell = this.createEditableTextCell(this.middle); |
| 256 this.contentElement.appendChild(cell); |
| 257 this.middleNameInput = cell.querySelector('input'); |
| 258 |
| 259 cell = this.createEditableTextCell(this.last); |
| 260 this.contentElement.appendChild(cell); |
| 261 this.lastNameInput = cell.querySelector('input'); |
| 262 |
| 263 if (this.isPlaceholder) { |
| 264 this.firstNameInput.placeholder = |
| 265 templateData.autofillAddFirstNamePlaceholder; |
| 266 this.middleNameInput.placeholder = |
| 267 templateData.autofillAddMiddleNamePlaceholder; |
| 268 this.lastNameInput.placeholder = |
| 269 templateData.autofillAddLastNamePlaceholder; |
| 270 this.deletable = false; |
| 271 } |
| 272 |
| 273 this.addEventListener('commitedit', this.onEditCommitted_); |
| 274 }, |
| 275 |
| 276 /** @inheritDoc */ |
| 277 value_: function() { |
| 278 return [ this.firstNameInput.value, |
| 279 this.middleNameInput.value, |
| 280 this.lastNameInput.value ]; |
| 281 }, |
| 282 |
| 283 /** @inheritDoc */ |
| 284 valueIsNonEmpty_: function(value) { |
| 285 return value[0] || value[1] || value[2]; |
| 286 }, |
| 287 |
| 288 /** @inheritDoc */ |
| 289 valuesAreEqual_: function(value1, value2) { |
| 290 // First, check for null values. |
| 291 if (!value1 || !value2) |
| 292 return value1 == value2; |
| 293 |
| 294 return value1[0] === value2[0] && |
| 295 value1[1] === value2[1] && |
| 296 value1[2] === value2[2]; |
| 297 }, |
| 298 |
| 299 /** @inheritDoc */ |
| 300 clearValue_: function() { |
| 301 this.firstNameInput.value = ''; |
| 302 this.middleNameInput.value = ''; |
| 303 this.lastNameInput.value = ''; |
| 304 }, |
| 305 }; |
| 306 |
| 307 /** |
| 308 * Base class for shared implementation between address and credit card lists. |
| 309 * @constructor |
| 310 * @extends {options.DeletableItemList} |
| 311 */ |
| 312 var AutofillProfileList = cr.ui.define('list'); |
| 313 |
| 314 AutofillProfileList.prototype = { |
| 315 __proto__: DeletableItemList.prototype, |
| 316 |
| 317 decorate: function() { |
| 318 DeletableItemList.prototype.decorate.call(this); |
| 319 |
| 320 this.addEventListener('blur', this.onBlur_); |
| 321 }, |
| 322 |
| 323 /** |
| 324 * When the list loses focus, unselect all items in the list. |
| 325 * @private |
| 326 */ |
| 327 onBlur_: function() { |
| 328 this.selectionModel.unselectAll(); |
| 329 }, |
| 330 }; |
| 331 |
| 332 /** |
| 333 * Create a new address list. |
| 334 * @constructor |
| 335 * @extends {options.AutofillProfileList} |
| 336 */ |
| 337 var AutofillAddressList = cr.ui.define('list'); |
| 338 |
| 339 AutofillAddressList.prototype = { |
| 340 __proto__: AutofillProfileList.prototype, |
| 341 |
| 342 decorate: function() { |
| 343 AutofillProfileList.prototype.decorate.call(this); |
| 344 }, |
| 345 |
| 346 /** @inheritDoc */ |
| 347 activateItemAtIndex: function(index) { |
| 348 AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]); |
| 349 }, |
| 350 |
| 351 /** @inheritDoc */ |
| 352 createItem: function(entry) { |
| 353 return new AddressListItem(entry); |
| 354 }, |
| 355 |
| 356 /** @inheritDoc */ |
| 357 deleteItemAtIndex: function(index) { |
| 358 AutofillOptions.removeAddress(this.dataModel.item(index)[0]); |
| 359 }, |
| 360 }; |
| 361 |
| 362 /** |
| 363 * Create a new credit card list. |
| 364 * @constructor |
| 365 * @extends {options.DeletableItemList} |
| 366 */ |
| 367 var AutofillCreditCardList = cr.ui.define('list'); |
| 368 |
| 369 AutofillCreditCardList.prototype = { |
| 370 __proto__: AutofillProfileList.prototype, |
| 371 |
| 372 decorate: function() { |
| 373 AutofillProfileList.prototype.decorate.call(this); |
| 374 }, |
| 375 |
| 376 /** @inheritDoc */ |
| 377 activateItemAtIndex: function(index) { |
| 378 AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]); |
| 379 }, |
| 380 |
| 381 /** @inheritDoc */ |
| 382 createItem: function(entry) { |
| 383 return new CreditCardListItem(entry); |
| 384 }, |
| 385 |
| 386 /** @inheritDoc */ |
| 387 deleteItemAtIndex: function(index) { |
| 388 AutofillOptions.removeCreditCard(this.dataModel.item(index)[0]); |
| 389 }, |
| 390 }; |
| 391 |
| 392 /** |
| 393 * Create a new value list. |
| 394 * @constructor |
| 395 * @extends {options.InlineEditableItemList} |
| 396 */ |
| 397 var AutofillValuesList = cr.ui.define('list'); |
| 398 |
| 399 AutofillValuesList.prototype = { |
| 400 __proto__: InlineEditableItemList.prototype, |
| 401 |
| 402 /** @inheritDoc */ |
| 403 createItem: function(entry) { |
| 404 return new ValuesListItem(this, entry); |
| 405 }, |
| 406 |
| 407 /** @inheritDoc */ |
| 408 deleteItemAtIndex: function(index) { |
| 409 this.dataModel.splice(index, 1); |
| 410 }, |
| 411 |
| 412 /** @inheritDoc */ |
| 413 shouldFocusPlaceholder: function() { |
| 414 return false; |
| 415 }, |
| 416 |
| 417 /** |
| 418 * Called when the list hierarchy as a whole loses or gains focus. |
| 419 * If the list was focused in response to a mouse click, call into the |
| 420 * superclass's implementation. If the list was focused in response to a |
| 421 * keyboard navigation, focus the first item. |
| 422 * If the list loses focus, unselect all the elements. |
| 423 * @param {Event} e The change event. |
| 424 * @private |
| 425 */ |
| 426 handleListFocusChange_: function(e) { |
| 427 // We check to see whether there is a selected item as a proxy for |
| 428 // distinguishing between mouse- and keyboard-originated focus events. |
| 429 var selectedItem = this.selectedItem; |
| 430 if (selectedItem) |
| 431 InlineEditableItemList.prototype.handleListFocusChange_.call(this, e); |
| 432 |
| 433 if (!e.newValue) { |
| 434 // When the list loses focus, unselect all the elements. |
| 435 this.selectionModel.unselectAll(); |
| 436 } else { |
| 437 // When the list gains focus, select the first item if nothing else is |
| 438 // selected. |
| 439 var firstItem = this.getListItemByIndex(0); |
| 440 if (!selectedItem && firstItem && e.newValue) |
| 441 firstItem.handleFocus_(); |
| 442 } |
| 443 }, |
| 444 |
| 445 /** |
| 446 * Called when a new list item should be validated; subclasses are |
| 447 * responsible for implementing if validation is required. |
| 448 * @param {number} index The index of the item that was inserted or changed. |
| 449 * @param {number} remove The number items to remove. |
| 450 * @param {string} value The value of the item to insert. |
| 451 */ |
| 452 validateAndSave: function(index, remove, value) { |
| 453 this.dataModel.splice(index, remove, value); |
| 454 }, |
| 455 }; |
| 456 |
| 457 /** |
| 458 * Create a new value list for phone number validation. |
| 459 * @constructor |
| 460 * @extends {options.AutofillValuesList} |
| 461 */ |
| 462 var AutofillNameValuesList = cr.ui.define('list'); |
| 463 |
| 464 AutofillNameValuesList.prototype = { |
| 465 __proto__: AutofillValuesList.prototype, |
| 466 |
| 467 /** @inheritDoc */ |
| 468 createItem: function(entry) { |
| 469 return new NameListItem(this, entry); |
| 470 }, |
| 471 }; |
| 472 |
| 473 /** |
| 474 * Create a new value list for phone number validation. |
| 475 * @constructor |
| 476 * @extends {options.AutofillValuesList} |
| 477 */ |
| 478 var AutofillPhoneValuesList = cr.ui.define('list'); |
| 479 |
| 480 AutofillPhoneValuesList.prototype = { |
| 481 __proto__: AutofillValuesList.prototype, |
| 482 |
| 483 /** @inheritDoc */ |
| 484 validateAndSave: function(index, remove, value) { |
| 485 var numbers = this.dataModel.slice(0, this.dataModel.length - 1); |
| 486 numbers.splice(index, remove, value); |
| 487 var info = new Array(); |
| 488 info[0] = index; |
| 489 info[1] = numbers; |
| 490 info[2] = $('country').value; |
| 491 chrome.send('validatePhoneNumbers', info); |
| 492 }, |
| 493 }; |
| 494 |
| 495 return { |
| 496 AddressListItem: AddressListItem, |
| 497 CreditCardListItem: CreditCardListItem, |
| 498 ValuesListItem: ValuesListItem, |
| 499 NameListItem: NameListItem, |
| 500 AutofillAddressList: AutofillAddressList, |
| 501 AutofillCreditCardList: AutofillCreditCardList, |
| 502 AutofillValuesList: AutofillValuesList, |
| 503 AutofillNameValuesList: AutofillNameValuesList, |
| 504 AutofillPhoneValuesList: AutofillPhoneValuesList, |
| 505 }; |
| 506 }); |
OLD | NEW |