| 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 |