@prose-reader/enhancer-koreader 15.0.1 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@prose-reader/koreader"),t=require("@prose-reader/shared"),n=require("rxjs");var r=t.Report.namespace(`@prose-reader/enhancer-koreader`),i=e=>e.type===`xpointer`,a=t=>a=>{let o=t(a),s=new n.BehaviorSubject(void 0),c=t=>{let n=(0,e.parseXPointer)(t);if(n&&o.spineItemsManager.get(n.spineItemIndex))return{type:`selector`,value:{spineItem:n.spineItemIndex,find:t=>{let r=(0,e.resolveXPointer)(n,t);return r&&(0,e.toCfiPosition)(r)}}}},l=e=>{let{target:t}=e;if(!i(t))return o.navigation.navigate({...e,target:t});let n=c(t.value);if(!n){r.warn(`Ignore navigation to ${t.value}, not in the book`);return}s.next({xpointer:t.value,target:n}),o.navigation.navigate({...e,target:n})},u=e=>{let t=o.spineItemsManager.get(e),n=t?.value.isLoaded?t.renderer.getDocumentFrame()?.contentDocument:void 0;return t&&n?{document:n,id:t.item.id}:void 0},d=t=>{if(o.cfi.isRootCfi(t)){let r=(0,e.cfiToXPointer)(t,u);return r===void 0?n.EMPTY:(0,n.of)(r)}let r=o.cfi.getSpineItemFromCfi(t);return r?r.watch(`isLoaded`).pipe((0,n.filter)(Boolean),(0,n.first)(),(0,n.map)(()=>(0,e.cfiToXPointer)(t,u)),(0,n.filter)(e=>e!==void 0)):n.EMPTY},f=(0,n.combineLatest)([o.navigation.readingPosition$,s,o.navigation.navigation$]).pipe((0,n.switchMap)(([e,t])=>t!==void 0&&o.navigation.getNavigation().target===t.target&&o.cfi.isRootCfi(e)?(0,n.of)(t.xpointer):d(e)),(0,n.distinctUntilChanged)(),(0,n.takeUntil)(o.$.destroy$),(0,n.shareReplay)({bufferSize:1,refCount:!0}));return{...o,navigation:{...o.navigation,navigate:l,goToXPointer:e=>l({target:{type:`xpointer`,value:e},animation:!1}),readingPositionXPointer$:f}}};exports.koreaderEnhancer=a;
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@prose-reader/koreader"),t=require("@prose-reader/shared"),n=require("rxjs");var r=t.Report.namespace(`@prose-reader/enhancer-koreader`),i=e=>e.type===`xpointer`,a=t=>a=>{let o=t(a),s=new n.BehaviorSubject(void 0),c=t=>{let n=(0,e.parseXPointer)(t);if(n&&o.spineItemsManager.get(n.spineItemIndex))return{type:`selector`,value:{spineItem:n.spineItemIndex,find:t=>{let r=(0,e.resolveXPointer)(n,t);return r&&(0,e.toCfiPosition)(r)}}}},l=e=>{let{target:t}=e;if(!i(t))return o.navigation.navigate({...e,target:t});let n=c(t.value);if(!n){r.warn(`Ignore navigation to ${t.value}, not in the book`);return}s.next({xpointer:t.value,target:n}),o.navigation.navigate({...e,target:n})},u=e=>{let t=o.spineItemsManager.get(e),n=t?.value.isLoaded?t.renderer.getDocumentFrame()?.contentDocument:void 0;return t&&n?{document:n,id:t.item.id}:void 0},d=t=>{if(o.cfi.isRootCfi(t)){let r=(0,e.cfiToXPointer)(t,u);return r===void 0?n.EMPTY:(0,n.of)(r)}let r=o.cfi.getSpineItemFromCfi(t);return r?r.watch(`isLoaded`).pipe((0,n.filter)(Boolean),(0,n.first)(),(0,n.map)(()=>(0,e.cfiToXPointer)(t,u)),(0,n.filter)(e=>e!==void 0)):n.EMPTY},f=(0,n.combineLatest)([o.navigation.readingPosition$,s,o.navigation.navigation$]).pipe((0,n.switchMap)(([{cfi:e},t])=>t!==void 0&&o.navigation.getNavigation().target===t.target&&o.cfi.isRootCfi(e)?(0,n.of)(t.xpointer):d(e)),(0,n.distinctUntilChanged)(),(0,n.takeUntil)(o.$.destroy$),(0,n.shareReplay)({bufferSize:1,refCount:!0}));return{...o,navigation:{...o.navigation,navigate:l,goToXPointer:e=>l({target:{type:`xpointer`,value:e},animation:!1}),readingPositionXPointer$:f}}};exports.koreaderEnhancer=a;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/koreaderEnhancer.ts"],"sourcesContent":["import type {\n EnhancerOutput,\n NavigationTarget,\n RootEnhancer,\n UserNavigationEntry,\n} from \"@prose-reader/core\"\nimport {\n cfiToXPointer,\n parseXPointer,\n resolveXPointer,\n toCfiPosition,\n} from \"@prose-reader/koreader\"\nimport { Report } from \"@prose-reader/shared\"\nimport {\n BehaviorSubject,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n map,\n type Observable,\n of,\n shareReplay,\n switchMap,\n takeUntil,\n} from \"rxjs\"\n\nconst report = Report.namespace(`@prose-reader/enhancer-koreader`)\n\n/** A KOReader (crengine) xpointer, such as `/body/DocFragment[14]/body/div/p[3]/text().42`. */\nexport type XPointerNavigationTarget = { type: \"xpointer\"; value: string }\n\nexport type KoreaderEnhancerOutput<InheritTarget> = {\n navigation: {\n navigate: (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => void\n /** Goes to the place an xpointer names. Never animates. */\n goToXPointer: (xpointer: string) => void\n /**\n * The reading position as an xpointer, the value a KOReader sync client\n * pushes. It only emits pointers as exact as the reading position.\n */\n readingPositionXPointer$: Observable<string>\n }\n}\n\nconst isXPointerTarget = (target: {\n type: string\n}): target is XPointerNavigationTarget => target.type === \"xpointer\"\n\ntype XPointerNavigation = {\n xpointer: string\n target: NavigationTarget<\"selector\">\n}\n\n/**\n * Lets a reader navigate to KOReader xpointers, and reports its reading\n * position as one.\n */\nexport const koreaderEnhancer =\n <\n InheritOptions,\n InheritTarget extends { type: string },\n InheritOutput extends EnhancerOutput<RootEnhancer>,\n >(\n next: (options: InheritOptions) => InheritOutput & {\n navigation: {\n navigate: (to: UserNavigationEntry<InheritTarget>) => void\n }\n },\n ) =>\n (\n options: InheritOptions,\n ): InheritOutput & KoreaderEnhancerOutput<InheritTarget> => {\n const reader = next(options)\n const lastXPointerNavigation = new BehaviorSubject<\n XPointerNavigation | undefined\n >(undefined)\n\n /**\n * The pointer names its spine item, which is known at once, and a place\n * in its document, found once the document is loaded.\n */\n const xpointerToNavigationTarget = (\n xpointer: string,\n ): NavigationTarget<\"selector\"> | undefined => {\n const parsed = parseXPointer(xpointer)\n\n if (!parsed || !reader.spineItemsManager.get(parsed.spineItemIndex))\n return undefined\n\n return {\n type: \"selector\",\n value: {\n spineItem: parsed.spineItemIndex,\n find: (document) => {\n const position = resolveXPointer(parsed, document)\n\n return position && toCfiPosition(position)\n },\n },\n }\n }\n\n const navigate = (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => {\n const { target } = to\n\n if (!isXPointerTarget(target))\n return reader.navigation.navigate({ ...to, target })\n\n const selector = xpointerToNavigationTarget(target.value)\n\n if (!selector) {\n report.warn(`Ignore navigation to ${target.value}, not in the book`)\n\n return\n }\n\n // Before navigating, so `readingPositionXPointer$` never reports what is\n // shown while its chapter loads.\n lastXPointerNavigation.next({ xpointer: target.value, target: selector })\n reader.navigation.navigate({ ...to, target: selector })\n }\n\n const getLoadedSpineItemDocument = (spineItemIndex: number) => {\n const spineItem = reader.spineItemsManager.get(spineItemIndex)\n const document = spineItem?.value.isLoaded\n ? spineItem.renderer.getDocumentFrame()?.contentDocument\n : undefined\n\n return spineItem && document\n ? { document, id: spineItem.item.id }\n : undefined\n }\n\n /**\n * The xpointer of a cfi. An item start converts at once; a place in the\n * text once its document is loaded, rather than as the item start in the\n * meantime, which would overwrite a better position on a sync server.\n */\n const readingPositionToXPointer = (cfi: string): Observable<string> => {\n if (reader.cfi.isRootCfi(cfi)) {\n const xpointer = cfiToXPointer(cfi, getLoadedSpineItemDocument)\n\n return xpointer === undefined ? EMPTY : of(xpointer)\n }\n\n const spineItem = reader.cfi.getSpineItemFromCfi(cfi)\n\n if (!spineItem) return EMPTY\n\n return spineItem.watch(\"isLoaded\").pipe(\n filter(Boolean),\n first(),\n map(() => cfiToXPointer(cfi, getLoadedSpineItemDocument)),\n filter((xpointer) => xpointer !== undefined),\n )\n }\n\n const readingPositionXPointer$ = combineLatest([\n reader.navigation.readingPosition$,\n lastXPointerNavigation,\n // Every navigation, so one away from the xpointer is seen even when the\n // reading position stays the same.\n reader.navigation.navigation$,\n ]).pipe(\n switchMap(([cfi, xpointerNavigation]) => {\n /**\n * On its way to an xpointer whose chapter is loading, the reading\n * position is the chapter start. The xpointer itself is the better\n * answer until the chapter gives the real one.\n */\n const isNavigatingToXPointer =\n xpointerNavigation !== undefined &&\n reader.navigation.getNavigation().target ===\n xpointerNavigation.target &&\n reader.cfi.isRootCfi(cfi)\n\n return isNavigatingToXPointer\n ? of(xpointerNavigation.xpointer)\n : readingPositionToXPointer(cfi)\n }),\n distinctUntilChanged(),\n takeUntil(reader.$.destroy$),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n ...reader,\n navigation: {\n ...reader.navigation,\n navigate,\n goToXPointer: (xpointer) =>\n navigate({\n target: { type: \"xpointer\", value: xpointer },\n animation: false,\n }),\n readingPositionXPointer$,\n },\n }\n }\n"],"mappings":"+JA4BA,IAAM,EAAS,EAAA,OAAO,UAAU,iCAAiC,EAoB3D,EAAoB,GAEgB,EAAO,OAAS,WAW7C,EAMT,GAOA,GAC0D,CAC1D,IAAM,EAAS,EAAK,CAAO,EACrB,EAAyB,IAAI,EAAA,gBAEjC,IAAA,EAAS,EAML,EACJ,GAC6C,CAC7C,IAAM,GAAA,EAAS,EAAA,cAAA,CAAc,CAAQ,EAEjC,GAAC,GAAW,EAAO,kBAAkB,IAAI,EAAO,cAAc,EAGlE,MAAO,CACL,KAAM,WACN,MAAO,CACL,UAAW,EAAO,eAClB,KAAO,GAAa,CAClB,IAAM,GAAA,EAAW,EAAA,gBAAA,CAAgB,EAAQ,CAAQ,EAEjD,OAAO,IAAA,EAAY,EAAA,cAAA,CAAc,CAAQ,CAC3C,CACF,CACF,CACF,EAEM,EACJ,GACG,CACH,GAAM,CAAE,UAAW,EAEnB,GAAI,CAAC,EAAiB,CAAM,EAC1B,OAAO,EAAO,WAAW,SAAS,CAAE,GAAG,EAAI,QAAO,CAAC,EAErD,IAAM,EAAW,EAA2B,EAAO,KAAK,EAExD,GAAI,CAAC,EAAU,CACb,EAAO,KAAK,wBAAwB,EAAO,MAAM,kBAAkB,EAEnE,MACF,CAIA,EAAuB,KAAK,CAAE,SAAU,EAAO,MAAO,OAAQ,CAAS,CAAC,EACxE,EAAO,WAAW,SAAS,CAAE,GAAG,EAAI,OAAQ,CAAS,CAAC,CACxD,EAEM,EAA8B,GAA2B,CAC7D,IAAM,EAAY,EAAO,kBAAkB,IAAI,CAAc,EACvD,EAAW,GAAW,MAAM,SAC9B,EAAU,SAAS,iBAAiB,CAAC,EAAE,gBACvC,IAAA,GAEJ,OAAO,GAAa,EAChB,CAAE,WAAU,GAAI,EAAU,KAAK,EAAG,EAClC,IAAA,EACN,EAOM,EAA6B,GAAoC,CACrE,GAAI,EAAO,IAAI,UAAU,CAAG,EAAG,CAC7B,IAAM,GAAA,EAAW,EAAA,cAAA,CAAc,EAAK,CAA0B,EAE9D,OAAO,IAAa,IAAA,GAAY,EAAA,OAAA,EAAQ,EAAA,GAAA,CAAG,CAAQ,CACrD,CAEA,IAAM,EAAY,EAAO,IAAI,oBAAoB,CAAG,EAIpD,OAFK,EAEE,EAAU,MAAM,UAAU,CAAC,CAAC,MAAA,EACjC,EAAA,OAAA,CAAO,OAAO,GAAA,EACd,EAAA,MAAA,CAAM,GAAA,EACN,EAAA,IAAA,MAAA,EAAU,EAAA,cAAA,CAAc,EAAK,CAA0B,CAAC,GAAA,EACxD,EAAA,OAAA,CAAQ,GAAa,IAAa,IAAA,EAAS,CAC7C,EAPuB,EAAA,KAQzB,EAEM,GAAA,EAA2B,EAAA,cAAA,CAAc,CAC7C,EAAO,WAAW,iBAClB,EAGA,EAAO,WAAW,WACpB,CAAC,CAAC,CAAC,MAAA,EACD,EAAA,UAAA,EAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/koreaderEnhancer.ts"],"sourcesContent":["import type {\n EnhancerOutput,\n NavigationTarget,\n RootEnhancer,\n UserNavigationEntry,\n} from \"@prose-reader/core\"\nimport {\n cfiToXPointer,\n parseXPointer,\n resolveXPointer,\n toCfiPosition,\n} from \"@prose-reader/koreader\"\nimport { Report } from \"@prose-reader/shared\"\nimport {\n BehaviorSubject,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n map,\n type Observable,\n of,\n shareReplay,\n switchMap,\n takeUntil,\n} from \"rxjs\"\n\nconst report = Report.namespace(`@prose-reader/enhancer-koreader`)\n\n/** A KOReader (crengine) xpointer, such as `/body/DocFragment[14]/body/div/p[3]/text().42`. */\nexport type XPointerNavigationTarget = { type: \"xpointer\"; value: string }\n\nexport type KoreaderEnhancerOutput<InheritTarget> = {\n navigation: {\n navigate: (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => void\n /** Goes to the place an xpointer names. Never animates. */\n goToXPointer: (xpointer: string) => void\n /**\n * The reading position as an xpointer, the value a KOReader sync client\n * pushes. It only emits pointers as exact as the reading position.\n */\n readingPositionXPointer$: Observable<string>\n }\n}\n\nconst isXPointerTarget = (target: {\n type: string\n}): target is XPointerNavigationTarget => target.type === \"xpointer\"\n\ntype XPointerNavigation = {\n xpointer: string\n target: NavigationTarget<\"selector\">\n}\n\n/**\n * Lets a reader navigate to KOReader xpointers, and reports its reading\n * position as one.\n */\nexport const koreaderEnhancer =\n <\n InheritOptions,\n InheritTarget extends { type: string },\n InheritOutput extends EnhancerOutput<RootEnhancer>,\n >(\n next: (options: InheritOptions) => InheritOutput & {\n navigation: {\n navigate: (to: UserNavigationEntry<InheritTarget>) => void\n }\n },\n ) =>\n (\n options: InheritOptions,\n ): InheritOutput & KoreaderEnhancerOutput<InheritTarget> => {\n const reader = next(options)\n const lastXPointerNavigation = new BehaviorSubject<\n XPointerNavigation | undefined\n >(undefined)\n\n /**\n * The pointer names its spine item, which is known at once, and a place\n * in its document, found once the document is loaded.\n */\n const xpointerToNavigationTarget = (\n xpointer: string,\n ): NavigationTarget<\"selector\"> | undefined => {\n const parsed = parseXPointer(xpointer)\n\n if (!parsed || !reader.spineItemsManager.get(parsed.spineItemIndex))\n return undefined\n\n return {\n type: \"selector\",\n value: {\n spineItem: parsed.spineItemIndex,\n find: (document) => {\n const position = resolveXPointer(parsed, document)\n\n return position && toCfiPosition(position)\n },\n },\n }\n }\n\n const navigate = (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => {\n const { target } = to\n\n if (!isXPointerTarget(target))\n return reader.navigation.navigate({ ...to, target })\n\n const selector = xpointerToNavigationTarget(target.value)\n\n if (!selector) {\n report.warn(`Ignore navigation to ${target.value}, not in the book`)\n\n return\n }\n\n // Before navigating, so `readingPositionXPointer$` never reports what is\n // shown while its chapter loads.\n lastXPointerNavigation.next({ xpointer: target.value, target: selector })\n reader.navigation.navigate({ ...to, target: selector })\n }\n\n const getLoadedSpineItemDocument = (spineItemIndex: number) => {\n const spineItem = reader.spineItemsManager.get(spineItemIndex)\n const document = spineItem?.value.isLoaded\n ? spineItem.renderer.getDocumentFrame()?.contentDocument\n : undefined\n\n return spineItem && document\n ? { document, id: spineItem.item.id }\n : undefined\n }\n\n /**\n * The xpointer of a cfi. An item start converts at once; a place in the\n * text once its document is loaded, rather than as the item start in the\n * meantime, which would overwrite a better position on a sync server.\n */\n const readingPositionToXPointer = (cfi: string): Observable<string> => {\n if (reader.cfi.isRootCfi(cfi)) {\n const xpointer = cfiToXPointer(cfi, getLoadedSpineItemDocument)\n\n return xpointer === undefined ? EMPTY : of(xpointer)\n }\n\n const spineItem = reader.cfi.getSpineItemFromCfi(cfi)\n\n if (!spineItem) return EMPTY\n\n return spineItem.watch(\"isLoaded\").pipe(\n filter(Boolean),\n first(),\n map(() => cfiToXPointer(cfi, getLoadedSpineItemDocument)),\n filter((xpointer) => xpointer !== undefined),\n )\n }\n\n const readingPositionXPointer$ = combineLatest([\n reader.navigation.readingPosition$,\n lastXPointerNavigation,\n // Every navigation, so one away from the xpointer is seen even when the\n // reading position stays the same.\n reader.navigation.navigation$,\n ]).pipe(\n switchMap(([{ cfi }, xpointerNavigation]) => {\n /**\n * On its way to an xpointer whose chapter is loading, the reading\n * position is the chapter start. The xpointer itself is the better\n * answer until the chapter gives the real one.\n */\n const isNavigatingToXPointer =\n xpointerNavigation !== undefined &&\n reader.navigation.getNavigation().target ===\n xpointerNavigation.target &&\n reader.cfi.isRootCfi(cfi)\n\n return isNavigatingToXPointer\n ? of(xpointerNavigation.xpointer)\n : readingPositionToXPointer(cfi)\n }),\n distinctUntilChanged(),\n takeUntil(reader.$.destroy$),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n ...reader,\n navigation: {\n ...reader.navigation,\n navigate,\n goToXPointer: (xpointer) =>\n navigate({\n target: { type: \"xpointer\", value: xpointer },\n animation: false,\n }),\n readingPositionXPointer$,\n },\n }\n }\n"],"mappings":"+JA4BA,IAAM,EAAS,EAAA,OAAO,UAAU,iCAAiC,EAoB3D,EAAoB,GAEgB,EAAO,OAAS,WAW7C,EAMT,GAOA,GAC0D,CAC1D,IAAM,EAAS,EAAK,CAAO,EACrB,EAAyB,IAAI,EAAA,gBAEjC,IAAA,EAAS,EAML,EACJ,GAC6C,CAC7C,IAAM,GAAA,EAAS,EAAA,cAAA,CAAc,CAAQ,EAEjC,GAAC,GAAW,EAAO,kBAAkB,IAAI,EAAO,cAAc,EAGlE,MAAO,CACL,KAAM,WACN,MAAO,CACL,UAAW,EAAO,eAClB,KAAO,GAAa,CAClB,IAAM,GAAA,EAAW,EAAA,gBAAA,CAAgB,EAAQ,CAAQ,EAEjD,OAAO,IAAA,EAAY,EAAA,cAAA,CAAc,CAAQ,CAC3C,CACF,CACF,CACF,EAEM,EACJ,GACG,CACH,GAAM,CAAE,UAAW,EAEnB,GAAI,CAAC,EAAiB,CAAM,EAC1B,OAAO,EAAO,WAAW,SAAS,CAAE,GAAG,EAAI,QAAO,CAAC,EAErD,IAAM,EAAW,EAA2B,EAAO,KAAK,EAExD,GAAI,CAAC,EAAU,CACb,EAAO,KAAK,wBAAwB,EAAO,MAAM,kBAAkB,EAEnE,MACF,CAIA,EAAuB,KAAK,CAAE,SAAU,EAAO,MAAO,OAAQ,CAAS,CAAC,EACxE,EAAO,WAAW,SAAS,CAAE,GAAG,EAAI,OAAQ,CAAS,CAAC,CACxD,EAEM,EAA8B,GAA2B,CAC7D,IAAM,EAAY,EAAO,kBAAkB,IAAI,CAAc,EACvD,EAAW,GAAW,MAAM,SAC9B,EAAU,SAAS,iBAAiB,CAAC,EAAE,gBACvC,IAAA,GAEJ,OAAO,GAAa,EAChB,CAAE,WAAU,GAAI,EAAU,KAAK,EAAG,EAClC,IAAA,EACN,EAOM,EAA6B,GAAoC,CACrE,GAAI,EAAO,IAAI,UAAU,CAAG,EAAG,CAC7B,IAAM,GAAA,EAAW,EAAA,cAAA,CAAc,EAAK,CAA0B,EAE9D,OAAO,IAAa,IAAA,GAAY,EAAA,OAAA,EAAQ,EAAA,GAAA,CAAG,CAAQ,CACrD,CAEA,IAAM,EAAY,EAAO,IAAI,oBAAoB,CAAG,EAIpD,OAFK,EAEE,EAAU,MAAM,UAAU,CAAC,CAAC,MAAA,EACjC,EAAA,OAAA,CAAO,OAAO,GAAA,EACd,EAAA,MAAA,CAAM,GAAA,EACN,EAAA,IAAA,MAAA,EAAU,EAAA,cAAA,CAAc,EAAK,CAA0B,CAAC,GAAA,EACxD,EAAA,OAAA,CAAQ,GAAa,IAAa,IAAA,EAAS,CAC7C,EAPuB,EAAA,KAQzB,EAEM,GAAA,EAA2B,EAAA,cAAA,CAAc,CAC7C,EAAO,WAAW,iBAClB,EAGA,EAAO,WAAW,WACpB,CAAC,CAAC,CAAC,MAAA,EACD,EAAA,UAAA,EAAW,CAAC,CAAE,OAAO,KAOjB,IAAuB,IAAA,IACvB,EAAO,WAAW,cAAc,CAAC,CAAC,SAChC,EAAmB,QACrB,EAAO,IAAI,UAAU,CAAG,GAAA,EAGtB,EAAA,GAAA,CAAG,EAAmB,QAAQ,EAC9B,EAA0B,CAAG,CAClC,GAAA,EACD,EAAA,qBAAA,CAAqB,GAAA,EACrB,EAAA,UAAA,CAAU,EAAO,EAAE,QAAQ,GAAA,EAC3B,EAAA,YAAA,CAAY,CAAE,WAAY,EAAG,SAAU,EAAK,CAAC,CAC/C,EAEA,MAAO,CACL,GAAG,EACH,WAAY,CACV,GAAG,EAAO,WACV,WACA,aAAe,GACb,EAAS,CACP,OAAQ,CAAE,KAAM,WAAY,MAAO,CAAS,EAC5C,UAAW,EACb,CAAC,EACH,0BACF,CACF,CACF"}
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ var g = i.namespace("@prose-reader/enhancer-koreader"), _ = (e) => e.type === "x
|
|
|
50
50
|
y.navigation.readingPosition$,
|
|
51
51
|
b,
|
|
52
52
|
y.navigation.navigation$
|
|
53
|
-
]).pipe(m(([e, t]) => t !== void 0 && y.navigation.getNavigation().target === t.target && y.cfi.isRootCfi(e) ? f(t.xpointer) : w(e)), c(), h(y.$.destroy$), p({
|
|
53
|
+
]).pipe(m(([{ cfi: e }, t]) => t !== void 0 && y.navigation.getNavigation().target === t.target && y.cfi.isRootCfi(e) ? f(t.xpointer) : w(e)), c(), h(y.$.destroy$), p({
|
|
54
54
|
bufferSize: 1,
|
|
55
55
|
refCount: !0
|
|
56
56
|
}));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/koreaderEnhancer.ts"],"sourcesContent":["import type {\n EnhancerOutput,\n NavigationTarget,\n RootEnhancer,\n UserNavigationEntry,\n} from \"@prose-reader/core\"\nimport {\n cfiToXPointer,\n parseXPointer,\n resolveXPointer,\n toCfiPosition,\n} from \"@prose-reader/koreader\"\nimport { Report } from \"@prose-reader/shared\"\nimport {\n BehaviorSubject,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n map,\n type Observable,\n of,\n shareReplay,\n switchMap,\n takeUntil,\n} from \"rxjs\"\n\nconst report = Report.namespace(`@prose-reader/enhancer-koreader`)\n\n/** A KOReader (crengine) xpointer, such as `/body/DocFragment[14]/body/div/p[3]/text().42`. */\nexport type XPointerNavigationTarget = { type: \"xpointer\"; value: string }\n\nexport type KoreaderEnhancerOutput<InheritTarget> = {\n navigation: {\n navigate: (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => void\n /** Goes to the place an xpointer names. Never animates. */\n goToXPointer: (xpointer: string) => void\n /**\n * The reading position as an xpointer, the value a KOReader sync client\n * pushes. It only emits pointers as exact as the reading position.\n */\n readingPositionXPointer$: Observable<string>\n }\n}\n\nconst isXPointerTarget = (target: {\n type: string\n}): target is XPointerNavigationTarget => target.type === \"xpointer\"\n\ntype XPointerNavigation = {\n xpointer: string\n target: NavigationTarget<\"selector\">\n}\n\n/**\n * Lets a reader navigate to KOReader xpointers, and reports its reading\n * position as one.\n */\nexport const koreaderEnhancer =\n <\n InheritOptions,\n InheritTarget extends { type: string },\n InheritOutput extends EnhancerOutput<RootEnhancer>,\n >(\n next: (options: InheritOptions) => InheritOutput & {\n navigation: {\n navigate: (to: UserNavigationEntry<InheritTarget>) => void\n }\n },\n ) =>\n (\n options: InheritOptions,\n ): InheritOutput & KoreaderEnhancerOutput<InheritTarget> => {\n const reader = next(options)\n const lastXPointerNavigation = new BehaviorSubject<\n XPointerNavigation | undefined\n >(undefined)\n\n /**\n * The pointer names its spine item, which is known at once, and a place\n * in its document, found once the document is loaded.\n */\n const xpointerToNavigationTarget = (\n xpointer: string,\n ): NavigationTarget<\"selector\"> | undefined => {\n const parsed = parseXPointer(xpointer)\n\n if (!parsed || !reader.spineItemsManager.get(parsed.spineItemIndex))\n return undefined\n\n return {\n type: \"selector\",\n value: {\n spineItem: parsed.spineItemIndex,\n find: (document) => {\n const position = resolveXPointer(parsed, document)\n\n return position && toCfiPosition(position)\n },\n },\n }\n }\n\n const navigate = (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => {\n const { target } = to\n\n if (!isXPointerTarget(target))\n return reader.navigation.navigate({ ...to, target })\n\n const selector = xpointerToNavigationTarget(target.value)\n\n if (!selector) {\n report.warn(`Ignore navigation to ${target.value}, not in the book`)\n\n return\n }\n\n // Before navigating, so `readingPositionXPointer$` never reports what is\n // shown while its chapter loads.\n lastXPointerNavigation.next({ xpointer: target.value, target: selector })\n reader.navigation.navigate({ ...to, target: selector })\n }\n\n const getLoadedSpineItemDocument = (spineItemIndex: number) => {\n const spineItem = reader.spineItemsManager.get(spineItemIndex)\n const document = spineItem?.value.isLoaded\n ? spineItem.renderer.getDocumentFrame()?.contentDocument\n : undefined\n\n return spineItem && document\n ? { document, id: spineItem.item.id }\n : undefined\n }\n\n /**\n * The xpointer of a cfi. An item start converts at once; a place in the\n * text once its document is loaded, rather than as the item start in the\n * meantime, which would overwrite a better position on a sync server.\n */\n const readingPositionToXPointer = (cfi: string): Observable<string> => {\n if (reader.cfi.isRootCfi(cfi)) {\n const xpointer = cfiToXPointer(cfi, getLoadedSpineItemDocument)\n\n return xpointer === undefined ? EMPTY : of(xpointer)\n }\n\n const spineItem = reader.cfi.getSpineItemFromCfi(cfi)\n\n if (!spineItem) return EMPTY\n\n return spineItem.watch(\"isLoaded\").pipe(\n filter(Boolean),\n first(),\n map(() => cfiToXPointer(cfi, getLoadedSpineItemDocument)),\n filter((xpointer) => xpointer !== undefined),\n )\n }\n\n const readingPositionXPointer$ = combineLatest([\n reader.navigation.readingPosition$,\n lastXPointerNavigation,\n // Every navigation, so one away from the xpointer is seen even when the\n // reading position stays the same.\n reader.navigation.navigation$,\n ]).pipe(\n switchMap(([cfi, xpointerNavigation]) => {\n /**\n * On its way to an xpointer whose chapter is loading, the reading\n * position is the chapter start. The xpointer itself is the better\n * answer until the chapter gives the real one.\n */\n const isNavigatingToXPointer =\n xpointerNavigation !== undefined &&\n reader.navigation.getNavigation().target ===\n xpointerNavigation.target &&\n reader.cfi.isRootCfi(cfi)\n\n return isNavigatingToXPointer\n ? of(xpointerNavigation.xpointer)\n : readingPositionToXPointer(cfi)\n }),\n distinctUntilChanged(),\n takeUntil(reader.$.destroy$),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n ...reader,\n navigation: {\n ...reader.navigation,\n navigate,\n goToXPointer: (xpointer) =>\n navigate({\n target: { type: \"xpointer\", value: xpointer },\n animation: false,\n }),\n readingPositionXPointer$,\n },\n }\n }\n"],"mappings":";;;;AA4BA,IAAM,IAAS,EAAO,UAAU,iCAAiC,GAoB3D,KAAoB,MAEgB,EAAO,SAAS,YAW7C,KAMT,OAOA,MAC0D;CAC1D,IAAM,IAAS,EAAK,CAAO,GACrB,IAAyB,IAAI,EAEjC,KAAA,CAAS,GAML,KACJ,MAC6C;EAC7C,IAAM,IAAS,EAAc,CAAQ;EAEjC,IAAC,KAAW,EAAO,kBAAkB,IAAI,EAAO,cAAc,GAGlE,OAAO;GACL,MAAM;GACN,OAAO;IACL,WAAW,EAAO;IAClB,OAAO,MAAa;KAClB,IAAM,IAAW,EAAgB,GAAQ,CAAQ;KAEjD,OAAO,KAAY,EAAc,CAAQ;IAC3C;GACF;EACF;CACF,GAEM,KACJ,MACG;EACH,IAAM,EAAE,cAAW;EAEnB,IAAI,CAAC,EAAiB,CAAM,GAC1B,OAAO,EAAO,WAAW,SAAS;GAAE,GAAG;GAAI;EAAO,CAAC;EAErD,IAAM,IAAW,EAA2B,EAAO,KAAK;EAExD,IAAI,CAAC,GAAU;GACb,EAAO,KAAK,wBAAwB,EAAO,MAAM,kBAAkB;GAEnE;EACF;EAKA,AADA,EAAuB,KAAK;GAAE,UAAU,EAAO;GAAO,QAAQ;EAAS,CAAC,GACxE,EAAO,WAAW,SAAS;GAAE,GAAG;GAAI,QAAQ;EAAS,CAAC;CACxD,GAEM,KAA8B,MAA2B;EAC7D,IAAM,IAAY,EAAO,kBAAkB,IAAI,CAAc,GACvD,IAAW,GAAW,MAAM,WAC9B,EAAU,SAAS,iBAAiB,CAAC,EAAE,kBACvC,KAAA;EAEJ,OAAO,KAAa,IAChB;GAAE;GAAU,IAAI,EAAU,KAAK;EAAG,IAClC,KAAA;CACN,GAOM,KAA6B,MAAoC;EACrE,IAAI,EAAO,IAAI,UAAU,CAAG,GAAG;GAC7B,IAAM,IAAW,EAAc,GAAK,CAA0B;GAE9D,OAAO,MAAa,KAAA,IAAY,IAAQ,EAAG,CAAQ;EACrD;EAEA,IAAM,IAAY,EAAO,IAAI,oBAAoB,CAAG;EAIpD,OAFK,IAEE,EAAU,MAAM,UAAU,CAAC,CAAC,KACjC,EAAO,OAAO,GACd,EAAM,GACN,QAAU,EAAc,GAAK,CAA0B,CAAC,GACxD,GAAQ,MAAa,MAAa,KAAA,CAAS,CAC7C,IAPuB;CAQzB,GAEM,IAA2B,EAAc;EAC7C,EAAO,WAAW;EAClB;EAGA,EAAO,WAAW;CACpB,CAAC,CAAC,CAAC,KACD,GAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/koreaderEnhancer.ts"],"sourcesContent":["import type {\n EnhancerOutput,\n NavigationTarget,\n RootEnhancer,\n UserNavigationEntry,\n} from \"@prose-reader/core\"\nimport {\n cfiToXPointer,\n parseXPointer,\n resolveXPointer,\n toCfiPosition,\n} from \"@prose-reader/koreader\"\nimport { Report } from \"@prose-reader/shared\"\nimport {\n BehaviorSubject,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n map,\n type Observable,\n of,\n shareReplay,\n switchMap,\n takeUntil,\n} from \"rxjs\"\n\nconst report = Report.namespace(`@prose-reader/enhancer-koreader`)\n\n/** A KOReader (crengine) xpointer, such as `/body/DocFragment[14]/body/div/p[3]/text().42`. */\nexport type XPointerNavigationTarget = { type: \"xpointer\"; value: string }\n\nexport type KoreaderEnhancerOutput<InheritTarget> = {\n navigation: {\n navigate: (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => void\n /** Goes to the place an xpointer names. Never animates. */\n goToXPointer: (xpointer: string) => void\n /**\n * The reading position as an xpointer, the value a KOReader sync client\n * pushes. It only emits pointers as exact as the reading position.\n */\n readingPositionXPointer$: Observable<string>\n }\n}\n\nconst isXPointerTarget = (target: {\n type: string\n}): target is XPointerNavigationTarget => target.type === \"xpointer\"\n\ntype XPointerNavigation = {\n xpointer: string\n target: NavigationTarget<\"selector\">\n}\n\n/**\n * Lets a reader navigate to KOReader xpointers, and reports its reading\n * position as one.\n */\nexport const koreaderEnhancer =\n <\n InheritOptions,\n InheritTarget extends { type: string },\n InheritOutput extends EnhancerOutput<RootEnhancer>,\n >(\n next: (options: InheritOptions) => InheritOutput & {\n navigation: {\n navigate: (to: UserNavigationEntry<InheritTarget>) => void\n }\n },\n ) =>\n (\n options: InheritOptions,\n ): InheritOutput & KoreaderEnhancerOutput<InheritTarget> => {\n const reader = next(options)\n const lastXPointerNavigation = new BehaviorSubject<\n XPointerNavigation | undefined\n >(undefined)\n\n /**\n * The pointer names its spine item, which is known at once, and a place\n * in its document, found once the document is loaded.\n */\n const xpointerToNavigationTarget = (\n xpointer: string,\n ): NavigationTarget<\"selector\"> | undefined => {\n const parsed = parseXPointer(xpointer)\n\n if (!parsed || !reader.spineItemsManager.get(parsed.spineItemIndex))\n return undefined\n\n return {\n type: \"selector\",\n value: {\n spineItem: parsed.spineItemIndex,\n find: (document) => {\n const position = resolveXPointer(parsed, document)\n\n return position && toCfiPosition(position)\n },\n },\n }\n }\n\n const navigate = (\n to: UserNavigationEntry<InheritTarget | XPointerNavigationTarget>,\n ) => {\n const { target } = to\n\n if (!isXPointerTarget(target))\n return reader.navigation.navigate({ ...to, target })\n\n const selector = xpointerToNavigationTarget(target.value)\n\n if (!selector) {\n report.warn(`Ignore navigation to ${target.value}, not in the book`)\n\n return\n }\n\n // Before navigating, so `readingPositionXPointer$` never reports what is\n // shown while its chapter loads.\n lastXPointerNavigation.next({ xpointer: target.value, target: selector })\n reader.navigation.navigate({ ...to, target: selector })\n }\n\n const getLoadedSpineItemDocument = (spineItemIndex: number) => {\n const spineItem = reader.spineItemsManager.get(spineItemIndex)\n const document = spineItem?.value.isLoaded\n ? spineItem.renderer.getDocumentFrame()?.contentDocument\n : undefined\n\n return spineItem && document\n ? { document, id: spineItem.item.id }\n : undefined\n }\n\n /**\n * The xpointer of a cfi. An item start converts at once; a place in the\n * text once its document is loaded, rather than as the item start in the\n * meantime, which would overwrite a better position on a sync server.\n */\n const readingPositionToXPointer = (cfi: string): Observable<string> => {\n if (reader.cfi.isRootCfi(cfi)) {\n const xpointer = cfiToXPointer(cfi, getLoadedSpineItemDocument)\n\n return xpointer === undefined ? EMPTY : of(xpointer)\n }\n\n const spineItem = reader.cfi.getSpineItemFromCfi(cfi)\n\n if (!spineItem) return EMPTY\n\n return spineItem.watch(\"isLoaded\").pipe(\n filter(Boolean),\n first(),\n map(() => cfiToXPointer(cfi, getLoadedSpineItemDocument)),\n filter((xpointer) => xpointer !== undefined),\n )\n }\n\n const readingPositionXPointer$ = combineLatest([\n reader.navigation.readingPosition$,\n lastXPointerNavigation,\n // Every navigation, so one away from the xpointer is seen even when the\n // reading position stays the same.\n reader.navigation.navigation$,\n ]).pipe(\n switchMap(([{ cfi }, xpointerNavigation]) => {\n /**\n * On its way to an xpointer whose chapter is loading, the reading\n * position is the chapter start. The xpointer itself is the better\n * answer until the chapter gives the real one.\n */\n const isNavigatingToXPointer =\n xpointerNavigation !== undefined &&\n reader.navigation.getNavigation().target ===\n xpointerNavigation.target &&\n reader.cfi.isRootCfi(cfi)\n\n return isNavigatingToXPointer\n ? of(xpointerNavigation.xpointer)\n : readingPositionToXPointer(cfi)\n }),\n distinctUntilChanged(),\n takeUntil(reader.$.destroy$),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n ...reader,\n navigation: {\n ...reader.navigation,\n navigate,\n goToXPointer: (xpointer) =>\n navigate({\n target: { type: \"xpointer\", value: xpointer },\n animation: false,\n }),\n readingPositionXPointer$,\n },\n }\n }\n"],"mappings":";;;;AA4BA,IAAM,IAAS,EAAO,UAAU,iCAAiC,GAoB3D,KAAoB,MAEgB,EAAO,SAAS,YAW7C,KAMT,OAOA,MAC0D;CAC1D,IAAM,IAAS,EAAK,CAAO,GACrB,IAAyB,IAAI,EAEjC,KAAA,CAAS,GAML,KACJ,MAC6C;EAC7C,IAAM,IAAS,EAAc,CAAQ;EAEjC,IAAC,KAAW,EAAO,kBAAkB,IAAI,EAAO,cAAc,GAGlE,OAAO;GACL,MAAM;GACN,OAAO;IACL,WAAW,EAAO;IAClB,OAAO,MAAa;KAClB,IAAM,IAAW,EAAgB,GAAQ,CAAQ;KAEjD,OAAO,KAAY,EAAc,CAAQ;IAC3C;GACF;EACF;CACF,GAEM,KACJ,MACG;EACH,IAAM,EAAE,cAAW;EAEnB,IAAI,CAAC,EAAiB,CAAM,GAC1B,OAAO,EAAO,WAAW,SAAS;GAAE,GAAG;GAAI;EAAO,CAAC;EAErD,IAAM,IAAW,EAA2B,EAAO,KAAK;EAExD,IAAI,CAAC,GAAU;GACb,EAAO,KAAK,wBAAwB,EAAO,MAAM,kBAAkB;GAEnE;EACF;EAKA,AADA,EAAuB,KAAK;GAAE,UAAU,EAAO;GAAO,QAAQ;EAAS,CAAC,GACxE,EAAO,WAAW,SAAS;GAAE,GAAG;GAAI,QAAQ;EAAS,CAAC;CACxD,GAEM,KAA8B,MAA2B;EAC7D,IAAM,IAAY,EAAO,kBAAkB,IAAI,CAAc,GACvD,IAAW,GAAW,MAAM,WAC9B,EAAU,SAAS,iBAAiB,CAAC,EAAE,kBACvC,KAAA;EAEJ,OAAO,KAAa,IAChB;GAAE;GAAU,IAAI,EAAU,KAAK;EAAG,IAClC,KAAA;CACN,GAOM,KAA6B,MAAoC;EACrE,IAAI,EAAO,IAAI,UAAU,CAAG,GAAG;GAC7B,IAAM,IAAW,EAAc,GAAK,CAA0B;GAE9D,OAAO,MAAa,KAAA,IAAY,IAAQ,EAAG,CAAQ;EACrD;EAEA,IAAM,IAAY,EAAO,IAAI,oBAAoB,CAAG;EAIpD,OAFK,IAEE,EAAU,MAAM,UAAU,CAAC,CAAC,KACjC,EAAO,OAAO,GACd,EAAM,GACN,QAAU,EAAc,GAAK,CAA0B,CAAC,GACxD,GAAQ,MAAa,MAAa,KAAA,CAAS,CAC7C,IAPuB;CAQzB,GAEM,IAA2B,EAAc;EAC7C,EAAO,WAAW;EAClB;EAGA,EAAO,WAAW;CACpB,CAAC,CAAC,CAAC,KACD,GAAW,CAAC,EAAE,UAAO,OAOjB,MAAuB,KAAA,KACvB,EAAO,WAAW,cAAc,CAAC,CAAC,WAChC,EAAmB,UACrB,EAAO,IAAI,UAAU,CAAG,IAGtB,EAAG,EAAmB,QAAQ,IAC9B,EAA0B,CAAG,CAClC,GACD,EAAqB,GACrB,EAAU,EAAO,EAAE,QAAQ,GAC3B,EAAY;EAAE,YAAY;EAAG,UAAU;CAAK,CAAC,CAC/C;CAEA,OAAO;EACL,GAAG;EACH,YAAY;GACV,GAAG,EAAO;GACV;GACA,eAAe,MACb,EAAS;IACP,QAAQ;KAAE,MAAM;KAAY,OAAO;IAAS;IAC5C,WAAW;GACb,CAAC;GACH;EACF;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prose-reader/enhancer-koreader",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"tsc": "tsc"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@prose-reader/koreader": "^
|
|
30
|
-
"@prose-reader/shared": "^
|
|
29
|
+
"@prose-reader/koreader": "^17.0.0",
|
|
30
|
+
"@prose-reader/shared": "^17.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@prose-reader/core": "^
|
|
33
|
+
"@prose-reader/core": "^17.0.0",
|
|
34
34
|
"rxjs": "^7.2.0"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "1802379abde5c84eb57406be55b7b5fc7c90fb09"
|
|
37
37
|
}
|