@prose-reader/enhancer-koreader 19.0.0 → 20.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 +3 -3
- 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)(([
|
|
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])=>{let{target:r}=o.navigation.getNavigation(),i=e.state===`standIn`;return t!==void 0&&r===t.target&&i?(0,n.of)(t.xpointer):i&&r.type===`cfi`&&!o.cfi.isRootCfi(r.value)?n.EMPTY:d(e.cfi)}),(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(([
|
|
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(([readingPosition, xpointerNavigation]) => {\n const { target } = reader.navigation.getNavigation()\n const isReadingPositionStandingIn = readingPosition.state === \"standIn\"\n\n /**\n * On its way to an xpointer, the reading position stands in at the\n * chapter start until the chapter gives the real one. The xpointer\n * itself is the better answer meanwhile.\n */\n const isNavigatingToXPointer =\n xpointerNavigation !== undefined &&\n target === xpointerNavigation.target &&\n isReadingPositionStandingIn\n\n if (isNavigatingToXPointer) return of(xpointerNavigation.xpointer)\n\n /**\n * On its way to a cfi naming a place, such as the one the book was\n * reopened at, the reading position stands in at the chapter start\n * until the chapter's document shows where the cfi leads. Pushed, it\n * would overwrite a better position on the server: the place, or the\n * page shown when the cfi names none, is reported once known.\n */\n const isStandingInForCfiPlace =\n isReadingPositionStandingIn &&\n target.type === \"cfi\" &&\n !reader.cfi.isRootCfi(target.value)\n\n return isStandingInForCfiPlace\n ? EMPTY\n : readingPositionToXPointer(readingPosition.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":"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"}
|
package/dist/index.js
CHANGED
|
@@ -50,9 +50,9 @@ 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(([
|
|
54
|
-
let { target:
|
|
55
|
-
return
|
|
53
|
+
]).pipe(m(([e, t]) => {
|
|
54
|
+
let { target: n } = y.navigation.getNavigation(), r = e.state === "standIn";
|
|
55
|
+
return t !== void 0 && n === t.target && r ? f(t.xpointer) : r && n.type === "cfi" && !y.cfi.isRootCfi(n.value) ? o : w(e.cfi);
|
|
56
56
|
}), c(), h(y.$.destroy$), p({
|
|
57
57
|
bufferSize: 1,
|
|
58
58
|
refCount: !0
|
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(([
|
|
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(([readingPosition, xpointerNavigation]) => {\n const { target } = reader.navigation.getNavigation()\n const isReadingPositionStandingIn = readingPosition.state === \"standIn\"\n\n /**\n * On its way to an xpointer, the reading position stands in at the\n * chapter start until the chapter gives the real one. The xpointer\n * itself is the better answer meanwhile.\n */\n const isNavigatingToXPointer =\n xpointerNavigation !== undefined &&\n target === xpointerNavigation.target &&\n isReadingPositionStandingIn\n\n if (isNavigatingToXPointer) return of(xpointerNavigation.xpointer)\n\n /**\n * On its way to a cfi naming a place, such as the one the book was\n * reopened at, the reading position stands in at the chapter start\n * until the chapter's document shows where the cfi leads. Pushed, it\n * would overwrite a better position on the server: the place, or the\n * page shown when the cfi names none, is reported once known.\n */\n const isStandingInForCfiPlace =\n isReadingPositionStandingIn &&\n target.type === \"cfi\" &&\n !reader.cfi.isRootCfi(target.value)\n\n return isStandingInForCfiPlace\n ? EMPTY\n : readingPositionToXPointer(readingPosition.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,GAAiB,OAAwB;EACnD,IAAM,EAAE,cAAW,EAAO,WAAW,cAAc,GAC7C,IAA8B,EAAgB,UAAU;EA0B9D,OAlBE,MAAuB,KAAA,KACvB,MAAW,EAAmB,UAC9B,IAEiC,EAAG,EAAmB,QAAQ,IAU/D,KACA,EAAO,SAAS,SAChB,CAAC,EAAO,IAAI,UAAU,EAAO,KAAK,IAGhC,IACA,EAA0B,EAAgB,GAAG;CACnD,CAAC,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": "20.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": "^20.0.0",
|
|
30
|
+
"@prose-reader/shared": "^20.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@prose-reader/core": "^
|
|
33
|
+
"@prose-reader/core": "^20.0.0",
|
|
34
34
|
"rxjs": "^7.2.0"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "4e67011dca495ab6efcc434c8e69cb8bcf1b6ff5"
|
|
37
37
|
}
|