5f7a14fde8cdb24b01fc0e5eadba145bfd9ecb04
max
Sat Jul 25 23:16:06 2026 -0700
hgLogin: social login (Google/ORCID), email login link, change email. refs #37929
diff --git src/hg/js/topLinks.js src/hg/js/topLinks.js
index 62cca2e458f..0ffa1e94e6b 100644
--- src/hg/js/topLinks.js
+++ src/hg/js/topLinks.js
@@ -1,383 +1,386 @@
// topLinks.js - behavior for the two top-right menu-bar links, "Login" and "Share a link".
//
// Login: when the user is logged in, the menu item shows their username and opens a small
// account dialog (change password, sign out). When logged out it is a plain link to hgSession,
// so this script does nothing for it.
//
// Share a link (browser pages only): saves the current view as a session and shows a copyable
// short link. Logged-in users get to name the session and choose whether others may open it;
// logged-out users get an anonymous link created on the fly.
//
// The C code that builds the menu bar (lib/web.c menuBar() for CGI pages, hgMenubar.c for static
// pages) fills in the data-* attributes that this script reads. The dialog is a self-contained
// overlay so it works on static pages too (no jQuery UI dependency).
/* jshint esversion: 8 */
/* global $, document, window, URL, getHgsid, copyToClipboard */
var topLinks = (function() {
"use strict";
// Small helper to build an element with properties and inline styles set from the DOM
// (assigning to element.style.* is CSP-safe, unlike a style="" attribute in markup).
function el(tag, props, styles) {
var e = document.createElement(tag);
var k;
if (props)
for (k in props)
if (props.hasOwnProperty(k)) e[k] = props[k];
if (styles)
for (k in styles)
if (styles.hasOwnProperty(k)) e.style[k] = styles[k];
return e;
}
var closeCurrent = null; // closer for the currently open modal, if any
function closeModal() {
if (closeCurrent) { closeCurrent(); closeCurrent = null; }
}
// Show a centered modal with the given title and body node. Returns the body element so the
// caller can replace its contents later (e.g. swap a spinner for the resulting link).
function showModal(titleText, bodyNode, widthPx) {
closeModal();
var overlay = el("div", {}, {
position: "fixed", top: "0", left: "0", right: "0", bottom: "0",
background: "rgba(0,0,0,0.4)", zIndex: "10000"
});
var box = el("div", {}, {
position: "fixed", top: "90px", left: "50%", transform: "translateX(-50%)",
background: "#fff", color: "#000", border: "1px solid #888", borderRadius: "4px",
boxShadow: "0 2px 12px rgba(0,0,0,0.4)", width: (widthPx || 430) + "px", maxWidth: "92%",
zIndex: "10001", fontSize: "14px"
});
var titleBar = el("div", {}, {
background: "#00457c", color: "#fff", padding: "8px 12px", fontWeight: "bold",
borderTopLeftRadius: "4px", borderTopRightRadius: "4px", position: "relative"
});
titleBar.appendChild(document.createTextNode(titleText));
var x = el("span", {title: "Close", textContent: "×"}, {
position: "absolute", right: "10px", top: "5px", cursor: "pointer",
fontSize: "18px", lineHeight: "18px"
});
x.addEventListener("click", closeModal);
titleBar.appendChild(x);
var body = el("div", {}, {padding: "14px"});
if (bodyNode) body.appendChild(bodyNode);
box.appendChild(titleBar);
box.appendChild(body);
overlay.appendChild(box);
document.body.appendChild(overlay);
// Swallow Escape and clicks so they don't also reach an underlying dialog's own
// close-on-escape / click-outside handlers (e.g. the hgc item-details popup).
function onKey(ev) {
if (ev.key === "Escape") {
ev.preventDefault();
ev.stopImmediatePropagation();
closeModal();
}
}
overlay.addEventListener("click", function(ev) {
if (ev.target === overlay)
closeModal();
ev.stopPropagation();
});
document.addEventListener("keydown", onKey, true); // capture: run before other handlers
closeCurrent = function() {
document.removeEventListener("keydown", onKey, true);
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
};
return body;
}
// ---- Login / account dialog --------------------------------------------------------------
function showLoginDialog(link) {
var user = link.getAttribute("data-username");
var logoutUrl = link.getAttribute("data-logouturl");
var changePwUrl = link.getAttribute("data-changepwurl");
+ var changeEmailUrl = link.getAttribute("data-changeemailurl");
var body = document.createElement("div");
var p = el("p");
p.appendChild(document.createTextNode("Signed in as "));
p.appendChild(el("b", {textContent: user})); // textContent avoids HTML injection
p.appendChild(document.createTextNode("."));
body.appendChild(p);
// helper: add an