Download static/foliate-reader/progress.js from VoiceOfML/Search: direct link, hf CLI and curl.
- Browser
- Download file 4.26 kB
-
https://huggingface.co/spaces/VoiceOfML/Search/resolve/main/static/foliate-reader/progress.js
- Command line
-
hf download hf://spaces/VoiceOfML/Search/static/foliate-reader/progress.js
-
curl -L -o progress.js https://huggingface.co/spaces/VoiceOfML/Search/resolve/main/static/foliate-reader/progress.js
4.26 kB
| // assign a unique ID for each TOC item | |
| const assignIDs = toc => { | |
| let id = 0 | |
| const assignID = item => { | |
| item.id = id++ | |
| if (item.subitems) for (const subitem of item.subitems) assignID(subitem) | |
| } | |
| for (const item of toc) assignID(item) | |
| return toc | |
| } | |
| const flatten = items => items | |
| .map(item => item.subitems?.length | |
| ? [item, flatten(item.subitems)].flat() | |
| : item) | |
| .flat() | |
| export class TOCProgress { | |
| async init({ toc, ids, splitHref, getFragment }) { | |
| assignIDs(toc) | |
| const items = flatten(toc) | |
| const grouped = new Map() | |
| for (const [i, item] of items.entries()) { | |
| const [id, fragment] = await splitHref(item?.href) ?? [] | |
| const value = { fragment, item } | |
| if (grouped.has(id)) grouped.get(id).items.push(value) | |
| else grouped.set(id, { prev: items[i - 1], items: [value] }) | |
| } | |
| const map = new Map() | |
| for (const [i, id] of ids.entries()) { | |
| if (grouped.has(id)) map.set(id, grouped.get(id)) | |
| else map.set(id, map.get(ids[i - 1])) | |
| } | |
| this.ids = ids | |
| this.map = map | |
| this.getFragment = getFragment | |
| } | |
| getProgress(index, range) { | |
| if (!this.ids) return | |
| const id = this.ids[index] | |
| const obj = this.map.get(id) | |
| if (!obj) return null | |
| const { prev, items } = obj | |
| if (!items) return prev | |
| if (!range || items.length === 1 && !items[0].fragment) return items[0].item | |
| const doc = range.startContainer.getRootNode() | |
| for (const [i, { fragment }] of items.entries()) { | |
| const el = this.getFragment(doc, fragment) | |
| if (!el) continue | |
| if (range.comparePoint(el, 0) > 0) | |
| return (items[i - 1]?.item ?? prev) | |
| } | |
| return items[items.length - 1].item | |
| } | |
| } | |
| export class SectionProgress { | |
| constructor(sections, sizePerLoc, sizePerTimeUnit) { | |
| this.sizes = sections.map(s => s.linear != 'no' && s.size > 0 ? s.size : 0) | |
| this.sizePerLoc = sizePerLoc | |
| this.sizePerTimeUnit = sizePerTimeUnit | |
| this.sizeTotal = this.sizes.reduce((a, b) => a + b, 0) | |
| this.sectionFractions = this.#getSectionFractions() | |
| } | |
| #getSectionFractions() { | |
| const { sizeTotal } = this | |
| const results = [0] | |
| let sum = 0 | |
| for (const size of this.sizes) results.push((sum += size) / sizeTotal) | |
| return results | |
| } | |
| // get progress given index of and fractions within a section | |
| getProgress(index, fractionInSection, pageFraction = 0) { | |
| const { sizes, sizePerLoc, sizePerTimeUnit, sizeTotal } = this | |
| const sizeInSection = sizes[index] ?? 0 | |
| const sizeBefore = sizes.slice(0, index).reduce((a, b) => a + b, 0) | |
| const size = sizeBefore + fractionInSection * sizeInSection | |
| const nextSize = size + pageFraction * sizeInSection | |
| const remainingTotal = sizeTotal - size | |
| const remainingSection = (1 - fractionInSection) * sizeInSection | |
| return { | |
| fraction: nextSize / sizeTotal, | |
| section: { | |
| current: index, | |
| total: sizes.length, | |
| }, | |
| location: { | |
| current: Math.floor(size / sizePerLoc), | |
| next: Math.floor(nextSize / sizePerLoc), | |
| total: Math.ceil(sizeTotal / sizePerLoc), | |
| }, | |
| time: { | |
| section: remainingSection / sizePerTimeUnit, | |
| total: remainingTotal / sizePerTimeUnit, | |
| }, | |
| } | |
| } | |
| // the inverse of `getProgress` | |
| // get index of and fraction in section based on total fraction | |
| getSection(fraction) { | |
| if (fraction <= 0) return [0, 0] | |
| if (fraction >= 1) return [this.sizes.length - 1, 1] | |
| fraction = fraction + Number.EPSILON | |
| const { sizeTotal } = this | |
| let index = this.sectionFractions.findIndex(x => x > fraction) - 1 | |
| if (index < 0) return [0, 0] | |
| while (!this.sizes[index]) index++ | |
| const fractionInSection = (fraction - this.sectionFractions[index]) | |
| / (this.sizes[index] / sizeTotal) | |
| return [index, fractionInSection] | |
| } | |
| } | |