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

Added render functions to rendition and views

This commit is contained in:
fchasen 2016-05-08 11:23:27 -04:00
parent 948c356597
commit ddb127cac5
17 changed files with 1402 additions and 679 deletions

1264
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

@ -62,7 +62,7 @@
var currentSectionIndex = 8;
// Load the opf
var book = ePub("../books/alice/OPS/package.opf");
var rendition = book.renderTo("viewer");
var rendition = book.renderTo("viewer", { view: "inline" });
rendition.display("chapter_008.xhtml");

View file

@ -68,6 +68,7 @@ Book.prototype.open = function(_url){
var book = this;
var containerPath = "META-INF/container.xml";
var location;
var absoluteUri;
if(!_url) {
this.opening.resolve(this);
@ -81,7 +82,13 @@ Book.prototype.open = function(_url){
// uri = core.uri(_url);
// }
uri = URI(_url);
this.url = _url;
if (window) {
absoluteUri = uri.absoluteTo(window.location.href);
this.url = absoluteUri.toString();
} else {
this.url = _url;
}
// Find path to the Container
if(uri.suffix() === "opf") {
@ -91,8 +98,9 @@ Book.prototype.open = function(_url){
if(uri.origin()) {
this.baseUrl = uri.origin() + "/" + uri.directory() + "/";
} else if(window){
this.baseUrl = uri.absoluteTo(window.location.href).directory() + "/";
} else if(absoluteUri){
this.baseUrl = absoluteUri.origin();
this.baseUrl += absoluteUri.directory() + "/";
} else {
this.baseUrl = uri.directory() + "/";
}
@ -130,14 +138,14 @@ Book.prototype.open = function(_url){
then(function(paths){
var packageUri = URI(paths.packagePath);
var absPackageUri = packageUri.absoluteTo(book.url);
var absWindowUri;
book.packageUrl = absPackageUri.toString();
book.encoding = paths.encoding;
// Set Url relative to the content
if(packageUri.origin()) {
book.baseUrl = packageUri.origin() + "/" + packageUri.directory() + "/";
} else if(window && !book.isArchivedUrl(uri)){
book.baseUrl = absPackageUri.absoluteTo(window.location.href).directory() + "/";
if(absPackageUri.origin()) {
book.baseUrl = absPackageUri.origin() + absPackageUri.directory() + "/";
} else {
book.baseUrl = "/" + packageUri.directory() + "/";
}

View file

@ -69,16 +69,14 @@ Contents.prototype.textHeight = function() {
return height;
};
Contents.prototype.scrollWidth = function(min) {
var prev;
var width = this.document.body.scrollWidth;
Contents.prototype.scrollWidth = function() {
var width = this.documentElement.scrollWidth;
return width;
};
Contents.prototype.scrollHeight = function(min) {
var prev;
var height = this.document.body.scrollHeight;
Contents.prototype.scrollHeight = function() {
var height = this.documentElement.scrollHeight;
return height;
};
@ -149,6 +147,10 @@ Contents.prototype.viewport = function() {
// // stub
// };
Contents.prototype.expand = function() {
//TODO: this should just report resize
};
Contents.prototype.listeners = function() {
this.imageLoadListeners();
@ -197,6 +199,28 @@ Contents.prototype.mediaQueryListeners = function() {
}
};
Contents.prototype.observe = function(target) {
var renderer = this;
// create an observer instance
var observer = new MutationObserver(function(mutations) {
if(renderer._expanding) {
renderer.expand();
}
// mutations.forEach(function(mutation) {
// console.log(mutation);
// });
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true, subtree: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
return observer;
};
Contents.prototype.imageLoadListeners = function(target) {
var images = this.contentDocument.querySelectorAll("img");
var img;
@ -403,6 +427,16 @@ Contents.prototype.map = function(layout){
return map.section();
};
Contents.prototype.destroy = function() {
// Stop observing
if(this.observer) {
this.observer.disconnect();
}
this.removeListeners();
};
RSVP.EventTarget.mixin(Contents.prototype);
module.exports = Contents;

View file

@ -28,6 +28,7 @@ ePub.register = {
// Default Views
ePub.register.view("iframe", require('./views/iframe'));
ePub.register.view("inline", require('./views/inline'));
// Default View Managers
ePub.register.manager("single", require('./managers/single'));

View file

@ -14,6 +14,8 @@ function Reflowable(){
this.column = 0;
this.gap = 0;
this.divisor = 0;
this.name = "reflowable";
};
Reflowable.prototype.calculate = function(_width, _height, _gap, _devisor){
@ -107,6 +109,9 @@ Reflowable.prototype.count = function(totalWidth) {
function Fixed(_width, _height){
this.width = 0;
this.height = 0;
this.name = "pre-paginated";
};
Fixed.prototype.calculate = function(_width, _height){
@ -166,6 +171,7 @@ function Scroll(){
this.spread = 0;
this.column = 0;
this.gap = 0;
this.name = "scrolled";
};
Scroll.prototype.calculate = function(_width, _height){
@ -180,7 +186,7 @@ Scroll.prototype.format = function(contents){
// $doc.style.width = "auto";
// $doc.style.height = "auto";
contents.width("auto");
// contents.width("auto");
contents.height("auto");
};

View file

@ -39,7 +39,7 @@ ContinuousViewManager.prototype.moveTo = function(offset){
}.bind(this));
};
/*
ContinuousViewManager.prototype.afterDisplayed = function(currView){
var next = currView.section.next();
var prev = currView.section.prev();
@ -61,7 +61,10 @@ ContinuousViewManager.prototype.afterDisplayed = function(currView){
this.trigger("added", currView.section);
};
*/
ContinuousViewManager.prototype.afterResized = function(view){
this.trigger("resize", view.section);
};
// Remove Previous Listeners if present
ContinuousViewManager.prototype.removeShownListeners = function(view){
@ -72,27 +75,31 @@ ContinuousViewManager.prototype.removeShownListeners = function(view){
};
ContinuousViewManager.prototype.append = function(view){
ContinuousViewManager.prototype.append = function(section){
var view = this.createView(section);
// view.on("shown", this.afterDisplayed.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
return this.q.enqueue(function() {
this.views.append(view);
this.views.append(view);
//this.q.enqueue(this.check);
return this.check();
return this.update();
}.bind(this));
};
ContinuousViewManager.prototype.prepend = function(view){
// view.on("shown", this.afterDisplayedAbove.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
ContinuousViewManager.prototype.prepend = function(section){
var view = this.createView(section);
view.on("resized", this.counter.bind(this));
this.views.prepend(view);
return this.q.enqueue(function() {
this.views.prepend(view);
return this.update();
}.bind(this));
// this.q.enqueue(this.check);
return this.check();
};
ContinuousViewManager.prototype.counter = function(bounds){
@ -104,7 +111,7 @@ ContinuousViewManager.prototype.counter = function(bounds){
}
};
/*
ContinuousViewManager.prototype.check = function(_offset){
var checking = new RSVP.defer();
var container = this.stage.bounds();
@ -154,6 +161,98 @@ ContinuousViewManager.prototype.check = function(_offset){
return checking.promise;
}
};
*/
ContinuousViewManager.prototype.update = function(_offset){
var container = this.stage.bounds();
var views = this.views;
var viewsLength = views.length;
var visible = [];
var isVisible;
var view;
var updating = new RSVP.defer();
var promises = [];
for (var i = 0; i < viewsLength; i++) {
view = views[i];
isVisible = this.isVisible(view, 0, 0, container);
if(isVisible === true) {
promises.push(view.display());
visible.push(view);
} else {
this.q.enqueue(view.destroy.bind(view));
clearTimeout(this.trimTimeout);
this.trimTimeout = setTimeout(function(){
this.q.enqueue(this.trim.bind(this));
}.bind(this), 250);
}
}
if(promises.length){
return RSVP.all(promises);
} else {
updating.resolve();
return updating.promise;
}
};
ContinuousViewManager.prototype.check = function(_offsetLeft, _offsetTop){
var next, prev;
var horizontal = (this.settings.axis === "horizontal");
var delta = this.settings.offset || 0;
if (_offsetLeft && horizontal) {
delta = _offsetLeft;
}
if (_offsetTop && !horizontal) {
delta = _offsetTop;
}
var bounds = this.stage.bounds(); // TODO: save this until resize
var offset = horizontal ? this.container.scrollLeft : this.container.scrollTop;
var visibleLength = horizontal ? bounds.width : bounds.height;
var contentLength = horizontal ? this.container.scrollWidth : this.container.scrollHeight;
var checking = new RSVP.defer();
var promises = [];
if (offset + visibleLength + delta >= contentLength) {
next = this.views.last().section.next();
if(next) {
promises.push(this.append(next));
}
}
if (offset - delta < 0 ) {
prev = this.views.first().section.prev();
if(prev) {
promises.push(this.prepend(prev));
}
}
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.update.bind(this));
}.bind(this));
} else {
checking.resolve();
return checking.promise;
}
};
ContinuousViewManager.prototype.trim = function(){
@ -309,15 +408,15 @@ ContinuousViewManager.prototype.onScroll = function(){
};
ContinuousViewManager.prototype.resizeView = function(view) {
if(this.settings.axis === "horizontal") {
view.lock("height", this.stage.width, this.stage.height);
} else {
view.lock("width", this.stage.width, this.stage.height);
}
};
// ContinuousViewManager.prototype.resizeView = function(view) {
//
// if(this.settings.axis === "horizontal") {
// view.lock("height", this.stage.width, this.stage.height);
// } else {
// view.lock("width", this.stage.width, this.stage.height);
// }
//
// };
ContinuousViewManager.prototype.currentLocation = function(){
var visible = this.visible();

View file

@ -2,7 +2,7 @@ var RSVP = require('rsvp');
var core = require('../core');
var ContinuousViewManager = require('./continuous');
var Map = require('../map');
// var Layout = require('./layout');
var Layout = require('../layout');
function PaginatedViewManager(book, options) {
@ -84,7 +84,7 @@ PaginatedViewManager.prototype.applyLayoutMethod = function() {
// Set the look ahead offset for what is visible
this.map = new Map(this.layout);
// this.map = new Map(this.layout);
// this.hooks.layout.register(this.layout.format.bind(this));

View file

@ -1,7 +1,9 @@
var RSVP = require('rsvp');
var core = require('../core');
var Stage = require('../stage');
var Views = require('../views');
var EpubCFI = require('../epubcfi');
var Layout = require('../layout');
function SingleViewManager(options) {
@ -15,23 +17,35 @@ function SingleViewManager(options) {
width: false,
height: null,
globalLayoutProperties : { layout: 'reflowable', spread: 'auto', orientation: 'auto'},
layout: null,
// layout: null,
axis: "vertical",
ignoreClass: ''
});
core.extend(this.settings, options.settings);
this.viewSettings = {
ignoreClass: this.settings.ignoreClass
ignoreClass: this.settings.ignoreClass,
globalLayoutProperties: this.settings.globalLayoutProperties,
axis: this.settings.axis,
layout: this.layout,
width: 0,
height: 0
};
}
SingleViewManager.prototype.start = function(stage){
SingleViewManager.prototype.render = function(element, size){
// Save the stage
this.stage = stage;
this.stage = new Stage({
width: size.width,
height: size.height,
hidden: this.settings.hidden
});
this.stage.attachTo(element);
// Get this stage container div
this.container = this.stage.getContainer();
@ -42,6 +56,10 @@ SingleViewManager.prototype.start = function(stage){
// Calculate Stage Size
this.bounds = this.stage.bounds();
// Set the dimensions for views
this.viewSettings.width = this.bounds.width;
this.viewSettings.height = this.bounds.height;
// Function to handle a resize event.
// Will only attach if width and height are both fixed.
this.stage.onResize(this.onResized.bind(this));
@ -50,7 +68,7 @@ SingleViewManager.prototype.start = function(stage){
this.addEventListeners();
// Add Layout method
// this.applyLayoutMethod();
this.applyLayoutMethod();
};
SingleViewManager.prototype.addEventListeners = function(){
@ -65,7 +83,14 @@ SingleViewManager.prototype.resize = function(width, height){
this.bounds = this.stage.bounds(width, height);
this.views.each(this.resizeView.bind(this));
// Update for new views
this.viewSettings.width = this.bounds.width;
this.viewSettings.height = this.bounds.height;
// Update for existing views
this.views.each(function(view) {
view.size(this.bounds.width, this.bounds.height);
}.bind(this));
this.trigger("resized", {
width: this.stage.width,
@ -74,10 +99,12 @@ SingleViewManager.prototype.resize = function(width, height){
};
SingleViewManager.prototype.layout = function(layoutFunc){
SingleViewManager.prototype.setLayout = function(layout){
this.viewSettings.layout = layout;
this.views.each(function(view){
layoutFunc(view);
view.setLayout(layout);
});
};
@ -106,10 +133,12 @@ SingleViewManager.prototype.display = function(section, target){
// Hide all current views
this.views.hide();
this.views.clear();
// Create a new view
view = this.createView(section);
return this.fill(view)
return this.add(view)
.then(function(){
// Move to correct place within the section, if needed
@ -129,34 +158,38 @@ SingleViewManager.prototype.display = function(section, target){
};
SingleViewManager.prototype.afterDisplayed = function(view){
this.trigger("added", view.section);
this.trigger("added", view);
};
SingleViewManager.prototype.afterResized = function(view){
this.trigger("resize", view.section);
};
SingleViewManager.prototype.moveTo = function(offset){
this.scrollTo(offset.left, offset.top);
};
SingleViewManager.prototype.fill = function(view){
this.views.clear();
SingleViewManager.prototype.add = function(view){
this.views.append(view);
// view.on("shown", this.afterDisplayed.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
view.onResize = this.afterResized.bind(this);
return this.renderer(view, this.views.hidden);
return view.display();
// return this.renderer(view, this.views.hidden);
};
SingleViewManager.prototype.resizeView = function(view) {
if(this.settings.globalLayoutProperties.layout === "pre-paginated") {
view.lock("both", this.bounds.width, this.bounds.height);
} else {
view.lock("width", this.bounds.width, this.bounds.height);
}
};
// SingleViewManager.prototype.resizeView = function(view) {
//
// if(this.settings.globalLayoutProperties.layout === "pre-paginated") {
// view.lock("both", this.bounds.width, this.bounds.height);
// } else {
// view.lock("width", this.bounds.width, this.bounds.height);
// }
//
// };
SingleViewManager.prototype.next = function(){
var next;
@ -167,8 +200,10 @@ SingleViewManager.prototype.next = function(){
next = this.views.last().section.next();
if(next) {
this.views.clear();
view = this.createView(next);
return this.fill(view)
return this.add(view)
.then(function(){
this.views.show();
}.bind(this));
@ -183,8 +218,10 @@ SingleViewManager.prototype.prev = function(){
prev = this.views.first().section.prev();
if(prev) {
this.views.clear();
view = this.createView(prev);
return this.fill(view)
return this.add(view)
.then(function(){
this.views.show();
}.bind(this));
@ -236,14 +273,17 @@ SingleViewManager.prototype.isVisible = function(view, offsetPrev, offsetNext, _
};
SingleViewManager.prototype.visible = function(){
return this.views.displayed();
/*
var container = this.stage.bounds();
var displayedViews = this.views.displayed();
var views = this.views;
var viewsLength = views.length;
var visible = [];
var isVisible;
var view;
for (var i = 0; i < displayedViews.length; i++) {
view = displayedViews[i];
for (var i = 0; i < viewsLength; i++) {
view = views[i];
isVisible = this.isVisible(view, 0, 0, container);
if(isVisible === true) {
@ -252,7 +292,7 @@ SingleViewManager.prototype.visible = function(){
}
return visible;
*/
};
SingleViewManager.prototype.scrollBy = function(x, y, silent){
@ -293,6 +333,40 @@ SingleViewManager.prototype.scrollTo = function(x, y, silent){
// };
};
SingleViewManager.prototype.bounds = function() {
var bounds;
if(!this.settings.height || !this.container) {
bounds = core.windowBounds();
} else {
bounds = this.container.getBoundingClientRect();
}
return bounds;
};
SingleViewManager.prototype.applyLayoutMethod = function() {
this.layout = new Layout.Scroll();
this.calculateLayout();
this.setLayout(this.layout);
// this.map = new Map(this.layout);
// this.manager.layout(this.layout.format);
};
SingleViewManager.prototype.calculateLayout = function() {
var bounds = this.stage.bounds();
this.layout.calculate(bounds.width, bounds.height);
};
SingleViewManager.prototype.updateLayout = function() {
this.calculateLayout();
this.setLayout(this.layout);
};
//-- Enable binding events to Manager
RSVP.EventTarget.mixin(SingleViewManager.prototype);

View file

@ -154,9 +154,11 @@ Parser.prototype.metadata = function(xml){
metadata.rights = p.getElementText(xml, "rights");
metadata.modified_date = p.getPropertyText(xml, 'dcterms:modified');
metadata.layout = p.getPropertyText(xml, "rendition:layout");
metadata.orientation = p.getPropertyText(xml, 'rendition:orientation');
metadata.spread = p.getPropertyText(xml, 'rendition:spread');
metadata.flow = p.getPropertyText(xml, 'rendition:flow');
metadata.viewport = p.getPropertyText(xml, 'rendition:viewport');
// metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction");
return metadata;

View file

@ -9,16 +9,15 @@ var View = require('./view');
var Views = require('./views');
var Layout = require('./layout');
var Map = require('./map');
var Stage = require('./stage');
function Rendition(book, options) {
this.settings = core.extend(this.settings || {}, {
infinite: true,
hidden: false,
width: false,
width: null,
height: null,
layoutOveride : null, // Default: { spread: 'reflowable', layout: 'auto', orientation: 'auto'},
layoutOveride : null, // Default: { spread: 'reflowable', layout: 'auto', orientation: 'auto', flow: 'auto', viewport: ''},
axis: "vertical",
ignoreClass: '',
manager: "single",
@ -67,6 +66,15 @@ function Rendition(book, options) {
this.replacements();
}
this.ViewManager = this.requireManager(this.settings.manager);
this.View = this.requireView(this.settings.view);
this.manager = new this.ViewManager({
view: this.View,
queue: this.q,
settings: this.settings
});
};
Rendition.prototype.setManager = function(manager) {
@ -104,30 +112,13 @@ Rendition.prototype.requireView = function(view) {
return View;
};
Rendition.prototype.start = function(stage){
var ViewManager, View;
// Add view manager
if (!this.manager) {
ViewManager = this.requireManager(this.settings.manager);
View = this.requireView(this.settings.view);
this.manager = new ViewManager({
view: View,
renderer: this.render.bind(this),
queue: this.q,
settings: this.settings
});
}
Rendition.prototype.start = function(){
// Listen for displayed views
this.manager.on("added", this.afterDisplayed.bind(this))
// Start rendering
this.manager.start(stage);
// Add Layout method
this.applyLayoutMethod();
// this.applyLayoutMethod();
this.on('displayed', this.reportLocation.bind(this));
@ -142,12 +133,13 @@ Rendition.prototype.start = function(stage){
// Container must be attached before rendering can begin
Rendition.prototype.attachTo = function(element){
this.container = new Stage(element, {
// Start rendering
this.manager.render(element, {
"width" : this.settings.width,
"height" : this.settings.height
});
this.start(this.container);
this.start();
// Trigger Attached
this.trigger("attached");
@ -197,6 +189,7 @@ Rendition.prototype._display = function(target){
};
/*
Rendition.prototype.render = function(view, show) {
// view.onLayout = this.layout.format.bind(this.layout);
@ -239,36 +232,17 @@ Rendition.prototype.render = function(view, show) {
}.bind(this));
};
*/
Rendition.prototype.afterDisplayed = function(section){
this.trigger("added", section);
Rendition.prototype.afterDisplayed = function(view){
this.hooks.content.trigger(view, this);
this.trigger("rendered", view.section);
this.reportLocation();
};
Rendition.prototype.applyLayoutMethod = function() {
this.layout = new Layout.Scroll();
this.calculateLayout();
// this.map = new Map(this.layout);
// this.manager.layout(this.layout.format);
};
Rendition.prototype.calculateLayout = function() {
// TODO: should this be a function to get the live bounds? It is cached and updated on resize for now.
var bounds = this.manager.bounds;
this.layout.calculate(bounds.width, bounds.height);
};
Rendition.prototype.updateLayout = function() {
this.calculateLayout();
this.manager.layout(this.layout.format);
};
Rendition.prototype.onResized = function(size){
this.updateLayout();
this.manager.updateLayout();
if(this.location) {
this.display(this.location.start);
@ -293,29 +267,21 @@ Rendition.prototype.prev = function(){
return this.q.enqueue(this.manager.prev.bind(this.manager));
};
Rendition.prototype.bounds = function() {
var bounds;
if(!this.settings.height || !this.container) {
bounds = core.windowBounds();
} else {
bounds = this.container.getBoundingClientRect();
}
return bounds;
};
//-- http://www.idpf.org/epub/fxl/
Rendition.prototype.parseLayoutProperties = function(_metadata){
var metadata = _metadata || this.book.package.metadata;
var layout = (this.layoutOveride && this.layoutOveride.layout) || metadata.layout || "reflowable";
var spread = (this.layoutOveride && this.layoutOveride.spread) || metadata.spread || "auto";
var orientation = (this.layoutOveride && this.layoutOveride.orientation) || metadata.orientation || "auto";
var flow = (this.layoutOveride && this.layoutOveride.flow) || metadata.flow || "auto";
var viewport = (this.layoutOveride && this.layoutOveride.viewport) || metadata.viewport || "";
this.settings.globalLayoutProperties = {
layout : layout,
spread : spread,
orientation : orientation
orientation : orientation,
flow : flow,
viewport : viewport
};
return this.settings.globalLayoutProperties;
@ -323,33 +289,23 @@ Rendition.prototype.parseLayoutProperties = function(_metadata){
/**
* Uses the settings to determine which Layout Method is needed
* Triggers events based on the method choosen
* Takes: Layout settings object
* Returns: String of appropriate for EPUBJS.Layout function
*/
// Paginate.prototype.determineLayout = function(settings){
// Rendition.prototype.determineLayout = function(settings){
// // Default is layout: reflowable & spread: auto
// var spreads = this.determineSpreads(this.settings.minSpreadWidth);
// console.log("spreads", spreads, this.settings.minSpreadWidth)
// var layoutMethod = spreads ? "ReflowableSpreads" : "Reflowable";
// var scroll = false;
//
// if(settings.layout === "pre-paginated") {
// layoutMethod = "Fixed";
// scroll = true;
// spreads = false;
//
// }
//
// if(settings.layout === "reflowable" && settings.spread === "none") {
// layoutMethod = "Reflowable";
// scroll = false;
// spreads = false;
//
// }
//
// if(settings.layout === "reflowable" && settings.spread === "both") {
// layoutMethod = "ReflowableSpreads";
// scroll = false;
// spreads = true;
//
// }
//
// this.spreads = spreads;

View file

@ -16,11 +16,10 @@ function base(doc, section){
if(!base) {
base = doc.createElement("base");
head.insertBefore(base, head.firstChild);
}
base.setAttribute("href", section.url);
head.insertBefore(base, head.firstChild);
}
function links(view, renderer) {

View file

@ -60,8 +60,9 @@ Section.prototype.base = function(_document){
var task = new RSVP.defer();
var base = _document.createElement("base"); // TODO: check if exists
var head;
console.log(window.location.origin + "/" +this.url);
base.setAttribute("href", this.url);
base.setAttribute("href", window.location.origin + "/" +this.url);
if(_document) {
head = _document.querySelector("head");

View file

@ -1,16 +1,12 @@
var core = require('./core');
function Stage(element, _options) {
function Stage(_options) {
this.settings = _options || {};
this.element = this.getElement(element);
this.container = this.create(this.settings);
if(this.settings.hidden) {
this.wrapper = this.wrap(this.container);
this.attachTo(this.wrapper, this.element);
} else {
this.attachTo(this.container, this.element);
}
}
@ -39,7 +35,7 @@ Stage.prototype.create = function(options){
container.classList.add("epub-container");
// Style Element
container.style.fontSize = "0";
// container.style.fontSize = "0";
container.style.wordSpacing = "0";
container.style.lineHeight = "0";
container.style.verticalAlign = "top";
@ -93,13 +89,24 @@ Stage.prototype.getElement = function(_element){
return element;
};
Stage.prototype.attachTo = function(what, element){
Stage.prototype.attachTo = function(what){
var element = this.getElement(what);
var base;
if(!element){
return;
}
element.appendChild(what);
if(this.settings.hidden) {
base = this.wrapper;
} else {
base = this.container;
}
element.appendChild(base);
this.element = element;
return element;
@ -125,7 +132,7 @@ Stage.prototype.bounds = function(_width, _height){
var height = _height || this.settings.height;
// If width or height are set to false, inherit them from containing element
if(width === false) {
if(width === null) {
bounds = this.element.getBoundingClientRect();
if(bounds.width) {
@ -134,7 +141,7 @@ Stage.prototype.bounds = function(_width, _height){
}
}
if(height === false) {
if(height === null) {
bounds = bounds || this.element.getBoundingClientRect();
if(bounds.height) {

View file

@ -13,10 +13,6 @@ Views.prototype.last = function() {
return this._views[this._views.length-1];
};
Views.prototype.each = function() {
return this._views.forEach.apply(this._views, arguments);
};
Views.prototype.indexOf = function(view) {
return this._views.indexOf(view);
};
@ -89,6 +85,10 @@ Views.prototype.destroy = function(view) {
// Iterators
Views.prototype.each = function() {
return this._views.forEach.apply(this._views, arguments);
};
Views.prototype.clear = function(){
// Remove all views
var view;

View file

@ -5,42 +5,50 @@ var Contents = require('../contents');
function IframeView(section, options) {
this.settings = core.extend({
ignoreClass : ''
ignoreClass : '',
axis: 'vertical',
width: 0,
height: 0,
layout: undefined,
globalLayoutProperties: {},
}, options || {});
this.id = "epubjs-view:" + core.uuid();
this.section = section;
this.index = section.index;
this.element = this.createContainer();
this.element = this.container(this.settings.axis);
this.added = false;
this.displayed = false;
this.rendered = false;
//this.width = 0;
//this.height = 0;
this.width = this.settings.width;
this.height = this.settings.height;
this.fixedWidth = 0;
this.fixedHeight = 0;
// Blank Cfi for Parsing
this.epubcfi = new EpubCFI();
this.layout = this.settings.layout;
// Dom events to listen for
// this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"];
};
IframeView.prototype.createContainer = function() {
IframeView.prototype.container = function(axis) {
var element = document.createElement('div');
element.classList.add("epub-view");
// this.element.style.minHeight = "100px";
element.style.height = "0px";
element.style.width = "0px";
element.style.overflow = "hidden";
if(this.settings.axis && this.settings.axis == "horizontal"){
if(axis && axis == "horizontal"){
element.style.display = "inline-block";
} else {
element.style.display = "block";
@ -92,20 +100,87 @@ IframeView.prototype.create = function() {
// }
// Firefox has trouble with baseURI and srcdoc
// Disabled for now
/*
// TODO: Disable for now in firefox
if(!!("srcdoc" in this.iframe)) {
this.supportsSrcdoc = true;
} else {
this.supportsSrcdoc = false;
}
*/
this.supportsSrcdoc = false;
return this.iframe;
};
IframeView.prototype.render = function(request, show) {
// view.onLayout = this.layout.format.bind(this.layout);
this.create();
// Fit to size of the container, apply padding
this.size();
// Render Chain
return this.section.render(request)
.then(function(contents){
return this.load(contents);
}.bind(this))
// .then(function(doc){
// return this.hooks.content.trigger(view, this);
// }.bind(this))
.then(function(){
// this.settings.layout.format(view.contents);
// return this.hooks.layout.trigger(view, this);
}.bind(this))
// .then(function(){
// return this.display();
// }.bind(this))
// .then(function(){
// return this.hooks.render.trigger(view, this);
// }.bind(this))
.then(function(){
// apply the layout function to the contents
this.settings.layout.format(this.contents);
// Expand the iframe to the full size of the content
this.expand();
// Listen for events that require an expansion of the iframe
this.addListeners();
if(show !== false) {
//this.q.enqueue(function(view){
this.show();
//}, view);
}
// this.map = new Map(view, this.layout);
//this.hooks.show.trigger(view, this);
this.trigger("rendered", this.section);
}.bind(this))
.catch(function(e){
this.trigger("loaderror", e);
}.bind(this));
};
// Determine locks base on settings
IframeView.prototype.size = function(_width, _height) {
var width = _width || this.settings.width;
var height = _height || this.settings.height;
if(this.layout.name === "pre-paginated") {
// TODO: check if these are different than the size set in chapter
this.lock("both", width, height);
} else if(this.settings.axis === "horizontal") {
this.lock("height", width, height);
} else {
this.lock("width", width, height);
}
};
// Lock an axis to element dimensions, taking borders into account
IframeView.prototype.lock = function(what, width, height) {
var elBorders = core.borders(this.element);
var iframeBorders;
@ -147,18 +222,20 @@ IframeView.prototype.lock = function(what, width, height) {
};
// Resize a single axis based on content dimensions
IframeView.prototype.expand = function(force) {
var width = this.lockedWidth;
var height = this.lockedHeight;
var textWidth, textHeight;
// console.log("expanding a")
if(!this.iframe || this._expanding) return;
this._expanding = true;
// Expand Horizontally
if(height && !width) {
// if(height && !width) {
if(this.settings.axis === "horizontal") {
// Get the width of the text
textWidth = this.contents.textWidth();
// Check if the textWidth has changed
@ -174,10 +251,8 @@ IframeView.prototype.expand = function(force) {
// Otherwise assume content height hasn't changed
width = this._contentWidth;
}
}
// Expand Vertically
if(width && !height) {
} // Expand Vertically
else if(this.settings.axis === "vertical") {
textHeight = this.contents.textHeight();
if(textHeight != this._textHeight){
height = this.contentHeight(textHeight);
@ -248,12 +323,12 @@ IframeView.prototype.resize = function(width, height) {
};
IframeView.prototype.reframe = function(width, height) {
//var prevBounds;
var size;
if(!this.displayed) {
this._needsReframe = true;
return;
}
// if(!this.displayed) {
// this._needsReframe = true;
// return;
// }
if(core.isNumber(width)){
this.element.style.width = width + "px";
@ -267,43 +342,19 @@ IframeView.prototype.reframe = function(width, height) {
this.elementBounds = core.bounds(this.element);
this.trigger("resized", {
size = {
width: this.elementBounds.width,
height: this.elementBounds.height,
widthDelta: this.elementBounds.width - this.prevBounds.width,
heightDelta: this.elementBounds.height - this.prevBounds.height,
});
};
this.onResize(this, size);
this.trigger("resized", size);
};
// View.prototype.resized = function(e) {
// /*
// if (!this.resizing) {
// if(this.iframe) {
// // this.expand();
// }
// } else {
// this.resizing = false;
// }*/
//
// };
/*
View.prototype.render = function(_request) {
// if(this.rendering){
// return this.displayed;
// }
this.rendering = true;
// this.displayingDefer = new RSVP.defer();
// this.displayedPromise = this.displaying.promise;
return this.section.render(_request)
.then(function(contents){
return this.load(contents);
}.bind(this));
};
*/
IframeView.prototype.load = function(contents) {
var loading = new RSVP.defer();
@ -375,50 +426,10 @@ IframeView.prototype.onLoad = function(event, promise) {
// // stub
// };
IframeView.prototype.setLayout = function(layoutFunc) {
this.layoutFunc = layoutFunc;
IframeView.prototype.setLayout = function(layout) {
this.layout = layout;
};
IframeView.prototype.listeners = function() {
/*
setTimeout(function(){
this.window.addEventListener("resize", this.resized.bind(this), false);
}.bind(this), 10); // Wait to listen for resize events
*/
// Wait for fonts to load to finish
// http://dev.w3.org/csswg/css-font-loading/
// Not implemented fully except in chrome
if(this.document.fonts && this.document.fonts.status === "loading") {
// console.log("fonts unloaded");
this.document.fonts.onloadingdone = function(){
// console.log("loaded fonts");
this.expand();
}.bind(this);
}
if(this.section.properties.indexOf("scripted") > -1){
this.observer = this.observe(this.document.body);
}
this.imageLoadListeners();
this.mediaQueryListeners();
// this.resizeListenters();
// this.addEventListeners();
// this.addSelectionListeners();
};
IframeView.prototype.removeListeners = function() {
// this.removeEventListeners();
// this.removeSelectionListeners();
};
IframeView.prototype.resizeListenters = function() {
// Test size again
@ -426,93 +437,34 @@ IframeView.prototype.resizeListenters = function() {
this.expanding = setTimeout(this.expand.bind(this), 350);
};
//https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js
IframeView.prototype.mediaQueryListeners = function() {
var sheets = this.document.styleSheets;
var mediaChangeHandler = function(m){
if(m.matches && !this._expanding) {
setTimeout(this.expand.bind(this), 1);
// this.expand();
}
}.bind(this);
for (var i = 0; i < sheets.length; i += 1) {
var rules = sheets[i].cssRules;
if(!rules) return; // Stylesheets changed
for (var j = 0; j < rules.length; j += 1) {
//if (rules[j].constructor === CSSMediaRule) {
if(rules[j].media){
var mql = this.window.matchMedia(rules[j].media.mediaText);
mql.addListener(mediaChangeHandler);
//mql.onchange = mediaChangeHandler;
}
}
}
IframeView.prototype.addListeners = function() {
//TODO: Add content listeners for expanding
};
IframeView.prototype.observe = function(target) {
var renderer = this;
// create an observer instance
var observer = new MutationObserver(function(mutations) {
if(renderer._expanding) {
renderer.expand();
}
// mutations.forEach(function(mutation) {
// console.log(mutation);
// });
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true, subtree: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
return observer;
IframeView.prototype.removeListeners = function(layoutFunc) {
//TODO: remove content listeners for expanding
};
// View.prototype.appendTo = function(element) {
// this.element = element;
// this.element.appendChild(this.iframe);
// };
//
// View.prototype.prependTo = function(element) {
// this.element = element;
// element.insertBefore(this.iframe, element.firstChild);
// };
IframeView.prototype.imageLoadListeners = function(target) {
var images = this.document.body.querySelectorAll("img");
var img;
for (var i = 0; i < images.length; i++) {
img = images[i];
if (typeof img.naturalWidth !== "undefined" &&
img.naturalWidth === 0) {
img.onload = this.expand.bind(this);
}
}
};
IframeView.prototype.display = function() {
IframeView.prototype.display = function(request) {
var displayed = new RSVP.defer();
this.displayed = true;
if (!this.displayed) {
// apply the layout function to the contents
// this.layout(layoutFunc);
this.render(request).then(function () {
// Expand the iframe to the full size of the content
this.expand();
this.trigger("displayed", this);
this.onDisplayed(this);
// Listen for event that require an expansion of the iframe
this.listeners();
this.displayed = true;
this.trigger("displayed", this);
this.onDisplayed(this);
displayed.resolve(this);
}.bind(this));
} else {
displayed.resolve(this);
}
displayed.resolve(this);
return displayed.promise;
};
@ -555,6 +507,10 @@ IframeView.prototype.onDisplayed = function(view) {
// Stub, override with a custom functions
};
IframeView.prototype.onResize = function(view, e) {
// Stub, override with a custom functions
};
IframeView.prototype.bounds = function() {
if(!this.elementBounds) {
this.elementBounds = core.bounds(this.element);
@ -563,12 +519,10 @@ IframeView.prototype.bounds = function() {
};
IframeView.prototype.destroy = function() {
// Stop observing
if(this.observer) {
this.observer.disconnect();
}
if(this.displayed){
this.displayed = false;
this.removeListeners();
this.stopExpanding = true;