194 lines
5.1 KiB
JavaScript
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);
|
|
})();
|