
svg.sequencediagram.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plantuml-mit Show documentation
Show all versions of plantuml-mit Show documentation
PlantUML is a component that allows to quickly write diagrams from text.
The newest version!
(function() {
const SVG_NS = "http://www.w3.org/2000/svg";
const LOCAL_STORAGE_FLOATING_HEADER_ACTIVE = "net.sourceforge.plantuml.sequence-diagram.floating-header.active";
function toggleFloatingHeader() {
try {
const shouldNowBeActive = ! isFloatingHeaderActive();
svgRoot.classList.toggle("floating-header-active", shouldNowBeActive);
window.localStorage?.setItem(LOCAL_STORAGE_FLOATING_HEADER_ACTIVE, `${shouldNowBeActive}`);
if (shouldNowBeActive) {
updateFloatingHeaderPosition(svgRoot.querySelector("g.floating-header"));
}
} catch (e) {
console.error("Error while toggling floating header:", e, svgRoot);
disableFloatingHeaderDueToError();
}
}
function isFloatingHeaderActive() {
return svgRoot.classList.contains("floating-header-active");
}
function disableFloatingHeaderDueToError() {
try {
svgRoot.classList.remove("floating-header-active");
svgRoot.classList.add("floating-header-error");
} catch(e) {
console.error("Failed to disable floating header:", e, svgRoot);
}
}
function findAncestorWithTagName(elem, tagName) {
while (elem && elem.nodeName.toLowerCase() !== tagName) {
elem = elem.parentElement;
}
return elem;
}
function groupParticipantHeaders() {
const group = document.createElementNS(SVG_NS, "g");
group.classList.add("header");
svgRoot.querySelectorAll("g.participant-head").forEach(participant => {
group.appendChild(participant);
});
svgRoot.querySelector("g").appendChild(group);
// Add a background rect, as a hit target
const headerBounds = group.getBBox();
const background = document.createElementNS(SVG_NS, "rect")
background.classList.add("header-background");
background.setAttribute("x", "0");
background.setAttribute("y", "0");
background.setAttribute("width", `${svgRoot.getBBox().width}`);
background.setAttribute("height", `${headerBounds.y + headerBounds.height + 10}`);
background.setAttribute("fill", svgRoot.style.backgroundColor);
group.insertAdjacentElement("afterbegin", background);
return group;
}
function isScrollableContainer(elem) {
const overflowY = getComputedStyle(elem).overflowY;
return (overflowY === "auto" || overflowY === "scroll") && elem.scrollHeight > elem.clientHeight
}
function createFloatingHeaderToggleButton(header) {
const buttonGroup = document.createElementNS(SVG_NS, "g");
buttonGroup.classList.add("floating-header-toggle-button");
buttonGroup.innerHTML = `
Pin the header while scrolling
`;
header.appendChild(buttonGroup);
return buttonGroup;
}
function createFloatingHeader(originalHeader) {
const floatingHeaderGroup = originalHeader.cloneNode(true);
floatingHeaderGroup.classList.add("floating-header");
svgRoot.querySelector("g").appendChild(floatingHeaderGroup);
return floatingHeaderGroup;
}
function ancestorsMaxClientY(startElement) {
let currentMax = 0;
let parent = startElement.parentElement;
while (parent) {
currentMax = Math.max(parent.getBoundingClientRect().y, currentMax);
parent = parent.parentElement;
}
return currentMax;
}
function updateFloatingHeaderPosition(floatingHeaderElement) {
try {
if (!isFloatingHeaderActive()) {
return;
}
const svgTop = svgRoot.getBoundingClientRect().y;
const ancestorsMaxTop = ancestorsMaxClientY(svgRoot);
const amountOfOverflow = Math.floor(Math.max(0, ancestorsMaxTop - svgTop));
floatingHeaderElement.setAttribute("transform", `translate(0, ${amountOfOverflow})`);
floatingHeaderElement.classList.toggle("floating", amountOfOverflow > 0);
} catch(e) {
console.error("Error while updating floating header position:", e, svgRoot);
disableFloatingHeaderDueToError();
}
}
function initFloatingHeader() {
try {
const header = groupParticipantHeaders()
const toggleButton = createFloatingHeaderToggleButton(header);
const floatingHeaderElement = createFloatingHeader(header)
svgRoot.querySelectorAll("g.floating-header-toggle-button").forEach(button => {
button.addEventListener("click", toggleFloatingHeader);
});
window.addEventListener("scroll", () => {
updateFloatingHeaderPosition(floatingHeaderElement);
});
let parentElement = svgRoot;
while (parentElement != null) {
if (isScrollableContainer(parentElement)) {
parentElement.addEventListener("scroll", () => {
updateFloatingHeaderPosition(floatingHeaderElement);
});
}
parentElement = parentElement.parentElement;
}
const isFloatingHeaderActive = window.localStorage?.getItem(LOCAL_STORAGE_FLOATING_HEADER_ACTIVE) === "true";
svgRoot.classList.toggle("floating-header-active", isFloatingHeaderActive);
console.log("In accordance with local storage, setting floating header active = ", isFloatingHeaderActive);
} catch(e) {
console.error("Error while initialising floating header:", e, svgRoot);
disableFloatingHeaderDueToError();
}
}
function handleParticipantFilterClick(clickedParticipantElem) {
const clickedParticipantName = clickedParticipantElem.getAttribute("data-participant");
const allFilteredParticipantsNames = new Set(Array
.from(svgRoot.querySelectorAll("g.participant.filter-highlight"))
.map(elem => elem.getAttribute("data-participant")));
if (allFilteredParticipantsNames.has(clickedParticipantName)) {
allFilteredParticipantsNames.delete(clickedParticipantName);
} else {
allFilteredParticipantsNames.add(clickedParticipantName);
}
svgRoot.querySelectorAll("g.participant").forEach(participantElem => {
const shouldHighlight = allFilteredParticipantsNames.has(participantElem.getAttribute("data-participant"));
participantElem.classList.toggle("filter-highlight", shouldHighlight);
});
svgRoot.querySelectorAll("g.message").forEach(messageElem => {
const participant1 = messageElem.getAttribute("data-participant-1");
const participant2 = messageElem.getAttribute("data-participant-2");
const participant1Matches = allFilteredParticipantsNames.has(participant1);
const participant2Matches = allFilteredParticipantsNames.has(participant2);
const shouldHighlight = (allFilteredParticipantsNames.size === 1)
? (participant1Matches || participant2Matches)
: (participant1Matches && participant2Matches && participant1 !== participant2);
messageElem.classList.toggle("filter-highlight", shouldHighlight);
});
if (allFilteredParticipantsNames.size === 0) {
svgRoot.classList.remove("filter-active", "filter-nomatch");
} else {
svgRoot.classList.add("filter-active");
if (svgRoot.querySelector("g.message.filter-highlight")) {
svgRoot.classList.remove("filter-nomatch");
} else {
svgRoot.classList.add("filter-nomatch");
}
}
}
function initParticipantFiltering() {
svgRoot.querySelectorAll(".participant").forEach(participantElem => {
participantElem.addEventListener("click", () => {
handleParticipantFilterClick(participantElem);
});
});
svgRoot.querySelector("svg > defs").innerHTML += `
`;
}
const svgRoot = findAncestorWithTagName(document.currentScript, "svg");
document.addEventListener("DOMContentLoaded", () => {
initFloatingHeader();
initParticipantFiltering();
});
})();
© 2015 - 2025 Weber Informatics LLC | Privacy Policy