@epubjs-react-native/core
Version:
A digital book reader in .opf .epub format for react native using epub.js library inside a webview.
339 lines (301 loc) • 13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var webViewJavaScriptFunctions = _interopRequireWildcard(require("./utils/webViewInjectFunctions"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var _default = exports.default = `
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EPUB.js</title>
<script id="jszip"></script>
<script id="epubjs"></script>
<style type="text/css">
body {
margin: 0;
}
#viewer {
height: 100vh;
width: 100vw;
overflow: hidden ;
display: flex;
justify-content: center;
align-items: center;
}
[ref="epubjs-mk-balloon"] {
background: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPScxLjEnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycgeG1sbnM6eGxpbms9J2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnIHg9JzBweCcgeT0nMHB4JyB2aWV3Qm94PScwIDAgNzUgNzUnPjxnIGZpbGw9JyNCREJEQkQnIGlkPSdidWJibGUnPjxwYXRoIGNsYXNzPSdzdDAnIGQ9J00zNy41LDkuNEMxOS42LDkuNCw1LDIwLjUsNSwzNC4zYzAsNS45LDIuNywxMS4zLDcuMSwxNS42TDkuNiw2NS42bDE5LTcuM2MyLjgsMC42LDUuOCwwLjksOC45LDAuOSBDNTUuNSw1OS4yLDcwLDQ4LjEsNzAsMzQuM0M3MCwyMC41LDU1LjQsOS40LDM3LjUsOS40eicvPjwvZz48L3N2Zz4=") no-repeat;
width: 20px;
height: 20px;
cursor: pointer;
margin-left: 0;
}
[ref="epubjs-mk-heart"] {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOUlEQVR4nLWUTWgTURDH14Oe9JiPNqFNujvvzdsm3bdvPxKMFUEPag/iwdaD3j1JDymlCMXiqUeRHvWgFRQUxKPirUU8eFARvCnUj7QXP7DiJtk8easJjRjzIQ784bEz82Pe7MzTtP9tpmnu8UbNpOM4uzvFKF+3GM1BHHIAbwjA7xyY5AaGPuCarZtHmzGcsGM+YevKp2JUrAN4XeW2wSxKMy6wrSkKtbsiJZ96SfnAGZbl8bG6DawhdLwqAK9xYI25XLaufCrmjkjJKQpVF3DLzrDRFtAHXJ9hUNsoxOTH8hn5afGcrBRjkR66w3I/0GoJaPWRO9T63tRGISanmVHzgK1FMBvGmSr/iZeUn5fL8svlRbl5aKQt6bGXjPQ7bKefA5MOIahZOpsuAQmUY3t1pWNSN5WABtwwT2kW4Mki0OqgoMov+YA1rrMTmk3IhCr3hd/5St303EtEV54Yw5xq4y4PcHOFt/etH12xRqQHWFGsn/MFuHAQaPCmGO8b9roQl5OEBpaB862xoZTuc4F+uJDLhv0CF/LZ0DPoe9M097YNNwd2hAMLb9rpnmGrdlr1LrQJO/zH9bMMnBWA4X0n1RV2T6TU6oUc2Pm/vQ0aN/CSAKzfFp0rvWWnI5gNbEnrxWwD59UOL+UzjXc7ftTbYlxezGca0X4Dm+sJ1jQO7LgA/Hoa9eCln5Cv/IQ8i3ogAL+pZdAGMYcQdAGfHSAkmCQkUOc8pXQgWNPUgysAl5XU+Z9gg9gPaBjV+CGbZVoAAAAASUVORK5CYII=") no-repeat;
width: 20px;
height: 20px;
cursor: pointer;
margin-left: 0;
}
</style>
</head>
<body oncopy='return false' oncut='return false'>
<div id="viewer"></div>
<script>
let book;
let rendition;
const type = window.type;
const file = window.book;
const theme = window.theme;
const initialLocations = window.locations;
const enableSelection = window.enable_selection;
if (!file) {
alert('Failed load book');
}
if (type === 'epub' || type === 'opf' || type === 'binary') {
book = ePub(file);
} else if (type === 'base64') {
book = ePub(file, { encoding: "base64" });
} else {
alert('Missing file type');
}
rendition = book.renderTo("viewer", {
width: "100%",
height: "100%",
manager: "default",
flow: "auto",
snap: undefined,
spread: undefined,
fullsize: undefined,
allowPopups: allowPopups,
allowScriptedContent: allowScriptedContent
});
const reactNativeWebview = window.ReactNativeWebView !== undefined && window.ReactNativeWebView!== null ? window.ReactNativeWebView: window;
reactNativeWebview.postMessage(JSON.stringify({ type: "onStarted" }));
function flatten(chapters) {
return [].concat.apply([], chapters.map((chapter) => [].concat.apply([chapter], flatten(chapter.subitems))));
}
function getCfiFromHref(book, href) {
const [_, id] = href.split('#')
let section = book.spine.get(href.split('/')[1]) || book.spine.get(href) || book.spine.get(href.split('/').slice(1).join('/'))
const el = (id ? section.document.getElementById(id) : section.document.body)
return section.cfiFromElement(el)
}
function getChapter(location) {
const locationHref = location.start.href
let match = flatten(book.navigation.toc)
.filter((chapter) => {
return book.canonical(chapter.href).includes(locationHref)
}, null)
.reduce((result, chapter) => {
const locationAfterChapter = ePub.CFI.prototype.compare(location.start.cfi, getCfiFromHref(book, chapter.href)) > 0
return locationAfterChapter ? chapter : result
}, null);
return match;
};
const makeRangeCfi = (a, b) => {
const CFI = new ePub.CFI()
const start = CFI.parse(a), end = CFI.parse(b)
const cfi = {
range: true,
base: start.base,
path: {
steps: [],
terminal: null
},
start: start.path,
end: end.path
}
const len = cfi.start.steps.length
for (let i = 0; i < len; i++) {
if (CFI.equalStep(cfi.start.steps[i], cfi.end.steps[i])) {
if (i == len - 1) {
// Last step is equal, check terminals
if (cfi.start.terminal === cfi.end.terminal) {
// CFI's are equal
cfi.path.steps.push(cfi.start.steps[i])
// Not a range
cfi.range = false
}
} else cfi.path.steps.push(cfi.start.steps[i])
} else break
}
cfi.start.steps = cfi.start.steps.slice(cfi.path.steps.length)
cfi.end.steps = cfi.end.steps.slice(cfi.path.steps.length)
return 'epubcfi(' + CFI.segmentString(cfi.base)
+ '!' + CFI.segmentString(cfi.path)
+ ',' + CFI.segmentString(cfi.start)
+ ',' + CFI.segmentString(cfi.end)
+ ')'
}
if (!enableSelection) {
rendition.themes.default({
'body': {
'-webkit-touch-callout': 'none', /* iOS Safari */
'-webkit-user-select': 'none', /* Safari */
'-khtml-user-select': 'none', /* Konqueror HTML */
'-moz-user-select': 'none', /* Firefox */
'-ms-user-select': 'none', /* Internet Explorer/Edge */
'user-select': 'none'
}
});
}
book.ready
.then(function () {
if (initialLocations) {
return book.locations.load(initialLocations);
}
book.locations.generate(1600).then(function () {
reactNativeWebview.postMessage(JSON.stringify({
type: "onLocationsReady",
epubKey: book.key(),
locations: book.locations.save(),
totalLocations: book.locations.total,
currentLocation: rendition.currentLocation(),
progress: book.locations.percentageFromCfi(rendition.currentLocation().start.cfi),
}));
});
})
.then(function () {
var displayed = rendition.display();
displayed.then(function () {
var currentLocation = rendition.currentLocation();
reactNativeWebview.postMessage(JSON.stringify({
type: "onReady",
totalLocations: book.locations.total,
currentLocation: currentLocation,
progress: book.locations.percentageFromCfi(currentLocation.start.cfi),
}));
});
book
.coverUrl()
.then(async (url) => {
var reader = new FileReader();
reader.onload = (res) => {
reactNativeWebview.postMessage(
JSON.stringify({
type: "meta",
metadata: {
cover: reader.result,
author: book.package.metadata.creator,
title: book.package.metadata.title,
description: book.package.metadata.description,
language: book.package.metadata.language,
publisher: book.package.metadata.publisher,
rights: book.package.metadata.rights,
},
})
);
};
reader.readAsDataURL(await fetch(url).then((res) => res.blob()));
})
.catch(() => {
reactNativeWebview.postMessage(
JSON.stringify({
type: "meta",
metadata: {
cover: undefined,
author: book.package.metadata.creator,
title: book.package.metadata.title,
description: book.package.metadata.description,
language: book.package.metadata.language,
publisher: book.package.metadata.publisher,
rights: book.package.metadata.rights,
},
})
);
});
book.loaded.navigation.then(function (item) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onNavigationLoaded',
toc: item.toc,
landmarks: item.landmarks
}));
});
})
.catch(function (err) {
reactNativeWebview.postMessage(JSON.stringify({
type: "onDisplayError",
reason: reason
}));
});
rendition.on('started', () => {
rendition.themes.register({ theme: theme });
rendition.themes.select('theme');
});
rendition.on("relocated", function (location) {
var percent = book.locations.percentageFromCfi(location.start.cfi);
var percentage = Math.floor(percent * 100);
var chapter = getChapter(location);
reactNativeWebview.postMessage(JSON.stringify({
type: "onLocationChange",
totalLocations: book.locations.total,
currentLocation: location,
progress: percentage,
currentSection: chapter,
}));
if (location.atStart) {
reactNativeWebview.postMessage(JSON.stringify({
type: "onBeginning",
}));
}
if (location.atEnd) {
reactNativeWebview.postMessage(JSON.stringify({
type: "onFinish",
}));
}
});
rendition.on("orientationchange", function (orientation) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onOrientationChange',
orientation: orientation
}));
});
rendition.on("rendered", function (section) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onRendered',
section: section,
currentSection: book.navigation.get(section.href),
}));
});
rendition.on("layout", function (layout) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onLayout',
layout: layout,
}));
});
rendition.on("selected", function (cfiRange, contents) {
book.getRange(cfiRange).then(function (range) {
if (range) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onSelected',
cfiRange: cfiRange,
text: range.toString(),
}));
}
});
});
rendition.on("markClicked", function (cfiRange, contents) {
const annotations = Object.values(rendition.annotations._annotations);
const annotation = annotations.find(item => item.cfiRange === cfiRange);
if (annotation) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onPressAnnotation',
annotation: ${webViewJavaScriptFunctions.mapObjectToAnnotation('annotation')}
}));
}
});
rendition.on("resized", function (layout) {
reactNativeWebview.postMessage(JSON.stringify({
type: 'onResized',
layout: layout,
}));
});
</script>
</body>
</html>
`;