hi-frontend/docs/public/scalar/scalar-embed.js
2025-12-11 03:29:07 +00:00

194 lines
5.1 KiB
JavaScript

(() => {
const params = new URLSearchParams(window.location.search);
const spec = params.get("spec") || "/swagger.json";
const title = params.get("title") || "API Reference";
const theme = params.get("theme") || "saturn";
const layout = params.get("layout") || "classic";
const operationId = params.get("operationId");
const scalarRoot = document.getElementById("scalar-root");
const getHeightSourceElement = () =>
document.querySelector(".narrow-references-container") || scalarRoot;
let resizeObserver;
const observedElements = new WeakSet();
const sendHeightToParent = () => {
if (window.parent === window) return;
const heightSource = getHeightSourceElement();
const sourceHeight =
heightSource?.scrollHeight ?? heightSource?.offsetHeight ?? 0;
const fallbackHeight = Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
0
);
const height = heightSource ? sourceHeight : fallbackHeight;
window.parent.postMessage(
{
type: "scalar-height",
height,
},
window.location.origin
);
};
const mountScalar = (sourceConfiguration) => {
Scalar.createApiReference("#scalar-root", {
title,
theme,
layout,
showSidebar: false,
hideModels: true,
defaultOpenAllTags: true,
documentDownloadType: "json",
hideSearch: false,
withDefaultFonts: true,
showOperationId: Boolean(operationId),
...sourceConfiguration,
});
requestAnimationFrame(sendHeightToParent);
};
const filterSpecByOperationId = (specContent, requestedId) => {
if (
!specContent ||
typeof specContent !== "object" ||
!specContent.paths ||
typeof specContent.paths !== "object"
) {
return null;
}
const filteredPaths = {};
const matchedTags = new Set();
let hasMatch = false;
Object.entries(specContent.paths).forEach(([path, methods]) => {
if (!methods || typeof methods !== "object") {
return;
}
const retainedOperations = Object.entries(methods).reduce(
(acc, [method, operation]) => {
if (
operation &&
typeof operation === "object" &&
operation.operationId === requestedId
) {
acc[method] = operation;
hasMatch = true;
if (Array.isArray(operation.tags)) {
operation.tags.forEach((tag) => matchedTags.add(tag));
}
}
return acc;
},
{}
);
if (Object.keys(retainedOperations).length > 0) {
filteredPaths[path] = retainedOperations;
}
});
if (!hasMatch) {
return null;
}
const filteredSpec = {
...specContent,
paths: filteredPaths,
};
if (Array.isArray(specContent.tags) && matchedTags.size > 0) {
filteredSpec.tags = specContent.tags.filter((tag) =>
matchedTags.has(tag.name)
);
}
delete filteredSpec["x-scalar-navigation"];
return filteredSpec;
};
const fetchSpecContent = async () => {
const response = await fetch(spec);
if (!response.ok) {
throw new Error(
`Failed to load spec (${response.status} ${response.statusText})`
);
}
const text = await response.text();
try {
return JSON.parse(text);
} catch (error) {
console.warn(
"Scalar embed: Spec is not valid JSON, falling back to full document.",
error
);
return null;
}
};
const init = async () => {
if (operationId) {
try {
const specContent = await fetchSpecContent();
const filteredSpec = filterSpecByOperationId(specContent, operationId);
if (filteredSpec) {
mountScalar({ content: filteredSpec });
return;
}
} catch (error) {
console.warn(
"Scalar embed: Unable to filter by the requested operationId.",
error
);
}
}
mountScalar({ url: spec });
};
init();
const ensureResizeObservers = () => {
if (typeof ResizeObserver === "undefined" || !scalarRoot) return;
if (!resizeObserver) {
resizeObserver = new ResizeObserver(() => {
requestAnimationFrame(sendHeightToParent);
});
}
[scalarRoot, getHeightSourceElement()]
.filter((element) => element && !observedElements.has(element))
.forEach((element) => {
resizeObserver.observe(element);
observedElements.add(element);
});
};
const mutationObserver =
typeof MutationObserver !== "undefined" && scalarRoot
? new MutationObserver(() => {
ensureResizeObservers();
requestAnimationFrame(sendHeightToParent);
})
: null;
mutationObserver?.observe(scalarRoot, { childList: true, subtree: true });
ensureResizeObservers();
if (typeof ResizeObserver === "undefined" || !scalarRoot) {
const intervalId = setInterval(() => {
if (document.readyState === "complete") {
sendHeightToParent();
clearInterval(intervalId);
}
}, 500);
}
window.addEventListener("load", sendHeightToParent);
window.addEventListener("resize", sendHeightToParent);
})();