UNPKG

@mieweb/wikigdrive

Version:

Google Drive to MarkDown synchronization

144 lines (122 loc) 3.65 kB
// Legacy menu generator, use: navigation2menu.mjs import {urlToFolderId} from '../../utils/idParsers.ts'; import {FileId} from '../../model/model.ts'; import {DocumentContent, TextLink, TextList, TextParagraph, TextSpace, TextSpan, TextTab} from '../../odt/LibreOffice.ts'; export interface NavigationHierarchyNode { name: string; weight: number; identifier: FileId; parent?: FileId; pageRef?: string; } export interface NavigationHierarchy { [k: string]: NavigationHierarchyNode; } interface Logger { warn(mxg: string): void; } interface NavigationProcessContext { headerCounter: number; lastContent: string; counter: number; levelParent: {[level: number]: string}; logger: Logger; result: NavigationHierarchy; } function extractText(obj: string | TextLink | TextSpan | TextSpace | TextTab): string { let retVal = ''; if (typeof obj === 'string') { retVal += obj; } else { if (obj.type === 'space') { retVal += ' '.substring(0, (<TextSpace>obj).chars); } if (obj.type === 'tab') { retVal += '\t'; } if (obj.type === 'link') { for (const item of (<TextLink>obj).list) { retVal += extractText(item); } } else if (obj.type === 'span') { for (const item of (<TextSpan>obj).list) { retVal += extractText(item); } } } return retVal; } function processPara(para: TextParagraph, ctx: NavigationProcessContext, level: number) { let paraUrl; let paraContent = ''; para.list = para.list.filter(item => { if (typeof item !== 'string') { return true; } return !!item.trim(); }); if (para.list.length === 0) { return; } const item = para.list[0]; if (typeof item === 'string') { paraContent += extractText(item); } else if (item.type === 'link') { const link = <TextLink>item; paraUrl = link.href; paraContent += extractText(item); } if (paraContent) { ctx.lastContent = paraContent; const fileId = paraUrl ? urlToFolderId(paraUrl) : 'header_' + ctx.headerCounter++; ctx.levelParent[level] = fileId; const hierarchyFrontMatter: NavigationHierarchyNode = { identifier: fileId, name: paraContent.trim(), weight: ctx.counter }; if (level > 0) { hierarchyFrontMatter.parent = ctx.levelParent[level - 1]; } ctx.result[hierarchyFrontMatter.identifier] = hierarchyFrontMatter; ctx.counter += 10; if (!paraUrl) { ctx.logger.warn(`Warning: .navigation menu has "${paraContent.trim()}" without url near: "${ctx.lastContent.trim()}"`); } } } function processList(textList: TextList, ctx: NavigationProcessContext, level = 0) { for (const textListItem of textList.list) { for (const paraOrList of textListItem.list) { switch (paraOrList.type) { case 'list': processList(<TextList>paraOrList, ctx, level + 1); break; case 'paragraph': processPara(<TextParagraph>paraOrList, ctx, level); break; } } } } // https://developers.google.com/docs/api/concepts/structure export async function generateNavigationHierarchy(doc: DocumentContent, logger: Logger): Promise<NavigationHierarchy> { const ctx: NavigationProcessContext = { headerCounter: 1, counter: 30, levelParent: {}, logger: logger, result: {}, lastContent: 'START' }; for (const structuralElement of doc.body.text.list) { if (structuralElement.type !== 'list') { continue; } const textList: TextList = <TextList>structuralElement; processList(textList, ctx); } return ctx.result; }