This repository has been archived on 2024-01-19. You can view files and clone it, but cannot push or open issues or pull requests.
epubreader/vendor/cbrjs/cbr.js
sysadminstory cd18e33e27 Separate static file and data saving URLs
Static files (like css, js, ...) are now loaded using the 'data-staticpath' parameter, that was added to every template.

Static files must be loaded using the real physical URL (un the
"customapps" folder if used) instead of the dynamic files, that must be
loaded using the index.php file (hidden if the URL rewriting is
activated)
2022-03-22 11:22:04 +01:00

1477 lines
48 KiB
JavaScript

var CBRJS = CBRJS || {};
CBRJS.VERSION = "0.0.1";
CBRJS.basePath = CBRJS.basePath || "";
CBRJS.session = CBRJS.session || {};
CBRJS.Reader = function(bookPath, _options) {
var reader = this,
$progressbar = $('.bar'),
search = window.location.search,
parameters,
options,
found;
this.options = options = $.extend(true, _options || {}, {
bookPath: bookPath,
session: {}
});
// Overide options with search parameters
if(search) {
parameters = search.slice(1).split("&");
parameters.forEach(function(p){
var split = p.split("=");
var name = split[0];
var value = split[1] || '';
reader.options[name] = decodeURIComponent(value);
});
}
function extractImages(url, opts) {
var images = [],
xhr = new XMLHttpRequest(),
filename = decodeURIComponent(url.split('/').pop()),
re_file_ext = new RegExp(/\.([a-z]+)$/),
format = filename.toLowerCase().match(re_file_ext)[1],
archive_class = ({ cbz: 'Unzipper', cbr: 'Unrarrer' })[format],
options = $.extend({
start: function () {},
extract: function (page_url) {},
progress: function (percent_complete) {},
finish: function (images) {}
}, opts);
if (!archive_class) {
alert('invalid file type, only cbz and cbr are supported.');
return false;
}
xhr.open('GET',url, true);
options.start(filename);
xhr.responseType = "arraybuffer";
xhr.onprogress = function (e) {
if (e.lengthComputable) {
$progressbar.css('width', Math.floor((e.loaded / e.total) * 100) + '%');
}
};
xhr.onloadstart = function (e) {
$progressbar.css('width', '0%');
};
xhr.onloadend = function (e) {
$('.icon-cloud_download').addClass('ok');
reader.options.session.size = e.total;
};
xhr.onload = function () {
if ((this.status === 200) && this.response) {
var done = false;
var ua = new bitjs.archive[archive_class](this.response, document.head.dataset.staticpath + 'vendor/bitjs/');
ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.START, function (e) {
$progressbar.css('width', '0%');
$('.icon-unarchive').addClass('active');
});
ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.EXTRACT, function (e) {
var mimetype, blob, url;
var file_extension = e.unarchivedFile.filename.toLowerCase().match(re_file_ext)[1];
switch (file_extension) {
case 'jpg':
case 'jpeg':
mimetype = 'image/jpeg';
break;
case 'png':
mimetype = 'image/png';
break;
case 'gif':
mimetype = 'image/gif';
break;
default:
return false;
}
blob = new Blob([e.unarchivedFile.fileData], { type: mimetype });
url = window.URL.createObjectURL(blob);
images.push(url);
options.extract(url, blob);
});
ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.PROGRESS, function (e) {
options.progress(Math.floor(e.currentBytesUnarchived / e.totalUncompressedBytesInArchive * 100));
});
ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.FINISH, function (e) {
options.finish(images);
});
ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.ERROR, function (e) {
$('.icon-unarchive').removeClass('active');
$('.icon-unarchive').addClass('error');
$('#message').text('Failed to extract images from archive, file corrupted?');
});
}
ua.start();
};
xhr.send();
}
function openComicArchive(url, options) {
var title, page = 0;
extractImages(url, {
start: function (filename) {
this.filename = filename;
$('.toolbar').addClass('hide');
$('.navigation').addClass('hide');
$('.icon-cloud_download').addClass('active');
$('.message-text').text(filename);
$('#progressbar').show();
},
extract: function (url, blob) {
$('.message-text').text('extracting page #' + ++page);
},
progress: function (percent_complete) {
$progressbar.css('width', percent_complete + '%');
},
finish: function (pages) {
$('.icon-unarchive').addClass('ok');
var name = this.filename.replace(/\.[a-z]+$/, '');
var id = encodeURIComponent(name.toLowerCase());
var book = new ComicBook('viewer', pages, options);
document.title = name;
$('.toolbar').removeClass('hide');
$('.navigation').removeClass('hide');
$('#progressbar').hide();
$('#viewer').show();
book.draw();
$(window).on('resize', function () {
book.draw();
});
$(window).on('beforeunload', function(e) {
book.destroy();
});
}
});
}
function getPref (arr, name) {
if ((arr.constructor === Array) && (found = arr.filter(function(e) { return e.name === name; }))) {
if (found.hasOwnProperty("value")) {
return found.value;
}
}
};
openComicArchive(bookPath, {
currentPage: parseInt(options.session.cursor.value) || 0,
enhance: getPref(options.session.preferences, "enhance") || {},
manga: getPref(options.session.preferences, "manga") || false,
thumbnails: getPref(options.session.defaults, "thumbnails"),
thumbnailWidth: parseInt(getPref(options.session.defaults, "thumbnailWidth")) || 200,
session: options.session
});
return this;
};
var ComicBook;
ComicBook = (function ($) {
'use strict';
/**
* Merge two arrays. Any properties in b will replace the same properties in
* a. New properties from b will be added to a.
*
* @param a {Object}
* @param b {Object}
*/
function merge(a, b) {
var prop;
if (typeof b === 'undefined') {
b = {};
}
for (prop in a) {
if (a.hasOwnProperty(prop)) {
if (prop in b) {
continue;
}
b[prop] = a[prop];
}
}
return b;
}
/**
* Exception class. Always throw an instance of this when throwing exceptions.
*
* @param {String} type
* @param {Object} object
* @returns {ComicBookException}
*/
var ComicBookException = {
INVALID_ACTION: 'invalid action',
INVALID_PAGE: 'invalid page',
INVALID_PAGE_TYPE: 'invalid page type',
UNDEFINED_CONTROL: 'undefined control',
INVALID_ZOOM_MODE: 'invalid zoom mode',
INVALID_NAVIGATION_EVENT: 'invalid navigation event'
};
function ComicBook(id, srcs, opts) {
var self = this;
var canvas_id = id; // canvas element id
this.srcs = srcs; // array of image srcs for pages
var defaults = {
displayMode: (window.innerWidth > window.innerHeight) ? 'double' : 'single', // single / double
zoomMode: 'fitWindow', // manual / fitWidth / fitWindow
manga: false, // true / false
fullscreen: false, // true / false
enhance: {}, // image filters to use
thumbnails: true, // true / false (use thumbnails in index)
thumbnailWidth: 200, // width of thumbnail
sidebarWide: false, // use wide sidbar
currentPage: 0, // current page
keyboard: {
32: 'next', // space
34: 'next', // page-down
39: 'next', // cursor-right
33: 'previous', // page-up
37: 'previous', // cursor-left
36: 'first', // home
35: 'last', // end
83: 'sidebar', // s
84: 'toolbar', // t
76: 'toggleLayout', // l
70: 'toggleFullscreen', // f
27: 'closeSidebar' // esc
},
vendorPath: document.head.dataset.staticpath + 'vendor/',
forward_buffer: 3,
session: {
getCursor: function() {},
setCursor: function(value) {},
getBookmark: function(name, type) {},
setBookmark: function(name, value, type, content) {},
getDefault: function(name) {},
setDefault: function(name, value) {},
getPreference: function(name) {},
setPreference: function(name, value) {}
}
};
var options = {};
this.isMobile = false;
// mobile enhancements
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(navigator.userAgent)) {
this.isMobile = true;
document.body.classList.add('mobile');
window.addEventListener('load', function () {
setTimeout(function () {
window.scrollTo(0, 1);
}, 0);
});
}
window.addEventListener('resize', function () {
self.setLayout((window.innerWidth > window.innerHeight) ? 'double' : 'single');
});
$.extend(true, options, defaults, opts); // options array for internal use
var no_pages = srcs.length;
var pages = []; // array of preloaded Image objects
var canvas; // the HTML5 canvas object
var context; // the 2d drawing context
var tcv = document.createElement("canvas"); // canvas used for thumbnailer
var tctx = tcv.getContext('2d'); // context used for thumbnailer
var toc = document.getElementById('toc'); // context used for thumbnailer
var loaded = []; // the images that have been loaded so far
var scale = 1; // page zoom scale, 1 = 100%
var is_double_page_spread = false;
var controlsRendered = false; // have the user controls been inserted into the dom yet?
var page_requested = false; // used to request non preloaded pages
var shiv = false;
/**
* Gets the window.innerWidth - scrollbars
*/
function windowWidth() {
var height = window.innerHeight + 1;
if (shiv === false) {
shiv = $(document.createElement('div'))
.attr('id', 'cbr-width-shiv')
.css({
width: '100%',
position: 'absolute',
top: 0,
zIndex: '-1000'
});
$('body').append(shiv);
}
shiv.height(height);
return shiv.innerWidth();
}
/**
* enables the back button
*/
function checkHash() {
var hash = getHash();
if (hash !== options.currentPage && loaded.indexOf(hash) > -1) {
options.currentPage = hash;
self.draw();
}
}
function getHash() {
var hash = parseInt(location.hash.substring(1), 10) - 1 || 0;
if (hash < 0) {
setHash(0);
hash = 0;
}
return hash;
}
function setHash(pageNo) {
location.hash = pageNo;
}
// page hash on first load
var hash = getHash();
/**
* Setup the canvas element for use throughout the class.
*/
function init() {
// setup canvas
canvas = document.getElementById(canvas_id);
context = canvas.getContext('2d');
// render user controls
if (controlsRendered === false) {
self.renderControls();
self.tocCreate(no_pages);
controlsRendered = true;
}
// add page controls
window.addEventListener('keydown', self.navigation, false);
window.addEventListener('hashchange', checkHash, false);
// fill in metadata
options.session.pagecount = srcs.length;
$('.book-title').text(options.session.title);
$('.book-format').text(options.session.format);
$('.book-pagecount').text(options.session.pagecount);
$('.book-size').text(options.session.size);
}
window.addEventListener('touchstart', function (e) {
var $el = $(e.target);
if ($el.attr('id') === 'viewer') {
self.toggleToolbar();
}
}, false);
/**
* Connect controls to events
*/
ComicBook.prototype.renderControls = function () {
var controls = {}, $toolbar;
// set values from preferences or defaults
// do this before connecting listeners to avoid triggering callbacks
for (var prop in options.enhance) {
if(options.enhance.hasOwnProperty(prop)) {
switch (prop) {
case 'brightness':
document.getElementById('brightness').value = options.enhance.brightness['brightness'];
document.getElementById('contrast').value = options.enhance.brightness['contrast'];
break;
case 'sharpen':
document.getElementById('sharpen').value = options.enhance.sharpen['strength'];
break;
case 'desaturate':
$('#image-desaturate').prop('checked', true);
break;
case 'removenoise':
$('#image-removenoise').prop('checked', true);
break;
default:
console.log("unknown enhancement: " + JSON.stringify(prop));
}
}
};
// thumbnail controls
$('#thumbnail-generate').prop('checked', options.thumbnails);
$('#thumbnail-width').val(options.thumbnailWidth);
if (!options.thumbnails) {
$('#toc-populate').addClass('open');
$('#thumbnail-width').prop('disabled', true);
}
// connect callbacks
$('.control').each(function () {
controls[$(this).attr('name')] = $(this);
// add event listeners to controls that specify callbacks
$(this).find('*').andSelf().filter('[data-action][data-trigger]').each(function () {
var $this = $(this);
var trigger = $this.data('trigger');
var action = $this.data('action');
// trigger a direct method if exists
if (typeof self[$this.data('action')] === 'function') {
$this.on(trigger, self[action]);
}
// throw an event to be caught outside if the app code
$this.on(trigger, function (e) {
$(self).trigger(trigger, e);
});
});
});
this.controls = controls;
$toolbar = this.getControl('toolbar');
$toolbar
.find('.manga-' + options.manga).show().end()
.find('.manga-' + !options.manga).hide().end()
.find('.layout').hide().end().find('.layout-' + options.displayMode).show().end()
.find('.fullscreen-' + options.fullscreen).show().end()
.find('.fullscreen-' + !options.fullscreen).hide();
if (parent !== window) {
$('.close').removeClass('hide');
$('.close').on('click', function() { parent.OCA.Epubreader.Plugin.hide(); });
}
};
ComicBook.prototype.getControl = function (control) {
if (typeof this.controls[control] !== 'object') {
throw ComicBookException.UNDEFINED_CONTROL + ' ' + control;
}
return this.controls[control];
};
ComicBook.prototype.showControl = function (control) {
this.getControl(control).show().addClass('open');
};
ComicBook.prototype.hideControl = function (control) {
this.getControl(control).removeClass('open').hide();
};
ComicBook.prototype.toggleControl = function (control) {
this.getControl(control).toggle().toggleClass('open');
};
ComicBook.prototype.toggleLayout = function () {
self.setLayout((options.displayMode === 'single') ? 'double' : 'single');
};
ComicBook.prototype.setLayout = function (layout) {
var $toolbar = self.getControl('toolbar');
options.displayMode = (layout === 'single') ? 'single' : 'double';
$toolbar.find('.layout').hide().end().find('.layout-' + options.displayMode).show();
self.drawPage();
};
/**
* Create thumbnail for image
*
* @return Image
*/
ComicBook.prototype.getThumb = function (image) {
var thumb = new Image();
var scale = image.width / options.thumbnailWidth;
tcv.width = options.thumbnailWidth;
tcv.height = Math.floor(image.height / scale);
tctx.drawImage(image, 0, 0, tcv.width, tcv.height);
thumb.src = tcv.toDataURL();
tctx.clearRect(0, 0, tcv.width, tcv.height);
return thumb;
};
/**
* Create empty TOC with placeholder images
*/
ComicBook.prototype.tocCreate = function (no_pages) {
// use small image with reasonable aspect ratio
tcv.width = 5;
tcv.height = 7;
// transparent, style with .placeholder in CSS
tctx.fillStyle = "rgba(200, 200, 200, 0)";
tctx.fillRect(0, 0, tcv.width, tcv.height);
var imgsrc = tcv.toDataURL();
for(var i = 0; i < no_pages; i++) {
var item = document.createElement('li');
item.setAttribute("id", "page-" + parseInt(i + 1));
var placeholder = new Image();
placeholder.src = imgsrc;
var label = document.createElement('span');
label.innerHTML = i + 1;
item.appendChild(placeholder);
item.appendChild(label);
toc.appendChild(item);
}
};
/**
* Insert thumbnail into TOC
*/
ComicBook.prototype.tocInsert = function (image, page, replace) {
var placeholder = toc.children[page].firstChild;
if (replace === true) {
placeholder.parentNode.replaceChild(
self.getThumb(image),
placeholder
);
}
toc.children[page].addEventListener('click', function (e) {
self.drawPage(page + 1, true);
});
};
/**
* Populate TOC on demand
*/
ComicBook.prototype.tocPopulate = function () {
var i = 0;
while (i < srcs.length) {
self.tocInsert(pages[i], i, true);
i++;
}
// set, but don't save for future sessions
options.thumbnails = true;
$('#toc-populate').removeClass('open');
};
/**
* Get the image for a given page.
*
* @return Image
*/
ComicBook.prototype.getPage = function (i) {
if (i < 0 || i > srcs.length - 1) {
throw ComicBookException.INVALID_PAGE + ' ' + i;
}
if (typeof pages[i] === 'object') {
return pages[i];
} else {
page_requested = i;
this.showControl('loadingOverlay');
}
};
/**
* @see #preload
*/
ComicBook.prototype.draw = function () {
init();
// resize navigation controls
$('.navigate').outerHeight(window.innerHeight);
$('.overlay').outerWidth(windowWidth()).height(window.innerHeight);
// preload images if needed
if (pages.length !== no_pages) {
this.preload();
} else {
this.drawPage();
}
};
/**
* Zoom the canvas
*
* @param new_scale {Number} Scale the canvas to this ratio
*/
ComicBook.prototype.zoom = function (new_scale) {
options.zoomMode = 'manual';
scale = new_scale;
if (typeof this.getPage(options.currentPage) === 'object') {
this.drawPage();
}
};
ComicBook.prototype.zoomIn = function () {
self.zoom(scale + 0.1);
};
ComicBook.prototype.zoomOut = function () {
self.zoom(scale - 0.1);
};
ComicBook.prototype.fitWidth = function () {
options.zoomMode = 'fitWidth';
self.drawPage();
};
ComicBook.prototype.fitWindow = function () {
options.zoomMode = 'fitWindow';
self.drawPage();
};
/**
* Preload all images, draw the page only after a given number have been loaded.
*
* @see #drawPage
*/
ComicBook.prototype.preload = function () {
var i = options.currentPage; // the current page counter for this method
var rendered = false;
var queue = [];
this.showControl('loadingOverlay');
function loadImage(i) {
var page = new Image();
page.src = srcs[i];
page.onload = function () {
pages[i] = this;
self.tocInsert(this, i, options.thumbnails);
loaded.push(i);
$('#cbr-progress-bar .progressbar-value').css('width', Math.floor((loaded.length / no_pages) * 100) + '%');
// double page mode needs an extra page added
var buffer = (options.displayMode === 'double' && options.currentPage < srcs.length - 1) ? 1 : 0;
// start rendering the comic when the requested page is ready
if ((rendered === false && ($.inArray(options.currentPage + buffer, loaded) !== -1) ||
(typeof page_requested === 'number' && $.inArray(page_requested, loaded) !== -1))) {
// if the user is waiting for a page to be loaded, render that one instead of the default options.currentPage
if (typeof page_requested === 'number') {
options.currentPage = page_requested - 1;
page_requested = false;
}
self.drawPage();
self.hideControl('loadingOverlay');
rendered = true;
}
if (queue.length) {
loadImage(queue[0]);
queue.splice(0, 1);
} else {
$('#cbr-status').delay(500).fadeOut();
}
};
}
// loads pages in both directions so you don't have to wait for all pages
// to be loaded before you can scroll backwards
function preload(start, stop) {
var j = 0;
var count = 1;
var forward = start;
var backward = start - 1;
while (forward <= stop) {
if (count > options.forward_buffer && backward > -1) {
queue.push(backward);
backward--;
count = 0;
} else {
queue.push(forward);
forward++;
}
count++;
}
while (backward > -1) {
queue.push(backward);
backward--;
}
loadImage(queue[j]);
}
preload(i, srcs.length - 1);
};
ComicBook.prototype.pageLoaded = function (page_no) {
return (typeof loaded[page_no - 1] !== 'undefined');
};
/**
* Draw the current page in the canvas
*/
ComicBook.prototype.drawPage = function (page_no, reset_scroll) {
var scrollY;
reset_scroll = (typeof reset_scroll !== 'undefined') ? reset_scroll : true;
scrollY = reset_scroll ? 0 : window.scrollY;
// if a specific page is given try to render it, if not bail and wait for preload() to render it
if (typeof page_no === 'number' && page_no < srcs.length && page_no > 0) {
options.currentPage = page_no - 1;
if (!this.pageLoaded(page_no)) {
this.showControl('loadingOverlay');
return;
}
}
if (options.currentPage < 0) {
options.currentPage = 0;
}
var zoom_scale;
var offsetW = 0,
offsetH = 0;
var page = self.getPage(options.currentPage);
var page2 = false;
if (options.displayMode === 'double' && options.currentPage < srcs.length - 1) {
page2 = self.getPage(options.currentPage + 1);
}
if (typeof page !== 'object') {
throw ComicBookException.INVALID_PAGE_TYPE + ' ' + typeof page;
}
var width = page.width,
height = page.height;
// reset the canvas to stop duplicate pages showing
canvas.width = 0;
canvas.height = 0;
// show double page spreads on a single page
is_double_page_spread = (
typeof page2 === 'object' &&
(page.width > page.height || page2.width > page2.height) &&
options.displayMode === 'double'
);
if (is_double_page_spread) {
options.displayMode = 'single';
}
if (options.displayMode === 'double') {
// for double page spreads, factor in the width of both pages
if (typeof page2 === 'object') {
width += page2.width;
}
// if this is the last page and there is no page2, still keep the canvas wide
else {
width += width;
}
}
// update the page scale if a non manual mode has been chosen
switch (options.zoomMode) {
case 'manual':
document.body.style.overflowX = 'auto';
zoom_scale = (options.displayMode === 'double') ? scale * 2 : scale;
break;
case 'fitWidth':
document.body.style.overflowX = 'hidden';
// scale up if the window is wider than the page, scale down if the window
// is narrower than the page
zoom_scale = (windowWidth() > width) ? ((windowWidth() - width) / windowWidth()) + 1 : windowWidth() / width;
// update the interal scale var so switching zoomModes while zooming will be smooth
scale = zoom_scale;
break;
case 'fitWindow':
document.body.style.overflowX = 'hidden';
var width_scale = (windowWidth() > width) ?
((windowWidth() - width) / windowWidth()) + 1 // scale up if the window is wider than the page
:
windowWidth() / width; // scale down if the window is narrower than the page
var windowHeight = window.innerHeight;
var height_scale = (windowHeight > height) ?
((windowHeight - height) / windowHeight) + 1 // scale up if the window is wider than the page
:
windowHeight / height; // scale down if the window is narrower than the page
zoom_scale = (width_scale > height_scale) ? height_scale : width_scale;
scale = zoom_scale;
break;
default:
throw ComicBookException.INVALID_ZOOM_MODE + ' ' + options.zoomMode;
}
var canvas_width = page.width * zoom_scale;
var canvas_height = page.height * zoom_scale;
var page_width = (options.zoomMode === 'manual') ? page.width * scale : canvas_width;
var page_height = (options.zoomMode === 'manual') ? page.height * scale : canvas_height;
canvas_height = page_height;
// make sure the canvas is always at least full screen, even if the page is more narrow than the screen
canvas.width = (canvas_width < windowWidth()) ? windowWidth() : canvas_width;
canvas.height = (canvas_height < window.innerHeight) ? window.innerHeight : canvas_height;
// always keep pages centered
if (options.zoomMode === 'manual' || options.zoomMode === 'fitWindow') {
// work out a horizontal position
if (canvas_width < windowWidth()) {
offsetW = (windowWidth() - page_width) / 2;
if (options.displayMode === 'double') {
offsetW = offsetW - page_width / 2;
}
}
// work out a vertical position
if (canvas_height < window.innerHeight) {
offsetH = (window.innerHeight - page_height) / 2;
}
}
// in manga double page mode reverse the page(s)
if (options.manga && options.displayMode === 'double' && typeof page2 === 'object') {
var tmpPage = page;
var tmpPage2 = page2;
page = tmpPage2;
page2 = tmpPage;
}
// draw the page(s)
context.drawImage(page, offsetW, offsetH, page_width, page_height);
if (options.displayMode === 'double' && typeof page2 === 'object') {
context.drawImage(page2, page_width + offsetW, offsetH, page_width, page_height);
}
this.pixastic = new Pixastic(context, options.vendorPath + 'pixastic/');
// apply any image enhancements previously defined
$.each(options.enhance, function (action, options) {
self.enhance[action](options);
});
var current_page =
(options.displayMode === 'double' &&
options.currentPage + 2 <= srcs.length) ? (options.currentPage + 1) + '-' + (options.currentPage + 2) : options.currentPage + 1;
this.getControl('toolbar')
.find('.current-page').text(current_page)
.end()
.find('.page-count').text(srcs.length);
// revert page mode back to double if it was auto switched for a double page spread
if (is_double_page_spread) {
options.displayMode = 'double';
}
// disable the fit width button if needed
$('button.cbr-fit-width').attr('disabled', (options.zoomMode === 'fitWidth'));
$('button.cbr-fit-window').attr('disabled', (options.zoomMode === 'fitWindow'));
// disable prev/next buttons if not needed
$('.navigate').show();
if (options.currentPage === 0) {
if (options.manga) {
$('.navigate-left').show();
$('.navigate-right').hide();
} else {
$('.navigate-left').hide();
$('.navigate-right').show();
}
}
if (options.currentPage === srcs.length - 1 || (typeof page2 === 'object' && options.currentPage === srcs.length - 2)) {
if (options.manga) {
$('.navigate-left').hide();
$('.navigate-right').show();
} else {
$('.navigate-left').show();
$('.navigate-right').hide();
}
}
if (options.currentPage !== getHash()) {
$(this).trigger('navigate');
}
// update hash location
if (getHash() !== options.currentPage) {
setHash(options.currentPage + 1);
}
options.session.setCursor(options.currentPage);
};
/**
* Increment the counter and draw the page in the canvas
*
* @see #drawPage
*/
ComicBook.prototype.drawNextPage = function () {
var page;
try {
page = self.getPage(options.currentPage + 1);
} catch (e) {
}
if (!page) {
return false;
}
if (options.currentPage + 1 < pages.length) {
options.currentPage += (options.displayMode === 'single' || is_double_page_spread) ? 1 : 2;
try {
self.drawPage();
} catch (e) {
}
}
// make sure the top of the page is in view
window.scroll(0, 0);
};
/**
* Decrement the counter and draw the page in the canvas
*
* @see #drawPage
*/
ComicBook.prototype.drawPrevPage = function () {
var page;
try {
page = self.getPage(options.currentPage - 1);
} catch (e) {
}
if (!page) {
return false;
}
is_double_page_spread = (page.width > page.height); // need to run double page check again here as we are going backwards
if (options.currentPage > 0) {
options.currentPage -= (options.displayMode === 'single' || is_double_page_spread) ? 1 : 2;
self.drawPage();
}
// make sure the top of the page is in view
window.scroll(0, 0);
};
/* default settings */
ComicBook.prototype.thumbnails = function() {
if ($(this).is(':checked')) {
options.thumbnails = true;
document.getElementById('thumbnail-width').disabled = false;
} else {
options.thumbnails = false;
document.getElementById('thumbnail-width').disabled = true;
}
options.session.setDefault("thumbnails", options.thumbnails);
};
ComicBook.prototype.thumbnailWidth = function() {
options.thumbnailWidth = $(this).val();
options.session.setDefault("thumbnailWidth", options.thumbnailWidth);
};
ComicBook.prototype.sidebarWide = function (wide) {
if (typeof(wide) !== "boolean") {
wide = ($(this).is(':checked') === true);
}
if (wide) {
options.sidebarWide = true;
document.getElementById('sidebar').classList.add('wide');
} else {
options.sidebarWide = false;
document.getElementById('sidebar').classList.remove('wide');
self.sidebarWidth(0);
}
options.session.setDefault("sidebarWide", options.sidebarWide);
};
ComicBook.prototype.sidebarWidth = function(width) {
if (typeof(width) !== "number") {
width = $(this).val();
}
options.sidebarWidth = width;
// width === 0 is interpreted as 'use value from CSS'
if (options.sidebarWidth > 0) {
document.getElementById('sidebar').style.width = options.sidebarWidth + "%";
} else {
document.getElementById('sidebar').style.width = "";
}
options.session.setDefault("sidebarWidth", options.sidebarWidth);
};
ComicBook.prototype.resetSidebar = function () {
self.sidebarWide(false);
self.sidebarWidth(0);
};
/* book-specific settings */
ComicBook.prototype.brightness = function () {
var $brightness = {
brightness: $('#brightness').val(),
contrast: $('#contrast').val()
};
self.enhance.brightness($brightness);
options.enhance.brightness = $brightness;
options.session.setPreference("enhance",options.enhance);
};
ComicBook.prototype.sharpen = function () {
options.enhance.sharpen = $(this).val();
self.enhance.sharpen({
strength: options.enhance.sharpen
});
options.session.setPreference("enhance",options.enhance);
};
ComicBook.prototype.desaturate = function () {
if ($(this).is(':checked')) {
options.enhance.desaturate = {};
self.enhance.desaturate();
} else {
delete options.enhance.desaturate;
self.enhance.resaturate();
}
options.session.setPreference("enhance",options.enhance);
};
ComicBook.prototype.removenoise = function () {
if ($(this).is(':checked')) {
options.enhance.removenoise = {};
self.enhance.removenoise();
} else {
delete options.enhance.removenoise;
self.enhance.unremovenoise();
}
options.session.setPreference("enhance",options.enhance);
};
ComicBook.prototype.resetEnhancements = function () {
self.enhance.reset();
options.session.setPreference("enhance",options.enhance);
};
/**
* Apply image enhancements to the canvas.
*/
ComicBook.prototype.enhance = {
/**
* Reset enhancements.
* This can reset a specific enhancement if the method name is passed, or
* it will reset all.
*
* @param method {string} the specific enhancement to reset
*/
reset: function (method) {
if (!method) {
options.enhance = {};
} else {
delete options.enhance[method];
}
self.drawPage(null, false);
},
/**
* Pixastic progress callback
* @param {float} progress
*/
// progress: function (progress) {
progress: function () {
// console.info(Math.floor(progress * 100));
},
/**
* Pixastic on complete callback
*/
done: function () {
},
/**
* Adjust brightness / contrast
*
* params
* brightness (int) -150 to 150
* contrast: (float) -1 to infinity
*
* @param {Object} params Brightness & contrast levels
* @param {Boolean} reset Reset before applying more enhancements?
*/
brightness: function (params, reset) {
if (reset !== false) {
this.reset('brightness');
}
// merge user options with defaults
var opts = merge({
brightness: 0,
contrast: 0
}, params);
options.enhance.brightness = opts;
// run the enhancement
self.pixastic.brightness({
brightness: opts.brightness,
contrast: opts.contrast
}).done(this.done, this.progress);
},
/**
* Force black and white
*/
desaturate: function () {
options.enhance.desaturate = {};
self.pixastic.desaturate().done(this.done, this.progress);
},
/**
* Undo desaturate
*/
resaturate: function () {
delete options.enhance.desaturate;
self.drawPage(null, false);
},
/**
* Sharpen
*
* options:
* strength: number (-1 to infinity)
*
* @param {Object} options
*/
sharpen: function (params) {
this.desharpen();
var opts = merge({
strength: 0
}, params);
options.enhance.sharpen = opts;
self.pixastic.sharpen3x3({
strength: opts.strength
}).done(this.done, this.progress);
},
desharpen: function () {
delete options.enhance.sharpen;
self.drawPage(null, false);
},
/**
* Remove noise
*/
removenoise: function () {
options.enhance.removenoise = {};
self.pixastic.removenoise().done(this.done, this.progress);
},
unremovenoise: function () {
delete options.enhance.removenoise;
self.drawPage(null, false);
}
};
ComicBook.prototype.navigation = function (e) {
// disable navigation when the overlay is showing
if ($('#cbr-loading-overlay').is(':visible')) {
return false;
}
var side = false, page_no = false;
switch (e.type) {
case 'click':
side = e.currentTarget.getAttribute('data-navigate-side');
break;
case 'keydown':
// console.log("keydown: " + e.keyCode);
switch (options.keyboard[e.keyCode]) {
case 'previous':
side = 'left';
break;
case 'next':
side = 'right';
break;
case 'first':
page_no = 1;
break;
case 'last':
page_no = srcs.length - 1;
break;
case 'sidebar':
self.toggleSidebar();
break;
case 'toolbar':
self.toggleToolbar();
break;
case 'toggleLayout':
self.toggleLayout();
break;
case 'toggleFullscreen':
self.toggleFullscreen();
break;
case 'closeSidebar':
self.closeSidebar();
break;
default:
/*
throw ComicBookException.INVALID_NAVIGATION_EVENT + ' ' + e.type;
*/
}
break;
default:
throw ComicBookException.INVALID_NAVIGATION_EVENT + ' ' + e.type;
}
if (side) {
e.stopPropagation();
// western style (left to right)
if (!options.manga) {
if (side === 'left') {
self.drawPrevPage();
}
if (side === 'right') {
self.drawNextPage();
}
}
// manga style (right to left)
else {
if (side === 'left') {
self.drawNextPage();
}
if (side === 'right') {
self.drawPrevPage();
}
}
return false;
}
if (page_no) {
self.drawPage(page_no, true);
return false;
}
};
ComicBook.prototype.toggleReadingMode = function () {
options.manga = !options.manga;
self.getControl('toolbar')
.find('.manga-' + options.manga).show().end()
.find('.manga-' + !options.manga).hide();
options.session.setPreference("manga",options.manga);
};
ComicBook.prototype.toggleToolbar = function () {
self.toggleControl('toolbar');
};
ComicBook.prototype.openSidebar = function () {
$('.sidebar').addClass('open');
$('.toolbar').addClass('open');
self.showControl('busyOverlay');
self.scrollToc();
};
ComicBook.prototype.closeSidebar = function () {
$('.sidebar').removeClass('open');
$('.toolbar').removeClass('open');
self.toggleToolbar();
self.hideControl('busyOverlay');
};
ComicBook.prototype.toggleSidebar = function () {
$('.sidebar').hasClass('open')
? self.closeSidebar()
: self.openSidebar();
};
ComicBook.prototype.toggleFullscreen = function () {
options.fullscreen = !options.fullscreen;
self.getControl('toolbar')
.find('.fullscreen-' + options.fullscreen).show().end()
.find('.fullscreen-' + !options.fullscreen).hide();
if (options.fullscreen) {
screenfull.request($('#container')[0]);
} else {
screenfull.exit($('#container')[0]);
}
};
/*
* Scroll TOC to page (default: current page)
*/
ComicBook.prototype.scrollToc = function (page) {
if (page === undefined) {
page = options.currentPage;
}
document.getElementById('toc').parentNode.scrollTop =
document.getElementById('page-' + String(page + 1)).offsetTop
- Math.floor($('.panels').height() * 1.5);
};
ComicBook.prototype.showToc = function () {
self.getControl('sidebar')
.find('.open').removeClass('open').end()
.find('.toc-view').addClass('open');
if (!options.thumbnails) {
$('#toc-populate').addClass('open');
}
};
ComicBook.prototype.showBookSettings = function () {
self.getControl('sidebar')
.find('.open').removeClass('open').end()
.find('.book-settings-view').addClass('open');
};
ComicBook.prototype.showSettings = function () {
self.getControl('sidebar')
.find('.open').removeClass('open').end()
.find('.settings-view').addClass('open');
};
ComicBook.prototype.destroy = function () {
$.each(this.controls, function (name, $control) {
$control.remove();
});
canvas.width = 0;
canvas.height = 0;
window.removeEventListener('keydown', this.navigation, false);
window.removeEventListener('hashchange', checkHash, false);
setHash('');
// $(this).trigger('destroy');
};
}
return ComicBook;
})(jQuery);
(function(root, $) {
var previousReader = root.cbReader || {};
var cbReader = root.cbReader = function(path, options) {
return new CBRJS.Reader(path, options);
};
//exports to multiple environments
if (typeof define === 'function' && define.amd) {
//AMD
define(function(){ return Reader; });
} else if (typeof module != "undefined" && module.exports) {
//Node
module.exports = cbReader;
}
})(window, jQuery);