Files
calibre-web/cps/static/js/reading/epub.js
2025-12-07 14:31:16 +01:00

191 lines
6.8 KiB
JavaScript

/* global $, calibre, EPUBJS, ePubReader */
var reader;
(function () {
"use strict";
EPUBJS.filePath = calibre.filePath;
EPUBJS.cssPath = calibre.cssPath;
reader = ePubReader(calibre.bookUrl, {
restore: true,
bookmarks: calibre.bookmark ? [calibre.bookmark] : [],
});
Object.keys(themes).forEach(function (theme) {
reader.rendition.themes.register(theme, themes[theme].css_path);
});
if (calibre.useBookmarks) {
reader.on("reader:bookmarked", updateBookmark.bind(reader, "add"));
reader.on("reader:unbookmarked", updateBookmark.bind(reader, "remove"));
} else {
$("#bookmark, #show-Bookmarks").remove();
}
// Enable swipe support
// I have no idea why swiperRight/swiperLeft from plugins is not working, events just don't get fired
var touchStart = 0;
var touchEnd = 0;
reader.rendition.on('touchstart', function(event) {
touchStart = event.changedTouches[0].screenX;
});
reader.rendition.on('touchend', function(event) {
touchEnd = event.changedTouches[0].screenX;
if (touchStart < touchEnd) {
if(reader.book.package.metadata.direction === "rtl") {
reader.rendition.next();
} else {
reader.rendition.prev();
}
// Swiped Right
}
if (touchStart > touchEnd) {
if(reader.book.package.metadata.direction === "rtl") {
reader.rendition.prev();
} else {
reader.rendition.next();
}
// Swiped Left
}
});
// Update progress percentage
let progressDiv = document.getElementById("progress");
// Pages counter (virtual pages via EPUB locations)
let pagesDiv = document.getElementById("pages-count");
// Honor saved visibility preference for pages counter
(function () {
try {
var pref = localStorage.getItem("calibre.reader.showPages");
var show = pref === null ? true : pref === "true";
if (pagesDiv)
pagesDiv.style.visibility = show ? "visible" : "hidden";
} catch (e) {}
})();
reader.book.ready.then(() => {
let locations_key = reader.book.key() + "-locations";
// Key to persist last-read position for this book in localStorage
let position_key = "calibre.reader.position." + reader.book.key();
let stored_locations = localStorage.getItem(locations_key);
let make_locations, save_locations;
if (stored_locations) {
make_locations = Promise.resolve(
reader.book.locations.load(stored_locations)
);
// No-op because locations are already saved
save_locations = () => {};
} else {
make_locations = reader.book.locations.generate();
save_locations = () => {
localStorage.setItem(
locations_key,
reader.book.locations.save()
);
};
}
make_locations
.then(() => {
// Try to restore last position (CFI) from localStorage if present
try {
var _savedPos = localStorage.getItem(position_key);
if (_savedPos) {
try {
var _posObj = JSON.parse(_savedPos);
if (_posObj && _posObj.cfi) {
// Display the saved CFI location
try {
reader.rendition.display(_posObj.cfi);
} catch (e) {}
}
} catch (e) {}
}
} catch (e) {}
reader.rendition.on("relocated", (location) => {
let percentage = Math.round(location.end.percentage * 100);
progressDiv.textContent = percentage + "%";
// Pages based on generated EPUB locations (CFI positions)
const cfi = location.start.cfi;
const current =
reader.book.locations.locationFromCfi(cfi) || 0; // 1-based index typically
const total = reader.book.locations.length() || 0;
if (total > 0) {
pagesDiv.textContent = current + "/" + total;
pagesDiv.style.visibility = "visible";
} else {
pagesDiv.textContent = "";
pagesDiv.style.visibility = "hidden";
}
// Persist last position (CFI + percentage) to localStorage so reader can restore on next open
try {
var posObj = {
cfi: location.start.cfi,
percentage: location.start.percentage,
};
localStorage.setItem(
position_key,
JSON.stringify(posObj)
);
} catch (e) {}
});
reader.rendition.reportLocation();
progressDiv.style.visibility = "visible";
})
.then(save_locations);
});
/**
* @param {string} action - Add or remove bookmark
* @param {string|int} location - Location or zero
*/
function updateBookmark(action, location) {
// Remove other bookmarks (there can only be one)
if (action === "add") {
this.settings.bookmarks
.filter(function (bookmark) {
return bookmark && bookmark !== location;
})
.map(
function (bookmark) {
this.removeBookmark(bookmark);
}.bind(this)
);
}
var csrftoken = $("input[name='csrf_token']").val();
// Save to database
$.ajax(calibre.bookmarkUrl, {
method: "post",
data: { bookmark: location || "" },
headers: { "X-CSRFToken": csrftoken },
}).fail(function (xhr, status, error) {
alert(error);
});
}
// Default settings load
const theme = localStorage.getItem("calibre.reader.theme") ?? "lightTheme";
selectTheme(theme);
// Restore saved font and font size after reader is ready
reader.book.ready.then(() => {
const savedFontSize = localStorage.getItem("calibre.reader.fontSize");
if (savedFontSize) {
reader.rendition.themes.fontSize(`${savedFontSize}%`);
}
const savedFont = localStorage.getItem("calibre.reader.font");
if (savedFont && window.selectFont) {
window.selectFont(savedFont);
}
});
})();