diff options
Diffstat (limited to 'reader.js')
-rwxr-xr-x | reader.js | 267 |
1 files changed, 251 insertions, 16 deletions
@@ -1,36 +1,271 @@ -function ComicArchive(file) { +const TCRSYNC_SERVER = "https://tt-rss.org/tcrsync/"; + +function Model() { const self = this; - self.zip = new AdmZip(file); + self.fileName = ko.observable(""); + self._zip = null; + self._zipEntries = ko.observableArray(); + self.syncAccount = ko.observable(""); + + self.cacheKey = function(suffix) { + const sha1 = require('js-sha1'); + + return sha1(self.fileName()) + ":" + suffix; + }; + + self.syncClient = { + _sha1_compat: function(str) { + const sha1 = require('js-sha1'); + const hash = sha1.create(); + hash.update(str); + + const chars = hash.array(''); + let rv = ""; + + for (let i = 0; i < chars.length; i++) { + rv += Number(chars[i]).toString(16); + } + + return rv; + }, + set: function(page) { + if (!self.syncAccount() || !self.fileName()) + return; + + const owner = this._sha1_compat(self.syncAccount()); + const hash = this._sha1_compat(self.fileName()); + + console.log('syncClient, set', owner, hash, page) + }, + get: function() { + if (!self.syncAccount() || !self.fileName()) + return; + + const owner = this._sha1_compat(self.syncAccount()); + const hash = this._sha1_compat(self.fileName()); + + console.log('syncClient, get', owner, hash); + + return new Promise((resolve, reject) => { + $.post(TCRSYNC_SERVER, {op: "get", version: 2, owner: owner, hash: hash}).then((resp) => { + console.log('tcrsync_get', resp); + + const page = isNaN(resp) ? -1 : parseInt(resp); + + resolve(page); + }); + }); + }, + }; + + self.openFile = function (file) { + console.log('openFile', file); + + self._zipEntries.removeAll(); + self.currentPage(-2); + + const AdmZip = require('adm-zip'); + self._zip = new AdmZip(file); + + const zipEntries = self._zip.getEntries(); -}; + for (let i = 0; i < zipEntries.length; i++) { + const ze = zipEntries[i]; -$(document).ready(function() { - const { remote, ipcRenderer } = require('electron'); - const { BrowserWindow } = remote; + if (ze.entryName.match(/\.(jpe?g|gif|bmp|png|webp)$/i)) { + self._zipEntries.push(ze); + } + } - console.log("document ready", BrowserWindow, remote, ipcRenderer); + self._zipEntries.sort(function(a, b) { + return a.entryName.localeCompare(b.entryName); + }); + + self.fileName(file.split(/[\\/]/).pop()); + + + localforage.getItem(self.cacheKey("POSITION")).then((page) => { + if (page) + self.currentPage(page); + else + self.currentPage(0); + }); + + }; + + self.documentTitle = ko.computed(function () { + if (self.fileName()) + return "Pow! Comics Reader: " + self.fileName(); + else + return "Pow! Comics Reader"; + }); + + self.totalPages = ko.computed(function () { + return self._zipEntries().length; + }); + + self._currentPage = ko.observable(-2); + + self.currentPage = ko.computed({ + read: function() { + return self._currentPage(); + }, + write: function(page) { + self._currentPage(page); + + localforage.getItem(model.cacheKey("POSITION")).then((stored) => { + if (stored < page) { + localforage.setItem(model.cacheKey("POSITION"), page).then(() => { + console.log('saved position', page); + + if (page % 10 == 0) { + model.syncClient.set(page); + } + }); + } + }); + }, + }); + + self.currentPageDisp = ko.computed(function () { + const page = self.currentPage(); + + if (page < 0) + return 0; + else + return page + 1; + }); - ipcRenderer.on("test-event", (event, args) => { - console.log('test-event', event, args); + self.progressPct = ko.computed(function () { + if (self.totalPages() > 0) { + return parseInt(self.currentPage() / self.totalPages() * 100) + "%"; + } else { + return ""; + } + }); + + self.getLeftPage = ko.computed(function () { + const page = self.currentPage(); + + if (self._zip && page >= 0 && page < self._zipEntries().length) { + const ze = self._zipEntries()[page]; + + if (ze) { + return 'data:image/jpeg;base64,' + ze.getData().toString("base64"); + } + } + }); + + self.getRightPage = ko.computed(function () { + const page = self.currentPage() + 1; + + if (self._zip && page >= 0 && page < self._zipEntries().length) { + const ze = self._zipEntries()[page]; + + if (ze) { + return 'data:image/jpeg;base64,' + ze.getData().toString("base64"); + } + } + }); +} + +const model = new Model(); + +$(document).ready(function () { + const {remote, ipcRenderer} = require('electron'); + const {BrowserWindow} = remote; + + localforage.getItem("TTC:SYNC-ACCOUNT").then((acct) => { + if (acct) + model.syncAccount(acct); + }); + + ko.applyBindings(model, document.querySelector('html')); + + ipcRenderer.on("open-settings", () => { + $('#settings-modal').modal('show'); }); ipcRenderer.on("open-file", (event, args) => { - console.log('open-file', event, args); + model.openFile(args[0]); + }); - const file = args[0]; + ipcRenderer.on("sync-to-last", (event, args) => { + model.syncClient.get().then((page) => { + if (confirm("You are currently on page %p. Furthest read page stored on the server is %r. Open it instead?" + .replace("%p", model.currentPageDisp()) + .replace("%r", page + 1))) { - const AdmZip = require('adm-zip'); - const zip = new AdmZip(file); + model.currentPage(page); + + } + }); + }); + + $(".location").click(function() { + const current = model.currentPageDisp(); + const total = model.totalPages(); - const zip_entries = zip.getEntries(); + const page = prompt("Jump to location [1-" + total + "]", parseInt(current)); + + if (page) { + $("#reader img").attr("data-page", parseInt(page - 1)); + } + }); - $.each(zip_entries, (i, ze) => { - console.log(ze); + $(".sync-account").on("change", function() { + const acct = $(this).val(); + console.log('sa', acct); + localforage.setItem("TTC:SYNC-ACCOUNT", acct).then(() => { + model.syncAccount(acct); }); + }); + + $("#left").on("click", (e) => { + e.stopPropagation(); + + let page = model.currentPage(); + + if ($(".right-page").is(":visible") && page > 1) + page = page - 1; + + if (page > 0) + model.currentPage(page - 1); + + }); + + $("#right").on("click", (e) => { + e.stopPropagation(); + + let page = model.currentPage(); + + if ($(".right-page").is(":visible") && page < total_pages - 2) + page = page + 1; + + if (page < model.totalPages()) { + model.currentPage(page + 1); + } + }); + + $(document).on("keydown", (e) => { + if ($(".modal").is(":visible")) + return; + switch (e.which) { + case 27: + $(".header,.footer").fadeIn(); + break; + case 37: + $("#left").click(); + break; + case 32: + case 39: + $("#right").click(); + break; + } }); }); |