1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-03 14:59:18 +02:00

Added flow, layout and spreads methods, combined managers

This commit is contained in:
Fred Chasen 2016-08-12 00:33:48 +02:00
parent 0d8deb5991
commit 1462d473df
14 changed files with 1001 additions and 1647 deletions

1522
dist/epub.js vendored

File diff suppressed because it is too large Load diff

2
dist/epub.js.map vendored

File diff suppressed because one or more lines are too long

View file

@ -78,7 +78,7 @@
var currentSectionIndex = 8; var currentSectionIndex = 8;
// Load the opf // Load the opf
var book = ePub("../books/moby-dick/OPS/package.opf"); var book = ePub("../books/moby-dick/OPS/package.opf");
var rendition = book.renderTo(document.body, { manager: "continuous", width: "60%" }); var rendition = book.renderTo(document.body, { manager: "continuous", width: "60%", flow: "scrolled" });
var displayed = rendition.display("epubcfi(/6/14[xchapter_001]!4/2/24/2[c001p0011]/1:799)"); var displayed = rendition.display("epubcfi(/6/14[xchapter_001]!4/2/24/2[c001p0011]/1:799)");

View file

@ -120,7 +120,8 @@
// Load the opf // Load the opf
var book = ePub("https://s3.amazonaws.com/moby-dick/OPS/package.opf"); var book = ePub("https://s3.amazonaws.com/moby-dick/OPS/package.opf");
var rendition = book.renderTo("viewer", { var rendition = book.renderTo("viewer", {
manager: "paginate", manager: "continuous",
flow: "paginated",
width: "100%", width: "100%",
height: 600 height: 600
}); });

View file

@ -1,8 +1,10 @@
var RSVP = require('rsvp'); var RSVP = require('rsvp');
var core = require('./core'); var core = require('./core');
var EpubCFI = require('./epubcfi'); var EpubCFI = require('./epubcfi');
var Mapping = require('./mapping');
function Contents(doc, content) {
function Contents(doc, content, cfiBase) {
// Blank Cfi for Parsing // Blank Cfi for Parsing
this.epubcfi = new EpubCFI(); this.epubcfi = new EpubCFI();
@ -13,11 +15,13 @@ function Contents(doc, content) {
// Dom events to listen for // Dom events to listen for
this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"]; this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"];
this.size = { this._size = {
width: 0, width: 0,
height: 0 height: 0
} }
this.cfiBase = cfiBase || "";
this.listeners(); this.listeners();
}; };
@ -226,14 +230,14 @@ Contents.prototype.resizeListeners = function() {
width = this.scrollWidth(); width = this.scrollWidth();
height = this.scrollHeight(); height = this.scrollHeight();
if (width != this.size.width || height != this.size.height) { if (width != this._size.width || height != this._size.height) {
this.size = { this._size = {
width: width, width: width,
height: height height: height
} }
this.trigger("resize", this.size); this.trigger("resize", this._size);
} }
this.expanding = setTimeout(this.resizeListeners.bind(this), 350); this.expanding = setTimeout(this.resizeListeners.bind(this), 350);
@ -490,7 +494,8 @@ Contents.prototype.triggerSelectedEvent = function(selection){
if (selection && selection.rangeCount > 0) { if (selection && selection.rangeCount > 0) {
range = selection.getRangeAt(0); range = selection.getRangeAt(0);
if(!range.collapsed) { if(!range.collapsed) {
cfirange = this.section.cfiFromRange(range); // cfirange = this.section.cfiFromRange(range);
cfirange = new EpubCFI(range, this.cfiBase).toString();
this.trigger("selected", cfirange); this.trigger("selected", cfirange);
this.trigger("selectedRange", range); this.trigger("selectedRange", range);
} }
@ -503,10 +508,75 @@ Contents.prototype.range = function(_cfi, ignoreClass){
}; };
Contents.prototype.map = function(layout){ Contents.prototype.map = function(layout){
var map = new Map(layout); var map = new Mapping(layout);
return map.section(); return map.section();
}; };
Contents.prototype.size = function(width, height){
if (width >= 0) {
this.width(width);
}
if (height >= 0) {
this.height(height);
}
if (width >= 0 && height >= 0) {
this.overflow("hidden");
}
};
Contents.prototype.columns = function(width, height, columnWidth, gap){
var COLUMN_AXIS = core.prefixed('columnAxis');
var COLUMN_GAP = core.prefixed('columnGap');
var COLUMN_WIDTH = core.prefixed('columnWidth');
var COLUMN_FILL = core.prefixed('columnFill');
this.size(width, height);
this.css("margin", "0");
this.css(COLUMN_AXIS, "horizontal");
this.css(COLUMN_FILL, "auto");
this.css(COLUMN_GAP, gap+"px");
this.css(COLUMN_WIDTH, columnWidth+"px");
};
Contents.prototype.scale = function(scale){
// this.css("position", "absolute"));
this.css("transformOrigin", "top left");
this.css("transform", "scale(" + scale + ")");
this.documentElement.style.display = "flex";
this.documentElement.style.flexDirection = "column";
this.documentElement.style.justifyContent = "center";
};
Contents.prototype.fit = function(width, height){
var viewport = this.viewport();
var widthScale = width / viewport.width;
var heightScale = height / viewport.height;
var scale = widthScale < heightScale ? widthScale : heightScale;
var offsetY = (height - (viewport.height * scale)) / 2;
this.width(width);
this.height(height);
this.overflow("hidden");
this.scale(scale, 0, offsetY);
this.css("backgroundColor", "transparent");
// this.css("marginTop", offsetY + "px");
};
Contents.prototype.destroy = function() { Contents.prototype.destroy = function() {
// Stop observing // Stop observing
if(this.observer) { if(this.observer) {

View file

@ -28,11 +28,10 @@ ePub.register = {
// Default Views // Default Views
ePub.register.view("iframe", require('./views/iframe')); ePub.register.view("iframe", require('./views/iframe'));
ePub.register.view("inline", require('./views/inline')); // ePub.register.view("inline", require('./views/inline'));
// Default View Managers // Default View Managers
ePub.register.manager("single", require('./managers/single')); ePub.register.manager("single", require('./managers/single'));
ePub.register.manager("continuous", require('./managers/continuous')); ePub.register.manager("continuous", require('./managers/continuous'));
ePub.register.manager("paginate", require('./managers/paginate'));
module.exports = ePub; module.exports = ePub;

View file

@ -1,40 +1,64 @@
var core = require('./core'); var core = require('./core');
var RSVP = require('rsvp'); var RSVP = require('rsvp');
function Reflowable(){ function Layout(settings){
this.columnAxis = core.prefixed('columnAxis'); this.name = settings.layout || "reflowable";
this.columnGap = core.prefixed('columnGap'); this._flow = (settings.flow === "paginated") ? "paginated" : "scrolled";
this.columnWidth = core.prefixed('columnWidth'); this._spread = (settings.spread === "none") ? false : true;
this.columnFill = core.prefixed('columnFill'); this.minSpreadWidth = 800;
this.width = 0; this.width = 0;
this.height = 0; this.height = 0;
this.spread = 0; this.spreadWidth = 0;
this.delta = 0; this.delta = 0;
this.column = 0; this.columnWidth = 0;
this.gap = 0; this.gap = 0;
this.divisor = 0; this.divisor = 1;
this.name = "reflowable";
}; };
Reflowable.prototype.calculate = function(_width, _height, _gap, _devisor){ // paginated | scrolled
Layout.prototype.flow = function(flow) {
this._flow = (flow === "paginated") ? "paginated" : "scrolled";
}
var divisor = _devisor || 1; // true | false
Layout.prototype.spread = function(spread, min) {
this._spread = (spread === "none") ? false : true;
if (min >= 0) {
this.minSpreadWidth = min;
}
}
Layout.prototype.calculate = function(_width, _height, _gap){
var divisor = 1;
var gap = _gap;
//-- Check the width and create even width columns //-- Check the width and create even width columns
var fullWidth = Math.floor(_width); var fullWidth = Math.floor(_width);
var width = _width; //(fullWidth % 2 === 0) ? fullWidth : fullWidth - 1; var width = _width;
var section = Math.floor(width / 8); var section = Math.floor(width / 8);
var gap = (_gap >= 0) ? _gap : ((section % 2 === 0) ? section : section - 1);
var colWidth; var colWidth;
var spreadWidth; var spreadWidth;
var delta; var delta;
if (this._spread && width >= this.minSpreadWidth) {
divisor = 2;
} else {
divisor = 1;
}
if (this.name === "reflowable" && this._flow === "paginated" && !(_gap >= 0)) {
gap = ((section % 2 === 0) ? section : section - 1);
} else {
gap = 0;
}
//-- Double Page //-- Double Page
if(divisor > 1) { if(divisor > 1) {
colWidth = Math.floor((width - gap) / divisor); colWidth = Math.floor((width - gap) / divisor);
@ -48,52 +72,31 @@ Reflowable.prototype.calculate = function(_width, _height, _gap, _devisor){
this.width = width; this.width = width;
this.height = _height; this.height = _height;
this.spread = spreadWidth; this.spreadWidth = spreadWidth;
this.delta = delta; this.delta = delta;
this.column = colWidth; this.columnWidth = colWidth;
this.gap = gap; this.gap = gap;
this.divisor = divisor; this.divisor = divisor;
}; };
Reflowable.prototype.format = function(contents){ Layout.prototype.format = function(contents){
var promises = []; var formating;
// var $doc = doc.documentElement;
// var $body = doc.body;//view.document.querySelector("body");
// $doc.style.overflow = "hidden"; if (this.name === "pre-paginated") {
promises.push(contents.overflow("hidden")); formating = contents.fit(this.columnWidth, this.height);
} else if (this._flow === "paginated") {
formating = contents.columns(this.width, this.height, this.columnWidth, this.gap);
} else { // scrolled
formating = contents.size(this.width, null);
}
// Must be set to the new calculated width or the columns will be off return formating; // might be a promise in some View Managers
// $body.style.width = this.width + "px";
// $doc.style.width = this.width + "px";
promises.push(contents.width(this.width));
//-- Adjust height
// $body.style.height = this.height + "px";
promises.push(contents.height(this.height));
promises.push(contents.css("margin", "0"));
//-- Add columns
// $body.style[this.columnAxis] = "horizontal";
promises.push(contents.css(this.columnAxis, "horizontal"));
// $body.style[this.columnFill] = "auto";
promises.push(contents.css(this.columnFill, "auto"));
// $body.style[this.columnGap] = this.gap+"px";
promises.push(contents.css(this.columnGap, this.gap+"px"));
// $body.style[this.columnWidth] = this.column +"px";
promises.push(contents.css(this.columnWidth, this.column+"px"));
// Add extra padding for the gap between this and the next view
// view.iframe.style.marginRight = this.gap+"px";
return RSVP.all(promises);
}; };
Reflowable.prototype.count = function(totalWidth) { Layout.prototype.count = function(totalWidth) {
// var totalWidth = view.root().scrollWidth; // var totalWidth = contents.scrollWidth();
var spreads = Math.ceil(totalWidth / this.spread); var spreads = Math.ceil( totalWidth / this.spreadWidth);
return { return {
spreads : spreads, spreads : spreads,
@ -101,140 +104,4 @@ Reflowable.prototype.count = function(totalWidth) {
}; };
}; };
function Fixed(_width, _height){ module.exports = Layout;
this.width = 0;
this.height = 0;
this.spread = 0;
this.delta = 0;
this.column = 0;
this.gap = 0;
this.divisor = 0;
this.name = "pre-paginated";
};
Fixed.prototype.calculate = function(_width, _height, _gap, _devisor){
var divisor = _devisor || 1;
var section = Math.floor(_width / 8);
var gap = (_gap >= 0) ? _gap : ((section % 2 === 0) ? section : section - 1);
var colWidth;
var spreadWidth;
var delta;
//-- Double Page
if(divisor > 1) {
colWidth = Math.floor((_width - gap) / divisor);
} else {
colWidth = _width;
}
spreadWidth = colWidth * divisor;
delta = (colWidth + gap) * divisor;
this.width = colWidth;
this.height = _height;
this.spread = spreadWidth;
this.delta = delta;
this.column = colWidth;
this.gap = gap;
this.divisor = divisor;
};
Fixed.prototype.format = function(contents){
var promises = [];
var viewport = contents.viewport();
var width = viewport.width;
var height = viewport.height;
var widthScale = this.column / width;
var heightScale = this.height / height;
var scale = widthScale < heightScale ? widthScale : heightScale;
var offsetX = (this.width - (width * scale)) / 2;
var offsetY = (this.height - (height * scale)) / 2;
promises.push(contents.width(this.width));
promises.push(contents.height(this.height));
promises.push(contents.css("position", "absolute"));
promises.push(contents.css("transform", "scale(" + scale + ")"));
promises.push(contents.overflow("hidden"));
promises.push(contents.css("transformOrigin", "top left"));
promises.push(contents.css("backgroundColor", "transparent"));
promises.push(contents.css("marginTop", offsetY + "px"));
// promises.push(contents.css("marginLeft", offsetX + "px"));
// page.style.transformOrigin = "top left";
// if (!view.offsetRight) {
// page.style.transformOrigin = "top right";
// page.style.right = 0;
// page.style.left = "auto";
// }
//-- Scroll
// $doc.style.overflow = "auto";
// promises.push(contents.overflow("auto"));
return RSVP.all(promises);
};
Fixed.prototype.count = function(){
return {
spreads : 1,
pages : 1
};
};
function Scroll(){
this.width = 0;
this.height = 0;
this.spread = 0;
this.column = 0;
this.gap = 0;
this.name = "scrolled";
};
Scroll.prototype.calculate = function(_width, _height){
this.spread = _width;
this.column = _width;
this.gap = 0;
};
Scroll.prototype.format = function(contents){
var promises = [];
// var $doc = doc.documentElement;
// $doc.style.width = "auto";
// $doc.style.height = "auto";
// contents.width("auto");
promises.push(contents.height("auto"));
return RSVP.all(promises);
};
Scroll.prototype.count = function(){
return {
spreads : 1,
pages : 1
};
};
module.exports = {
'Reflowable': Reflowable,
'Fixed': Fixed,
'Scroll': Scroll
};

View file

@ -11,10 +11,26 @@ function ContinuousViewManager(options) {
overflow: "auto", overflow: "auto",
axis: "vertical", axis: "vertical",
offset: 500, offset: 500,
offsetDelta: 250 offsetDelta: 250,
width: undefined,
height: undefined
}); });
core.defaults(this.settings, options.settings || {}); core.extend(this.settings, options.settings || {});
// Gap can be 0, byt defaults doesn't handle that
if (options.settings.gap != "undefined" && options.settings.gap === 0) {
this.settings.gap = options.settings.gap;
}
// this.viewSettings.axis = this.settings.axis;
this.viewSettings = {
ignoreClass: this.settings.ignoreClass,
axis: this.settings.axis,
layout: this.layout,
width: 0,
height: 0
};
this.scrollTop = 0; this.scrollTop = 0;
this.scrollLeft = 0; this.scrollLeft = 0;
@ -48,19 +64,26 @@ ContinuousViewManager.prototype.fill = function(_full){
ContinuousViewManager.prototype.moveTo = function(offset){ ContinuousViewManager.prototype.moveTo = function(offset){
// var bounds = this.stage.bounds(); // var bounds = this.stage.bounds();
// var dist = Math.floor(offset.top / bounds.height) * bounds.height; // var dist = Math.floor(offset.top / bounds.height) * bounds.height;
return this.check( var distX = 0,
offset.left+this.settings.offset, distY = 0;
offset.top+this.settings.offset)
var offsetX = 0,
offsetY = 0;
if(this.settings.axis === "vertical") {
distY = offset.top;
offsetY = offset.top+this.settings.offset;
} else {
distX = Math.floor(offset.left / this.layout.delta) * this.layout.delta;
offsetX = distX+this.settings.offset;
}
return this.check(offsetX, offsetY)
.then(function(){ .then(function(){
this.scrollBy(distX, distY);
if(this.settings.axis === "vertical") {
this.scrollBy(0, offset.top);
} else {
this.scrollBy(offset.left, 0);
}
}.bind(this)); }.bind(this));
}; };
/* /*
ContinuousViewManager.prototype.afterDisplayed = function(currView){ ContinuousViewManager.prototype.afterDisplayed = function(currView){
var next = currView.section.next(); var next = currView.section.next();
@ -84,6 +107,47 @@ ContinuousViewManager.prototype.afterDisplayed = function(currView){
}; };
*/ */
ContinuousViewManager.prototype.resize = function(width, height){
// Clear the queue
this.q.clear();
this._stageSize = this.stage.size(width, height);
this._bounds = this.bounds();
// Update for new views
this.viewSettings.width = this._stageSize.width;
this.viewSettings.height = this._stageSize.height;
// Update for existing views
this.views.each(function(view) {
view.size(this._stageSize.width, this._stageSize.height);
}.bind(this));
this.updateLayout();
// if(this.location) {
// this.rendition.display(this.location.start);
// }
this.trigger("resized", {
width: this.stage.width,
height: this.stage.height
});
};
ContinuousViewManager.prototype.onResized = function(e) {
// this.views.clear();
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(function(){
this.resize();
}.bind(this), 150);
};
ContinuousViewManager.prototype.afterResized = function(view){ ContinuousViewManager.prototype.afterResized = function(view){
this.trigger("resize", view.section); this.trigger("resize", view.section);
}; };
@ -140,58 +204,6 @@ ContinuousViewManager.prototype.counter = function(bounds){
} }
}; };
/*
ContinuousViewManager.prototype.check = function(_offset){
var checking = new RSVP.defer();
var container = this.stage.bounds();
var promises = [];
var offset = _offset || this.settings.offset;
this.views.each(function(view){
var visible = this.isVisible(view, offset, offset, container);
if(visible) {
if(!view.displayed && !view.rendering) {
// console.log("render",view.section.index)
promises.push(this.render(view));
}
} else {
if(view.displayed) {
// console.log("destroy", view.section.index)
this.q.enqueue(view.destroy.bind(view));
// view.destroy();
// this.q.enqueue(this.trim);
clearTimeout(this.trimTimeout);
this.trimTimeout = setTimeout(function(){
this.q.enqueue(this.trim.bind(this));
}.bind(this), 250);
}
}
}.bind(this));
if(promises.length){
return RSVP.all(promises)
.then(function(posts) {
// Check to see if anything new is on screen after rendering
this.q.enqueue(this.check.bind(this));
}.bind(this));
} else {
checking.resolve();
return checking.promise;
}
};
*/
ContinuousViewManager.prototype.update = function(_offset){ ContinuousViewManager.prototype.update = function(_offset){
var container = this.bounds(); var container = this.bounds();
@ -258,7 +270,6 @@ ContinuousViewManager.prototype.check = function(_offsetLeft, _offsetTop){
var visibleLength = horizontal ? bounds.width : bounds.height; var visibleLength = horizontal ? bounds.width : bounds.height;
var contentLength = horizontal ? this.container.scrollWidth : this.container.scrollHeight; var contentLength = horizontal ? this.container.scrollWidth : this.container.scrollHeight;
if (offset + visibleLength + delta >= contentLength) { if (offset + visibleLength + delta >= contentLength) {
last = this.views.last(); last = this.views.last();
next = last && last.section.next(); next = last && last.section.next();
@ -465,6 +476,17 @@ ContinuousViewManager.prototype.onScroll = function(){
ContinuousViewManager.prototype.currentLocation = function(){ ContinuousViewManager.prototype.currentLocation = function(){
if (this.settings.axis === "vertical") {
this.location = this.scrolledLocation();
} else {
this.location = this.paginatedLocation();
}
return this.location;
};
ContinuousViewManager.prototype.scrolledLocation = function(){
var visible = this.visible(); var visible = this.visible();
var startPage, endPage; var startPage, endPage;
@ -487,6 +509,39 @@ ContinuousViewManager.prototype.currentLocation = function(){
}; };
ContinuousViewManager.prototype.paginatedLocation = function(){
var visible = this.visible();
var startA, startB, endA, endB;
var pageLeft, pageRight;
var container = this.container.getBoundingClientRect();
if(visible.length === 1) {
startA = container.left - visible[0].position().left;
endA = startA + this.layout.spreadWidth;
return this.mapping.page(visible[0], startA, endA);
}
if(visible.length > 1) {
// Left Col
startA = container.left - visible[0].position().left;
endA = startA + this.layout.columnWidth;
// Right Col
startB = container.left + this.layout.spreadWidth - visible[visible.length-1].position().left;
endB = startB + this.layout.columnWidth;
pageLeft = this.mapping.page(visible[0], startA, endA);
pageRight = this.mapping.page(visible[visible.length-1], startB, endB);
return {
start: pageLeft.start,
end: pageRight.end
};
}
};
/* /*
Continuous.prototype.current = function(what){ Continuous.prototype.current = function(what){
var view, top; var view, top;
@ -533,4 +588,77 @@ Continuous.prototype.current = function(what){
}; };
*/ */
ContinuousViewManager.prototype.updateLayout = function() {
if (!this.stage) {
return;
}
if(this.settings.axis === "vertical") {
this.layout.calculate(this._stageSize.width, this._stageSize.height);
} else {
this.layout.calculate(
this._stageSize.width,
this._stageSize.height,
this.settings.gap
);
// Set the look ahead offset for what is visible
this.settings.offset = this.layout.delta;
this.stage.addStyleRules("iframe", [{"margin-right" : this.layout.gap + "px"}]);
}
this.setLayout(this.layout);
};
ContinuousViewManager.prototype.next = function(){
if(this.settings.axis === "horizontal") {
this.scrollLeft = this.container.scrollLeft;
if(this.container.scrollLeft +
this.container.offsetWidth +
this.layout.delta < this.container.scrollWidth) {
this.scrollBy(this.layout.delta, 0);
} else {
this.scrollTo(this.container.scrollWidth - this.layout.delta, 0);
}
} else {
this.scrollBy(0, this.layout.height);
}
};
ContinuousViewManager.prototype.prev = function(){
if(this.settings.axis === "horizontal") {
this.scrollBy(-this.layout.delta, 0);
} else {
this.scrollBy(0, -this.layout.height);
}
};
ContinuousViewManager.prototype.updateFlow = function(flow){
var axis = (flow === "paginated") ? "horizontal" : "vertical";
this.settings.axis = axis;
this.viewSettings.axis = axis;
this.settings.overflow = (flow === "paginated") ? "hidden" : "auto";
// this.views.each(function(view){
// view.setAxis(axis);
// });
if (this.settings.axis === "vertical") {
this.settings.infinite = true;
} else {
this.settings.infinite = false;
}
};
module.exports = ContinuousViewManager; module.exports = ContinuousViewManager;

View file

@ -1,229 +0,0 @@
var RSVP = require('rsvp');
var core = require('../core');
var ContinuousViewManager = require('./continuous');
var Mapping = require('../map');
var Layout = require('../layout');
function PaginatedViewManager(options) {
ContinuousViewManager.apply(this, arguments); // call super constructor.
this.settings = core.extend(this.settings || {}, {
width: 600,
height: 400,
axis: "horizontal",
forceSingle: false,
minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always)
gap: "auto", //-- "auto" or int
overflow: "hidden",
infinite: false
});
core.defaults(this.settings, options.settings || {});
// Gap can be 0, byt defaults doesn't handle that
if (options.settings.gap != "undefined" && options.settings.gap === 0) {
this.settings.gap = options.settings.gap;
}
this.isForcedSingle = this.settings.forceSingle;
this.viewSettings.axis = this.settings.axis;
};
PaginatedViewManager.prototype = Object.create(ContinuousViewManager.prototype);
PaginatedViewManager.prototype.constructor = PaginatedViewManager;
PaginatedViewManager.prototype.determineSpreads = function(cutoff){
if(this.isForcedSingle || !cutoff || this.stage.bounds().width < cutoff) {
return 1; //-- Single Page
}else{
return 2; //-- Double Page
}
};
PaginatedViewManager.prototype.forceSingle = function(bool){
if(bool === false) {
this.isForcedSingle = false;
// this.spreads = false;
} else {
this.isForcedSingle = true;
// this.spreads = this.determineSpreads(this.minSpreadWidth);
}
this.applyLayoutMethod();
};
// PaginatedViewManager.prototype.addEventListeners = function(){
// On display
// this.layoutSettings = this.reconcileLayoutSettings(globalLayout, chapter.properties);
// this.layoutMethod = this.determineLayout(this.layoutSettings);
// this.layout = new EPUBJS.Layout[this.layoutMethod]();
//this.hooks.display.register(this.registerLayoutMethod.bind(this));
// this.hooks.display.register(this.reportLocation);
// this.on('displayed', this.reportLocation.bind(this));
// this.hooks.content.register(this.adjustImages.bind(this));
// this.currentPage = 0;
// window.addEventListener('unload', function(e){
// this.ignore = true;
// this.destroy();
// }.bind(this));
// };
PaginatedViewManager.prototype.applyLayoutMethod = function() {
//var task = new RSVP.defer();
// this.spreads = this.determineSpreads(this.settings.minSpreadWidth);
console.log(this.settings.globalLayoutProperties);
this.layout = new Layout.Reflowable();
this.updateLayout();
this.setLayout(this.layout);
this.stage.addStyleRules("iframe", [{"margin-right" : this.layout.gap + "px"}]);
// Set the look ahead offset for what is visible
this.settings.offset = this.layout.delta;
this.mapping = new Mapping(this.layout);
// this.hooks.layout.register(this.layout.format.bind(this));
//task.resolve();
//return task.promise;
// return layout;
};
PaginatedViewManager.prototype.updateLayout = function() {
this.spreads = this.determineSpreads(this.settings.minSpreadWidth);
this.layout.calculate(
this._stageSize.width,
this._stageSize.height,
this.settings.gap,
this.spreads
);
this.settings.offset = this.layout.delta;
};
PaginatedViewManager.prototype.moveTo = function(offset){
var dist = Math.floor(offset.left / this.layout.delta) * this.layout.delta;
return this.check(0, dist+this.settings.offset).then(function(){
this.scrollBy(dist, 0);
}.bind(this));
};
PaginatedViewManager.prototype.page = function(pg){
// this.currentPage = pg;
// this.renderer.infinite.scrollTo(this.currentPage * this.formated.pageWidth, 0);
//-- Return false if page is greater than the total
// return false;
};
PaginatedViewManager.prototype.next = function(){
this.scrollLeft = this.container.scrollLeft;
if(this.container.scrollLeft +
this.container.offsetWidth +
this.layout.delta < this.container.scrollWidth) {
this.scrollBy(this.layout.delta, 0);
} else {
// this.scrollTo(this.container.scrollWidth, 0);
this.scrollTo(this.container.scrollWidth - this.layout.delta, 0);
}
// this.reportLocation();
// this.check();
};
PaginatedViewManager.prototype.prev = function(){
this.scrollBy(-this.layout.delta, 0);
// this.reportLocation();
// this.check();
};
// Paginate.prototype.reportLocation = function(){
// return this.q.enqueue(function(){
// this.location = this.currentLocation();
// this.trigger("locationChanged", this.location);
// }.bind(this));
// };
PaginatedViewManager.prototype.currentLocation = function(){
var visible = this.visible();
var startA, startB, endA, endB;
var pageLeft, pageRight;
var container = this.container.getBoundingClientRect();
if(visible.length === 1) {
startA = container.left - visible[0].position().left;
endA = startA + this.layout.spread;
return this.mapping.page(visible[0], startA, endA);
}
if(visible.length > 1) {
// Left Col
startA = container.left - visible[0].position().left;
endA = startA + this.layout.column;
// Right Col
startB = container.left + this.layout.spread - visible[visible.length-1].position().left;
endB = startB + this.layout.column;
pageLeft = this.mapping.page(visible[0], startA, endA);
pageRight = this.mapping.page(visible[visible.length-1], startB, endB);
return {
start: pageLeft.start,
end: pageRight.end
};
}
};
PaginatedViewManager.prototype.resize = function(width, height){
// Clear the queue
this.q.clear();
this.bounds = this.stage.bounds(width, height);
// this.updateLayout();
// if(this.location) {
// this.display(this.location.start);
// }
this.trigger("resized", {
width: this.stage.width,
height: this.stage.height
});
};
PaginatedViewManager.prototype.onResized = function(e) {
this.views.clear();
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(function(){
this.resize();
}.bind(this), 150);
};
module.exports = PaginatedViewManager;

View file

@ -3,8 +3,8 @@ var core = require('../core');
var Stage = require('../stage'); var Stage = require('../stage');
var Views = require('../views'); var Views = require('../views');
var EpubCFI = require('../epubcfi'); var EpubCFI = require('../epubcfi');
var Layout = require('../layout'); // var Layout = require('../layout');
var Mapping = require('../map'); var Mapping = require('../mapping');
var Queue = require('../queue'); var Queue = require('../queue');
function SingleViewManager(options) { function SingleViewManager(options) {
@ -17,19 +17,18 @@ function SingleViewManager(options) {
this.settings = core.extend(this.settings || {}, { this.settings = core.extend(this.settings || {}, {
infinite: true, infinite: true,
hidden: false, hidden: false,
width: false, width: undefined,
height: null, height: undefined,
globalLayoutProperties : { layout: 'reflowable', spread: 'auto', orientation: 'auto'}, // globalLayoutProperties : { layout: 'reflowable', spread: 'auto', orientation: 'auto'},
// layout: null, // layout: null,
axis: "vertical", axis: "vertical",
ignoreClass: '' ignoreClass: ''
}); });
core.defaults(this.settings, options.settings || {}); core.extend(this.settings, options.settings || {});
this.viewSettings = { this.viewSettings = {
ignoreClass: this.settings.ignoreClass, ignoreClass: this.settings.ignoreClass,
globalLayoutProperties: this.settings.globalLayoutProperties,
axis: this.settings.axis, axis: this.settings.axis,
layout: this.layout, layout: this.layout,
width: 0, width: 0,
@ -73,7 +72,10 @@ SingleViewManager.prototype.render = function(element, size){
this.addEventListeners(); this.addEventListeners();
// Add Layout method // Add Layout method
this.applyLayoutMethod(); // this.applyLayoutMethod();
if (this.layout) {
this.updateLayout();
}
}; };
SingleViewManager.prototype.addEventListeners = function(){ SingleViewManager.prototype.addEventListeners = function(){
@ -113,16 +115,6 @@ SingleViewManager.prototype.resize = function(width, height){
}; };
SingleViewManager.prototype.setLayout = function(layout){
this.viewSettings.layout = layout;
this.views.each(function(view){
view.setLayout(layout);
});
};
SingleViewManager.prototype.createView = function(section) { SingleViewManager.prototype.createView = function(section) {
return new this.View(section, this.viewSettings); return new this.View(section, this.viewSettings);
}; };
@ -352,44 +344,69 @@ SingleViewManager.prototype.scrollTo = function(x, y, silent){
// }; // };
}; };
SingleViewManager.prototype.onScroll = function(){ SingleViewManager.prototype.onScroll = function(){
}; };
SingleViewManager.prototype.bounds = function() { SingleViewManager.prototype.bounds = function() {
var bounds; var bounds;
if(!this.settings.height || !this.container) { if(!this.settings.height || !this.container) {
bounds = core.windowBounds(); bounds = core.windowBounds();
} else { } else {
bounds = this.stage.bounds(); bounds = this.stage.bounds();
} }
return bounds; return bounds;
}; };
SingleViewManager.prototype.applyLayoutMethod = function() { SingleViewManager.prototype.applyLayout = function(layout) {
this.layout = new Layout.Scroll(); this.layout = layout;
this.calculateLayout(); this.updateLayout();
this.setLayout(this.layout); this.mapping = new Mapping(this.layout);
this.mapping = new Mapping(this.layout);
// this.manager.layout(this.layout.format); // this.manager.layout(this.layout.format);
}; };
SingleViewManager.prototype.calculateLayout = function() { SingleViewManager.prototype.updateLayout = function() {
var bounds = this.stage.bounds(); var bounds;
this.layout.calculate(bounds.width, bounds.height);
};
SingleViewManager.prototype.updateLayout = function() { if (this.stage) {
this.calculateLayout(); bound = this.stage.bounds();
this.layout.calculate(bounds.width, bounds.height);
this.setLayout(this.layout); this.setLayout(this.layout);
}; }
};
SingleViewManager.prototype.setLayout = function(layout){
this.viewSettings.layout = layout;
if(this.views) {
this.views.each(function(view){
view.setLayout(layout);
});
}
};
SingleViewManager.prototype.updateFlow = function(flow){
var axis = (flow === "paginated") ? "horizontal" : "paginated";
this.settings.axis = axis;
this.viewSettings.axis = axis;
// this.views.each(function(view){
// view.setAxis(axis);
// });
};
//-- Enable binding events to Manager //-- Enable binding events to Manager
RSVP.EventTarget.mixin(SingleViewManager.prototype); RSVP.EventTarget.mixin(SingleViewManager.prototype);

View file

@ -1,15 +1,15 @@
function Map(layout){ function Mapping(layout){
this.layout = layout; this.layout = layout;
}; };
Map.prototype.section = function(view) { Mapping.prototype.section = function(view) {
var ranges = this.findRanges(view); var ranges = this.findRanges(view);
var map = this.rangeListToCfiList(view, ranges); var map = this.rangeListToCfiList(view, ranges);
return map; return map;
}; };
Map.prototype.page = function(view, start, end) { Mapping.prototype.page = function(view, start, end) {
var contents = view.contents; var contents = view.contents;
var root = contents && contents.document ? contents.document.body : false; var root = contents && contents.document ? contents.document.body : false;
@ -23,7 +23,7 @@ Map.prototype.page = function(view, start, end) {
}); });
}; };
Map.prototype.walk = function(root, func) { Mapping.prototype.walk = function(root, func) {
//var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, null, false); //var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, null, false);
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode: function (node) { acceptNode: function (node) {
@ -44,9 +44,10 @@ Map.prototype.walk = function(root, func) {
return result; return result;
}; };
Map.prototype.findRanges = function(view){ Mapping.prototype.findRanges = function(view){
var columns = []; var columns = [];
var count = this.layout.count(view); var scrollWidth = view.contents.scrollWidth();
var count = this.layout.count(scrollWidth);
var column = this.layout.column; var column = this.layout.column;
var gap = this.layout.gap; var gap = this.layout.gap;
var start, end; var start, end;
@ -63,7 +64,7 @@ Map.prototype.findRanges = function(view){
return columns; return columns;
}; };
Map.prototype.findStart = function(root, start, end){ Mapping.prototype.findStart = function(root, start, end){
var stack = [root]; var stack = [root];
var $el; var $el;
var found; var found;
@ -110,7 +111,7 @@ Map.prototype.findStart = function(root, start, end){
return this.findTextStartRange($prev, start, end); return this.findTextStartRange($prev, start, end);
}; };
Map.prototype.findEnd = function(root, start, end){ Mapping.prototype.findEnd = function(root, start, end){
var stack = [root]; var stack = [root];
var $el; var $el;
var $prev = root; var $prev = root;
@ -161,7 +162,7 @@ Map.prototype.findEnd = function(root, start, end){
}; };
Map.prototype.findTextStartRange = function(node, start, end){ Mapping.prototype.findTextStartRange = function(node, start, end){
var ranges = this.splitTextNodeIntoRanges(node); var ranges = this.splitTextNodeIntoRanges(node);
var prev; var prev;
var range; var range;
@ -183,7 +184,7 @@ Map.prototype.findTextStartRange = function(node, start, end){
return ranges[0]; return ranges[0];
}; };
Map.prototype.findTextEndRange = function(node, start, end){ Mapping.prototype.findTextEndRange = function(node, start, end){
var ranges = this.splitTextNodeIntoRanges(node); var ranges = this.splitTextNodeIntoRanges(node);
var prev; var prev;
var range; var range;
@ -209,7 +210,7 @@ Map.prototype.findTextEndRange = function(node, start, end){
}; };
Map.prototype.splitTextNodeIntoRanges = function(node, _splitter){ Mapping.prototype.splitTextNodeIntoRanges = function(node, _splitter){
var ranges = []; var ranges = [];
var textContent = node.textContent || ""; var textContent = node.textContent || "";
var text = textContent.trim(); var text = textContent.trim();
@ -258,7 +259,7 @@ Map.prototype.splitTextNodeIntoRanges = function(node, _splitter){
Map.prototype.rangePairToCfiPair = function(section, rangePair){ Mapping.prototype.rangePairToCfiPair = function(section, rangePair){
var startRange = rangePair.start; var startRange = rangePair.start;
var endRange = rangePair.end; var endRange = rangePair.end;
@ -276,7 +277,7 @@ Map.prototype.rangePairToCfiPair = function(section, rangePair){
}; };
Map.prototype.rangeListToCfiList = function(view, columns){ Mapping.prototype.rangeListToCfiList = function(view, columns){
var map = []; var map = [];
var rangePair, cifPair; var rangePair, cifPair;
@ -290,4 +291,4 @@ Map.prototype.rangeListToCfiList = function(view, columns){
return map; return map;
}; };
module.exports = Map; module.exports = Mapping;

View file

@ -8,19 +8,20 @@ var Queue = require('./queue');
// var View = require('./view'); // var View = require('./view');
var Views = require('./views'); var Views = require('./views');
var Layout = require('./layout'); var Layout = require('./layout');
var Mapping = require('./mapping');
function Rendition(book, options) { function Rendition(book, options) {
this.settings = core.extend(this.settings || {}, { this.settings = core.extend(this.settings || {}, {
// infinite: true,
// hidden: false,
width: null, width: null,
height: null, height: null,
// layoutOveride : null, // Default: { spread: 'reflowable', layout: 'auto', orientation: 'auto', flow: 'auto', viewport: ''},
// axis: "vertical",
ignoreClass: '', ignoreClass: '',
manager: "single", manager: "continuous",
view: "iframe" view: "iframe",
flow: null,
layout: null,
spread: null,
minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always)
}); });
core.extend(this.settings, options); core.extend(this.settings, options);
@ -53,12 +54,10 @@ function Rendition(book, options) {
this.q.enqueue(this.book.opened); this.q.enqueue(this.book.opened);
this.q.enqueue(this.parseLayoutProperties);
// Block the queue until rendering is started // Block the queue until rendering is started
this.starting = new RSVP.defer(); // this.starting = new RSVP.defer();
this.started = this.starting.promise; // this.started = this.starting.promise;
this.q.enqueue(this.started); this.q.enqueue(this.start);
// TODO: move this somewhere else // TODO: move this somewhere else
if(this.book.archive) { if(this.book.archive) {
@ -90,8 +89,6 @@ Rendition.prototype.requireManager = function(manager) {
Rendition.prototype.requireView = function(view) { Rendition.prototype.requireView = function(view) {
var View; var View;
// If view is a string, try to load from register managers,
// or require included managers directly
if (typeof view == "string") { if (typeof view == "string") {
View = typeof ePub != "undefined" ? ePub.Views[view] : undefined; //require('./views/'+view); View = typeof ePub != "undefined" ? ePub.Views[view] : undefined; //require('./views/'+view);
} else { } else {
@ -116,11 +113,19 @@ Rendition.prototype.start = function(){
}); });
} }
// Listen for displayed views // Parse metadata to get layout props
this.manager.on("added", this.afterDisplayed.bind(this)) this.settings.globalLayoutProperties = this.determineLayoutProperties(this.book.package.metadata);
this.flow(this.settings.globalLayoutProperties.flow);
this.layout(this.settings.globalLayoutProperties);
// Listen for displayed views
this.manager.on("added", this.afterDisplayed.bind(this));
// Listen for resizing
this.manager.on("resized", this.onResized.bind(this));
// Add Layout method
// this.applyLayoutMethod();
this.on('displayed', this.reportLocation.bind(this)); this.on('displayed', this.reportLocation.bind(this));
@ -128,23 +133,25 @@ Rendition.prototype.start = function(){
this.trigger("started"); this.trigger("started");
// Start processing queue // Start processing queue
this.starting.resolve(); // this.starting.resolve();
}; };
// Call to attach the container to an element in the dom // Call to attach the container to an element in the dom
// Container must be attached before rendering can begin // Container must be attached before rendering can begin
Rendition.prototype.attachTo = function(element){ Rendition.prototype.attachTo = function(element){
this.start(); return this.q.enqueue(function () {
// Start rendering // Start rendering
this.manager.render(element, { this.manager.render(element, {
"width" : this.settings.width, "width" : this.settings.width,
"height" : this.settings.height "height" : this.settings.height
}); });
// Trigger Attached // Trigger Attached
this.trigger("attached"); this.trigger("attached");
}.bind(this));
}; };
@ -244,8 +251,6 @@ Rendition.prototype.afterDisplayed = function(view){
Rendition.prototype.onResized = function(size){ Rendition.prototype.onResized = function(size){
this.manager.updateLayout();
if(this.location) { if(this.location) {
this.display(this.location.start); this.display(this.location.start);
} }
@ -271,16 +276,20 @@ Rendition.prototype.prev = function(){
.then(this.reportLocation.bind(this)); .then(this.reportLocation.bind(this));
}; };
//-- http://www.idpf.org/epub/fxl/ //-- http://www.idpf.org/epub/301/spec/epub-publications.html#meta-properties-rendering
Rendition.prototype.parseLayoutProperties = function(_metadata){ Rendition.prototype.determineLayoutProperties = function(metadata){
var metadata = _metadata || this.book.package.metadata; var settings;
var layout = (this.layoutOveride && this.layoutOveride.layout) || metadata.layout || "reflowable"; var layout = this.settings.layout || metadata.layout || "reflowable";
var spread = (this.layoutOveride && this.layoutOveride.spread) || metadata.spread || "auto"; var spread = this.settings.spread || metadata.spread || "auto";
var orientation = (this.layoutOveride && this.layoutOveride.orientation) || metadata.orientation || "auto"; var orientation = this.settings.orientation || metadata.orientation || "auto";
var flow = (this.layoutOveride && this.layoutOveride.flow) || metadata.flow || "auto"; var flow = this.settings.flow || metadata.flow || "auto";
var viewport = (this.layoutOveride && this.layoutOveride.viewport) || metadata.viewport || ""; var viewport = metadata.viewport || "";
this.settings.globalLayoutProperties = { if (this.settings.width >= 0 && this.settings.height >= 0) {
viewport = "width="+this.settings.width+", height="+this.settings.height+"";
}
settings = {
layout : layout, layout : layout,
spread : spread, spread : spread,
orientation : orientation, orientation : orientation,
@ -288,35 +297,62 @@ Rendition.prototype.parseLayoutProperties = function(_metadata){
viewport : viewport viewport : viewport
}; };
return this.settings.globalLayoutProperties; return settings;
}; };
/** // Rendition.prototype.applyLayoutProperties = function(){
* Uses the settings to determine which Layout Method is needed // var settings = this.determineLayoutProperties(this.book.package.metadata);
*/
// Rendition.prototype.determineLayout = function(settings){
// // Default is layout: reflowable & spread: auto
// var spreads = this.determineSpreads(this.settings.minSpreadWidth);
// var layoutMethod = spreads ? "ReflowableSpreads" : "Reflowable";
// var scroll = false;
// //
// if(settings.layout === "pre-paginated") { // this.flow(settings.flow);
// //
// } // this.layout(settings);
//
// if(settings.layout === "reflowable" && settings.spread === "none") {
//
// }
//
// if(settings.layout === "reflowable" && settings.spread === "both") {
//
// }
//
// this.spreads = spreads;
//
// return layoutMethod;
// }; // };
// paginated | scrolled
// (scrolled-continuous vs scrolled-doc are handled by different view managers)
Rendition.prototype.flow = function(_flow){
var flow;
if (_flow === "scrolled-doc" || _flow === "scrolled-continuous") {
flow = "scrolled";
}
if (_flow === "auto" || _flow === "paginated") {
flow = "paginated";
}
if (this._layout) {
this._layout.flow(flow);
}
if (this.manager) {
this.manager.updateFlow(flow);
}
};
// reflowable | pre-paginated
Rendition.prototype.layout = function(settings){
this._layout = new Layout(settings);
this._layout.spread(settings.spread, this.settings.minSpreadWidth);
this.mapping = new Mapping(this._layout);
if (this.manager) {
this.manager.applyLayout(this._layout);
}
return this._layout;
};
// none | auto (TODO: implement landscape, portrait, both)
Rendition.prototype.spread = function(spread, min){
this._layout.spread(spread, min);
if (this.manager.isRendered()) {
this.manager.updateLayout();
}
};
Rendition.prototype.reportLocation = function(){ Rendition.prototype.reportLocation = function(){
return this.q.enqueue(function(){ return this.q.enqueue(function(){
@ -484,8 +520,8 @@ Rendition.prototype.adjustImages = function(view) {
view.addStylesheetRules([ view.addStylesheetRules([
["img", ["img",
["max-width", (this.layout.spread) + "px"], ["max-width", (view.layout.spread) + "px"],
["max-height", (this.layout.height) + "px"] ["max-height", (view.layout.height) + "px"]
] ]
]); ]);
return new RSVP.Promise(function(resolve, reject){ return new RSVP.Promise(function(resolve, reject){

View file

@ -81,6 +81,7 @@ Views.prototype.destroy = function(view) {
if(view.displayed){ if(view.displayed){
view.destroy(); view.destroy();
} }
if(this.container){ if(this.container){
this.container.removeChild(view.element); this.container.removeChild(view.element);
} }

View file

@ -399,7 +399,7 @@ IframeView.prototype.onLoad = function(event, promise) {
this.window = this.iframe.contentWindow; this.window = this.iframe.contentWindow;
this.document = this.iframe.contentDocument; this.document = this.iframe.contentDocument;
this.contents = new Contents(this.document, this.document.body); this.contents = new Contents(this.document, this.document.body, this.section.cfiBase);
this.rendering = false; this.rendering = false;
@ -449,6 +449,9 @@ IframeView.prototype.setLayout = function(layout) {
this.layout = layout; this.layout = layout;
}; };
IframeView.prototype.setAxis = function(axis) {
this.settings.axis = axis;
};
IframeView.prototype.resizeListenters = function() { IframeView.prototype.resizeListenters = function() {
// Test size again // Test size again