-Added a more subtle progress bar which only shows the page number on hover

-Added Current page indicator to the thumbnail list, as well as a hover state
-Thumbnail list now scrolls to the current page on open or page change
-Added @andy29485's request for controlling page with space/shift+space
-Added the book title to the top of the page -Fixed IE11 failing on imagecallback
pull/406/head
Yury Tilis 7 years ago
parent 4e11272023
commit 2fe03961bb

@ -12,6 +12,10 @@ body {
z-index: 2; z-index: 2;
} }
.view {
padding-top:0px;
}
#sidebar a, #sidebar a,
#sidebar ul, #sidebar ul,
#sidebar li, #sidebar li,
@ -20,10 +24,28 @@ body {
text-align: center; text-align: center;
} }
#sidebar ul {
position: relative;
}
#sidebar a { #sidebar a {
display: inline-block; display: inline-block;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
padding: 4px;
transition: all .2s ease;
}
#sidebar a:hover,
#sidebar a:focus {
outline: none;
box-shadow: 0px 2px 8px 1px black;
}
#sidebar a.active,
#sidebar a.active img + span {
background-color: #45B29D;
} }
#sidebar li img { #sidebar li img {
@ -49,11 +71,54 @@ body {
#progress { #progress {
position: absolute; position: absolute;
display: inline; display: inline;
left: 20%; top: 0;
right: 20%; left: 0;
height: 20px; right: 0;
margin-top: 4px; min-height: 4px;
font-family: sans-serif;
font-size: 10px;
line-height: 10px;
text-align: right; text-align: right;
transition: min-height 150ms ease-in-out;
}
#progress .bar-load,
#progress .bar-read {
display: flex;
align-items: flex-end;
justify-content: flex-end;
position: absolute;
top: 0;
left: 0;
bottom: 0;
transition: width 150ms ease-in-out;
}
#progress .bar-load {
color: #000;
background-color: #CCC;
}
#progress .bar-read {
color: #FFF;
background-color: #45B29D;
}
#progress .text {
display: none;
padding: 0 5px;
}
#progress.loading,
#titlebar:hover #progress {
min-height: 10px;
}
#progress.loading .text,
#titlebar:hover #progress .text {
display: inline-block;
} }
.hide { .hide {
@ -62,6 +127,7 @@ body {
#mainContent { #mainContent {
overflow: auto; overflow: auto;
outline: none;
} }
#mainText { #mainText {
@ -82,6 +148,10 @@ body {
height: auto; height: auto;
} }
#metainfo {
max-width: 70%;
}
#prev { #prev {
left: 40px; left: 40px;
} }
@ -176,43 +246,11 @@ th {
color: #FFF; color: #FFF;
} }
.dark-theme .overlay { .dark-theme #progress .bar-read {
background-color: rgba(0,0,0,0.8); background-color: red;
}
.view {
padding-top:0px;
}
#Progress {
margin: auto;
width: 80%;
background-color: #ddd;
display: inline-block;
margin-top: 5px;
} }
#meter {
width: 0%;
height: 5px;
background-color: #1C5484;
}
#meter2 {
width: 0%;
height: 15px;
background-color: #028138;
}
#page { .dark-theme .overlay {
text-align:left; background-color: rgba(0,0,0,0.8);
margin-top:-20px;
font-size:80%;
width: 0%;
}
#progress_title {
text-align:right;
margin-top:-15px;
font-size:80%;
} }

@ -17,8 +17,6 @@
*/ */
/* global screenfull */ /* global screenfull */
// var start = 0;
if (window.opera) { if (window.opera) {
window.console.log = function(str) { window.console.log = function(str) {
opera.postError(str); opera.postError(str);
@ -44,6 +42,7 @@ if (typeof window.kthoom === "undefined" ) {
// key codes // key codes
kthoom.Key = { kthoom.Key = {
ESCAPE: 27, ESCAPE: 27,
SPACE: 32,
LEFT: 37, LEFT: 37,
UP: 38, UP: 38,
RIGHT: 39, RIGHT: 39,
@ -106,41 +105,9 @@ kthoom.ImageFile = function(file) {
this.data = file; this.data = file;
}; };
kthoom.initProgressMeter = function() {
$("#Progress").removeClass("hide");
$("#Progress").click(function(e) {
var page = Math.max(1, Math.ceil((e.offsetX / $(this).width()) * totalImages)) - 1;
currentImage = page;
updatePage();
});
};
kthoom.setProgressMeter = function(optLabel) {
var pct = imageFiles.length / totalImages * 100;
if (pct === 100) {
//smartpct = 100;
getElem("progress_title").innerHTML = "Complete";
} else {
var labelText = pct.toFixed(2) + "% " + imageFiles.length + "/" + totalImages + "";
if (optLabel) {
labelText = optLabel + " " + labelText;
}
getElem("progress_title").innerHTML=labelText;
}
if (!isNaN(pct)) {
getElem("meter").style.width = pct + "%";
}
getElem("meter2").style.width= 100 * (totalImages === 0 ? 0 : ((currentImage + 1) / totalImages)) + "%";
getElem("page").innerHTML=(currentImage + 1) + "/" + totalImages ;
};
function loadFromArrayBuffer(ab) { function loadFromArrayBuffer(ab) {
var f = []; var f = [];
if (typeof ab !== "object") {
ab = JSON.parse(ab);
}
f.fileData = ab.content; f.fileData = ab.content;
f.filename = ab.name; f.filename = ab.name;
// add any new pages based on the filename // add any new pages based on the filename
@ -158,10 +125,10 @@ function loadFromArrayBuffer(ab) {
"</li>" "</li>"
); );
} }
// var percentage = (ab.page + 1) / (ab.last + 1); var percentage = ((ab.page + 1) / (ab.last + 1)) * 100;
updateProgress(percentage);
totalImages = ab.last + 1; totalImages = ab.last + 1;
kthoom.setProgressMeter("Unzipping");
// lastCompletion = percentage * 100;
// display first page if we haven't yet // display first page if we haven't yet
if (imageFiles.length === currentImage + 1) { if (imageFiles.length === currentImage + 1) {
@ -169,10 +136,28 @@ function loadFromArrayBuffer(ab) {
} }
} }
function scrollTocToActive() {
// Scroll to the thumbnail in the TOC on page change
$('#tocView').stop().animate({
scrollTop: $('#tocView a.active').position().top
}, 200);
}
function updatePage() { function updatePage() {
getElem("page").innerHTML=(currentImage + 1) + "/" + totalImages ; $('.page').text((currentImage + 1 ) + "/" + totalImages);
getElem("meter2").style.width= 100 * (totalImages === 0 ? 0 : ((currentImage + 1) / totalImages)) + "%";
// Mark the current page in the TOC
$('#tocView a[data-page]')
// Remove the currently active thumbnail
.removeClass('active')
// Find the new one
.filter('[data-page='+ (currentImage + 1) +']')
// Set it to active
.addClass('active');
scrollTocToActive();
updateProgress();
if (imageFiles[currentImage]) { if (imageFiles[currentImage]) {
setImage(imageFiles[currentImage].dataURI); setImage(imageFiles[currentImage].dataURI);
} else { } else {
@ -185,6 +170,22 @@ function updatePage() {
kthoom.saveSettings(); kthoom.saveSettings();
} }
function updateProgress(loadPercentage) {
// Set the load/unzip progress if it's passed in
if (loadPercentage) {
$("#progress .bar-load").css({ width: loadPercentage + "%" });
if (loadPercentage === 100) {
$("#progress")
.removeClass('loading')
.find(".load").text('');
}
}
// Set page progress bar
$("#progress .bar-read").css({ width: totalImages === 0 ? 0 : Math.round((currentImage + 1) / totalImages * 100) + "%"});
}
function setImage(url) { function setImage(url) {
var canvas = $("#mainImage")[0]; var canvas = $("#mainImage")[0];
var x = $("#mainImage")[0].getContext("2d"); var x = $("#mainImage")[0].getContext("2d");
@ -332,23 +333,18 @@ function updateScale(clear) {
} }
function keyHandler(evt) { function keyHandler(evt) {
var code = evt.keyCode; var hasModifier = evt.ctrlKey || evt.shiftKey || evt.metaKey;
switch (evt.keyCode) {
if ($("#progress").css("display") === "none") {
return;
}
// canKeyNext = (($("body").css("offsetWidth") + $("body").css("scrollLeft")) / $("body").css("scrollWidth")) >= 1;
// canKeyPrev = (scrollX <= 0);
if (evt.ctrlKey || evt.shiftKey || evt.metaKey) return;
switch (code) {
case kthoom.Key.LEFT: case kthoom.Key.LEFT:
if (hasModifier) break;
showPrevPage(); showPrevPage();
break; break;
case kthoom.Key.RIGHT: case kthoom.Key.RIGHT:
if (hasModifier) break;
showNextPage(); showNextPage();
break; break;
case kthoom.Key.L: case kthoom.Key.L:
if (hasModifier) break;
settings.rotateTimes--; settings.rotateTimes--;
if (settings.rotateTimes < 0) { if (settings.rotateTimes < 0) {
settings.rotateTimes = 3; settings.rotateTimes = 3;
@ -356,6 +352,7 @@ function keyHandler(evt) {
updatePage(); updatePage();
break; break;
case kthoom.Key.R: case kthoom.Key.R:
if (hasModifier) break;
settings.rotateTimes++; settings.rotateTimes++;
if (settings.rotateTimes > 3) { if (settings.rotateTimes > 3) {
settings.rotateTimes = 0; settings.rotateTimes = 0;
@ -363,6 +360,7 @@ function keyHandler(evt) {
updatePage(); updatePage();
break; break;
case kthoom.Key.F: case kthoom.Key.F:
if (hasModifier) break;
if (!settings.hflip && !settings.vflip) { if (!settings.hflip && !settings.vflip) {
settings.hflip = true; settings.hflip = true;
} else if (settings.hflip === true && settings.vflip === true) { } else if (settings.hflip === true && settings.vflip === true) {
@ -377,23 +375,43 @@ function keyHandler(evt) {
updatePage(); updatePage();
break; break;
case kthoom.Key.W: case kthoom.Key.W:
if (hasModifier) break;
settings.fitMode = kthoom.Key.W; settings.fitMode = kthoom.Key.W;
updateScale(false); updateScale(false);
break; break;
case kthoom.Key.H: case kthoom.Key.H:
if (hasModifier) break;
settings.fitMode = kthoom.Key.H; settings.fitMode = kthoom.Key.H;
updateScale(false); updateScale(false);
break; break;
case kthoom.Key.B: case kthoom.Key.B:
if (hasModifier) break;
settings.fitMode = kthoom.Key.B; settings.fitMode = kthoom.Key.B;
updateScale(false); updateScale(false);
break; break;
case kthoom.Key.N: case kthoom.Key.N:
if (hasModifier) break;
settings.fitMode = kthoom.Key.N; settings.fitMode = kthoom.Key.N;
updateScale(false); updateScale(false);
break; break;
case kthoom.Key.SPACE:
var container = $('#mainContent');
var atTop = container.scrollTop() === 0;
var atBottom = container.scrollTop() >= container[0].scrollHeight - container.height();
if (evt.shiftKey && atTop) {
evt.preventDefault();
// If it's Shift + Space and the container is at the top of the page
showPrevPage();
} else if (!evt.shiftKey && atBottom) {
evt.preventDefault();
// If you're at the bottom of the page and you only pressed space
showNextPage();
container.scrollTop(0);
}
break;
default: default:
//console.log('KeyCode = ' + code); //console.log('KeyCode', evt.keyCode);
break; break;
} }
} }
@ -404,31 +422,31 @@ function ImageLoadCallback() {
if (jso === null) { if (jso === null) {
setImage("error"); setImage("error");
} else { } else {
// IE 11 sometimes sees the response as a string
if (typeof jso !== "object") {
jso = JSON.parse(jso);
}
if (jso.page !== jso.last) { if (jso.page !== jso.last) {
this.open("GET", this.fileid + "/" + (jso.page + 1)); this.open("GET", this.fileid + "/" + (jso.page + 1));
this.addEventListener("load", ImageLoadCallback); this.addEventListener("load", ImageLoadCallback);
this.send(); this.send();
} }
/*else
{
var diff = ((new Date).getTime() - start) / 1000;
console.log("Transfer done in " + diff + "s");
}*/
loadFromArrayBuffer(jso); loadFromArrayBuffer(jso);
} }
} }
function init(fileid) { function init(fileid) {
// start = (new Date).getTime();
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open("GET", fileid); request.open("GET", fileid);
request.responseType = "json"; request.responseType = "json";
request.fileid = fileid.substring(0, fileid.length - 2); request.fileid = fileid.substring(0, fileid.length - 2);
request.addEventListener("load", ImageLoadCallback); request.addEventListener("load", ImageLoadCallback);
request.send(); request.send();
kthoom.initProgressMeter();
document.body.className += /AppleWebKit/.test(navigator.userAgent) ? " webkit" : ""; document.body.className += /AppleWebKit/.test(navigator.userAgent) ? " webkit" : "";
kthoom.loadSettings(); kthoom.loadSettings();
updateScale(true); updateScale(true);
$(document).keydown(keyHandler); $(document).keydown(keyHandler);
$(window).resize(function() { $(window).resize(function() {
@ -440,6 +458,13 @@ function init(fileid) {
$("#sidebar").toggleClass("open"); $("#sidebar").toggleClass("open");
$("#main").toggleClass("closed"); $("#main").toggleClass("closed");
$(this).toggleClass("icon-menu icon-right"); $(this).toggleClass("icon-menu icon-right");
// We need this in a timeout because if we call it during the CSS transition, IE11 shakes the page ¯\_(ツ)_/¯
setTimeout(function(){
// Focus on the TOC or the main content area, depending on which is open
$('#main:not(.closed) #mainContent, #sidebar.open #tocView').focus();
scrollTocToActive();
}, 500);
}); });
// Open Settings modal // Open Settings modal
@ -487,6 +512,9 @@ function init(fileid) {
} }
} }
// Focus the scrollable area so that keyboard scrolling work as expected
$('#mainContent').focus();
$("#mainImage").click(function(evt) { $("#mainImage").click(function(evt) {
// Firefox does not support offsetX/Y so we have to manually calculate // Firefox does not support offsetX/Y so we have to manually calculate
// where the user clicked in the image. // where the user clicked in the image.

@ -25,11 +25,13 @@
</head> </head>
<body> <body>
<div id="sidebar"> <div id="sidebar">
<!--div id="panels"--> <!--
<!-- <a id="show-Toc" class="show_view icon-list-1 active" data-view="Toc">TOC</a> --> <div id="panels">
<!-- <a id="show-Bookmarks" class="show_view icon-bookmark" data-view="Bookmarks">Bookmarks</a> --> <a id="show-Toc" class="show_view icon-list-1 active" data-view="Toc">TOC</a>
<!--/div--> <a id="show-Bookmarks" class="show_view icon-bookmark" data-view="Bookmarks">Bookmarks</a>
<div id="tocView" class="view"> </div>
-->
<div id="tocView" class="view" tabindex="-1">
<ul id="thumbnails"></ul> <ul id="thumbnails"></ul>
</div> </div>
<div id="bookmarksView" class="view"> <div id="bookmarksView" class="view">
@ -43,21 +45,27 @@
<a id="slider" class="icon-menu">Menu</a> <a id="slider" class="icon-menu">Menu</a>
</div> </div>
<div id="metainfo"> <div id="metainfo">
<span id="book-title"></span> <span id="book-title">{{ book.title | shortentitle }}</span>
<span id="title-seperator">&nbsp;&nbsp;&nbsp;&nbsp;</span> <span id="title-seperator">&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span id="chapter-title"></span> <span id="chapter-title"></span>
</div> </div>
<div id="Progress" class="hide">
<div id="meter2"></div><div class="progress_title" id="progress_title" ></div>
<div id="meter"></div><div class="page" id="page" ></div>
</div>
<div id="title-controls"> <div id="title-controls">
<a id="setting" class="icon-cog">Settings</a> <a id="setting" class="icon-cog">Settings</a>
<a id="fullscreen" class="icon-resize-full">Fullscreen</a> <a id="fullscreen" class="icon-resize-full">Fullscreen</a>
</div> </div>
<div id="progress" role="progressbar" class="loading">
<div class="bar-load">
<div class="text load">
Loading...
</div>
</div>
<div class="bar-read">
<div class="text page"></div>
</div>
</div>
</div> </div>
<div id="mainContent"> <div id="mainContent" tabindex="-1">
<div id="mainText" style="display:none"></div> <div id="mainText" style="display:none"></div>
<canvas id="mainImage"></canvas> <canvas id="mainImage"></canvas>
</div> </div>

@ -1972,7 +1972,7 @@ def read_book(book_id, book_format):
extensionList = ["cbt","cbz"] extensionList = ["cbt","cbz"]
for fileext in extensionList: for fileext in extensionList:
if book_format.lower() == fileext: if book_format.lower() == fileext:
return render_title_template('readcbr.html', comicfile=book_id, extension=fileext, title=_(u"Read a Book")) return render_title_template('readcbr.html', comicfile=book_id, extension=fileext, title=_(u"Read a Book"), book=book)
flash(_(u"Error opening eBook. File does not exist or file is not accessible."), category="error") flash(_(u"Error opening eBook. File does not exist or file is not accessible."), category="error")
return redirect(url_for("index")) return redirect(url_for("index"))

Loading…
Cancel
Save