refactoring of cmaps, widgets, etc.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { buildBundle } from "./interchange.js";
|
||||
|
||||
/** Convert an ArrayBuffer to the base64 representation used in JSON bundles. */
|
||||
function arrayBufferToBase64(buffer) {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let binary = "";
|
||||
const chunkSize = 0x8000;
|
||||
for (let offset = 0; offset < bytes.length; offset += chunkSize) {
|
||||
binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
|
||||
}
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a stored CMap model and its linked resources as a complete JSON bundle.
|
||||
* CMaps are loaded through their repository. Page loading, bundle construction
|
||||
* and attachment encoding remain independent of workspace presentation.
|
||||
*/
|
||||
export class CmapJsonExporter {
|
||||
constructor(cmapRepository, loadWikiPage, fetchFile, generator = "Racket Wiki") {
|
||||
if (!cmapRepository || typeof cmapRepository.load !== "function" ||
|
||||
typeof loadWikiPage !== "function" || typeof fetchFile !== "function") {
|
||||
throw new TypeError("A CMap repository, wiki-page loader and file loader are required");
|
||||
}
|
||||
this.cmapRepository = cmapRepository;
|
||||
this.loadWikiPage = loadWikiPage;
|
||||
this.fetchFile = fetchFile;
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a validated JSON bundle without changing the source CMaps.
|
||||
* Linked CMaps are followed up to maxDepth; linked pages and attachments
|
||||
* are included by the interchange format.
|
||||
*/
|
||||
async export(rootMap, maxDepth = 0) {
|
||||
return buildBundle({
|
||||
rootMap: this.bundleMap(rootMap),
|
||||
maxDepth,
|
||||
generator: this.generator,
|
||||
loadConceptMap: async (slug) => this.bundleMap(await this.cmapRepository.load(slug)),
|
||||
loadWikiPage: this.loadWikiPage,
|
||||
loadAttachment: (url) => this.loadAttachment(url)
|
||||
});
|
||||
}
|
||||
|
||||
/** Present one stored model through the public interchange record shape. */
|
||||
bundleMap(storedMap) {
|
||||
if (!storedMap?.slug || typeof storedMap.toDocument !== "function") {
|
||||
throw new TypeError("JSON export requires a stored CMap");
|
||||
}
|
||||
return {
|
||||
slug: storedMap.slug,
|
||||
title: storedMap.title,
|
||||
document: storedMap.toDocument()
|
||||
};
|
||||
}
|
||||
|
||||
/** Load and encode one attachment referenced by an exported wiki page. */
|
||||
async loadAttachment(url) {
|
||||
const response = await this.fetchFile(url);
|
||||
if (!response.ok) throw new Error(`Attachment could not be exported: ${url}`);
|
||||
const content = await response.arrayBuffer();
|
||||
return {
|
||||
mimeType: response.headers.get("content-type") || "application/octet-stream",
|
||||
contentBase64: arrayBufferToBase64(content)
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user