diff --git a/package.json b/package.json index 76b2d8c56..1eee26bcb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "epubjs", - "version": "0.3.88", + "name": "epubjs-myh", + "version": "0.3.107", "description": "Parse and Render Epubs", "main": "lib/index.js", "module": "src/index.js", @@ -16,15 +16,15 @@ "docs:md": "documentation build src/epub.js -f md -o documentation/md/API.md", "lint": "eslint -c .eslintrc.js src; exit 0", "start": "webpack-dev-server --inline --d", - "build": "NODE_ENV=production webpack --progress", - "minify": "NODE_ENV=production MINIMIZE=true webpack --progress", - "legacy": "NODE_ENV=production LEGACY=true webpack --progress", - "productionLegacy": "NODE_ENV=production MINIMIZE=true LEGACY=true webpack --progress", + "build": "set NODE_ENV=production && webpack --progress", + "minify": "set NODE_ENV=production && set MINIMIZE=true && webpack --progress", + "legacy": "set NODE_ENV=production && set LEGACY=true && webpack --progress", + "productionLegacy": "set NODE_ENV=production && set MINIMIZE=true && set LEGACY=true && webpack --progress", "compile": "babel -d lib/ src/", "watch": "babel --watch -d lib/ src/", "prepare": "npm run compile && npm run build && npm run minify && npm run legacy && npm run productionLegacy" }, - "author": "fchasen@gmail.com", + "author": "myh@live.com", "license": "BSD-2-Clause", "devDependencies": { "@babel/cli": "^7.10.1", diff --git a/src/contents.js b/src/contents.js index 3effe7252..715f4d6d5 100644 --- a/src/contents.js +++ b/src/contents.js @@ -655,7 +655,9 @@ class Contents { console.error(e, e.stack); } } else { - position = range.getBoundingClientRect(); + // range.toString() might be an empty string and range.getBoundingClientRect() returns a all-zeros position. + // Thus, we use the parent element containing the range to get a valid bounding rect. + position = range.startContainer.parentElement.getBoundingClientRect(); } } } diff --git a/src/index.js b/src/index.js index 16ef8b6a3..2884523da 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import Rendition from "./rendition"; import Contents from "./contents"; import Layout from "./layout"; import ePub from "./epub"; +import { EVENTS } from "./utils/constants"; export default ePub; export { @@ -11,5 +12,6 @@ export { EpubCFI, Rendition, Contents, - Layout + Layout, + EVENTS, }; diff --git a/src/layout.js b/src/layout.js index 4f16a0f1d..7f91c14e1 100644 --- a/src/layout.js +++ b/src/layout.js @@ -103,18 +103,21 @@ class Layout { calculate(_width, _height, _gap){ var divisor = 1; - var gap = _gap || 0; + var gap = _gap || 20; //-- Check the width and create even width columns // var fullWidth = Math.floor(_width); var width = _width; var height = _height; - var section = Math.floor(width / 12); + //var section = Math.floor(width / 12); var columnWidth; + var columnHeight; var spreadWidth; + var spreadHeight; var pageWidth; + var pageHeight; var delta; if (this._spread && width >= this._minSpreadWidth) { @@ -123,9 +126,11 @@ class Layout { divisor = 1; } + /* if (this.name === "reflowable" && this._flow === "paginated" && !(_gap >= 0)) { gap = ((section % 2 === 0) ? section : section - 1); } + */ if (this.name === "pre-paginated" ) { gap = 0; @@ -138,16 +143,22 @@ class Layout { // gap = gap / divisor; columnWidth = (width / divisor) - gap; pageWidth = columnWidth + gap; + columnHeight = (height / divisor) - gap; + pageHeight = columnHeight + gap; } else { columnWidth = width; pageWidth = width; + columnWidth = height; + pageHeight = height; } if (this.name === "pre-paginated" && divisor > 1) { width = columnWidth; + height = columnHeight; } spreadWidth = (columnWidth * divisor) + gap; + spreadHeight = (columnHeight * divisor) + gap; delta = width; @@ -155,9 +166,12 @@ class Layout { this.height = height; this.spreadWidth = spreadWidth; this.pageWidth = pageWidth; + this.spreadHeight = spreadHeight; + this.pageHeight = pageHeight; this.delta = delta; this.columnWidth = columnWidth; + this.columnHeight = columnHeight; this.gap = gap; this.divisor = divisor; @@ -175,9 +189,12 @@ class Layout { width, height, spreadWidth, + spreadHeight, pageWidth, + pageHeight, delta, columnWidth, + columnHeight, gap, divisor }); diff --git a/src/managers/default/index.js b/src/managers/default/index.js index 1944889ad..2add102be 100644 --- a/src/managers/default/index.js +++ b/src/managers/default/index.js @@ -1,5 +1,5 @@ import EventEmitter from "event-emitter"; -import {extend, defer, windowBounds, isNumber} from "../../utils/core"; +import { extend, defer, windowBounds, isNumber } from "../../utils/core"; import scrollType from "../../utils/scrolltype"; import Mapping from "../../mapping"; import Queue from "../../utils/queue"; @@ -46,13 +46,13 @@ class DefaultViewManager { } - render(element, size){ + render(element, size) { let tag = element.tagName; if (typeof this.settings.fullsize === "undefined" && - tag && (tag.toLowerCase() == "body" || + tag && (tag.toLowerCase() == "body" || tag.toLowerCase() == "html")) { - this.settings.fullsize = true; + this.settings.fullsize = true; } if (this.settings.fullsize) { @@ -87,9 +87,9 @@ class DefaultViewManager { this._bounds = this.bounds(); this._stageSize = this.stage.size(); + // Set the dimensions for views - this.viewSettings.width = this._stageSize.width; - this.viewSettings.height = this._stageSize.height; + this.updateLayout(); // Function to handle a resize event. // Will only attach if width and height are both fixed. @@ -110,14 +110,14 @@ class DefaultViewManager { } - addEventListeners(){ + addEventListeners() { var scroller; - window.addEventListener("unload", function(e){ + window.addEventListener("unload", function (e) { this.destroy(); }.bind(this)); - if(!this.settings.fullsize) { + if (!this.settings.fullsize) { scroller = this.container; } else { scroller = window; @@ -127,10 +127,10 @@ class DefaultViewManager { scroller.addEventListener("scroll", this._onScroll); } - removeEventListeners(){ + removeEventListeners() { var scroller; - if(!this.settings.fullsize) { + if (!this.settings.fullsize) { scroller = this.container; } else { scroller = window; @@ -140,7 +140,7 @@ class DefaultViewManager { this._onScroll = undefined; } - destroy(){ + destroy() { clearTimeout(this.orientationTimeout); clearTimeout(this.resizeTimeout); clearTimeout(this.afterScrolled); @@ -165,9 +165,9 @@ class DefaultViewManager { } onOrientationChange(e) { - let {orientation} = window; + let { orientation } = window; - if(this.optsSettings.resizeOnOrientationChange) { + if (this.optsSettings.resizeOnOrientationChange) { this.resize(); } @@ -177,10 +177,10 @@ class DefaultViewManager { // happens in window.resize event. Adding a timeout for correct // measurement. See https://github.com/ampproject/amphtml/issues/8479 clearTimeout(this.orientationTimeout); - this.orientationTimeout = setTimeout(function(){ + this.orientationTimeout = setTimeout(function () { this.orientationTimeout = undefined; - if(this.optsSettings.resizeOnOrientationChange) { + if (this.optsSettings.resizeOnOrientationChange) { this.resize(); } @@ -193,22 +193,22 @@ class DefaultViewManager { this.resize(); } - resize(width, height, epubcfi){ + resize(width, height, epubcfi) { let stageSize = this.stage.size(width, height); // For Safari, wait for orientation to catch up // if the window is a square this.winBounds = windowBounds(); if (this.orientationTimeout && - this.winBounds.width === this.winBounds.height) { + this.winBounds.width === this.winBounds.height) { // reset the stage size for next resize this._stageSize = undefined; return; } if (this._stageSize && - this._stageSize.width === stageSize.width && - this._stageSize.height === stageSize.height ) { + this._stageSize.width === stageSize.width && + this._stageSize.height === stageSize.height) { // Size is the same, no need to resize return; } @@ -220,9 +220,11 @@ class DefaultViewManager { // Clear current views this.clear(); + /* Redundant codes? this.updateLayout() will update them!? // Update for new views this.viewSettings.width = this._stageSize.width; this.viewSettings.height = this._stageSize.height; + */ this.updateLayout(); @@ -233,7 +235,7 @@ class DefaultViewManager { } createView(section, forceRight) { - return new this.View(section, extend(this.viewSettings, { forceRight }) ); + return new this.View(section, extend(this.viewSettings, { forceRight })); } handleNextPrePaginated(forceRight, section, action) { @@ -251,7 +253,7 @@ class DefaultViewManager { } } - display(section, target){ + display(section, target) { var displaying = new defer(); var displayed = displaying.promise; @@ -265,7 +267,7 @@ class DefaultViewManager { var visible = this.views.find(section); // View is already shown, just move to correct location in view - if(visible && section && this.layout.name !== "pre-paginated") { + if (visible && section && this.layout.name !== "pre-paginated") { let offset = visible.offset(); if (this.settings.direction === "ltr") { @@ -275,7 +277,7 @@ class DefaultViewManager { this.scrollTo(offset.left + width, offset.top, true); } - if(target) { + if (target) { let offset = visible.locationOf(target); this.moveTo(offset); } @@ -293,10 +295,10 @@ class DefaultViewManager { } this.add(section, forceRight) - .then(function(view){ + .then(function (view) { // Move to correct place within the section, if needed - if(target) { + if (target) { let offset = view.locationOf(target); this.moveTo(offset); } @@ -304,10 +306,10 @@ class DefaultViewManager { }.bind(this), (err) => { displaying.reject(err); }) - .then(function(){ + .then(function () { return this.handleNextPrePaginated(forceRight, section, this.add); }.bind(this)) - .then(function(){ + .then(function () { this.views.show(); @@ -323,19 +325,19 @@ class DefaultViewManager { return displayed; } - afterDisplayed(view){ + afterDisplayed(view) { this.emit(EVENTS.MANAGERS.ADDED, view); } - afterResized(view){ + afterResized(view) { this.emit(EVENTS.MANAGERS.RESIZE, view.section); } - moveTo(offset){ + moveTo(offset) { var distX = 0, - distY = 0; + distY = 0; - if(!this.isPaginated) { + if (!this.isPaginated) { distY = offset.top; } else { distX = Math.floor(offset.left / this.layout.delta) * this.layout.delta; @@ -343,11 +345,17 @@ class DefaultViewManager { if (distX + this.layout.delta > this.container.scrollWidth) { distX = this.container.scrollWidth - this.layout.delta; } + + distY = Math.floor(offset.top / this.layout.delta) * this.layout.delta; + + if (distY + this.layout.delta > this.container.scrollHeight) { + distY = this.container.scrollHeight - this.layout.delta; + } } this.scrollTo(distX, distY, true); } - add(section, forceRight){ + add(section, forceRight) { var view = this.createView(section, forceRight); this.views.append(view); @@ -367,7 +375,7 @@ class DefaultViewManager { return view.display(this.request); } - append(section, forceRight){ + append(section, forceRight) { var view = this.createView(section, forceRight); this.views.append(view); @@ -385,7 +393,7 @@ class DefaultViewManager { return view.display(this.request); } - prepend(section, forceRight){ + prepend(section, forceRight) { var view = this.createView(section, forceRight); view.on(EVENTS.VIEWS.RESIZED, (bounds) => { @@ -408,8 +416,8 @@ class DefaultViewManager { return view.display(this.request); } - counter(bounds){ - if(this.settings.axis === "vertical") { + counter(bounds) { + if (this.settings.axis === "vertical") { this.scrollBy(0, bounds.heightDelta, true); } else { this.scrollBy(bounds.widthDelta, 0, true); @@ -427,21 +435,21 @@ class DefaultViewManager { // // }; - next(){ + next() { var next; var left; let dir = this.settings.direction; - if(!this.views.length) return; + if (!this.views.length) return; - if(this.isPaginated && this.settings.axis === "horizontal" && (!dir || dir === "ltr")) { + if (this.isPaginated && this.settings.axis === "horizontal" && (!dir || dir === "ltr")) { this.scrollLeft = this.container.scrollLeft; left = this.container.scrollLeft + this.container.offsetWidth + this.layout.delta; - if(left <= this.container.scrollWidth) { + if (left <= this.container.scrollWidth) { this.scrollBy(this.layout.delta, 0, true); } else { next = this.views.last().section.next(); @@ -450,8 +458,10 @@ class DefaultViewManager { this.scrollLeft = this.container.scrollLeft; - if (this.settings.rtlScrollType === "default"){ - left = this.container.scrollLeft; + if (this.settings.rtlScrollType === "default") { + this.scrollLeft = Math.floor(this.container.scrollLeft); + // this.container.scrollLeft could has fractional part. + left = Math.floor(this.container.scrollLeft); if (left > 0) { this.scrollBy(this.layout.delta, 0, true); @@ -459,9 +469,9 @@ class DefaultViewManager { next = this.views.last().section.next(); } } else { - left = this.container.scrollLeft + ( this.layout.delta * -1 ); + left = this.container.scrollLeft + (this.layout.delta * -1); - if (left > this.container.scrollWidth * -1){ + if (left > this.container.scrollWidth * -1) { this.scrollBy(this.layout.delta, 0, true); } else { next = this.views.last().section.next(); @@ -472,9 +482,9 @@ class DefaultViewManager { this.scrollTop = this.container.scrollTop; - let top = this.container.scrollTop + this.container.offsetHeight; + let top = this.container.scrollTop + this.container.offsetHeight; - if(top < this.container.scrollHeight) { + if (top < this.container.scrollHeight) { this.scrollBy(0, this.layout.height, true); } else { next = this.views.last().section.next(); @@ -484,8 +494,9 @@ class DefaultViewManager { next = this.views.last().section.next(); } - if(next) { + if (next) { this.clear(); + this.updateLayout(); let forceRight = false; if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && next.properties.includes("page-spread-right")) { @@ -493,19 +504,19 @@ class DefaultViewManager { } return this.append(next, forceRight) - .then(function(){ + .then(function () { return this.handleNextPrePaginated(forceRight, next, this.append); }.bind(this), (err) => { return err; }) - .then(function(){ + .then(function () { // Reset position to start for scrolled-doc vertical-rl in default mode if (!this.isPaginated && this.settings.axis === "horizontal" && this.settings.direction === "rtl" && this.settings.rtlScrollType === "default") { - + this.scrollTo(this.container.scrollWidth, 0, true); } this.views.show(); @@ -515,20 +526,20 @@ class DefaultViewManager { } - prev(){ + prev() { var prev; var left; let dir = this.settings.direction; - if(!this.views.length) return; + if (!this.views.length) return; - if(this.isPaginated && this.settings.axis === "horizontal" && (!dir || dir === "ltr")) { + if (this.isPaginated && this.settings.axis === "horizontal" && (!dir || dir === "ltr")) { this.scrollLeft = this.container.scrollLeft; left = this.container.scrollLeft; - if(left > 0) { + if (left > 0) { this.scrollBy(-this.layout.delta, 0, true); } else { prev = this.views.first().section.prev(); @@ -538,7 +549,7 @@ class DefaultViewManager { this.scrollLeft = this.container.scrollLeft; - if (this.settings.rtlScrollType === "default"){ + if (this.settings.rtlScrollType === "default") { left = this.container.scrollLeft + this.container.offsetWidth; if (left < this.container.scrollWidth) { @@ -547,7 +558,7 @@ class DefaultViewManager { prev = this.views.first().section.prev(); } } - else{ + else { left = this.container.scrollLeft; if (left < 0) { @@ -559,11 +570,12 @@ class DefaultViewManager { } else if (this.isPaginated && this.settings.axis === "vertical") { - this.scrollTop = this.container.scrollTop; + this.scrollTop = Math.floor(this.container.scrollTop); - let top = this.container.scrollTop; + // this.container.scrollTop could has fractional part. + let top = Math.floor(this.container.scrollTop); - if(top > 0) { + if (top > 0) { this.scrollBy(0, -(this.layout.height), true); } else { prev = this.views.first().section.prev(); @@ -575,8 +587,9 @@ class DefaultViewManager { } - if(prev) { + if (prev) { this.clear(); + this.updateLayout(); let forceRight = false; if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && typeof prev.prev() !== "object") { @@ -584,7 +597,7 @@ class DefaultViewManager { } return this.prepend(prev, forceRight) - .then(function(){ + .then(function () { var left; if (this.layout.name === "pre-paginated" && this.layout.divisor > 1) { left = prev.prev(); @@ -595,13 +608,13 @@ class DefaultViewManager { }.bind(this), (err) => { return err; }) - .then(function(){ - if(this.isPaginated && this.settings.axis === "horizontal") { + .then(function () { + if (this.isPaginated && this.settings.axis === "horizontal") { if (this.settings.direction === "rtl") { - if (this.settings.rtlScrollType === "default"){ + if (this.settings.rtlScrollType === "default") { this.scrollTo(0, 0, true); } - else{ + else { this.scrollTo((this.container.scrollWidth * -1) + this.layout.delta, 0, true); } } else { @@ -613,28 +626,28 @@ class DefaultViewManager { } } - current(){ + current() { var visible = this.visible(); - if(visible.length){ + if (visible.length) { // Current is the last visible view - return visible[visible.length-1]; + return visible[visible.length - 1]; } return null; } - clear () { + clear() { // this.q.clear(); if (this.views) { this.views.hide(); - this.scrollTo(0,0, true); + this.scrollTo(0, 0, true); this.views.clear(); } } - currentLocation(){ - if (this.isPaginated && this.settings.axis === "horizontal") { + currentLocation() { + if (this.isPaginated) { this.location = this.paginatedLocation(); } else { this.location = this.scrolledLocation(); @@ -648,17 +661,17 @@ class DefaultViewManager { let pageHeight = (container.height < window.innerHeight) ? container.height : window.innerHeight; let pageWidth = (container.width < window.innerWidth) ? container.width : window.innerWidth; let vertical = (this.settings.axis === "vertical"); - let rtl = (this.settings.direction === "rtl"); - + let rtl = (this.settings.direction === "rtl"); + let offset = 0; let used = 0; - if(this.settings.fullsize) { + if (this.settings.fullsize) { offset = vertical ? window.scrollY : window.scrollX; } let sections = visible.map((view) => { - let {index, href} = view.section; + let { index, href } = view.section; let position = view.position(); let width = view.width(); let height = view.height(); @@ -711,49 +724,65 @@ class DefaultViewManager { return sections; } - paginatedLocation(){ + paginatedLocation() { let visible = this.visible(); let container = this.container.getBoundingClientRect(); + let vertical = (this.settings.axis === "vertical"); let left = 0; + let top = 0; let used = 0; - if(this.settings.fullsize) { + if (this.settings.fullsize) { left = window.scrollX; + top = window.scrollY; } let sections = visible.map((view) => { - let {index, href} = view.section; + let { index, href } = view.section; let offset; let position = view.position(); let width = view.width(); + let height = view.height(); // Find mapping let start; let end; let pageWidth; + let pageHeight; - if (this.settings.direction === "rtl") { + if (!vertical && this.settings.direction === "rtl") { offset = container.right - left; pageWidth = Math.min(Math.abs(offset - position.left), this.layout.width) - used; end = position.width - (position.right - offset) - used; start = end - pageWidth; } else { - offset = container.left + left; - pageWidth = Math.min(position.right - offset, this.layout.width) - used; - start = offset - position.left + used; - end = start + pageWidth; + if (vertical) { + offset = container.top + top; + pageHeight = Math.min(position.bottom - offset, this.layout.height) - used; + start = offset - position.top + used; + end = start + pageHeight; + } else { + offset = container.left + left; + pageWidth = Math.min(position.right - offset, this.layout.width) - used; + start = offset - position.left + used; + end = start + pageWidth; + } } + // Because scrollBy() is not precise (see its comment), + // applying ceiling to workaround the page calculation problem! + start = Math.ceil(start); + end = Math.ceil(end); - used += pageWidth; + used += vertical ? pageHeight : pageWidth; let mapping = this.mapping.page(view.contents, view.section.cfiBase, start, end); - let totalPages = this.layout.count(width).pages; - let startPage = Math.floor(start / this.layout.pageWidth); + let totalPages = this.layout.count(vertical ? height : width).pages; + let startPage = Math.floor(start / (vertical ? this.layout.pageHeight : this.layout.pageWidth)); let pages = []; - let endPage = Math.floor(end / this.layout.pageWidth); - + let endPage = Math.floor(end / (vertical ? this.layout.pageHeight : this.layout.pageWidth)); + // start page should not be negative if (startPage < 0) { startPage = 0; @@ -761,7 +790,7 @@ class DefaultViewManager { } // Reverse page counts for rtl - if (this.settings.direction === "rtl") { + if (!vertical && this.settings.direction === "rtl") { let tempStartPage = startPage; startPage = totalPages - endPage; endPage = totalPages - tempStartPage; @@ -785,17 +814,17 @@ class DefaultViewManager { return sections; } - isVisible(view, offsetPrev, offsetNext, _container){ + isVisible(view, offsetPrev, offsetNext, _container) { var position = view.position(); var container = _container || this.bounds(); - if(this.settings.axis === "horizontal" && + if (this.settings.axis === "horizontal" && position.right > container.left - offsetPrev && position.left < container.right + offsetNext) { return true; - } else if(this.settings.axis === "vertical" && + } else if (this.settings.axis === "vertical" && position.bottom > container.top - offsetPrev && position.top < container.bottom + offsetNext) { @@ -806,7 +835,7 @@ class DefaultViewManager { } - visible(){ + visible() { var container = this.bounds(); var views = this.views.displayed(); var viewsLength = views.length; @@ -818,7 +847,7 @@ class DefaultViewManager { view = views[i]; isVisible = this.isVisible(view, 0, 0, container); - if(isVisible === true) { + if (isVisible === true) { visible.push(view); } @@ -826,41 +855,66 @@ class DefaultViewManager { return visible; } - scrollBy(x, y, silent){ + scrollBy(x, y, silent) { let dir = this.settings.direction === "rtl" ? -1 : 1; - if(silent) { + if (silent) { this.ignore = true; } - if(!this.settings.fullsize) { - if(x) this.container.scrollLeft += x * dir; - if(y) this.container.scrollTop += y; + if (!this.settings.fullsize) { + // Notice! Operations on scrollLeft and scrollTop are not precise! + // E.g., this.container.scrollTop is 0 and y is 100, + // after this.container.scrollTop += y + // this.container.scrollTop might become 99.xxx! + // Without compensation, this error increases after each scroll operation! + if (x) { + this.container.scrollLeft += x * dir; + const absX = Math.abs(x); + const remainder = this.container.scrollLeft % absX; + if (Math.abs(absX - remainder) >= 1) { + // Determine if the current scroll position is closer to the next page or current page. + const compensationDir = Math.round(remainder / absX) === 1 ? 1 : -1; + // Compensate it by 1 pixel. + this.container.scrollLeft += compensationDir; + } + } + if (y) { + this.container.scrollTop += y; + const absY = Math.abs(y); + const remainder = this.container.scrollTop % absY; + if (Math.abs(absY - remainder) >= 1) { + // Determine if the current scroll position is closer to the next page or current page. + const compensationDir = Math.round(remainder / absY) === 1 ? 1 : -1; + // Compensate it by 1 pixel. + this.container.scrollTop += compensationDir; + } + } } else { window.scrollBy(x * dir, y * dir); } this.scrolled = true; } - scrollTo(x, y, silent){ - if(silent) { + scrollTo(x, y, silent) { + if (silent) { this.ignore = true; } - if(!this.settings.fullsize) { + if (!this.settings.fullsize) { this.container.scrollLeft = x; this.container.scrollTop = y; } else { - window.scrollTo(x,y); + window.scrollTo(x, y); } this.scrolled = true; } - onScroll(){ + onScroll() { let scrollTop; let scrollLeft; - if(!this.settings.fullsize) { + if (!this.settings.fullsize) { scrollTop = this.container.scrollTop; scrollLeft = this.container.scrollLeft; } else { @@ -871,7 +925,7 @@ class DefaultViewManager { this.scrollTop = scrollTop; this.scrollLeft = scrollLeft; - if(!this.ignore) { + if (!this.ignore) { this.emit(EVENTS.MANAGERS.SCROLL, { top: scrollTop, left: scrollLeft @@ -908,7 +962,7 @@ class DefaultViewManager { if (this.views && this.views.length > 0 && this.layout.name === "pre-paginated") { this.display(this.views.first().section); } - // this.manager.layout(this.layout.format); + // this.manager.layout(this.layout.format); } updateLayout() { @@ -919,13 +973,23 @@ class DefaultViewManager { this._stageSize = this.stage.size(); - if(!this.isPaginated) { - this.layout.calculate(this._stageSize.width, this._stageSize.height); + if (!this.isPaginated) { + let width; + let height; + if (this.settings.axis === "vertical") { + width = this._stageSize.width - (this.settings.scrollbarWidth | 0); + height = this._stageSize.height; + } else { + width = this._stageSize.width; + height = this._stageSize.height - (this.settings.scrollbarWidth | 0); + } + this.layout.calculate(width, height, undefined, this.settings.axis); } else { this.layout.calculate( this._stageSize.width, this._stageSize.height, - this.settings.gap + this.settings.gap, + this.settings.axis ); // Set the look ahead offset for what is visible @@ -942,15 +1006,15 @@ class DefaultViewManager { this.setLayout(this.layout); } - setLayout(layout){ + setLayout(layout) { this.viewSettings.layout = layout; this.mapping = new Mapping(layout.props, this.settings.direction, this.settings.axis); - if(this.views) { + if (this.views) { - this.views.forEach(function(view){ + this.views.forEach(function (view) { if (view) { view.setLayout(layout); } @@ -964,7 +1028,7 @@ class DefaultViewManager { this.writingMode = mode; } - updateAxis(axis, forceUpdate){ + updateAxis(axis, forceUpdate) { if (!forceUpdate && axis === this.settings.axis) { return; @@ -989,14 +1053,14 @@ class DefaultViewManager { } } - updateFlow(flow, defaultScrolledOverflow="auto"){ + updateFlow(flow, defaultScrolledOverflow = "auto") { let isPaginated = (flow === "paginated" || flow === "auto"); this.isPaginated = isPaginated; if (flow === "scrolled-doc" || - flow === "scrolled-continuous" || - flow === "scrolled") { + flow === "scrolled-continuous" || + flow === "scrolled") { this.updateAxis("vertical"); } else { this.updateAxis("horizontal"); @@ -1016,12 +1080,12 @@ class DefaultViewManager { } - getContents(){ + getContents() { var contents = []; if (!this.views) { return contents; } - this.views.forEach(function(view){ + this.views.forEach(function (view) { const viewContents = view && view.contents; if (viewContents) { contents.push(viewContents); @@ -1030,7 +1094,7 @@ class DefaultViewManager { return contents; } - direction(dir="ltr") { + direction(dir = "ltr") { this.settings.direction = dir; this.stage && this.stage.direction(dir); diff --git a/src/managers/views/iframe.js b/src/managers/views/iframe.js index 76b2c1d3e..4a0d066f9 100644 --- a/src/managers/views/iframe.js +++ b/src/managers/views/iframe.js @@ -610,7 +610,15 @@ class IframeView { } let m = new Highlight(range, className, data, attributes); - let h = this.pane.addMark(m); + let h; + try { + h = this.pane.addMark(m); + } catch (err) { + // Print error only for invalid highlights. + // Throwing an error here will cause ePub display stops after navigate out and then in a section with an invalid highlight. + console.error(err); + return; + } this.highlights[cfiRange] = { "mark": h, "element": h.element, "listeners": [emitter, cb] }; diff --git a/types/index.d.ts b/types/index.d.ts index 6a8ec9b9e..8277af2d4 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,6 +14,7 @@ export { default as Rendition, Location } from './rendition'; export { default as Contents } from './contents'; export { default as Layout } from './layout'; export { NavItem } from './navigation'; +export { EVENTS } from './utils/constants'; declare namespace ePub { diff --git a/types/rendition.d.ts b/types/rendition.d.ts index 434286c42..74b24a198 100644 --- a/types/rendition.d.ts +++ b/types/rendition.d.ts @@ -15,6 +15,7 @@ export interface RenditionOptions { manager?: string | Function | object, view?: string | Function | object, flow?: string, + scrollbarWidth?: number, layout?: string, spread?: string, minSpreadWidth?: number,