gecko-dev/devtools/startup/aboutdevtools/subscribe.js

159 строки
5.1 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";
/**
* This file handles the newsletter subscription form on about:devtools.
* It is largely inspired from https://mozilla.github.io/basket-example/
*/
window.addEventListener(
"load",
function() {
// Timeout for the subscribe XHR.
const REQUEST_TIMEOUT = 5000;
const emailInput = document.getElementById("email");
const newsletterErrors = document.getElementById("newsletter-errors");
const newsletterForm = document.getElementById("newsletter-form");
const newsletterPrivacySection = document.getElementById(
"newsletter-privacy"
);
const newsletterThanks = document.getElementById("newsletter-thanks");
/**
* Update the error panel to display the provided errors. If the argument is null or
* empty, a default error message will be displayed.
*
* @param {Array} errors
* Array of strings, each item being an error message to display.
*/
async function updateErrorPanel(errors) {
clearErrorPanel();
if (!errors || errors.length == 0) {
errors = [
await document.l10n.formatValues([
{ id: "newsletter-error-unknown" },
]),
];
}
// Create errors markup.
const fragment = document.createDocumentFragment();
for (const error of errors) {
const item = document.createElement("p");
item.classList.add("error");
item.appendChild(document.createTextNode(error));
fragment.appendChild(item);
}
newsletterErrors.appendChild(fragment);
newsletterErrors.classList.add("show");
}
/**
* Hide the error panel and remove all errors.
*/
function clearErrorPanel() {
newsletterErrors.classList.remove("show");
newsletterErrors.innerHTML = "";
}
// Show the additional form fields on focus of the email input.
function onEmailInputFocus() {
// Create a hidden measuring container, append it to the parent of the privacy section
const container = document.createElement("div");
container.style.cssText =
"visibility: hidden; overflow: hidden; position: absolute";
newsletterPrivacySection.parentNode.appendChild(container);
// Clone the privacy section, append the clone to the measuring container.
const clone = newsletterPrivacySection.cloneNode(true);
container.appendChild(clone);
// Measure the target height of the privacy section.
clone.style.height = "auto";
const height = clone.offsetHeight;
// Cleanup the measuring container.
container.remove();
// Set the animate class and set the height to the measured height.
newsletterPrivacySection.classList.add("animate");
newsletterPrivacySection.style.cssText = `height: ${height}px; margin-bottom: 0;`;
}
// XHR subscribe; handle errors; display thanks message on success.
function onFormSubmit(evt) {
evt.preventDefault();
evt.stopPropagation();
// New submission, clear old errors
clearErrorPanel();
const xhr = new XMLHttpRequest();
xhr.onload = async function(r) {
if (r.target.status >= 200 && r.target.status < 300) {
const { response } = r.target;
if (response.success === true) {
// Hide form and show success message.
newsletterForm.style.display = "none";
newsletterThanks.classList.add("show");
} else {
// We trust the error messages from the service to be meaningful for the user.
updateErrorPanel(response.errors);
}
} else {
const { status, statusText } = r.target;
const statusInfo = `${status} - ${statusText}`;
const error = await document.l10n.formatValues([
{
id: "newsletter-error-common",
args: { errorDescription: statusInfo },
},
]);
updateErrorPanel([error]);
}
};
xhr.onerror = () => {
updateErrorPanel();
};
xhr.ontimeout = async () => {
const error = await document.l10n.formatValues([
{ id: "newsletter-error-timeout" },
]);
updateErrorPanel([error]);
};
const url = newsletterForm.getAttribute("action");
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.timeout = REQUEST_TIMEOUT;
xhr.responseType = "json";
// Create form data.
const formData = new FormData(newsletterForm);
formData.append("source_url", document.location.href);
const params = new URLSearchParams(formData);
// Send the request.
xhr.send(params.toString());
}
// Attach event listeners.
newsletterForm.addEventListener("submit", onFormSubmit);
emailInput.addEventListener("focus", onEmailInputFocus);
},
{ once: true }
);