@asteasolutions/epub-reader 1.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/.prettierrc.js +6 -0
- package/README.md +104 -0
- package/dist/array.d.ts +1 -0
- package/dist/array.js +19 -0
- package/dist/array.js.map +1 -0
- package/dist/audio-player.d.ts +28 -0
- package/dist/audio-player.js +77 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/book-loaders/ajax-unpacked.d.ts +12 -0
- package/dist/book-loaders/ajax-unpacked.js +46 -0
- package/dist/book-loaders/ajax-unpacked.js.map +1 -0
- package/dist/book-loaders/index.d.ts +13 -0
- package/dist/book-loaders/index.js +15 -0
- package/dist/book-loaders/index.js.map +1 -0
- package/dist/book-loaders/zip.d.ts +12 -0
- package/dist/book-loaders/zip.js +67 -0
- package/dist/book-loaders/zip.js.map +1 -0
- package/dist/book.d.ts +16 -0
- package/dist/book.js +60 -0
- package/dist/book.js.map +1 -0
- package/dist/content-controller/reflowable-content.d.ts +24 -0
- package/dist/content-controller/reflowable-content.js +115 -0
- package/dist/content-controller/reflowable-content.js.map +1 -0
- package/dist/content-controller.d.ts +10 -0
- package/dist/content-controller.js +2 -0
- package/dist/content-controller.js.map +1 -0
- package/dist/epub-types/container.d.ts +19 -0
- package/dist/epub-types/container.js +2 -0
- package/dist/epub-types/container.js.map +1 -0
- package/dist/epub-types/package/collection.d.ts +13 -0
- package/dist/epub-types/package/collection.js +2 -0
- package/dist/epub-types/package/collection.js.map +1 -0
- package/dist/epub-types/package/index.d.ts +18 -0
- package/dist/epub-types/package/index.js +2 -0
- package/dist/epub-types/package/index.js.map +1 -0
- package/dist/epub-types/package/manifest.d.ts +12 -0
- package/dist/epub-types/package/manifest.js +2 -0
- package/dist/epub-types/package/manifest.js.map +1 -0
- package/dist/epub-types/package/metadata.d.ts +44 -0
- package/dist/epub-types/package/metadata.js +2 -0
- package/dist/epub-types/package/metadata.js.map +1 -0
- package/dist/epub-types/package/spine.d.ts +11 -0
- package/dist/epub-types/package/spine.js +2 -0
- package/dist/epub-types/package/spine.js.map +1 -0
- package/dist/epub-types/smil.d.ts +97 -0
- package/dist/epub-types/smil.js +2 -0
- package/dist/epub-types/smil.js.map +1 -0
- package/dist/epub-types/xml.d.ts +9 -0
- package/dist/epub-types/xml.js +2 -0
- package/dist/epub-types/xml.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/media-overlay.d.ts +32 -0
- package/dist/media-overlay.js +177 -0
- package/dist/media-overlay.js.map +1 -0
- package/dist/package-parser/index.d.ts +27 -0
- package/dist/package-parser/index.js +72 -0
- package/dist/package-parser/index.js.map +1 -0
- package/dist/package-parser/nav-parser.d.ts +17 -0
- package/dist/package-parser/nav-parser.js +30 -0
- package/dist/package-parser/nav-parser.js.map +1 -0
- package/dist/package-parser/overlay-parser.d.ts +10 -0
- package/dist/package-parser/overlay-parser.js +35 -0
- package/dist/package-parser/overlay-parser.js.map +1 -0
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +2 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugins/chapter-links-plugin.d.ts +11 -0
- package/dist/plugins/chapter-links-plugin.js +36 -0
- package/dist/plugins/chapter-links-plugin.js.map +1 -0
- package/dist/plugins/reader-events-plugin.d.ts +14 -0
- package/dist/plugins/reader-events-plugin.js +32 -0
- package/dist/plugins/reader-events-plugin.js.map +1 -0
- package/dist/plugins/text-highlighter-plugin.d.ts +11 -0
- package/dist/plugins/text-highlighter-plugin.js +32 -0
- package/dist/plugins/text-highlighter-plugin.js.map +1 -0
- package/dist/reader-events-listener.d.ts +4 -0
- package/dist/reader-events-listener.js +2 -0
- package/dist/reader-events-listener.js.map +1 -0
- package/dist/reader.d.ts +51 -0
- package/dist/reader.js +179 -0
- package/dist/reader.js.map +1 -0
- package/dist/smil/helper.d.ts +25 -0
- package/dist/smil/helper.js +71 -0
- package/dist/smil/helper.js.map +1 -0
- package/dist/smil/index.d.ts +21 -0
- package/dist/smil/index.js +105 -0
- package/dist/smil/index.js.map +1 -0
- package/dist/styles.d.ts +2 -0
- package/dist/styles.js +6 -0
- package/dist/styles.js.map +1 -0
- package/dist/uri.d.ts +5 -0
- package/dist/uri.js +19 -0
- package/dist/uri.js.map +1 -0
- package/eslint.config.mjs +31 -0
- package/package.json +32 -0
- package/tsconfig.json +19 -0
package/.prettierrc.js
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# EPub Reader
|
|
2
|
+
|
|
3
|
+
A library that loads and renders ePub documents in the browser.
|
|
4
|
+
|
|
5
|
+
It provides support for:
|
|
6
|
+
* [Media Overlays](https://www.w3.org/TR/epub/#sec-media-overlays) - if the ePub has smil files the library plays the narration and highlights the corresponding html element. The client can also obtain the current time of the audio book and the total duration.
|
|
7
|
+
* [Navigation](https://www.w3.org/TR/epub-33/#sec-nav)
|
|
8
|
+
* For EPub 3.0 the manifest `<item properties="nav" />` element is parsed and provided in the form of a Table of contents (page list is also included).
|
|
9
|
+
* Support for EPub 2.0 `<item media-type="application/x-dtbncx+xml" />` is not yet implemented.
|
|
10
|
+
* Cover image
|
|
11
|
+
* For EPub 3.0 a manfiest `<item properties="cover-image" />` element is checked.
|
|
12
|
+
* For EPub 2.0 `<meta name="cover" />` is not yet implemented.
|
|
13
|
+
* Metadata
|
|
14
|
+
* Identifier (`<dc:identifier>`)
|
|
15
|
+
* Title (`<dc:title>`)
|
|
16
|
+
* Language (`<dc:language>`)
|
|
17
|
+
* Author (`<dc:creator>`)
|
|
18
|
+
|
|
19
|
+
## Getting started
|
|
20
|
+
|
|
21
|
+
### Installation
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
npm i @asteasolutions/epub-reader
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Usage
|
|
28
|
+
|
|
29
|
+
First you need an HTML element where the content will be rendered
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<div id="epub-reader"></div>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then you need to pass the epub document. The library provides two loaders which you can use depending on the source of the epub:
|
|
36
|
+
* `ZipLoader` - use this when you have a Blob/File (e.g. selected by the user from the file system or downloaded from a url) or a url to an epub file. In the case of a url the epub is streamed instead of being downloaded in its entirety.
|
|
37
|
+
* `AJAXUnpackedLoader` - use this when the epub is extracted and served statically from a url.
|
|
38
|
+
|
|
39
|
+
You can also use the `createLoader` function which will create a loader for you based on the parameters passed.
|
|
40
|
+
```javascript
|
|
41
|
+
// This expects the epub files to be statically served on the corresponding path appended to the url - e.g. https://example-epub-library.com/awesome-epub/META-INF/container.xml
|
|
42
|
+
createLoader('https://example-epub-library.com/awesome-epub')
|
|
43
|
+
|
|
44
|
+
// This will create a ZipLoader
|
|
45
|
+
createLoader('https://example-epub-library.com/awesome-zipped-epub', { isZipURL: true })
|
|
46
|
+
// same as this
|
|
47
|
+
createLoader(file)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Putting it all together
|
|
51
|
+
```typescript
|
|
52
|
+
import { createLoader, Book, Reader } from '@asteasolutions/reader-core'
|
|
53
|
+
|
|
54
|
+
const container = document.getElementById('epub-reader')
|
|
55
|
+
|
|
56
|
+
const loader = await createLoader(source: Blob | string)
|
|
57
|
+
Book.open(loader).subscribe({
|
|
58
|
+
next: book => {
|
|
59
|
+
const reader = new Reader(container, book)
|
|
60
|
+
|
|
61
|
+
reader.loadContent().subscribe(() => {
|
|
62
|
+
// EPub is now rendered
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Events
|
|
69
|
+
|
|
70
|
+
Once initialized you can listen to the following `Reader` events:
|
|
71
|
+
| Property | Description |
|
|
72
|
+
| -------- | ----------- |
|
|
73
|
+
| `audioEvents.onPlaybackStateChanged` | Fired with a single argument `playing: boolean` |
|
|
74
|
+
| `audioEvents.onAudioEnded` | Fired when an audio file reached its end |
|
|
75
|
+
| `audioEvents.onPositionChanged` | Fired every 200 ms while a media overlay is playing. It is passed a single argument `currentTime: number`, indicating the current time (in seconds) within the audio file |
|
|
76
|
+
| `events.pageChange` | Fired when the reader navigates to a new page |
|
|
77
|
+
| `events.chapterChange` | Fired when a new chapter is loaded (new html from the epub is rendered) |
|
|
78
|
+
| `events.textSelected` | Fired when the user selects something in the epub's html. Passed a `range: DOMRange` argument |
|
|
79
|
+
| `mediaOverlayEvents.onGlobalPositionChanged` | Similar to the `audioEvents.onPositionChanged` but in the context of the entire book. Also the argument is in milliseconds |
|
|
80
|
+
|
|
81
|
+
### API
|
|
82
|
+
|
|
83
|
+
| Object | Property | Description |
|
|
84
|
+
| ------ | -------- | ----------- |
|
|
85
|
+
| **Reader** | `currentChapterIndex` | The index of the current chapter as defined in the epub's `<spine>` tag |
|
|
86
|
+
| | `totalChapters` | The number of chapters in the epub |
|
|
87
|
+
| | `currentPage` | The index of the current page within the chapter. <br><br> *Note*: Since only a paginated layout is currently supported, the page properties are referring to the currently dispalyed pages and not the page list of the navigation document. This means that resizing the container holding the epub will cause the pages to be recalculated. |
|
|
88
|
+
| | `totalPages` | The number of pages within the chapter |
|
|
89
|
+
| | `playing` | Whether or not a media overlay is being played |
|
|
90
|
+
| | `togglePlayback()` | Turns the media overlay playback on/off |
|
|
91
|
+
| | `goToHref(href: string).subscribe()` | Navigates to an anchor within the epub. For example passing the `href` property from a `TocEntry` |
|
|
92
|
+
| | `previousChapter(goToEnd?: boolean)` | Navigates to the previous chapter. In case `true` is passed the epub will be navigated to the last page of the previous chapter, otherwise it will go to the beginning |
|
|
93
|
+
| | `previousPage()` | Navigates to the previous page in the chapter. If the user is on the first page of a chapter the epub is navigated to the last page of the previous chapter |
|
|
94
|
+
| | `nextPage()` | Navigates to the first page of the next chapter |
|
|
95
|
+
| | `nextPage()` | Navigates to the next page in the chapter. If the user is on the last page it will navigate to the first page of the next chapter |
|
|
96
|
+
| **Book** | `metadata` | The `Metadata` object for the epub |
|
|
97
|
+
| | `nav` | The `Navigation` object for the epub |
|
|
98
|
+
| | `cover` | The cover image url |
|
|
99
|
+
| | `overlayMetadata` | The `OverlayMetadata` object for the epub. Contains the total duration of the epub and the durations for each smil file |
|
|
100
|
+
| | `open(loader: BookLoader)` *static* | Creates the book object with the given loader |
|
|
101
|
+
|
|
102
|
+
### Rendering
|
|
103
|
+
|
|
104
|
+
Currently on a paginated layout is supported.
|
package/dist/array.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function binarySearch<T>(arr: T[], comp: (el: T) => number): T | undefined;
|
package/dist/array.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function binarySearch(arr, comp) {
|
|
2
|
+
let start = 0, end = arr.length - 1;
|
|
3
|
+
while (start <= end) {
|
|
4
|
+
const mid = Math.floor((start + end) / 2);
|
|
5
|
+
const elem = arr[mid];
|
|
6
|
+
const res = comp(elem);
|
|
7
|
+
if (res === 0) {
|
|
8
|
+
return elem;
|
|
9
|
+
}
|
|
10
|
+
if (res < 0) {
|
|
11
|
+
end = mid - 1;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
start = mid + 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=array.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array.js","sourceRoot":"","sources":["../lib/array.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAI,GAAQ,EAAE,IAAuB;IAC/D,IAAI,KAAK,GAAG,CAAC,EACX,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAA;IACtB,OAAO,KAAK,IAAI,GAAG,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;QACzC,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAE,CAAA;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAA;QACtB,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;YACd,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;YACZ,GAAG,GAAG,GAAG,GAAG,CAAC,CAAA;QACf,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,GAAG,GAAG,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default class AudioPlayer {
|
|
2
|
+
private player;
|
|
3
|
+
private positionChangedInterval?;
|
|
4
|
+
private playbackStateChangedSubject;
|
|
5
|
+
private positionChangedSubject;
|
|
6
|
+
private audioEndedSubject;
|
|
7
|
+
events: {
|
|
8
|
+
onPlaybackStateChanged: import("rxjs").Observable<boolean>;
|
|
9
|
+
onPositionChanged: import("rxjs").Observable<number>;
|
|
10
|
+
onAudioEnded: import("rxjs").Observable<void>;
|
|
11
|
+
};
|
|
12
|
+
constructor();
|
|
13
|
+
play(): void;
|
|
14
|
+
pause(): void;
|
|
15
|
+
get playing(): boolean;
|
|
16
|
+
togglePlayback(): void;
|
|
17
|
+
seekTo({ src, offset }: {
|
|
18
|
+
src: string;
|
|
19
|
+
offset: number;
|
|
20
|
+
}): void;
|
|
21
|
+
destroy(): void;
|
|
22
|
+
private onPlay;
|
|
23
|
+
private onPause;
|
|
24
|
+
private onEnded;
|
|
25
|
+
private onAbort;
|
|
26
|
+
private onError;
|
|
27
|
+
private resetState;
|
|
28
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Subject } from 'rxjs';
|
|
2
|
+
export default class AudioPlayer {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.playbackStateChangedSubject = new Subject();
|
|
5
|
+
this.positionChangedSubject = new Subject();
|
|
6
|
+
this.audioEndedSubject = new Subject();
|
|
7
|
+
this.events = {
|
|
8
|
+
onPlaybackStateChanged: this.playbackStateChangedSubject.asObservable(),
|
|
9
|
+
onPositionChanged: this.positionChangedSubject.asObservable(),
|
|
10
|
+
onAudioEnded: this.audioEndedSubject.asObservable(),
|
|
11
|
+
};
|
|
12
|
+
this.player = new Audio();
|
|
13
|
+
this.player.addEventListener('play', this.onPlay.bind(this));
|
|
14
|
+
this.player.addEventListener('pause', this.onPause.bind(this));
|
|
15
|
+
this.player.addEventListener('ended', this.onEnded.bind(this));
|
|
16
|
+
this.player.addEventListener('abort', this.onAbort.bind(this));
|
|
17
|
+
this.player.addEventListener('error', this.onError.bind(this));
|
|
18
|
+
}
|
|
19
|
+
play() {
|
|
20
|
+
this.player.play().catch(console.error);
|
|
21
|
+
}
|
|
22
|
+
pause() {
|
|
23
|
+
this.player.pause();
|
|
24
|
+
}
|
|
25
|
+
get playing() {
|
|
26
|
+
return !this.player.paused;
|
|
27
|
+
}
|
|
28
|
+
togglePlayback() {
|
|
29
|
+
if (this.playing) {
|
|
30
|
+
this.pause();
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
this.play();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
seekTo({ src, offset }) {
|
|
37
|
+
if (this.player.src !== src) {
|
|
38
|
+
this.player.src = src;
|
|
39
|
+
this.play();
|
|
40
|
+
}
|
|
41
|
+
this.player.currentTime = offset / 1000;
|
|
42
|
+
}
|
|
43
|
+
destroy() {
|
|
44
|
+
this.player.pause();
|
|
45
|
+
this.resetState();
|
|
46
|
+
this.player.removeEventListener('play', this.onPlay.bind(this));
|
|
47
|
+
this.player.removeEventListener('pause', this.onPause.bind(this));
|
|
48
|
+
this.player.removeEventListener('ended', this.onEnded.bind(this));
|
|
49
|
+
this.player.removeEventListener('abort', this.onAbort.bind(this));
|
|
50
|
+
this.player.removeEventListener('error', this.onError.bind(this));
|
|
51
|
+
}
|
|
52
|
+
onPlay() {
|
|
53
|
+
this.playbackStateChangedSubject.next(true);
|
|
54
|
+
this.positionChangedInterval = window.setInterval(() => {
|
|
55
|
+
this.positionChangedSubject.next(this.player.currentTime);
|
|
56
|
+
}, 200);
|
|
57
|
+
}
|
|
58
|
+
onPause() {
|
|
59
|
+
this.playbackStateChangedSubject.next(false);
|
|
60
|
+
this.resetState();
|
|
61
|
+
}
|
|
62
|
+
onEnded() {
|
|
63
|
+
this.resetState();
|
|
64
|
+
this.audioEndedSubject.next();
|
|
65
|
+
}
|
|
66
|
+
onAbort() {
|
|
67
|
+
this.resetState();
|
|
68
|
+
}
|
|
69
|
+
onError() {
|
|
70
|
+
console.error(this.player.error);
|
|
71
|
+
this.resetState();
|
|
72
|
+
}
|
|
73
|
+
resetState() {
|
|
74
|
+
clearInterval(this.positionChangedInterval);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=audio-player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-player.js","sourceRoot":"","sources":["../lib/audio-player.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { BookLoader } from '.';
|
|
3
|
+
export declare class AJAXUnpackedLoader implements BookLoader {
|
|
4
|
+
private url;
|
|
5
|
+
private abortController;
|
|
6
|
+
constructor(url: string);
|
|
7
|
+
fetch(path: string, asURL?: boolean): Observable<string>;
|
|
8
|
+
root(path: string): Observable<string>;
|
|
9
|
+
cancel(): void;
|
|
10
|
+
processChapterHTML(html: string, basePath: string): Observable<Document>;
|
|
11
|
+
private urlToPath;
|
|
12
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { map, Observable } from 'rxjs';
|
|
2
|
+
export class AJAXUnpackedLoader {
|
|
3
|
+
constructor(url) {
|
|
4
|
+
this.url = url;
|
|
5
|
+
this.abortController = new AbortController();
|
|
6
|
+
}
|
|
7
|
+
fetch(path, asURL = false) {
|
|
8
|
+
if (asURL) {
|
|
9
|
+
return this.root(path);
|
|
10
|
+
}
|
|
11
|
+
return new Observable((subscriber) => {
|
|
12
|
+
fetch(this.urlToPath(path), {
|
|
13
|
+
signal: this.abortController.signal,
|
|
14
|
+
})
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
16
|
+
.then((response) => (response.ok ? response.text() : Promise.reject(response)))
|
|
17
|
+
.then((response) => {
|
|
18
|
+
subscriber.next(response);
|
|
19
|
+
subscriber.complete();
|
|
20
|
+
}, (error) => subscriber.error(error));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
root(path) {
|
|
24
|
+
return new Observable((subscriber) => {
|
|
25
|
+
subscriber.next(this.urlToPath(path));
|
|
26
|
+
subscriber.complete();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
cancel() {
|
|
30
|
+
this.abortController.abort('Load cancelled');
|
|
31
|
+
}
|
|
32
|
+
processChapterHTML(html, basePath) {
|
|
33
|
+
return this.root(basePath).pipe(map((root) => {
|
|
34
|
+
const parser = new DOMParser();
|
|
35
|
+
const content = parser.parseFromString(html, 'text/html');
|
|
36
|
+
const base = document.createElement('base');
|
|
37
|
+
base.href = `${root}/`;
|
|
38
|
+
content.head.prepend(base);
|
|
39
|
+
return content;
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
urlToPath(path) {
|
|
43
|
+
return `${this.url.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=ajax-unpacked.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ajax-unpacked.js","sourceRoot":"","sources":["../../lib/book-loaders/ajax-unpacked.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAGtC,MAAM,OAAO,kBAAkB;IAG7B,YAAoB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;QAFvB,oBAAe,GAAG,IAAI,eAAe,EAAE,CAAA;IAEb,CAAC;IAE5B,KAAK,CAAC,IAAY,EAAE,KAAK,GAAG,KAAK;QACtC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;QACD,OAAO,IAAI,UAAU,CAAS,CAAC,UAAU,EAAE,EAAE;YAC3C,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;gBAC1B,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM;aACpC,CAAC;gBACA,2EAA2E;iBAC1E,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;iBAC9E,IAAI,CACH,CAAC,QAAQ,EAAE,EAAE;gBACX,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBACzB,UAAU,CAAC,QAAQ,EAAE,CAAA;YACvB,CAAC,EACD,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CACnC,CAAA;QACL,CAAC,CAAC,CAAA;IACJ,CAAC;IAEM,IAAI,CAAC,IAAY;QACtB,OAAO,IAAI,UAAU,CAAS,CAAC,UAAU,EAAE,EAAE;YAC3C,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;YACrC,UAAU,CAAC,QAAQ,EAAE,CAAA;QACvB,CAAC,CAAC,CAAA;IACJ,CAAC;IAEM,MAAM;QACX,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;IAC9C,CAAC;IAEM,kBAAkB,CAAC,IAAY,EAAE,QAAgB;QACtD,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC7B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACX,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAA;YAC9B,MAAM,OAAO,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;YAEzD,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;YAC3C,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,GAAG,CAAA;YACtB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAC1B,OAAO,OAAO,CAAA;QAChB,CAAC,CAAC,CACH,CAAA;IACH,CAAC;IAEO,SAAS,CAAC,IAAY;QAC5B,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAA;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
export interface BookLoader {
|
|
3
|
+
fetch(path: string, asURL?: boolean): Observable<string>;
|
|
4
|
+
root(path: string): Observable<string>;
|
|
5
|
+
cancel(): void;
|
|
6
|
+
processChapterHTML(html: string, basePath: string): Observable<Document>;
|
|
7
|
+
}
|
|
8
|
+
export interface CreateLoaderOptions {
|
|
9
|
+
isZipURL?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function createLoader(source: string | Blob, options?: CreateLoaderOptions): Promise<BookLoader>;
|
|
12
|
+
export * from './ajax-unpacked';
|
|
13
|
+
export * from './zip';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ZipLoader } from './zip';
|
|
2
|
+
import { AJAXUnpackedLoader } from './ajax-unpacked';
|
|
3
|
+
export async function createLoader(source, options = {}) {
|
|
4
|
+
if (source instanceof Blob || options.isZipURL) {
|
|
5
|
+
return new ZipLoader(source);
|
|
6
|
+
}
|
|
7
|
+
const response = await fetch(source, { method: 'HEAD' });
|
|
8
|
+
if (response.headers.get('content-type') === 'application/epub+zip') {
|
|
9
|
+
return new ZipLoader(source);
|
|
10
|
+
}
|
|
11
|
+
return new AJAXUnpackedLoader(source);
|
|
12
|
+
}
|
|
13
|
+
export * from './ajax-unpacked';
|
|
14
|
+
export * from './zip';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/book-loaders/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAgBpD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAqB,EAAE,UAA+B,EAAE;IACzF,IAAI,MAAM,YAAY,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QAC/C,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxD,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,sBAAsB,EAAE,CAAC;QACpE,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IACD,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;AACvC,CAAC;AAED,cAAc,iBAAiB,CAAA;AAC/B,cAAc,OAAO,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { BookLoader } from '.';
|
|
3
|
+
export declare class ZipLoader implements BookLoader {
|
|
4
|
+
private entries;
|
|
5
|
+
private objectURLCache;
|
|
6
|
+
constructor(source: Blob | string);
|
|
7
|
+
fetch(path: string, asURL?: boolean): Observable<string>;
|
|
8
|
+
root(path: string): Observable<string>;
|
|
9
|
+
cancel(): void;
|
|
10
|
+
processChapterHTML(html: string, basePath: string): Observable<Document>;
|
|
11
|
+
private rewriteTagAttribute;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { catchError, defaultIfEmpty, forkJoin, map, Observable, of, tap } from 'rxjs';
|
|
2
|
+
import { BlobReader, BlobWriter, HttpReader, TextWriter, ZipReader } from '@zip.js/zip.js';
|
|
3
|
+
import { absoluteOrPrefixed, normalize } from '../uri';
|
|
4
|
+
import { isString } from 'lodash';
|
|
5
|
+
export class ZipLoader {
|
|
6
|
+
constructor(source) {
|
|
7
|
+
this.objectURLCache = new Map();
|
|
8
|
+
const zipReader = new ZipReader(source instanceof Blob ? new BlobReader(source) : new HttpReader(source, { forceRangeRequests: true }));
|
|
9
|
+
this.entries = zipReader
|
|
10
|
+
.getEntries()
|
|
11
|
+
.catch((err) => {
|
|
12
|
+
console.error(`Error loading entry list for ${source instanceof File ? source.name : isString(source) ? source : 'blob'}`);
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
|
|
14
|
+
return Promise.reject(err);
|
|
15
|
+
})
|
|
16
|
+
.finally(() => void zipReader.close());
|
|
17
|
+
}
|
|
18
|
+
fetch(path, asURL = false) {
|
|
19
|
+
path = normalize(path);
|
|
20
|
+
const cached = this.objectURLCache.get(path);
|
|
21
|
+
if (cached) {
|
|
22
|
+
return of(cached);
|
|
23
|
+
}
|
|
24
|
+
return new Observable((subscriber) => {
|
|
25
|
+
this.entries
|
|
26
|
+
.then((entries) => {
|
|
27
|
+
const entry = entries.find((e) => e.filename === path);
|
|
28
|
+
if (!entry) {
|
|
29
|
+
return Promise.reject(new Error(`Entry "${path}" not found`));
|
|
30
|
+
}
|
|
31
|
+
return asURL
|
|
32
|
+
? entry.getData(new BlobWriter()).then((blob) => {
|
|
33
|
+
const url = URL.createObjectURL(blob);
|
|
34
|
+
this.objectURLCache.set(path, url);
|
|
35
|
+
return url;
|
|
36
|
+
})
|
|
37
|
+
: entry.getData(new TextWriter());
|
|
38
|
+
})
|
|
39
|
+
.then((data) => subscriber.next(data))
|
|
40
|
+
.catch((error) => subscriber.error(error))
|
|
41
|
+
.finally(() => subscriber.complete());
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
root(path) {
|
|
45
|
+
return of(path);
|
|
46
|
+
}
|
|
47
|
+
cancel() {
|
|
48
|
+
// Noop
|
|
49
|
+
}
|
|
50
|
+
processChapterHTML(html, basePath) {
|
|
51
|
+
const content = new DOMParser().parseFromString(html, 'text/html');
|
|
52
|
+
return forkJoin([
|
|
53
|
+
this.rewriteTagAttribute(content, 'img', 'src', true, basePath),
|
|
54
|
+
this.rewriteTagAttribute(content, 'link', 'href', true, basePath),
|
|
55
|
+
]).pipe(map(() => content));
|
|
56
|
+
}
|
|
57
|
+
rewriteTagAttribute(content, tag, attr, asURL, basePath) {
|
|
58
|
+
return forkJoin(Array.from(content.querySelectorAll(`${tag}[${attr}]`)).map((elem) => forkJoin([
|
|
59
|
+
of(elem),
|
|
60
|
+
this.fetch(absoluteOrPrefixed(elem.getAttribute(attr), basePath), asURL).pipe(catchError((error) => {
|
|
61
|
+
console.warn(`Error rewriting ${tag}[${attr}=${elem.getAttribute(attr)}].`, error);
|
|
62
|
+
return of(null);
|
|
63
|
+
})),
|
|
64
|
+
]))).pipe(defaultIfEmpty([]), tap((res) => res.forEach(([elem, url]) => (url ? elem.setAttribute(attr, url) : undefined))));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=zip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zip.js","sourceRoot":"","sources":["../../lib/book-loaders/zip.ts"],"names":[],"mappings":"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"}
|
package/dist/book.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { BookLoader } from './book-loaders';
|
|
3
|
+
import { PackageParser, Metadata } from './package-parser';
|
|
4
|
+
export declare class Book {
|
|
5
|
+
loader: BookLoader;
|
|
6
|
+
packageParser: PackageParser;
|
|
7
|
+
static open(loader: BookLoader): Observable<Book>;
|
|
8
|
+
constructor(loader: BookLoader, packageParser: PackageParser);
|
|
9
|
+
getSpineRefIds(): string[];
|
|
10
|
+
getChapterHTML(id: string): Observable<Document>;
|
|
11
|
+
private getItemParent;
|
|
12
|
+
get metadata(): Metadata;
|
|
13
|
+
get nav(): Observable<import(".").Navigation>;
|
|
14
|
+
get cover(): Observable<string | undefined>;
|
|
15
|
+
get overlayMetadata(): import(".").OverlayMetadata | null;
|
|
16
|
+
}
|
package/dist/book.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { map, of, switchMap, throwError } from 'rxjs';
|
|
2
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
3
|
+
import { PackageParser } from './package-parser';
|
|
4
|
+
import { parentURI } from './uri';
|
|
5
|
+
const CONTAINER_PARSER = new XMLParser({
|
|
6
|
+
ignoreAttributes: false,
|
|
7
|
+
isArray: (tag) => tag === 'rootfile',
|
|
8
|
+
});
|
|
9
|
+
const PACKAGE_PARSER = new XMLParser({
|
|
10
|
+
ignoreAttributes: false,
|
|
11
|
+
isArray: (tag) => ['dc:identifier', 'dc:title', 'dc:language', 'dc:creator', 'meta'].includes(tag),
|
|
12
|
+
alwaysCreateTextNode: true,
|
|
13
|
+
});
|
|
14
|
+
export class Book {
|
|
15
|
+
static open(loader) {
|
|
16
|
+
return loader.fetch('META-INF/container.xml').pipe(map((content) => CONTAINER_PARSER.parse(content)), switchMap(({ container }) => {
|
|
17
|
+
const rootFilePath = container.rootfiles.rootfile[0]['@_full-path'];
|
|
18
|
+
return loader.fetch(rootFilePath).pipe(map((packageContent) => PACKAGE_PARSER.parse(packageContent)), switchMap((packageXml) => {
|
|
19
|
+
const packageParser = new PackageParser(packageXml, parentURI(rootFilePath), loader);
|
|
20
|
+
return of(new Book(loader, packageParser));
|
|
21
|
+
}));
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
constructor(loader, packageParser) {
|
|
25
|
+
this.loader = loader;
|
|
26
|
+
this.packageParser = packageParser;
|
|
27
|
+
}
|
|
28
|
+
getSpineRefIds() {
|
|
29
|
+
return this.packageParser.getSpineRefIds();
|
|
30
|
+
}
|
|
31
|
+
getChapterHTML(id) {
|
|
32
|
+
const path = this.packageParser.getManifestItemPath(id);
|
|
33
|
+
if (!path) {
|
|
34
|
+
return throwError(() => `No chapter with id '${id}'`);
|
|
35
|
+
}
|
|
36
|
+
return this.loader
|
|
37
|
+
.fetch(path)
|
|
38
|
+
.pipe(switchMap((chapterHTML) => this.loader.processChapterHTML(chapterHTML, this.getItemParent(id))));
|
|
39
|
+
}
|
|
40
|
+
getItemParent(id) {
|
|
41
|
+
const path = this.packageParser.getManifestItemPath(id);
|
|
42
|
+
if (!path) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return parentURI(path);
|
|
46
|
+
}
|
|
47
|
+
get metadata() {
|
|
48
|
+
return this.packageParser.metadata;
|
|
49
|
+
}
|
|
50
|
+
get nav() {
|
|
51
|
+
return this.packageParser.fetchNAV();
|
|
52
|
+
}
|
|
53
|
+
get cover() {
|
|
54
|
+
return this.packageParser.fetchCover();
|
|
55
|
+
}
|
|
56
|
+
get overlayMetadata() {
|
|
57
|
+
return this.packageParser.getOverlayMetadata();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=book.js.map
|
package/dist/book.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"book.js","sourceRoot":"","sources":["../lib/book.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAc,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAEjE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAG3C,OAAO,EAAE,aAAa,EAAY,MAAM,kBAAkB,CAAA;AAG1D,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEjC,MAAM,gBAAgB,GAAG,IAAI,SAAS,CAAC;IACrC,gBAAgB,EAAE,KAAK;IACvB,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,UAAU;CACrC,CAAC,CAAA;AAEF,MAAM,cAAc,GAAG,IAAI,SAAS,CAAC;IACnC,gBAAgB,EAAE,KAAK;IACvB,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,eAAe,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;IAClG,oBAAoB,EAAE,IAAI;CAC3B,CAAC,CAAA;AAEF,MAAM,OAAO,IAAI;IACR,MAAM,CAAC,IAAI,CAAC,MAAkB;QACnC,OAAO,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,IAAI,CAChD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAkB,CAAC,EAClE,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;YAC1B,MAAM,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,aAAa,CAAC,CAAA;YAEpE,OAAO,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CACpC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,cAAc,CAAgB,CAAC,EAC5E,SAAS,CAAC,CAAC,UAAU,EAAE,EAAE;gBACvB,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAA;gBAEpF,OAAO,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;YAC5C,CAAC,CAAC,CACH,CAAA;QACH,CAAC,CAAC,CACH,CAAA;IACH,CAAC;IAED,YACS,MAAkB,EAClB,aAA4B;QAD5B,WAAM,GAAN,MAAM,CAAY;QAClB,kBAAa,GAAb,aAAa,CAAe;IAClC,CAAC;IAEG,cAAc;QACnB,OAAO,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE,CAAA;IAC5C,CAAC;IAEM,cAAc,CAAC,EAAU;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAA;QAEvD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,UAAU,CAAC,GAAG,EAAE,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAA;QACvD,CAAC;QAED,OAAO,IAAI,CAAC,MAAM;aACf,KAAK,CAAC,IAAI,CAAC;aACX,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,EAAE,CAAE,CAAC,CAAC,CAAC,CAAA;IAC3G,CAAC;IAEO,aAAa,CAAC,EAAU;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAA;QACvD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,SAAS,CAAC,IAAI,CAAC,CAAA;IACxB,CAAC;IAED,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAA;IACpC,CAAC;IAED,IAAW,GAAG;QACZ,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAA;IACtC,CAAC;IAED,IAAW,KAAK;QACd,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,CAAA;IACxC,CAAC;IAED,IAAW,eAAe;QACxB,OAAO,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,CAAA;IAChD,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ContentController } from '../content-controller';
|
|
2
|
+
export declare const DOCUMENT_CSS = "\n html, body {\n margin: 0;\n padding: 0;\n\n position: relative;\n }\n\n html {\n height: 100%;\n\n box-sizing: border-box;\n\n column-fill: auto;\n\n overflow: hidden;\n }\n";
|
|
3
|
+
export declare class ReflowableContent implements ContentController {
|
|
4
|
+
private iframe;
|
|
5
|
+
private document;
|
|
6
|
+
private page;
|
|
7
|
+
private columns;
|
|
8
|
+
private htmlPadding;
|
|
9
|
+
private columnGap;
|
|
10
|
+
private resizeObserver;
|
|
11
|
+
constructor(iframe: HTMLIFrameElement);
|
|
12
|
+
get currentPage(): number;
|
|
13
|
+
get totalPages(): number;
|
|
14
|
+
attach(): void;
|
|
15
|
+
detach(): void;
|
|
16
|
+
prepareChapter(): void;
|
|
17
|
+
private setColumnCount;
|
|
18
|
+
goToPage(page: number): void;
|
|
19
|
+
ensureVisible(elem: Element): void;
|
|
20
|
+
getFirstVisibleElement(): Element | null;
|
|
21
|
+
private updatePageOffset;
|
|
22
|
+
private get offset();
|
|
23
|
+
private updateStyles;
|
|
24
|
+
}
|