ements, record, addresses) { super(elements); this._addresses = addresses; Object.assign(this._elements, { ccNumber: this._elements.form.querySelector("#cc-number"), invalidCardNumberStringElement: this._elements.form.querySelector( "#invalidCardNumberString" ), month: this._elements.form.querySelector("#cc-exp-month"), year: this._elements.form.querySelector("#cc-exp-year"), billingAddress: this._elements.form.querySelector("#billingAddressGUID"), billingAddressRow: this._elements.form.querySelector(".billingAddressRow"), }); this.attachEventListeners(); this.loadRecord(record, addresses); } loadRecord(record, addresses, preserveFieldValues) { // _record must be updated before generateYears and generateBillingAddressOptions are called. this._record = record; this._addresses = addresses; this.generateBillingAddressOptions(preserveFieldValues); if (!preserveFieldValues) { // Re-generating the months will reset the selected option. this.generateMonths(); // Re-generating the years will reset the selected option. this.generateYears(); super.loadRecord(record); } } generateMonths() { const count = 12; // Clear the list this._elements.month.textContent = ""; // Empty month option this._elements.month.appendChild(new Option()); // Populate month list. Format: "month number - month name" let dateFormat = new Intl.DateTimeFormat(navigator.language, { month: "long", }).format; for (let i = 0; i < count; i++) { let monthNumber = (i + 1).toString(); let monthName = dateFormat(new Date(1970, i)); let option = new Option(); option.value = monthNumber; // XXX: Bug 1446164 - Localize this string. option.textContent = `${monthNumber.padStart(2, "0")} - ${monthName}`; this._elements.month.appendChild(option); } } generateYears() { const count = 11; const currentYear = new Date().getFullYear(); const ccExpYear = this._record && this._record["cc-exp-year"]; // Clear the list this._elements.year.textContent = ""; // Provide an empty year option this._elements.year.appendChild(new Option()); if (ccExpYear && ccExpYear < currentYear) { this._elements.year.appendChild(new Option(ccExpYear)); } for (let i = 0; i < count; i++) { let year = currentYear + i; let option = new Option(year); this._elements.year.appendChild(option); } if (ccExpYear && ccExpYear > currentYear + count) { this._elements.year.appendChild(new Option(ccExpYear)); } } generateBillingAddressOptions(preserveFieldValues) { let billingAddressGUID; if (preserveFieldValues && this._elements.billingAddress.value) { billingAddressGUID = this._elements.billingAddress.value; } else if (this._record) { billingAddressGUID = this._record.billingAddressGUID; } this._elements.billingAddress.textContent = ""; this._elements.billingAddress.appendChild(new Option("", "")); let hasAddresses = false; for (let [guid, address] of Object.entries(this._addresses)) { hasAddresses = true; let selected = guid == billingAddressGUID; let option = new Option( lazy.FormAutofillUtils.getAddressLabel(address), guid, selected, selected ); this._elements.billingAddress.appendChild(option); } this._elements.billingAddressRow.hidden = !hasAddresses; } attachEventListeners() { this._elements.form.addEventListener("change", this); super.attachEventListeners(); } handleInput(event) { // Clear the error message if cc-number is valid if ( event.target == this._elements.ccNumber && lazy.FormAutofillUtils.isCCNumber(this._elements.ccNumber.value) ) { this._elements.ccNumber.setCustomValidity(""); } super.handleInput(event); } updateCustomValidity(field) { super.updateCustomValidity(field); // Mark the cc-number field as invalid if the number is empty or invalid. if ( field == this._elements.ccNumber && !lazy.FormAutofillUtils.isCCNumber(field.value) ) { let invalidCardNumberString = this._elements.invalidCardNumberStringElement.textContent; field.setCustomValidity(invalidCardNumberString || " "); } } } PK