+ /**
+ * Determine if the user agent is mobile Firefox.
+ */
+ function ua_is_mobile_ff() {
+ const ua = navigator.userAgent.toLowerCase();
+ return (ua.includes("firefox") && ua.includes("mobile"));
+ }
+
+ /**
+ * Validate the MARC form's origin/destination.
+ *
+ * We don't want the user to be able to choose a pair of stops that
+ * aren't actually connected by the same MARC line. If we don't have
+ * zone information for the (origin,destination) pair, that indicates
+ * that it's probably not a valid choice; otherwise I would have
+ * filled in the information from the CharmPass app already.
+ *
+ * All browsers except mobile Firefox let us call setCustomValidity()
+ * to provide an error message that is displayed if the user tries
+ * to submit invalid choices. But amazingly, mobile Firefox does not:
+ *
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1510450
+ *
+ * Instead we have to work around it (in that one browser) by
+ * showing/hiding a paragraph that we fill with the errors.
+ */
+ function validate_origin_destination(event) {
+ const origins = document.getElementsByName("origin");
+ const destinations = document.getElementsByName("destination");
+ const mfe = document.getElementById("marc-form-errors");
+ const marcsubmit = document.getElementById("marc-submit");
+
+ if (ua_is_mobile_ff()) {
+ /* Even though this is only for one browser, empty paragraphs
+ * are handled inconsistently and should be avoided as a rule.
+ * So, we make it say "OK" before we hide it. */
+ mfe.textContent = "OK";
+ mfe.style.visibility = "hidden";
+ marcsubmit.disabled = false;
+ }
+
+ let src = null;
+ let dest = null;
+ origins.forEach((x) => { if (x.checked) src = x; })
+ destinations.forEach((x) => {
+ if (x.checked) dest = x;
+
+ /* clear all errors before possibly setting one */
+ x.setCustomValidity('');
+ })
+
+ if (src.value === dest.value) {
+ let err = "Origin and destination are the same";
+ dest.setCustomValidity(err);
+
+ if (ua_is_mobile_ff()) {
+ mfe.textContent = err;
+ mfe.style.visibility = "visible";
+ marcsubmit.disabled = true;
+ }
+ }
+ else if (compute_marc_zone(src.value, dest.value) === null) {
+ let err = "Origin and destination are on different lines";
+ dest.setCustomValidity(err);
+
+ if (ua_is_mobile_ff()) {
+ mfe.textContent = err;
+ mfe.style.visibility = "visible";
+ marcsubmit.disabled = true;
+ }
+ }
+ }
+