import { SectionItem, TOCItem, CFI, BookDoc } from '@/libs/document'; export const findParentPath = (toc: TOCItem[], href: string): TOCItem[] => { for (const item of toc) { if (item.href === href) { return [item]; } if (item.subitems) { const path = findParentPath(item.subitems, href); if (path.length) { return [item, ...path]; } } } return []; }; export const findTocItemBS = (toc: TOCItem[], cfi: string): TOCItem | null => { let left = 0; let right = toc.length - 1; let result: TOCItem | null = null; while (left <= right) { const mid = Math.floor((left + right) / 2); const currentCfi = toc[mid]!.cfi || ''; const comparison = CFI.compare(currentCfi, cfi); if (comparison === 0) { return toc[mid]!; } else if (comparison < 0) { result = toc[mid]!; left = mid + 1; } else { right = mid - 1; } } return result; }; export const updateToc = (bookDoc: BookDoc, sortedTOC: boolean): void => { const items = bookDoc?.toc || []; const sections = bookDoc?.sections || []; if (!items.length || !sections.length) return; const sizes = sections.map((s) => (s.linear != 'no' && s.size > 0 ? s.size : 0)); let cumulativeSize = 0; const cumulativeSizes = sizes.reduce((acc: number[], size) => { acc.push(cumulativeSize); cumulativeSize += size; return acc; }, []); const totalSize = cumulativeSizes[cumulativeSizes.length - 1] || 0; const sizePerLoc = 1500; sections.forEach((section, index) => { section.location = { current: Math.floor(cumulativeSizes[index]! / sizePerLoc), next: Math.floor((cumulativeSizes[index]! + sizes[index]!) / sizePerLoc), total: Math.floor(totalSize / sizePerLoc), }; }); const sectionsMap = sections.reduce((map: Record, section) => { map[section.id] = section; return map; }, {}); updateTocData(bookDoc, items, sections, sectionsMap); if (sortedTOC) { sortTocItems(items); } }; const updateTocData = ( bookDoc: BookDoc, items: TOCItem[], sections: SectionItem[], sectionsMap: { [id: string]: SectionItem }, index = 0, ): number => { items.forEach((item) => { item.id ??= index++; if (item.href) { const id = bookDoc.splitTOCHref(item.href)[0]!; const section = sectionsMap[id]; if (section) { item.cfi = section.cfi; // Add location only when toc item is at the same level as the section // otherwise the location will not be accurate if (id === item.href || items.length <= sections.length) { item.location = section.location; } } } if (item.subitems) { index = updateTocData(bookDoc, item.subitems, sections, sectionsMap, index); } }); return index; }; const sortTocItems = (items: TOCItem[]): void => { items.sort((a, b) => { if (a.location && b.location) { return a.location.current - b.location.current; } return 0; }); };