(() => {
let gtnWebhookLoaded = false;
let lastUpdate = 0;
const urlParams = new URLSearchParams(document.location.search);
const autoLoadTutorial =
urlParams.get("autoload_gtn_tutorial") === null ? "" : urlParams.get("autoload_gtn_tutorial");
function removeOverlay() {
const container = document.getElementById("gtn-container");
if (container) {
container.style.visibility = "hidden";
}
}
function showOverlay() {
document.getElementById("gtn-container").style.visibility = "visible";
}
function getIframeUrl() {
let loc;
try {
loc = document.getElementById("gtn-embed").contentWindow.location.pathname;
} catch (e) {
loc = null;
}
return loc;
}
function getIframeScroll() {
let loc;
try {
loc = parseInt(document.getElementById("gtn-embed").contentWindow.scrollY);
} catch (e) {
loc = 0;
}
return loc;
}
function persistLocation() {
// Don't save every scroll event.
const time = new Date().getTime();
if (time - lastUpdate < 1000) {
return;
}
lastUpdate = time;
window.localStorage.setItem("gtn-in-galaxy", `${getIframeScroll()} ${getIframeUrl()}`);
}
function addIframe() {
let url;
let message;
let onloadscroll;
gtnWebhookLoaded = true;
let storedData = false;
let safe = false;
// Test for the presence of /training-material/. If that is available we
// can opt in the fancy click-to-run features. Otherwise we fallback to
// displaying the real GTN.
fetch("/training-material/")
.then((response) => {
if (!response.ok) {
url = `https://training.galaxyproject.org/training-material/${autoLoadTutorial}?utm_source=webhook&utm_medium=noproxy&utm_campaign=gxy`;
message = `
Click to run unavailable.
`;
} else {
safe = true;
const storedLocation = window.localStorage.getItem("gtn-in-galaxy");
if (
storedLocation !== null &&
storedLocation.split(" ")[1] !== undefined &&
storedLocation.split(" ")[1].startsWith("/training-material/")
) {
onloadscroll = storedLocation.split(" ")[0];
url = storedLocation.split(" ")[1];
} else {
url = `/training-material/${autoLoadTutorial}?utm_source=webhook&utm_medium=proxy&utm_campaign=gxy`;
}
message = "";
}
})
.then(() => {
document.querySelector("body.full-content").insertAdjacentHTML(
"afterbegin",
`
`
);
// Clicking outside of GTN closes it
document.getElementById("gtn-screen").addEventListener("click", () => {
removeOverlay();
});
// Only setup the listener if it won't crash things.
if (safe) {
// Listen to the scroll position
document.getElementById("gtn-embed").contentWindow.addEventListener("scroll", () => {
persistLocation();
});
}
// Depends on the iframe being present
document.getElementById("gtn-embed").addEventListener("load", () => {
// Save our current location when possible
const gtnEmbed = document.getElementById("gtn-embed");
if (onloadscroll !== undefined) {
document.getElementById("gtn-embed").contentWindow.scrollTo(0, parseInt(onloadscroll));
onloadscroll = undefined;
}
if (safe) {
persistLocation();
}
// Add the class to the entire GTN page
document
.getElementById("gtn-embed")
.contentDocument.getElementsByTagName("body")[0]
.classList.add("galaxy-proxy-active");
const gtnToolElements = document
.getElementById("gtn-embed")
.contentDocument.querySelectorAll("span[data-tool],a[data-tool]");
// Buttonify
gtnToolElements.forEach(function (el) {
el.addEventListener("click", function (e) {
let target = e.target;
// Sometimes we get the i or the strong, not the parent.
if (e.target.tagName.toLowerCase() !== "span" && e.target.tagName.toLowerCase() !== "a") {
target = e.target.parentElement;
}
tool_id = target.dataset.tool;
if (tool_id === "upload1" || tool_id === "upload") {
document.getElementById("tool-panel-upload-button").click();
} else {
Galaxy.router.push({ path: `/?tool_id=${encodeURIComponent(tool_id)}` });
}
removeOverlay();
});
});
const gtnWorkflowElements = document
.getElementById("gtn-embed")
.contentDocument.querySelectorAll("span[data-workflow],a[data-workflow]");
// Buttonify
gtnWorkflowElements.forEach(function (el) {
el.addEventListener("click", (e) => {
let target = e.target;
// Sometimes we get the i or the strong, not the parent.
if (e.target.tagName.toLowerCase() !== "span" && e.target.tagName.toLowerCase() !== "a") {
target = e.target.parentElement;
}
trs_url = target.dataset.workflow;
Galaxy.router.push({
path: `/workflows/trs_import?trs_url=${encodeURIComponent(trs_url)}&run_form=true`,
});
removeOverlay();
});
});
});
});
}
/* The masthead icon may not exist yet when this webhook executes; we need this to wait for that to happen.
* elementReady function from gist:
* https://gist.github.com/jwilson8767/db379026efcbd932f64382db4b02853e
*/
function elementReady(selector) {
return new Promise((resolve, reject) => {
let el = document.querySelector(selector);
if (el) {
resolve(el);
}
new MutationObserver((mutationRecords, observer) => {
// Query for elements matching the specified selector
Array.from(document.querySelectorAll(selector)).forEach((element) => {
resolve(element);
//Once we have resolved we don't need the observer anymore.
observer.disconnect();
});
}).observe(document.documentElement, {
childList: true,
subtree: true,
});
});
}
elementReady("#gtn a").then((el) => {
// External stuff may also have attached a click handler here (vue-based masthead)
// replace with a clean copy of the node to remove all that cruft.
clean = el.cloneNode(true);
el.parentNode.replaceChild(clean, el);
clean.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
if (!gtnWebhookLoaded) {
addIframe();
} else {
showOverlay();
}
});
if (autoLoadTutorial) {
clean.click();
}
});
// Remove the overlay on escape button click
document.addEventListener("keydown", (event) => {
// Check for escape button - "Escape" (modern browsers), "27" (old browsers)
if (event.key === "Escape" || event.keyCode === 27) {
removeOverlay();
}
});
})();