mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-11 13:48:23 +02:00
MozReview-Commit-ID: J5QnWv343xq --HG-- extra : rebase_source : 25bb9534f4f5aabd7f33875f52f2d00cd480fda8
270 lines
7.5 KiB
JavaScript
270 lines
7.5 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|
const EDIT_PROFILE_URL = "chrome://formautofill/content/editProfile.xhtml";
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://formautofill/FormAutofillUtils.jsm");
|
|
|
|
this.log = null;
|
|
FormAutofillUtils.defineLazyLogGetter(this, "manageProfiles");
|
|
|
|
function ManageProfileDialog() {
|
|
this.prefWin = window.opener;
|
|
window.addEventListener("DOMContentLoaded", this, {once: true});
|
|
}
|
|
|
|
ManageProfileDialog.prototype = {
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
|
|
|
|
_elements: {},
|
|
|
|
/**
|
|
* Count the number of "formautofill-storage-changed" events epected to
|
|
* receive to prevent repeatedly loading addresses.
|
|
* @type {number}
|
|
*/
|
|
_pendingChangeCount: 0,
|
|
|
|
/**
|
|
* Get the selected options on the addresses element.
|
|
*
|
|
* @returns {array<DOMElement>}
|
|
*/
|
|
get _selectedOptions() {
|
|
return Array.from(this._elements.addresses.selectedOptions);
|
|
},
|
|
|
|
init() {
|
|
this._elements = {
|
|
addresses: document.getElementById("profiles"),
|
|
controlsContainer: document.getElementById("controls-container"),
|
|
remove: document.getElementById("remove"),
|
|
add: document.getElementById("add"),
|
|
edit: document.getElementById("edit"),
|
|
};
|
|
this.attachEventListeners();
|
|
},
|
|
|
|
uninit() {
|
|
log.debug("uninit");
|
|
this.detachEventListeners();
|
|
this._elements = null;
|
|
},
|
|
|
|
/**
|
|
* Load addresses and render them.
|
|
*
|
|
* @returns {promise}
|
|
*/
|
|
loadAddresses() {
|
|
return this.getAddresses().then(addresses => {
|
|
log.debug("addresses:", addresses);
|
|
// Sort by last modified time starting with most recent
|
|
addresses.sort((a, b) => b.timeLastModified - a.timeLastModified);
|
|
this.renderAddressElements(addresses);
|
|
this.updateButtonsStates(this._selectedOptions.length);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get addresses from storage.
|
|
*
|
|
* @returns {promise}
|
|
*/
|
|
getAddresses() {
|
|
return new Promise(resolve => {
|
|
Services.cpmm.addMessageListener("FormAutofill:Addresses", function getResult(result) {
|
|
Services.cpmm.removeMessageListener("FormAutofill:Addresses", getResult);
|
|
resolve(result.data);
|
|
});
|
|
Services.cpmm.sendAsyncMessage("FormAutofill:GetAddresses", {});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Render the addresses onto the page while maintaining selected options if
|
|
* they still exist.
|
|
*
|
|
* @param {array<object>} addresses
|
|
*/
|
|
renderAddressElements(addresses) {
|
|
let selectedGuids = this._selectedOptions.map(option => option.value);
|
|
this.clearAddressElements();
|
|
for (let address of addresses) {
|
|
let option = new Option(this.getAddressLabel(address),
|
|
address.guid,
|
|
false,
|
|
selectedGuids.includes(address.guid));
|
|
option.address = address;
|
|
this._elements.addresses.appendChild(option);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove all existing address elements.
|
|
*/
|
|
clearAddressElements() {
|
|
let parent = this._elements.addresses;
|
|
while (parent.lastChild) {
|
|
parent.removeChild(parent.lastChild);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove addresses by guids.
|
|
* Keep track of the number of "formautofill-storage-changed" events to
|
|
* ignore before loading addresses.
|
|
*
|
|
* @param {array<string>} guids
|
|
*/
|
|
removeAddresses(guids) {
|
|
this._pendingChangeCount += guids.length - 1;
|
|
Services.cpmm.sendAsyncMessage("FormAutofill:RemoveAddresses", {guids});
|
|
},
|
|
|
|
/**
|
|
* Get address display label. It should display up to two pieces of
|
|
* information, separated by a comma.
|
|
*
|
|
* @param {object} address
|
|
* @returns {string}
|
|
*/
|
|
getAddressLabel(address) {
|
|
// TODO: Implement a smarter way for deciding what to display
|
|
// as option text. Possibly improve the algorithm in
|
|
// ProfileAutoCompleteResult.jsm and reuse it here.
|
|
const fieldOrder = [
|
|
"name",
|
|
"street-address", // Street address
|
|
"address-level2", // City/Town
|
|
"organization", // Company or organization name
|
|
"address-level1", // Province/State (Standardized code if possible)
|
|
"country", // Country
|
|
"postal-code", // Postal code
|
|
"tel", // Phone number
|
|
"email", // Email address
|
|
];
|
|
|
|
let parts = [];
|
|
for (const fieldName of fieldOrder) {
|
|
let string = address[fieldName];
|
|
if (string) {
|
|
parts.push(string);
|
|
}
|
|
if (parts.length == 2) {
|
|
break;
|
|
}
|
|
}
|
|
return parts.join(", ");
|
|
},
|
|
|
|
/**
|
|
* Open the edit address dialog to create/edit an address.
|
|
*
|
|
* @param {object} address [optional]
|
|
*/
|
|
openEditDialog(address) {
|
|
this.prefWin.gSubDialog.open(EDIT_PROFILE_URL, null, address);
|
|
},
|
|
|
|
/**
|
|
* Enable/disable the Edit and Remove buttons based on number of selected
|
|
* options.
|
|
*
|
|
* @param {number} selectedCount
|
|
*/
|
|
updateButtonsStates(selectedCount) {
|
|
log.debug("updateButtonsStates:", selectedCount);
|
|
if (selectedCount == 0) {
|
|
this._elements.edit.setAttribute("disabled", "disabled");
|
|
this._elements.remove.setAttribute("disabled", "disabled");
|
|
} else if (selectedCount == 1) {
|
|
this._elements.edit.removeAttribute("disabled");
|
|
this._elements.remove.removeAttribute("disabled");
|
|
} else if (selectedCount > 1) {
|
|
this._elements.edit.setAttribute("disabled", "disabled");
|
|
this._elements.remove.removeAttribute("disabled");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handle events
|
|
*
|
|
* @param {DOMEvent} event
|
|
*/
|
|
handleEvent(event) {
|
|
switch (event.type) {
|
|
case "DOMContentLoaded": {
|
|
this.init();
|
|
this.loadAddresses();
|
|
break;
|
|
}
|
|
case "click": {
|
|
this.handleClick(event);
|
|
break;
|
|
}
|
|
case "change": {
|
|
this.updateButtonsStates(this._selectedOptions.length);
|
|
break;
|
|
}
|
|
case "unload": {
|
|
this.uninit();
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handle click events
|
|
*
|
|
* @param {DOMEvent} event
|
|
*/
|
|
handleClick(event) {
|
|
if (event.target == this._elements.remove) {
|
|
this.removeAddresses(this._selectedOptions.map(option => option.value));
|
|
} else if (event.target == this._elements.add) {
|
|
this.openEditDialog();
|
|
} else if (event.target == this._elements.edit) {
|
|
this.openEditDialog(this._selectedOptions[0].address);
|
|
}
|
|
},
|
|
|
|
observe(subject, topic, data) {
|
|
switch (topic) {
|
|
case "formautofill-storage-changed": {
|
|
if (this._pendingChangeCount) {
|
|
this._pendingChangeCount -= 1;
|
|
return;
|
|
}
|
|
this.loadAddresses();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Attach event listener
|
|
*/
|
|
attachEventListeners() {
|
|
window.addEventListener("unload", this, {once: true});
|
|
this._elements.addresses.addEventListener("change", this);
|
|
this._elements.controlsContainer.addEventListener("click", this);
|
|
Services.obs.addObserver(this, "formautofill-storage-changed");
|
|
},
|
|
|
|
/**
|
|
* Remove event listener
|
|
*/
|
|
detachEventListeners() {
|
|
this._elements.addresses.removeEventListener("change", this);
|
|
this._elements.controlsContainer.removeEventListener("click", this);
|
|
Services.obs.removeObserver(this, "formautofill-storage-changed");
|
|
},
|
|
};
|
|
|
|
new ManageProfileDialog();
|