Compare commits
3 Commits
5f8d8873b3
...
c8451c07aa
Author | SHA1 | Date |
---|---|---|
Christian Aribaud | c8451c07aa | |
caribaud | 5f3060995b | |
caribaud | 230abf08e8 |
|
@ -1,2 +1,6 @@
|
|||
# ESLINT report
|
||||
eslint_report.html
|
||||
|
||||
# Demo sources
|
||||
demos/src_highres
|
||||
demos/src_lowres
|
||||
|
|
41
README.md
41
README.md
|
@ -25,16 +25,19 @@ flowchart TB
|
|||
```
|
||||
|
||||
Melpomene is mainly one JS file and one CSS file. You only need to include them in you web page. They are :
|
||||
+ `melpomene.js`
|
||||
+ `melpomene.css`
|
||||
|
||||
+ `melpomene.js`
|
||||
|
||||
+ `melpomene.css`
|
||||
|
||||
The JS files expect you to write some very specific HTML tags to be able to work.
|
||||
To simplify things, you only need to copy-paste the content of `melpomene.html` into your own page and change a few things :
|
||||
|
||||
+ You must duplicate the `img` tag for each of you comic page and :
|
||||
+ set `url` to the actual URL of your page
|
||||
+ set `height` and `width` to the actual image sizes
|
||||
+ set `data-zooms` with the zooms information, like so : `<zoom 1 width>, <zoom 1 height>, <zoom 1 x offset>, <zoom 1 y offset>; <zoom 2 width> ...`
|
||||
+ example : `<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.44702,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/>`
|
||||
+ example : `<img loading="lazy" src="https://www.peppercarrot.com/0_sources/ep35_The-Reflection/hi-res/en_Pepper-and-Carrot_by-David-Revoy_E35P01.jpg" data-zooms="2481.0,1327.1057,0.0,0.0;593.15338,1076.4635,0.0,1364.053;890.72864,491.29874,830.81415,1751.5;2481.0,1078.4192,0.0,1364.053;562.77032,909.4470 2,102.48115,2491.6567;920.74463,909.44702,698.55927,2491.6567;728.776,909.44702,1652.3695,2491.6567"/>`
|
||||
|
||||
Because doing this by hand would be very tedious, Melpomene comes with an helper script that allows generating that HTML for you from SVGs.
|
||||
|
||||
|
@ -45,7 +48,7 @@ The following limitations are known and will be improved upon :
|
|||
+ Mobile support is currently limited
|
||||
+ There are some performences issues
|
||||
|
||||
# How to setup Melpomene ?
|
||||
# How to use Melpomene ?
|
||||
|
||||
## Defining the zooms
|
||||
|
||||
|
@ -55,21 +58,21 @@ To do so, you can use your favorite SVG editor. If you don't have one, [Inkscape
|
|||
To create the zooms for a comic page, what you need to do is :
|
||||
|
||||
1. Open your comic page in your editor (make sure to keep the image's original size when importing!)
|
||||
* If your software ask, there is no need to actually import the image, you only need to link it.
|
||||
* If your software ask, there is no need to actually import the image, you only need to link it.
|
||||
2. Create a simple rectangle over each zoom you want, in the order you want them to show up
|
||||
* You can set the rectangle's color to be translucent so you can still see the page underneath them!
|
||||
* If you want to change the zoom order, you can change their order in the layer view of your SVG tool if they support it
|
||||
* You can set the rectangle's color to be translucent so you can still see the page underneath them!
|
||||
* If you want to change the zoom order, you can change their order in the layer view of your SVG tool if they support it
|
||||
3. Once you are done, save the SGV
|
||||
|
||||
|
||||
## Generating the HTML files
|
||||
|
||||
Once the SVG for your pages are done, put them in one folder. Then, you can run the zoom generator.
|
||||
|
||||
You need to open a terminal and then run :
|
||||
|
||||
+ `python zooms_generator.py <path to the SVG folder> html -p <prefix of the img's url> -e <extension of the img's url>`
|
||||
+ For example, if your comic pages are hosted at `static/comic/page_x.jpg`, the svg must be named `page_x.svg`
|
||||
and you must run `python zooms_generator.py <path to the SVG folder> html -p /static/comic/ -e jpg`
|
||||
and you must run `python zooms_generator.py <path to the SVG folder> html -p /static/comic/ -e jpg`
|
||||
+ It will generate the following `img` tag : `<img loading="lazy" src="static/comic/page_x.jpg" data-zooms="..."/>`
|
||||
|
||||
The pages need to be in alphabetical order! It assumes the first page is page 1, the next one is page 2, etc..
|
||||
|
@ -89,6 +92,24 @@ If you need to do some global scaling / offset of all zooms in HTML (if for exam
|
|||
+ If they become greater than the page size, they get clamped to the page size and width / height get reduced to compensate
|
||||
+ `data-global-zoom-scale="<float value>"` : scale all positions / sizes by this factor
|
||||
|
||||
# Developpement
|
||||
|
||||
## Setting up quality checking for JS
|
||||
|
||||
Regarding JS, quality checking is done using [eslint](https://eslint.org/).
|
||||
|
||||
The configuration file is `eslint/eslintrc.json`.
|
||||
|
||||
To setup eslint, you can either install it on your system reading it's documentation, or use the provided Dockerfile to run it. This requires [docker](https://www.docker.com/).
|
||||
|
||||
To do so, assuming you are using linux, after installing docker, you can run from this repository root:
|
||||
|
||||
+ `docker build -t melpomene-eslint eslint/`, once
|
||||
|
||||
+ `docker run -v .:/melpomene:rw -w /melpomene --user $(id -u):$(id -g) melpomene-eslint`, every time you want to run the analysis
|
||||
|
||||
You can now open `eslint_report.html` to see the result.
|
||||
|
||||
# Credits
|
||||
|
||||
Most examples and the documentation of Melpomene uses illustrations from David "Deevad" Revoy's "Pepper & Carrot" webcomic, which is published under CC-BY 4.0. Full licence [here](https://www.peppercarrot.com/en/license/index.html).
|
||||
Most examples and the documentation of Melpomene uses illustrations from David "Deevad" Revoy's "Pepper & Carrot" webcomic, which is published under CC-BY 4.0. Full licence [here](https://www.peppercarrot.com/en/license/index.html).
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
FROM debian:latest
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends npm
|
||||
|
||||
RUN npm install eslint --global
|
||||
|
||||
COPY eslintrc.json /eslintrc.json
|
||||
|
||||
ENTRYPOINT ["eslint", "--no-eslintrc"]
|
||||
|
||||
CMD ["--help"]
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:all"
|
||||
],
|
||||
"rules": {
|
||||
"semi": "warn",
|
||||
"spaced-comment": "warn",
|
||||
"padded-blocks": ["warn", "never"],
|
||||
"camelcase": "warn",
|
||||
"multiline-comment-style": "off",
|
||||
"max-len": "warn",
|
||||
"prefer-template": "warn",
|
||||
"object-curly-spacing" : ["warn", "always"],
|
||||
"func-style": ["warn", "declaration"],
|
||||
"key-spacing": "warn",
|
||||
"one-var": ["warn", "never"],
|
||||
"quotes": "warn",
|
||||
"space-before-function-paren": ["warn", "never"],
|
||||
"no-undefined": "off",
|
||||
"strict": ["error", "global"],
|
||||
"keyword-spacing": "warn",
|
||||
"function-call-argument-newline": "warn",
|
||||
"operator-assignment": "off",
|
||||
"space-before-blocks": "warn",
|
||||
"brace-style": ["warn", "allman", {"allowSingleLine": true}],
|
||||
"function-call-argument-newline" : ["warn", "consistent"],
|
||||
"function-paren-newline": ["warn", "consistent"],
|
||||
"no-magic-numbers": ["error", {"ignore": [0,1,2,100]}]
|
||||
}
|
||||
}
|
683
melpomene.js
683
melpomene.js
|
@ -2,60 +2,63 @@
|
|||
/* Version 1.0.0_RC1 */
|
||||
/* CC-BY-NC-SA : https://git.aribaud.net/caribaud/melpomene/ */
|
||||
|
||||
//============
|
||||
// CONTROLS
|
||||
//============
|
||||
"use strict";
|
||||
|
||||
MOVE_NEXT = "ArrowRight"
|
||||
MOVE_BACK = "ArrowLeft"
|
||||
TOGGLE_FULLSCREEN = "F"
|
||||
TOGGLE_PROGRESSBAR = "P"
|
||||
TOGGLE_VIEW_MODE = "V"
|
||||
// ============
|
||||
// CONTROLS
|
||||
// ============
|
||||
|
||||
const MOVE_NEXT = "ArrowRight";
|
||||
const MOVE_BACK = "ArrowLeft";
|
||||
const TOGGLE_FULLSCREEN = "F";
|
||||
const TOGGLE_PROGRESSBAR = "P";
|
||||
const TOGGLE_VIEW_MODE = "V";
|
||||
|
||||
|
||||
//========================
|
||||
// NAVIGATION CONSTANTS
|
||||
//========================
|
||||
// ========================
|
||||
// NAVIGATION CONSTANTS
|
||||
// ========================
|
||||
|
||||
PAGE_TRANSITION_SPEED = "1.5s"
|
||||
MOUSEWHELL_MIN_DELAY = 50
|
||||
DELAY_BEFORE_HIDDING_CONTROLS = 4000;
|
||||
const MOUSEWHELL_MIN_DELAY = 50;
|
||||
const DELAY_BEFORE_HIDDING_CONTROLS = 4000;
|
||||
|
||||
//====================
|
||||
// STATES CONSTANTS
|
||||
//====================
|
||||
// ====================
|
||||
// STATES CONSTANTS
|
||||
// ====================
|
||||
|
||||
MELPOMENE_VERSION = "1.0.0_RC1"
|
||||
const MELPOMENE_VERSION = "1.0.0_UNSTABLE";
|
||||
|
||||
READER_FRAME = document.getElementById("melpomene")
|
||||
READER_CONTENT_FRAME = document.getElementById("melpomene-content-frame")
|
||||
READER_PAGES = document.getElementById("melpomene-pages")
|
||||
FOCUS_OVERLAY_HEIGHT = document.getElementById("melpomene-focus")
|
||||
FOCUS_OVERLAY_WIDTH = document.getElementById("melpomene-focus-col")
|
||||
HELP_CONTROLS = document.getElementById("melpomene-help-menu")
|
||||
PROGRESS_BAR_CONTAINER = document.getElementById("melpomene-progress-container")
|
||||
PROGRESS_BAR = document.getElementById("melpomene-progress-bar")
|
||||
PROGRESS_BAR_PAGES = document.getElementById("melpomene-progress-sections")
|
||||
VERSION_DISPLAY = document.getElementById("melpomene-version")
|
||||
const READER_FRAME = document.getElementById("melpomene");
|
||||
const READER_CONTENT_FRAME = document.getElementById("melpomene-content-frame");
|
||||
const READER_PAGES = document.getElementById("melpomene-pages");
|
||||
const FOCUS_OVERLAY_HEIGHT = document.getElementById("melpomene-focus");
|
||||
const FOCUS_OVERLAY_WIDTH = document.getElementById("melpomene-focus-col");
|
||||
const HELP_CONTROLS = document.getElementById("melpomene-help-menu");
|
||||
const PROGRESS_BAR_CONTAINER = document.getElementById("melpomene-progress-container");
|
||||
const PROGRESS_BAR = document.getElementById("melpomene-progress-bar");
|
||||
const PROGRESS_BAR_PAGES = document.getElementById("melpomene-progress-sections");
|
||||
const VERSION_DISPLAY = document.getElementById("melpomene-version");
|
||||
|
||||
//===========================
|
||||
// STATES GLOBAL VARIABLES
|
||||
//===========================
|
||||
// ===========================
|
||||
// STATES GLOBAL VARIABLES
|
||||
// ===========================
|
||||
|
||||
var PAGES_ZOOMS;
|
||||
// The variable ZOOMS can either be defined by another JS file or contructed at init
|
||||
if (typeof PAGES_ZOOMS == 'undefined') {
|
||||
PAGES_ZOOMS = null
|
||||
if (typeof PAGES_ZOOMS === 'undefined')
|
||||
{
|
||||
PAGES_ZOOMS = null;
|
||||
}
|
||||
|
||||
CURRENT_ZOOM = 0
|
||||
CURRENT_PAGE = 1
|
||||
CURRENT_WIDTH = 0
|
||||
CURRENT_HEIGHT = 0
|
||||
CURRENT_X = 0
|
||||
CURRENT_Y = 0
|
||||
var CURRENT_ZOOM = 0;
|
||||
var CURRENT_PAGE = 1;
|
||||
var CURRENT_WIDTH = 0;
|
||||
var CURRENT_HEIGHT = 0;
|
||||
var CURRENT_X = 0;
|
||||
var CURRENT_Y = 0;
|
||||
|
||||
IS_PAGE_MODE = false
|
||||
MOUSEWHELL_WAIT = false
|
||||
var IS_PAGE_MODE = false;
|
||||
var MOUSEWHELL_WAIT = false;
|
||||
|
||||
// =============
|
||||
// UTILITIES
|
||||
|
@ -64,360 +67,433 @@ MOUSEWHELL_WAIT = false
|
|||
// Zooms utilites
|
||||
// --------------
|
||||
|
||||
function globalZoomScale(){
|
||||
|
||||
if (READER_PAGES.dataset.globalZoomScale != undefined){
|
||||
return parseFloat(READER_PAGES.dataset.globalZoomScale)
|
||||
function globalZoomScale()
|
||||
{
|
||||
if (READER_PAGES.dataset.globalZoomScale !== undefined)
|
||||
{
|
||||
return parseFloat(READER_PAGES.dataset.globalZoomScale);
|
||||
}
|
||||
|
||||
return 1.0
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
function globalZoomOffsetX(){
|
||||
|
||||
if (READER_PAGES.dataset.globalZoomOffset != undefined){
|
||||
return parseFloat(READER_PAGES.dataset.globalZoomOffset.split(',')[0])
|
||||
function globalZoomOffsetX()
|
||||
{
|
||||
if (READER_PAGES.dataset.globalZoomOffset !== undefined)
|
||||
{
|
||||
return parseFloat(READER_PAGES.dataset.globalZoomOffset.split(',')[0]);
|
||||
}
|
||||
|
||||
return 0.0
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
function globalZoomOffsetY(){
|
||||
|
||||
if (READER_PAGES.dataset.globalZoomOffset != undefined){
|
||||
return parseFloat(READER_PAGES.dataset.globalZoomOffset.split(',')[1])
|
||||
function globalZoomOffsetY()
|
||||
{
|
||||
if (READER_PAGES.dataset.globalZoomOffset !== undefined)
|
||||
{
|
||||
return parseFloat(READER_PAGES.dataset.globalZoomOffset.split(',')[1]);
|
||||
}
|
||||
|
||||
return 0.0
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
function loadZoomsFromImgTagsIfRequired(){
|
||||
|
||||
function loadZoomsFromImgTagsIfRequired()
|
||||
{
|
||||
// Zooms may be defined by another JS file
|
||||
if (PAGES_ZOOMS == null){
|
||||
|
||||
PAGES_ZOOMS = []
|
||||
if (PAGES_ZOOMS === null)
|
||||
{
|
||||
PAGES_ZOOMS = [];
|
||||
|
||||
// parse the data-zooms of each img and and the page number info
|
||||
for (var i = 0; i < READER_PAGES.children.length; i++) {
|
||||
|
||||
zooms_raw_data = READER_PAGES.children[i].dataset.zooms
|
||||
for (let idx = 0; idx < READER_PAGES.children.length; idx += 1)
|
||||
{
|
||||
const zoomsRawData = READER_PAGES.children[idx].dataset.zooms;
|
||||
|
||||
// ';' separates zooms data, ',' separates values
|
||||
// We add the page number (adding 1 because of indexing)
|
||||
zooms = zooms_raw_data.split(";").map(
|
||||
zoom => [i + 1].concat(
|
||||
const zooms = zoomsRawData.split(";").map(
|
||||
zoom => [idx + 1].concat(
|
||||
zoom.split(',').map(
|
||||
value => parseFloat(value)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
PAGES_ZOOMS = PAGES_ZOOMS.concat(zooms)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getFirstZoomOfPage(pageNumber){
|
||||
|
||||
for (var zoom_idx = 0; zoom_idx < PAGES_ZOOMS.length; zoom_idx++){
|
||||
if (PAGES_ZOOMS[zoom_idx][0] == pageNumber) {
|
||||
return zoom_idx
|
||||
PAGES_ZOOMS = PAGES_ZOOMS.concat(zooms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getLastZoomOfPage(pageNumber){
|
||||
let res = null
|
||||
function getFirstZoomOfPage(pageNumber)
|
||||
{
|
||||
for (let zoomIdx = 0; zoomIdx < PAGES_ZOOMS.length; zoomIdx += 1)
|
||||
{
|
||||
if (PAGES_ZOOMS[zoomIdx][0] === pageNumber)
|
||||
{
|
||||
return zoomIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var zoom_idx = 0; zoom_idx < PAGES_ZOOMS.length; zoom_idx++){
|
||||
if (PAGES_ZOOMS[zoom_idx][0] == pageNumber) {
|
||||
res = zoom_idx
|
||||
function getLastZoomOfPage(pageNumber)
|
||||
{
|
||||
let res = null;
|
||||
|
||||
for (let zoomIdx = 0; zoomIdx < PAGES_ZOOMS.length; zoomIdx += 1)
|
||||
{
|
||||
if (PAGES_ZOOMS[zoomIdx][0] === pageNumber)
|
||||
{
|
||||
res = zoomIdx;
|
||||
}
|
||||
|
||||
if (res != null && PAGES_ZOOMS[zoom_idx][0] != pageNumber) {
|
||||
break
|
||||
if (res !== null && PAGES_ZOOMS[zoomIdx][0] !== pageNumber)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
|
||||
function getZoomCountForPage(pageNumber) {
|
||||
return PAGES_ZOOMS.filter(zoom => zoom[0] == pageNumber).length
|
||||
function getZoomCountForPage(pageNumber)
|
||||
{
|
||||
return PAGES_ZOOMS.filter(zoom => zoom[0] === pageNumber).length;
|
||||
}
|
||||
|
||||
function getCurrentZoomIndexForPage() {
|
||||
previousZoomsCount = PAGES_ZOOMS.filter(zoom => zoom[0] < CURRENT_PAGE).length
|
||||
return CURRENT_ZOOM - previousZoomsCount + 1
|
||||
function getCurrentZoomIndexForPage()
|
||||
{
|
||||
const previousZoomsCount = PAGES_ZOOMS.filter(zoom => zoom[0] < CURRENT_PAGE).length;
|
||||
return CURRENT_ZOOM - previousZoomsCount + 1;
|
||||
}
|
||||
|
||||
function getReadingProgressPercent() {
|
||||
progressPerPage = 1 / getPagesCount()
|
||||
function getReadingProgressPercent()
|
||||
{
|
||||
const progressPerPage = 1 / getPagesCount();
|
||||
|
||||
if (IS_PAGE_MODE){
|
||||
return 100 * progressPerPage * CURRENT_PAGE
|
||||
if (IS_PAGE_MODE)
|
||||
{
|
||||
return 100 * progressPerPage * CURRENT_PAGE;
|
||||
}
|
||||
|
||||
progressPerZoom = progressPerPage / getZoomCountForPage(CURRENT_PAGE)
|
||||
const progressPerZoom = progressPerPage / getZoomCountForPage(CURRENT_PAGE);
|
||||
|
||||
readingProgress = (CURRENT_PAGE - 1) * progressPerPage + getCurrentZoomIndexForPage() * progressPerZoom
|
||||
const readingProgress = (CURRENT_PAGE - 1) * progressPerPage + getCurrentZoomIndexForPage() * progressPerZoom;
|
||||
|
||||
return 100 * readingProgress
|
||||
return 100 * readingProgress;
|
||||
}
|
||||
|
||||
function updateProgressBar(){
|
||||
PROGRESS_BAR.style.width = getReadingProgressPercent() + "%"
|
||||
function updateProgressBar()
|
||||
{
|
||||
PROGRESS_BAR.style.width = getReadingProgressPercent() + "%";
|
||||
}
|
||||
|
||||
// Dimensions utilites
|
||||
// -------------------
|
||||
|
||||
function getPagesCount() {
|
||||
return READER_PAGES.childElementCount
|
||||
function getPagesCount()
|
||||
{
|
||||
return READER_PAGES.childElementCount;
|
||||
}
|
||||
|
||||
function pageOriginalHeight(pageNumber) {
|
||||
return READER_PAGES.children[pageNumber - 1].naturalHeight
|
||||
function pageOriginalHeight(pageNumber)
|
||||
{
|
||||
return READER_PAGES.children[pageNumber - 1].naturalHeight;
|
||||
}
|
||||
|
||||
function pageOriginalWidth(pageNumber) {
|
||||
return READER_PAGES.children[pageNumber - 1].naturalWidth
|
||||
function pageOriginalWidth(pageNumber)
|
||||
{
|
||||
return READER_PAGES.children[pageNumber - 1].naturalWidth;
|
||||
}
|
||||
|
||||
function readerFrameRatio() {
|
||||
return READER_CONTENT_FRAME.clientWidth / READER_CONTENT_FRAME.clientHeight
|
||||
function readerFrameRatio()
|
||||
{
|
||||
return READER_CONTENT_FRAME.clientWidth / READER_CONTENT_FRAME.clientHeight;
|
||||
}
|
||||
|
||||
function pageRatio(pageNumber) {
|
||||
return READER_PAGES.children[pageNumber - 1].naturalWidth / READER_PAGES.children[pageNumber - 1].naturalHeight
|
||||
function pageRatio(pageNumber)
|
||||
{
|
||||
return READER_PAGES.children[pageNumber - 1].naturalWidth / READER_PAGES.children[pageNumber - 1].naturalHeight;
|
||||
}
|
||||
|
||||
function pageMaxHeight(){
|
||||
let max_height = 0
|
||||
function pageMaxHeight()
|
||||
{
|
||||
let maxHeight = 0;
|
||||
|
||||
for (var i = 0; i < READER_PAGES.children.length; i++) {
|
||||
if(READER_PAGES.children[i].naturalHeight > max_height){
|
||||
max_height = READER_PAGES.children[i].naturalHeight
|
||||
for (let i = 0; i < READER_PAGES.children.length; i += 1)
|
||||
{
|
||||
if(READER_PAGES.children[i].naturalHeight > maxHeight)
|
||||
{
|
||||
maxHeight = READER_PAGES.children[i].naturalHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return max_height
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
function pageVerticalOffset(pageNumber) {
|
||||
return ( pageMaxHeight() - pageOriginalHeight(pageNumber) ) / 2
|
||||
function pageVerticalOffset(pageNumber)
|
||||
{
|
||||
return ( pageMaxHeight() - pageOriginalHeight(pageNumber) ) / 2;
|
||||
}
|
||||
|
||||
function previousPagesWidth(pageNumber) {
|
||||
function previousPagesWidth(pageNumber)
|
||||
{
|
||||
// The width of all previous pages relative to the provided index
|
||||
|
||||
let totalWidth = 0
|
||||
let totalWidth = 0;
|
||||
|
||||
for (let idx = 0; idx < pageNumber - 1; idx++){
|
||||
totalWidth = totalWidth + READER_PAGES.children[idx].naturalWidth
|
||||
for (let idx = 0; idx < pageNumber - 1; idx += 1)
|
||||
{
|
||||
totalWidth += READER_PAGES.children[idx].naturalWidth;
|
||||
}
|
||||
|
||||
return totalWidth
|
||||
return totalWidth;
|
||||
}
|
||||
|
||||
// =========
|
||||
// ACTIONS
|
||||
// =========
|
||||
|
||||
function initReader(){
|
||||
loadZoomsFromImgTagsIfRequired()
|
||||
moveReaderDisplayToZoom(0)
|
||||
function initReader()
|
||||
{
|
||||
VERSION_DISPLAY.innerText = VERSION_DISPLAY.innerText.replace("Unknown version", MELPOMENE_VERSION);
|
||||
|
||||
loadZoomsFromImgTagsIfRequired();
|
||||
moveReaderDisplayToZoom(0);
|
||||
|
||||
// Smoothly show pictures when they intersect with the viewport
|
||||
let visibilityObserver = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.style.opacity = 1
|
||||
entry.target.style.visibility = "visible"
|
||||
} else {
|
||||
entry.target.style.opacity = 0
|
||||
entry.target.style.visibility = "hidden"
|
||||
}
|
||||
});
|
||||
}, { root: READER_CONTENT_FRAME, rootMargin: "-10px" });
|
||||
const visibilityObserver = new IntersectionObserver(
|
||||
(entries, observer) =>
|
||||
{
|
||||
entries.forEach((entry) =>
|
||||
{
|
||||
if (entry.isIntersecting)
|
||||
{
|
||||
entry.target.style.opacity = 1;
|
||||
entry.target.style.visibility = "visible";
|
||||
}
|
||||
|
||||
for (var i = 0; i < READER_PAGES.children.length; i++) {
|
||||
let img = READER_PAGES.children[i];
|
||||
visibilityObserver.observe(img)
|
||||
else
|
||||
{
|
||||
entry.target.style.opacity = 0;
|
||||
entry.target.style.visibility = "hidden";
|
||||
}
|
||||
});
|
||||
},
|
||||
{ root: READER_CONTENT_FRAME, rootMargin: "-10px" }
|
||||
);
|
||||
|
||||
PROGRESS_BAR_PAGES.appendChild(document.createElement("div"))
|
||||
for (let i = 0; i < READER_PAGES.children.length; i += 1)
|
||||
{
|
||||
const img = READER_PAGES.children[i];
|
||||
visibilityObserver.observe(img);
|
||||
|
||||
PROGRESS_BAR_PAGES.appendChild(document.createElement("div"));
|
||||
}
|
||||
|
||||
READER_PAGES.style.display = "flex"
|
||||
READER_PAGES.style.display = "flex";
|
||||
|
||||
setTimeout(() => {
|
||||
READER_PAGES.hidden = false
|
||||
}, "300")
|
||||
setTimeout(
|
||||
() => { READER_PAGES.hidden = false },
|
||||
"300"
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
HELP_CONTROLS.style.opacity = null;
|
||||
HELP_CONTROLS.style.transform = null;
|
||||
}, DELAY_BEFORE_HIDDING_CONTROLS)
|
||||
setTimeout(
|
||||
() =>
|
||||
{
|
||||
HELP_CONTROLS.style.opacity = null;
|
||||
HELP_CONTROLS.style.transform = null;
|
||||
},
|
||||
DELAY_BEFORE_HIDDING_CONTROLS
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function moveReaderDisplayToArea(pageNumber, width, height, posx, posy){
|
||||
|
||||
function moveReaderDisplayToArea(pageNumber, oWidth, oHeight, oPosx, oPosy)
|
||||
{
|
||||
// Keep original values for registering
|
||||
o_width = width
|
||||
o_height = height
|
||||
o_posx = posx
|
||||
o_posy = posy
|
||||
let width = oWidth;
|
||||
let height = oHeight;
|
||||
let posx = oPosx;
|
||||
let posy = oPosy;
|
||||
|
||||
// Apply global offsets before scales if we are displaying a zoom
|
||||
// Pages display uses width & height = 0
|
||||
if (width != 0 || height != 0){
|
||||
width = width * globalZoomScale()
|
||||
height = height * globalZoomScale()
|
||||
posx = (posx + globalZoomOffsetX()) * globalZoomScale()
|
||||
posy = (posy + globalZoomOffsetY()) * globalZoomScale()
|
||||
if (width !== 0 || height !== 0)
|
||||
{
|
||||
width = width * globalZoomScale();
|
||||
height = height * globalZoomScale();
|
||||
posx = (posx + globalZoomOffsetX()) * globalZoomScale();
|
||||
posy = (posy + globalZoomOffsetY()) * globalZoomScale();
|
||||
}
|
||||
|
||||
// reduce width if offset sent us outside of page
|
||||
if (posx < 0) {
|
||||
width = width + posx
|
||||
posx = 0
|
||||
if (posx < 0)
|
||||
{
|
||||
width = width + posx;
|
||||
posx = 0;
|
||||
}
|
||||
if ((posx + width) > pageOriginalWidth(pageNumber)) {
|
||||
width = pageOriginalWidth(pageNumber) - posx
|
||||
|
||||
if ((posx + width) > pageOriginalWidth(pageNumber))
|
||||
{
|
||||
width = pageOriginalWidth(pageNumber) - posx;
|
||||
}
|
||||
|
||||
// reduce height if offset sent us outside of page
|
||||
if (posy < 0) {
|
||||
height = height + posy
|
||||
posy = 0
|
||||
if (posy < 0)
|
||||
{
|
||||
height = height + posy;
|
||||
posy = 0;
|
||||
}
|
||||
if ((posy + height) > pageOriginalHeight(pageNumber)) {
|
||||
height = pageOriginalHeight(pageNumber) - posy
|
||||
|
||||
if ((posy + height) > pageOriginalHeight(pageNumber))
|
||||
{
|
||||
height = pageOriginalHeight(pageNumber) - posy;
|
||||
}
|
||||
|
||||
|
||||
// Align the top-left corner of the frame with the page
|
||||
READER_PAGES.style.transform = "translate(-" + previousPagesWidth(pageNumber) + "px, -" + pageVerticalOffset(pageNumber) + "px )"
|
||||
READER_PAGES.style.transform = "translate(-" + previousPagesWidth(pageNumber) + "px, -" + pageVerticalOffset(pageNumber) + "px )";
|
||||
|
||||
// Then move so the top-left point of the zoom match the frame top-left
|
||||
READER_PAGES.style.transform = "translate(" + (- posx) + "px, " + (-posy) + "px )" + READER_PAGES.style.transform
|
||||
READER_PAGES.style.transform = "translate(" + (- posx) + "px, " + (-posy) + "px )" + READER_PAGES.style.transform;
|
||||
|
||||
// Then, scale so the zoom would fit the frame, and center the zoom
|
||||
if (width == 0){
|
||||
width = pageOriginalWidth(pageNumber)
|
||||
if (width === 0)
|
||||
{
|
||||
width = pageOriginalWidth(pageNumber);
|
||||
}
|
||||
|
||||
if (height == 0){
|
||||
height = pageOriginalHeight(pageNumber)
|
||||
if (height === 0)
|
||||
{
|
||||
height = pageOriginalHeight(pageNumber);
|
||||
}
|
||||
|
||||
zoomRatio = width / height
|
||||
const zoomRatio = width / height;
|
||||
|
||||
if (readerFrameRatio() > zoomRatio) {
|
||||
if (readerFrameRatio() > zoomRatio)
|
||||
{
|
||||
// Frame wider than zoom => scale so heights are the same, offset on x
|
||||
var zoomToFrameScaleFactor = READER_CONTENT_FRAME.clientHeight / height
|
||||
const zoomToFrameScaleFactor = READER_CONTENT_FRAME.clientHeight / height;
|
||||
|
||||
READER_PAGES.style.transform = "scale(" + zoomToFrameScaleFactor + ")" + READER_PAGES.style.transform
|
||||
READER_PAGES.style.transform = "scale(" + zoomToFrameScaleFactor + ")" + READER_PAGES.style.transform;
|
||||
|
||||
var scaledWidth = width * zoomToFrameScaleFactor
|
||||
var offset = (READER_CONTENT_FRAME.clientWidth - scaledWidth) / 2
|
||||
const scaledWidth = width * zoomToFrameScaleFactor;
|
||||
const offset = (READER_CONTENT_FRAME.clientWidth - scaledWidth) / 2;
|
||||
|
||||
READER_PAGES.style.transform = "translateX(" + offset + "px)" + READER_PAGES.style.transform
|
||||
READER_PAGES.style.transform = "translateX(" + offset + "px)" + READER_PAGES.style.transform;
|
||||
|
||||
updateFocusByWidth(scaledWidth)
|
||||
} else {
|
||||
updateFocusByWidth(scaledWidth);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Frame narower than zoom => scale so left/right match, offset on y
|
||||
var zoomToFrameScaleFactor = READER_CONTENT_FRAME.clientWidth / width
|
||||
const zoomToFrameScaleFactor = READER_CONTENT_FRAME.clientWidth / width;
|
||||
|
||||
READER_PAGES.style.transform = "scale(" + zoomToFrameScaleFactor + ")" + READER_PAGES.style.transform
|
||||
READER_PAGES.style.transform = "scale(" + zoomToFrameScaleFactor + ")" + READER_PAGES.style.transform;
|
||||
|
||||
var scaledHeight = height * zoomToFrameScaleFactor
|
||||
var offset = (READER_CONTENT_FRAME.clientHeight - scaledHeight) / 2
|
||||
const scaledHeight = height * zoomToFrameScaleFactor;
|
||||
const offset = (READER_CONTENT_FRAME.clientHeight - scaledHeight) / 2;
|
||||
|
||||
READER_PAGES.style.transform = "translateY(" + offset + "px)" + READER_PAGES.style.transform
|
||||
READER_PAGES.style.transform = "translateY(" + offset + "px)" + READER_PAGES.style.transform;
|
||||
|
||||
updateFocusByHeight(scaledHeight)
|
||||
updateFocusByHeight(scaledHeight);
|
||||
}
|
||||
|
||||
// Use values before global offset / scale
|
||||
CURRENT_PAGE = pageNumber
|
||||
CURRENT_WIDTH = o_width
|
||||
CURRENT_HEIGHT = o_height
|
||||
CURRENT_X = o_posx
|
||||
CURRENT_Y = o_posy
|
||||
CURRENT_PAGE = pageNumber;
|
||||
CURRENT_WIDTH = oWidth;
|
||||
CURRENT_HEIGHT = oHeight;
|
||||
CURRENT_X = oPosx;
|
||||
CURRENT_Y = oPosy;
|
||||
}
|
||||
|
||||
function refreshReaderDisplay() {
|
||||
moveReaderDisplayToArea(CURRENT_PAGE, CURRENT_WIDTH, CURRENT_HEIGHT, CURRENT_X, CURRENT_Y)
|
||||
function refreshReaderDisplay()
|
||||
{
|
||||
moveReaderDisplayToArea(CURRENT_PAGE, CURRENT_WIDTH, CURRENT_HEIGHT, CURRENT_X, CURRENT_Y);
|
||||
}
|
||||
|
||||
function moveReaderDisplayToPage(pageNumber) {
|
||||
moveReaderDisplayToArea(pageNumber, 0, 0, 0, 0)
|
||||
function moveReaderDisplayToPage(pageNumber)
|
||||
{
|
||||
moveReaderDisplayToArea(pageNumber, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
function moveReaderDisplayToZoom(index) {
|
||||
function moveReaderDisplayToZoom(index)
|
||||
{
|
||||
moveReaderDisplayToArea(PAGES_ZOOMS[index][0], PAGES_ZOOMS[index][1], PAGES_ZOOMS[index][2], PAGES_ZOOMS[index][3], PAGES_ZOOMS[index][4]);
|
||||
|
||||
moveReaderDisplayToArea(PAGES_ZOOMS[index][0], PAGES_ZOOMS[index][1], PAGES_ZOOMS[index][2], PAGES_ZOOMS[index][3], PAGES_ZOOMS[index][4])
|
||||
|
||||
CURRENT_ZOOM = index
|
||||
CURRENT_ZOOM = index;
|
||||
}
|
||||
|
||||
function updateFocusByWidth(width){
|
||||
FOCUS_OVERLAY_WIDTH.style.width = (width / READER_CONTENT_FRAME.clientWidth * 100) + "%"
|
||||
FOCUS_OVERLAY_HEIGHT.style.height = "100%"
|
||||
function updateFocusByWidth(width)
|
||||
{
|
||||
FOCUS_OVERLAY_WIDTH.style.width = (width / READER_CONTENT_FRAME.clientWidth * 100) + "%";
|
||||
FOCUS_OVERLAY_HEIGHT.style.height = "100%";
|
||||
}
|
||||
|
||||
function updateFocusByHeight(height){
|
||||
FOCUS_OVERLAY_WIDTH.style.width = "100%"
|
||||
FOCUS_OVERLAY_HEIGHT.style.height = (height / READER_CONTENT_FRAME.clientHeight * 100) + "%"
|
||||
function updateFocusByHeight(height)
|
||||
{
|
||||
FOCUS_OVERLAY_WIDTH.style.width = "100%";
|
||||
FOCUS_OVERLAY_HEIGHT.style.height = (height / READER_CONTENT_FRAME.clientHeight * 100) + "%";
|
||||
}
|
||||
|
||||
function toggleViewMode() {
|
||||
if (IS_PAGE_MODE){
|
||||
if (CURRENT_ZOOM != null){
|
||||
moveReaderDisplayToZoom(CURRENT_ZOOM)
|
||||
} else {
|
||||
moveReaderDisplayToZoom(getFirstZoomOfPage(CURRENT_PAGE))
|
||||
function toggleViewMode()
|
||||
{
|
||||
if (IS_PAGE_MODE)
|
||||
{
|
||||
if (CURRENT_ZOOM !== null)
|
||||
{
|
||||
moveReaderDisplayToZoom(CURRENT_ZOOM);
|
||||
}
|
||||
IS_PAGE_MODE = false
|
||||
} else {
|
||||
moveReaderDisplayToPage(CURRENT_PAGE)
|
||||
IS_PAGE_MODE = true
|
||||
|
||||
else
|
||||
{
|
||||
moveReaderDisplayToZoom(getFirstZoomOfPage(CURRENT_PAGE));
|
||||
}
|
||||
|
||||
IS_PAGE_MODE = false;
|
||||
}
|
||||
|
||||
updateProgressBar()
|
||||
else
|
||||
{
|
||||
moveReaderDisplayToPage(CURRENT_PAGE);
|
||||
IS_PAGE_MODE = true;
|
||||
}
|
||||
|
||||
updateProgressBar();
|
||||
}
|
||||
|
||||
function moveReader(to_next) {
|
||||
|
||||
if (IS_PAGE_MODE){
|
||||
if (to_next && CURRENT_PAGE < getPagesCount()) {
|
||||
moveReaderDisplayToPage(CURRENT_PAGE + 1)
|
||||
CURRENT_ZOOM = null
|
||||
function moveReader(toNext)
|
||||
{
|
||||
if (IS_PAGE_MODE)
|
||||
{
|
||||
if (toNext && CURRENT_PAGE < getPagesCount())
|
||||
{
|
||||
moveReaderDisplayToPage(CURRENT_PAGE + 1);
|
||||
CURRENT_ZOOM = null;
|
||||
}
|
||||
|
||||
else if (!to_next && CURRENT_PAGE > 1) {
|
||||
moveReaderDisplayToPage(CURRENT_PAGE - 1)
|
||||
CURRENT_ZOOM = null
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (to_next && CURRENT_ZOOM < PAGES_ZOOMS.length - 1) {
|
||||
moveReaderDisplayToZoom(CURRENT_ZOOM + 1)
|
||||
}
|
||||
|
||||
else if (!to_next && CURRENT_ZOOM > 0) {
|
||||
moveReaderDisplayToZoom(CURRENT_ZOOM - 1)
|
||||
else if (!toNext && CURRENT_PAGE > 1)
|
||||
{
|
||||
moveReaderDisplayToPage(CURRENT_PAGE - 1);
|
||||
CURRENT_ZOOM = null;
|
||||
}
|
||||
}
|
||||
|
||||
updateProgressBar()
|
||||
else
|
||||
{
|
||||
if (toNext && CURRENT_ZOOM < PAGES_ZOOMS.length - 1)
|
||||
{
|
||||
moveReaderDisplayToZoom(CURRENT_ZOOM + 1);
|
||||
}
|
||||
|
||||
else if (!toNext && CURRENT_ZOOM > 0)
|
||||
{
|
||||
moveReaderDisplayToZoom(CURRENT_ZOOM - 1);
|
||||
}
|
||||
}
|
||||
|
||||
updateProgressBar();
|
||||
}
|
||||
|
||||
|
||||
|
@ -425,69 +501,89 @@ function moveReader(to_next) {
|
|||
// CALLBACKS
|
||||
// =============
|
||||
|
||||
function handleKeyPress(key){
|
||||
|
||||
if (key == MOVE_NEXT) {
|
||||
moveReader(true)
|
||||
function handleKeyPress(key)
|
||||
{
|
||||
if (key === MOVE_NEXT)
|
||||
{
|
||||
moveReader(true);
|
||||
}
|
||||
|
||||
else if (key == MOVE_BACK) {
|
||||
moveReader(false)
|
||||
else if (key === MOVE_BACK)
|
||||
{
|
||||
moveReader(false);
|
||||
}
|
||||
|
||||
else if (key.toUpperCase() == TOGGLE_FULLSCREEN){
|
||||
if (document.fullscreenElement == null){
|
||||
else if (key.toUpperCase() === TOGGLE_FULLSCREEN)
|
||||
{
|
||||
if (document.fullscreenElement === null)
|
||||
{
|
||||
READER_FRAME.requestFullscreen();
|
||||
} else {
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
document.exitFullscreen();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (key.toUpperCase() == TOGGLE_PROGRESSBAR){
|
||||
if (PROGRESS_BAR_CONTAINER.hidden == true) {
|
||||
PROGRESS_BAR_CONTAINER.hidden = false
|
||||
} else {
|
||||
PROGRESS_BAR_CONTAINER.hidden = true
|
||||
else if (key.toUpperCase() === TOGGLE_PROGRESSBAR)
|
||||
{
|
||||
if (PROGRESS_BAR_CONTAINER.hidden === true)
|
||||
{
|
||||
PROGRESS_BAR_CONTAINER.hidden = false;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
PROGRESS_BAR_CONTAINER.hidden = true;
|
||||
}
|
||||
|
||||
refreshReaderDisplay();
|
||||
}
|
||||
|
||||
else if (key.toUpperCase() == TOGGLE_VIEW_MODE) {
|
||||
toggleViewMode()
|
||||
else if (key.toUpperCase() === TOGGLE_VIEW_MODE)
|
||||
{
|
||||
toggleViewMode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function handleMouseWhell(event){
|
||||
|
||||
function handleMouseWhell(event)
|
||||
{
|
||||
// Only handle scroll event if the target is the nav controls
|
||||
// to avoid preventing page scrolling.
|
||||
|
||||
// Do disable page scrolling when we do prev/next, though
|
||||
|
||||
if (! READER_FRAME.contains(event.target)){
|
||||
return
|
||||
if (! READER_FRAME.contains(event.target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (MOUSEWHELL_WAIT){
|
||||
return
|
||||
} else {
|
||||
MOUSEWHELL_WAIT = true
|
||||
setTimeout(() => {
|
||||
MOUSEWHELL_WAIT = false
|
||||
}, MOUSEWHELL_MIN_DELAY)
|
||||
if (MOUSEWHELL_WAIT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.deltaY > 0) {
|
||||
moveReader(true, false)
|
||||
else
|
||||
{
|
||||
MOUSEWHELL_WAIT = true;
|
||||
setTimeout(
|
||||
() => { MOUSEWHELL_WAIT = false; },
|
||||
MOUSEWHELL_MIN_DELAY
|
||||
);
|
||||
}
|
||||
|
||||
else {
|
||||
moveReader(false, false)
|
||||
if (event.deltaY > 0)
|
||||
{
|
||||
moveReader(true, false);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
moveReader(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -495,19 +591,26 @@ function handleMouseWhell(event){
|
|||
// INIT
|
||||
// ======
|
||||
|
||||
window.addEventListener("load", (event) => {
|
||||
VERSION_DISPLAY.innerText = VERSION_DISPLAY.innerText.replace("Unknown version", MELPOMENE_VERSION)
|
||||
initReader()
|
||||
});
|
||||
window.addEventListener(
|
||||
"load",
|
||||
(event) => { initReader() }
|
||||
);
|
||||
|
||||
addEventListener("resize", (event) => {
|
||||
refreshReaderDisplay();
|
||||
});
|
||||
addEventListener(
|
||||
"resize",
|
||||
(event) => { refreshReaderDisplay() }
|
||||
);
|
||||
|
||||
addEventListener("keydown", (event) => {
|
||||
handleKeyPress(event.key, event.shiftKey)
|
||||
});
|
||||
addEventListener(
|
||||
"keydown",
|
||||
(event) =>
|
||||
{
|
||||
handleKeyPress(event.key, event.shiftKey)
|
||||
}
|
||||
);
|
||||
|
||||
addEventListener("wheel", (event) => {
|
||||
handleMouseWhell(event)
|
||||
}, { passive:false });
|
||||
addEventListener(
|
||||
"wheel",
|
||||
(event) => { handleMouseWhell(event) },
|
||||
{ passive:false }
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue