1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-02 14:49:16 +02:00
This commit is contained in:
Valdrin Trena 2024-06-08 20:14:31 +02:00
parent f09089cf77
commit 7c7c554ee7
84 changed files with 13879 additions and 14510 deletions

View file

@ -1,11 +1,14 @@
{
"presets": [
["@babel/preset-env", {
[
"@babel/preset-env",
{
"targets": "last 2 Chrome versions, last 2 Safari versions, last 2 ChromeAndroid versions, last 2 iOS versions, last 2 Firefox versions, last 2 Edge versions",
"corejs": 3,
"useBuiltIns": "usage",
"bugfixes": true,
"modules": "auto"
}]
],
}
]
]
}

View file

@ -1,43 +1,25 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": true
env: {
browser: true,
commonjs: true,
es6: true,
node: true,
},
"globals": {
"ePub": true,
"JSZip": true
globals: {
ePub: true,
JSZip: true,
},
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module"
extends: "eslint:recommended",
parserOptions: {
sourceType: "module",
},
"rules": {
"indent": [
"error",
"tab",
{ "VariableDeclarator": { "var": 2, "let": 2, "const": 3 } }
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"warn",
"double"
],
"semi": [
"error",
"always"
],
"no-unused-vars" : ["warn"],
"no-console" : ["warn"],
"no-unused-vars": [
"error",
{ "vars": "all", "args": "none" }
],
rules: {
"linebreak-style": ["error", "unix"],
quotes: ["warn", "double"],
semi: ["error", "always"],
"no-console": ["warn"],
"no-unused-vars": ["error", { vars: "all", args: "none" }],
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"valid-jsdoc": ["warn"]
}
"no-prototype-builtins": "off",
},
};

View file

@ -2,10 +2,8 @@
"browser": true,
"devel": true,
"worker": true,
"trailing": true,
"strict": false,
"boss": true,
"funcscope": true,
"globalstrict": true,
@ -14,10 +12,9 @@
"nonstandard": true,
"sub": true,
"validthis": true,
"globals": {
"_": false,
"define" : false,
"module" : false
"define": false,
"module": false
}
}

View file

@ -1,6 +1,3 @@
{
"ignore_dirs": [
".git",
"node_modules"
]
"ignore_dirs": [".git", "node_modules"]
}

View file

@ -43,7 +43,7 @@ Create the new ePub, and then render it to that element:
```html
<script>
var book = ePub("url/to/book/package.opf");
var rendition = book.renderTo("area", {width: 600, height: 400});
var rendition = book.renderTo("area", { width: 600, height: 400 });
var displayed = rendition.display();
</script>
```
@ -65,6 +65,7 @@ The default manager only displays a single section at a time.
```js
book.renderTo("area", { method: "continuous", width: "100%", height: "100%" });
```
[View example](http://futurepress.github.io/epub.js/examples/continuous-scrolled.html)
The continuous manager will display as many sections as need to fill the screen, and preload the next section offscreen. This enables seamless swiping / scrolling between pages on mobile and desktop, but is less performant than the default method.
@ -72,6 +73,7 @@ The continuous manager will display as many sections as need to fill the screen,
## Flow Overrides
### Auto (Default)
`book.renderTo("area", { flow: "auto", width: "900", height: "600" });`
Flow will be based on the settings in the OPF, defaults to `paginated`.
@ -101,7 +103,7 @@ If a trusted ePub contains interactivity, it can be enabled by passing `allowScr
var rendition = book.renderTo("area", {
width: 600,
height: 400,
allowScriptedContent: true
allowScriptedContent: true,
});
</script>
```
@ -132,11 +134,11 @@ npm start
## Examples
+ [Spreads](http://futurepress.github.io/epub.js/examples/spreads.html)
+ [Scrolled](http://futurepress.github.io/epub.js/examples/scrolled.html)
+ [Swipe](http://futurepress.github.io/epub.js/examples/swipe.html)
+ [Input](http://futurepress.github.io/epub.js/examples/input.html)
+ [Highlights](http://futurepress.github.io/epub.js/examples/highlights.html)
- [Spreads](http://futurepress.github.io/epub.js/examples/spreads.html)
- [Scrolled](http://futurepress.github.io/epub.js/examples/scrolled.html)
- [Swipe](http://futurepress.github.io/epub.js/examples/swipe.html)
- [Input](http://futurepress.github.io/epub.js/examples/input.html)
- [Highlights](http://futurepress.github.io/epub.js/examples/highlights.html)
[View All Examples](http://futurepress.github.io/epub.js/examples/)
@ -175,29 +177,28 @@ Hooks require an event to register to and a can return a promise to block until
Example hook:
```javascript
rendition.hooks.content.register(function(contents, view) {
var elements = contents.document.querySelectorAll('[video]');
rendition.hooks.content.register(function (contents, view) {
var elements = contents.document.querySelectorAll("[video]");
var items = Array.prototype.slice.call(elements);
items.forEach(function(item){
items.forEach(function (item) {
// do something with the video item
});
})
});
```
The parts of the rendering process that can be hooked into are below.
```js
book.spine.hooks.serialize // Section is being converted to text
book.spine.hooks.content // Section has been loaded and parsed
rendition.hooks.render // Section is rendered to the screen
rendition.hooks.content // Section contents have been loaded
rendition.hooks.unloaded // Section contents are being unloaded
book.spine.hooks.serialize; // Section is being converted to text
book.spine.hooks.content; // Section has been loaded and parsed
rendition.hooks.render; // Section is rendered to the screen
rendition.hooks.content; // Section contents have been loaded
rendition.hooks.unloaded; // Section contents are being unloaded
```
## Reader
The reader has moved to its own repo at: https://github.com/futurepress/epubjs-reader/
## Additional Resources
@ -210,7 +211,7 @@ IRC Server: freenode.net Channel: #epub.js
Follow us on twitter: @Epubjs
+ http://twitter.com/#!/Epubjs
- http://twitter.com/#!/Epubjs
## Other

View file

@ -1,19 +1,11 @@
{
"name": "epubjs",
"version": "0.3.0",
"authors": [
"Fred Chasen <fchasen@gmail.com>"
],
"authors": ["Fred Chasen <fchasen@gmail.com>"],
"description": "Enhanced eBooks in the browser.",
"main": "dist/epub.js",
"moduleType": [
"amd",
"globals",
"node"
],
"keywords": [
"epub"
],
"moduleType": ["amd", "globals", "node"],
"keywords": ["epub"],
"license": "MIT",
"homepage": "http://futurepress.org",
"ignore": [

View file

@ -3,115 +3,75 @@ webpackConfig.mode = "development";
webpackConfig.externals = {};
webpackConfig.module.rules.push({
test: /\.xhtml$/i,
use: 'raw-loader',
use: "raw-loader",
});
module.exports = function(config) {
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
basePath: "",
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha'],
frameworks: ["mocha"],
// list of files / patterns to load in the browser
files: [
{ pattern: "src/*.js", watched: true, included: false, served: false },
{pattern: 'src/*.js', watched: true, included: false, served: false},
{ pattern: "test/*.js", watched: false },
{pattern: 'test/*.js', watched: false},
{pattern: 'test/fixtures/**/*', watched: false, included: false, served: true},
{
pattern: "test/fixtures/**/*",
watched: false,
included: false,
served: true,
},
// {pattern: 'node_modules/jszip/dist/jszip.js', watched: false, included: true, served: true},
// {pattern: 'node_modules/es6-promise/dist/es6-promise.auto.js', watched: false, included: true, served: true},
// {pattern: 'libs/url/url-polyfill.js', watched: false, included: true, served: true}
],
// list of files to exclude
exclude: [
],
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
// add webpack as preprocessor
'test/*.js': ['webpack', 'sourcemap'],
// 'test/**/*.js': ['webpack', 'sourcemap']
"test/*.js": ["webpack", "sourcemap"],
},
webpack: webpackConfig,
// {
// mode: "development",
// externals: {
// "jszip": "JSZip"
// // "xmldom": "xmldom"
// },
// devtool: 'inline-source-map',
// resolve: {
// alias: {
// path: "path-webpack"
// }
// },
// module: {
// rules: [
// {
// test: /\.js$/,
// exclude: /node_modules/,
// loader: "babel-loader",
// query: {
// presets: [["@babel/preset-env", {
// targets: "defaults",
// }]],
// }
// },
// {
// test: /\.xhtml$/i,
// use: 'raw-loader',
// }
// ]
// }
// },
webpackMiddleware: {
stats: 'errors-only'
stats: "errors-only",
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['mocha'],
reporters: ["mocha"],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['ChromeHeadless', 'ChromeHeadlessNoSandbox'],
browsers: ["ChromeHeadless", "ChromeHeadlessNoSandbox"],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
@ -121,27 +81,19 @@ module.exports = function(config) {
// how many browser should be started simultaneous
concurrency: Infinity,
proxies: {
"/fixtures/": "/base/test/fixtures/"
},
proxies: { "/fixtures/": "/base/test/fixtures/" },
client: {
config: {
browserConsoleLogOptions: true
},
config: { browserConsoleLogOptions: true },
captureConsole: true,
mocha: {
reporter: 'html'
// bail: true
}
mocha: { reporter: "html" },
},
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: ['--no-sandbox']
}
}
})
}
base: "ChromeHeadless",
flags: ["--no-sandbox"],
},
},
});
};

View file

@ -8,8 +8,7 @@ import { EVENTS } from "./utils/constants";
* @class
*/
class Annotations {
constructor (rendition) {
constructor(rendition) {
this.rendition = rendition;
this.highlights = [];
this.underlines = [];
@ -31,7 +30,7 @@ class Annotations {
* @param {object} styles CSS styles to assign to annotation
* @returns {Annotation} annotation
*/
add (type, cfiRange, data, cb, className, styles) {
add(type, cfiRange, data, cb, className, styles) {
let hash = encodeURI(cfiRange + type);
let cfi = new EpubCFI(cfiRange);
let sectionIndex = cfi.spinePos;
@ -42,7 +41,7 @@ class Annotations {
sectionIndex,
cb,
className,
styles
styles,
});
this._annotations[hash] = annotation;
@ -55,7 +54,7 @@ class Annotations {
let views = this.rendition.views();
views.forEach( (view) => {
views.forEach((view) => {
if (annotation.sectionIndex === view.index) {
annotation.attach(view);
}
@ -69,7 +68,7 @@ class Annotations {
* @param {EpubCFI} cfiRange EpubCFI range the annotation is attached to
* @param {string} type Type of annotation to add: "highlight", "underline", "mark"
*/
remove (cfiRange, type) {
remove(cfiRange, type) {
let hash = encodeURI(cfiRange + type);
if (hash in this._annotations) {
@ -80,7 +79,7 @@ class Annotations {
}
let views = this.rendition.views();
views.forEach( (view) => {
views.forEach((view) => {
this._removeFromAnnotationBySectionIndex(annotation.sectionIndex, hash);
if (annotation.sectionIndex === view.index) {
annotation.detach(view);
@ -95,19 +94,20 @@ class Annotations {
* Remove an annotations by Section Index
* @private
*/
_removeFromAnnotationBySectionIndex (sectionIndex, hash) {
this._annotationsBySectionIndex[sectionIndex] = this._annotationsAt(sectionIndex).filter(h => h !== hash);
_removeFromAnnotationBySectionIndex(sectionIndex, hash) {
this._annotationsBySectionIndex[sectionIndex] = this._annotationsAt(
sectionIndex
).filter((h) => h !== hash);
}
/**
* Get annotations by Section Index
* @private
*/
_annotationsAt (index) {
_annotationsAt(index) {
return this._annotationsBySectionIndex[index];
}
/**
* Add a highlight to the store
* @param {EpubCFI} cfiRange EpubCFI range to attach annotation to
@ -116,7 +116,7 @@ class Annotations {
* @param {string} className CSS class to assign to annotation
* @param {object} styles CSS styles to assign to annotation
*/
highlight (cfiRange, data, cb, className, styles) {
highlight(cfiRange, data, cb, className, styles) {
return this.add("highlight", cfiRange, data, cb, className, styles);
}
@ -128,7 +128,7 @@ class Annotations {
* @param {string} className CSS class to assign to annotation
* @param {object} styles CSS styles to assign to annotation
*/
underline (cfiRange, data, cb, className, styles) {
underline(cfiRange, data, cb, className, styles) {
return this.add("underline", cfiRange, data, cb, className, styles);
}
@ -138,14 +138,14 @@ class Annotations {
* @param {object} data Data to assign to annotation
* @param {function} cb Callback after annotation is clicked
*/
mark (cfiRange, data, cb) {
mark(cfiRange, data, cb) {
return this.add("mark", cfiRange, data, cb);
}
/**
* iterate over annotations in the store
*/
each () {
each() {
return this._annotations.forEach.apply(this._annotations, arguments);
}
@ -154,7 +154,7 @@ class Annotations {
* @param {View} view
* @private
*/
inject (view) {
inject(view) {
let sectionIndex = view.index;
if (sectionIndex in this._annotationsBySectionIndex) {
let annotations = this._annotationsBySectionIndex[sectionIndex];
@ -170,7 +170,7 @@ class Annotations {
* @param {View} view
* @private
*/
clear (view) {
clear(view) {
let sectionIndex = view.index;
if (sectionIndex in this._annotationsBySectionIndex) {
let annotations = this._annotationsBySectionIndex[sectionIndex];
@ -185,18 +185,13 @@ class Annotations {
* [Not Implemented] Show annotations
* @TODO: needs implementation in View
*/
show () {
}
show() {}
/**
* [Not Implemented] Hide annotations
* @TODO: needs implementation in View
*/
hide () {
}
hide() {}
}
/**
@ -213,16 +208,7 @@ class Annotations {
* @returns {Annotation} annotation
*/
class Annotation {
constructor ({
type,
cfiRange,
data,
sectionIndex,
cb,
className,
styles
}) {
constructor({ type, cfiRange, data, sectionIndex, cb, className, styles }) {
this.type = type;
this.cfiRange = cfiRange;
this.data = data;
@ -237,7 +223,7 @@ class Annotation {
* Update stored data
* @param {object} data
*/
update (data) {
update(data) {
this.data = data;
}
@ -245,8 +231,8 @@ class Annotation {
* Add to a view
* @param {View} view
*/
attach (view) {
let {cfiRange, data, type, mark, cb, className, styles} = this;
attach(view) {
let { cfiRange, data, type, cb, className, styles } = this;
let result;
if (type === "highlight") {
@ -266,8 +252,8 @@ class Annotation {
* Remove from a view
* @param {View} view
*/
detach (view) {
let {cfiRange, type} = this;
detach(view) {
let { cfiRange, type } = this;
let result;
if (view) {
@ -289,13 +275,9 @@ class Annotation {
* [Not Implemented] Get text of an annotation
* @TODO: needs implementation in contents
*/
text () {
}
text() {}
}
EventEmitter(Annotation.prototype);
export default Annotations
export default Annotations;

View file

@ -1,21 +1,19 @@
import {defer, isXml, parse} from "./utils/core";
import request from "./utils/request";
import JSZip from "jszip/dist/jszip";
import { defer, isXml, parse } from "./utils/core";
import mime from "./utils/mime";
import Path from "./utils/path";
import JSZip from "jszip/dist/jszip";
import request from "./utils/request";
/**
* Handles Unzipping a requesting files from an Epub Archive
* @class
*/
class Archive {
constructor() {
this.zip = undefined;
this.urlCache = {};
this.checkRequirements();
}
/**
@ -23,7 +21,7 @@ class Archive {
* Requires JSZip if it isn't there
* @private
*/
checkRequirements(){
checkRequirements() {
try {
this.zip = new JSZip();
} catch (e) {
@ -37,8 +35,8 @@ class Archive {
* @param {boolean} [isBase64] tells JSZip if the input data is base64 encoded
* @return {Promise} zipfile
*/
open(input, isBase64){
return this.zip.loadAsync(input, {"base64": isBase64});
open(input, isBase64) {
return this.zip.loadAsync(input, { base64: isBase64 });
}
/**
@ -47,11 +45,12 @@ class Archive {
* @param {boolean} [isBase64] tells JSZip if the input data is base64 encoded
* @return {Promise} zipfile
*/
openUrl(zipUrl, isBase64){
return request(zipUrl, "binary")
.then(function(data){
return this.zip.loadAsync(data, {"base64": isBase64});
}.bind(this));
openUrl(zipUrl, isBase64) {
return request(zipUrl, "binary").then(
function (data) {
return this.zip.loadAsync(data, { base64: isBase64 });
}.bind(this)
);
}
/**
@ -60,31 +59,33 @@ class Archive {
* @param {string} [type] specify the type of the returned result
* @return {Promise<Blob | string | JSON | Document | XMLDocument>}
*/
request(url, type){
request(url, type) {
var deferred = new defer();
var response;
var path = new Path(url);
// If type isn't set, determine it from the file extension
if(!type) {
if (!type) {
type = path.extension;
}
if(type == "blob"){
if (type == "blob") {
response = this.getBlob(url);
} else {
response = this.getText(url);
}
if (response) {
response.then(function (r) {
response.then(
function (r) {
let result = this.handleResponse(r, type);
deferred.resolve(result);
}.bind(this));
}.bind(this)
);
} else {
deferred.reject({
message : "File not found in the epub: " + url,
stack : new Error().stack
message: "File not found in the epub: " + url,
stack: new Error().stack,
});
}
return deferred.promise;
@ -97,22 +98,16 @@ class Archive {
* @param {string} [type]
* @return {any} the parsed result
*/
handleResponse(response, type){
handleResponse(response, type) {
var r;
if(type == "json") {
if (type == "json") {
r = JSON.parse(response);
}
else
if(isXml(type)) {
} else if (isXml(type)) {
r = parse(response, "text/xml");
}
else
if(type == "xhtml") {
} else if (type == "xhtml") {
r = parse(response, "application/xhtml+xml");
}
else
if(type == "html" || type == "htm") {
} else if (type == "html" || type == "htm") {
r = parse(response, "text/html");
} else {
r = response;
@ -127,14 +122,14 @@ class Archive {
* @param {string} [mimeType]
* @return {Blob}
*/
getBlob(url, mimeType){
getBlob(url, mimeType) {
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl);
if(entry) {
if (entry) {
mimeType = mimeType || mime.lookup(entry.name);
return entry.async("uint8array").then(function(uint8array) {
return new Blob([uint8array], {type : mimeType});
return entry.async("uint8array").then(function (uint8array) {
return new Blob([uint8array], { type: mimeType });
});
}
}
@ -145,12 +140,12 @@ class Archive {
* @param {string} [encoding]
* @return {string}
*/
getText(url, encoding){
getText(url, encoding) {
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl);
if(entry) {
return entry.async("string").then(function(text) {
if (entry) {
return entry.async("string").then(function (text) {
return text;
});
}
@ -162,13 +157,13 @@ class Archive {
* @param {string} [mimeType]
* @return {string} base64 encoded
*/
getBase64(url, mimeType){
getBase64(url, mimeType) {
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl);
if(entry) {
if (entry) {
mimeType = mimeType || mime.lookup(entry.name);
return entry.async("base64").then(function(data) {
return entry.async("base64").then(function (data) {
return "data:" + mimeType + ";base64," + data;
});
}
@ -180,14 +175,14 @@ class Archive {
* @param {object} [options.base64] use base64 encoding or blob url
* @return {Promise} url promise with Url string
*/
createUrl(url, options){
createUrl(url, options) {
var deferred = new defer();
var _URL = window.URL || window.webkitURL || window.mozURL;
var tempUrl;
var response;
var useBase64 = options && options.base64;
if(url in this.urlCache) {
if (url in this.urlCache) {
deferred.resolve(this.urlCache[url]);
return deferred.promise;
}
@ -196,36 +191,31 @@ class Archive {
response = this.getBase64(url);
if (response) {
response.then(function(tempUrl) {
response.then(
function (tempUrl) {
this.urlCache[url] = tempUrl;
deferred.resolve(tempUrl);
}.bind(this));
}.bind(this)
);
}
} else {
response = this.getBlob(url);
if (response) {
response.then(function(blob) {
response.then(
function (blob) {
tempUrl = _URL.createObjectURL(blob);
this.urlCache[url] = tempUrl;
deferred.resolve(tempUrl);
}.bind(this));
}.bind(this)
);
}
}
if (!response) {
deferred.reject({
message : "File not found in the epub: " + url,
stack : new Error().stack
message: "File not found in the epub: " + url,
stack: new Error().stack,
});
}
@ -236,10 +226,10 @@ class Archive {
* Revoke Temp Url for a archive item
* @param {string} url url of the item in the archive
*/
revokeUrl(url){
revokeUrl(url) {
var _URL = window.URL || window.webkitURL || window.mozURL;
var fromCache = this.urlCache[url];
if(fromCache) _URL.revokeObjectURL(fromCache);
if (fromCache) _URL.revokeObjectURL(fromCache);
}
destroy() {

View file

@ -1,24 +1,25 @@
import EventEmitter from "event-emitter";
import {extend, defer} from "./utils/core";
import Url from "./utils/url";
import Path from "./utils/path";
import Spine from "./spine";
import Locations from "./locations";
import Archive from "./archive";
import Container from "./container";
import Packaging from "./packaging";
import DisplayOptions from "./displayoptions";
import EpubCFI from "./epubcfi";
import Locations from "./locations";
import Navigation from "./navigation";
import Resources from "./resources";
import Packaging from "./packaging";
import PageList from "./pagelist";
import Rendition from "./rendition";
import Archive from "./archive";
import request from "./utils/request";
import EpubCFI from "./epubcfi";
import Resources from "./resources";
import Spine from "./spine";
import Store from "./store";
import DisplayOptions from "./displayoptions";
import { EPUBJS_VERSION, EVENTS } from "./utils/constants";
import { defer, extend } from "./utils/core";
import Path from "./utils/path";
import request from "./utils/request";
import Url from "./utils/url";
const CONTAINER_PATH = "META-INF/container.xml";
const IBOOKS_DISPLAY_OPTIONS_PATH = "META-INF/com.apple.ibooks.display-options.xml";
const IBOOKS_DISPLAY_OPTIONS_PATH =
"META-INF/com.apple.ibooks.display-options.xml";
const INPUT_TYPE = {
BINARY: "binary",
@ -26,7 +27,7 @@ const INPUT_TYPE = {
EPUB: "epub",
OPF: "opf",
MANIFEST: "json",
DIRECTORY: "directory"
DIRECTORY: "directory",
};
/**
@ -50,10 +51,12 @@ const INPUT_TYPE = {
class Book {
constructor(url, options) {
// Allow passing just options to the Book
if (typeof(options) === "undefined" &&
typeof(url) !== "string" &&
if (
typeof options === "undefined" &&
typeof url !== "string" &&
url instanceof Blob === false &&
url instanceof ArrayBuffer === false) {
url instanceof ArrayBuffer === false
) {
options = url;
url = undefined;
}
@ -66,12 +69,11 @@ class Book {
replacements: undefined,
canonical: undefined,
openAs: undefined,
store: undefined
store: undefined,
});
extend(this.settings, options);
// Promises
this.opening = new defer();
/**
@ -89,7 +91,7 @@ class Book {
navigation: new defer(),
pageList: new defer(),
resources: new defer(),
displayOptions: new defer()
displayOptions: new defer(),
};
this.loaded = {
@ -100,7 +102,7 @@ class Book {
navigation: this.loading.navigation.promise,
pageList: this.loading.pageList.promise,
resources: this.loading.resources.promise,
displayOptions: this.loading.displayOptions.promise
displayOptions: this.loading.displayOptions.promise,
};
/**
@ -115,10 +117,9 @@ class Book {
this.loaded.cover,
this.loaded.navigation,
this.loaded.resources,
this.loaded.displayOptions
this.loaded.displayOptions,
]);
// Queue for methods used before opening
this.isRendered = false;
// this._q = queue(this);
@ -229,9 +230,9 @@ class Book {
this.store(this.settings.store);
}
if(url) {
if (url) {
this.open(url, this.settings.openAs).catch((error) => {
var err = new Error("Cannot load book at "+ url );
var err = new Error("Cannot load book at " + url);
this.emit(EVENTS.BOOK.OPEN_FAILED, err);
});
}
@ -259,18 +260,23 @@ class Book {
} else if (type === INPUT_TYPE.EPUB) {
this.archived = true;
this.url = new Url("/", "");
opening = this.request(input, "binary", this.settings.requestCredentials, this.settings.requestHeaders)
.then(this.openEpub.bind(this));
} else if(type == INPUT_TYPE.OPF) {
opening = this.request(
input,
"binary",
this.settings.requestCredentials,
this.settings.requestHeaders
).then(this.openEpub.bind(this));
} else if (type == INPUT_TYPE.OPF) {
this.url = new Url(input);
opening = this.openPackaging(this.url.Path.toString());
} else if(type == INPUT_TYPE.MANIFEST) {
} else if (type == INPUT_TYPE.MANIFEST) {
this.url = new Url(input);
opening = this.openManifest(this.url.Path.toString());
} else {
this.url = new Url(input);
opening = this.openContainer(CONTAINER_PATH)
.then(this.openPackaging.bind(this));
opening = this.openContainer(CONTAINER_PATH).then(
this.openPackaging.bind(this)
);
}
return opening;
@ -300,8 +306,7 @@ class Book {
* @return {string} packagePath
*/
openContainer(url) {
return this.load(url)
.then((xml) => {
return this.load(url).then((xml) => {
this.container = new Container(xml);
return this.resolve(this.container.packagePath);
});
@ -315,8 +320,7 @@ class Book {
*/
openPackaging(url) {
this.path = new Path(url);
return this.load(url)
.then((xml) => {
return this.load(url).then((xml) => {
this.packaging = new Packaging(xml);
return this.unpack(this.packaging);
});
@ -330,8 +334,7 @@ class Book {
*/
openManifest(url) {
this.path = new Path(url);
return this.load(url)
.then((json) => {
return this.load(url).then((json) => {
this.packaging = new Packaging();
this.packaging.load(json);
return this.unpack(this.packaging);
@ -345,10 +348,15 @@ class Book {
*/
load(path) {
var resolved = this.resolve(path);
if(this.archived) {
if (this.archived) {
return this.archive.request(resolved);
} else {
return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders);
return this.request(
resolved,
null,
this.settings.requestCredentials,
this.settings.requestHeaders
);
}
}
@ -363,7 +371,7 @@ class Book {
return;
}
var resolved = path;
var isAbsolute = (path.indexOf("://") > -1);
var isAbsolute = path.indexOf("://") > -1;
if (isAbsolute) {
return path;
@ -373,7 +381,7 @@ class Book {
resolved = this.path.resolve(path);
}
if(absolute != false && this.url) {
if (absolute != false && this.url) {
resolved = this.url.resolve(resolved);
}
@ -416,7 +424,7 @@ class Book {
return INPUT_TYPE.BASE64;
}
if(typeof(input) != "string") {
if (typeof input != "string") {
return INPUT_TYPE.BINARY;
}
@ -433,20 +441,19 @@ class Book {
return INPUT_TYPE.DIRECTORY;
}
if(extension === "epub"){
if (extension === "epub") {
return INPUT_TYPE.EPUB;
}
if(extension === "opf"){
if (extension === "opf") {
return INPUT_TYPE.OPF;
}
if(extension === "json"){
if (extension === "json") {
return INPUT_TYPE.MANIFEST;
}
}
/**
* unpack the contents of the Books packaging
* @private
@ -457,10 +464,12 @@ class Book {
if (this.packaging.metadata.layout === "") {
// rendition:layout not set - check display options if book is pre-paginated
this.load(this.url.resolve(IBOOKS_DISPLAY_OPTIONS_PATH)).then((xml) => {
this.load(this.url.resolve(IBOOKS_DISPLAY_OPTIONS_PATH))
.then((xml) => {
this.displayOptions = new DisplayOptions(xml);
this.loading.displayOptions.resolve(this.displayOptions);
}).catch((err) => {
})
.catch((err) => {
this.displayOptions = new DisplayOptions();
this.loading.displayOptions.resolve(this.displayOptions);
});
@ -469,13 +478,18 @@ class Book {
this.loading.displayOptions.resolve(this.displayOptions);
}
this.spine.unpack(this.packaging, this.resolve.bind(this), this.canonical.bind(this));
this.spine.unpack(
this.packaging,
this.resolve.bind(this),
this.canonical.bind(this)
);
this.resources = new Resources(this.packaging.manifest, {
archive: this.archive,
resolver: this.resolve.bind(this),
request: this.request.bind(this),
replacements: this.settings.replacements || (this.archived ? "blobUrl" : "base64")
replacements:
this.settings.replacements || (this.archived ? "blobUrl" : "base64"),
});
this.loadNavigation(this.packaging).then(() => {
@ -496,8 +510,12 @@ class Book {
this.isOpen = true;
if(this.archived || this.settings.replacements && this.settings.replacements != "none") {
this.replacements().then(() => {
if (
this.archived ||
(this.settings.replacements && this.settings.replacements != "none")
) {
this.replacements()
.then(() => {
this.loaded.displayOptions.then(() => {
this.opening.resolve(this);
});
@ -511,7 +529,6 @@ class Book {
this.opening.resolve(this);
});
}
}
/**
@ -545,8 +562,7 @@ class Book {
});
}
return this.load(navPath, "xml")
.then((xml) => {
return this.load(navPath, "xml").then((xml) => {
this.navigation = new Navigation(xml);
this.pageList = new PageList(xml);
return this.navigation;
@ -613,7 +629,8 @@ class Book {
*/
store(name) {
// Use "blobUrl" or "base64" for replacements
let replacementsSetting = this.settings.replacements && this.settings.replacements !== "none";
let replacementsSetting =
this.settings.replacements && this.settings.replacements !== "none";
// Save original url
let originalUrl = this.url;
// Save original request method
@ -635,8 +652,7 @@ class Book {
// Set to use replacements
this.resources.settings.replacements = replacementsSetting || "blobUrl";
// Create replacement urls
this.resources.replacements().
then(() => {
this.resources.replacements().then(() => {
return this.resources.replaceCss();
});
@ -653,7 +669,6 @@ class Book {
// Remove hook
this.spine.hooks.serialize.deregister(substituteResources);
});
});
return this.storage;
@ -687,8 +702,7 @@ class Book {
section.output = this.resources.substitute(output, section.url);
});
return this.resources.replacements().
then(() => {
return this.resources.replacements().then(() => {
return this.resources.replaceCss();
});
}
@ -719,7 +733,8 @@ class Book {
* @return {string} key
*/
key(identifier) {
var ident = identifier || this.packaging.metadata.identifier || this.url.filename;
var ident =
identifier || this.packaging.metadata.identifier || this.url.filename;
return `epubjs:${EPUBJS_VERSION}:${ident}`;
}
@ -759,7 +774,6 @@ class Book {
this.path = undefined;
this.archived = false;
}
}
//-- Enable binding events to book

View file

@ -1,5 +1,5 @@
import path from "path-webpack";
import {qs} from "./utils/core";
import { qs } from "./utils/core";
/**
* Handles Parsing and Accessing an Epub Container
@ -8,9 +8,9 @@ import {qs} from "./utils/core";
*/
class Container {
constructor(containerDocument) {
this.packagePath = '';
this.directory = '';
this.encoding = '';
this.packagePath = "";
this.directory = "";
this.encoding = "";
if (containerDocument) {
this.parse(containerDocument);
@ -21,17 +21,17 @@ class Container {
* Parse the Container XML
* @param {document} containerDocument
*/
parse(containerDocument){
parse(containerDocument) {
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
var rootfile;
if(!containerDocument) {
if (!containerDocument) {
throw new Error("Container File Not Found");
}
rootfile = qs(containerDocument, "rootfile");
if(!rootfile) {
if (!rootfile) {
throw new Error("No RootFile Found");
}

View file

@ -1,17 +1,17 @@
import EventEmitter from "event-emitter";
import {isNumber, prefixed, borders, defaults} from "./utils/core";
import EpubCFI from "./epubcfi";
import Mapping from "./mapping";
import {replaceLinks} from "./utils/replacements";
import { EPUBJS_VERSION, EVENTS, DOM_EVENTS } from "./utils/constants";
import { DOM_EVENTS, EPUBJS_VERSION, EVENTS } from "./utils/constants";
import { borders, defaults, isNumber, prefixed } from "./utils/core";
import { replaceLinks } from "./utils/replacements";
const hasNavigator = typeof (navigator) !== "undefined";
const hasNavigator = typeof navigator !== "undefined";
const isChrome = hasNavigator && /Chrome/.test(navigator.userAgent);
const isWebkit = hasNavigator && !isChrome && /AppleWebKit/.test(navigator.userAgent);
const isWebkit =
hasNavigator && !isChrome && /AppleWebKit/.test(navigator.userAgent);
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
/**
* Handles DOM manipulation, queries and events for View contents
@ -33,7 +33,7 @@ class Contents {
this._size = {
width: 0,
height: 0
height: 0,
};
this.sectionIndex = sectionIndex || 0;
@ -71,8 +71,6 @@ class Contents {
}
return parseInt(this.window.getComputedStyle(frame)["width"]);
}
/**
@ -94,7 +92,6 @@ class Contents {
}
return parseInt(this.window.getComputedStyle(frame)["height"]);
}
/**
@ -103,7 +100,6 @@ class Contents {
* @returns {number} width
*/
contentWidth(w) {
var content = this.content || this.document.body;
if (w && isNumber(w)) {
@ -115,8 +111,6 @@ class Contents {
}
return parseInt(this.window.getComputedStyle(content)["width"]);
}
/**
@ -125,7 +119,6 @@ class Contents {
* @returns {number} height
*/
contentHeight(h) {
var content = this.content || this.document.body;
if (h && isNumber(h)) {
@ -137,7 +130,6 @@ class Contents {
}
return parseInt(this.window.getComputedStyle(content)["height"]);
}
/**
@ -208,7 +200,6 @@ class Contents {
* @param {string} [overflow]
*/
overflow(overflow) {
if (overflow) {
this.documentElement.style.overflow = overflow;
}
@ -221,7 +212,6 @@ class Contents {
* @param {string} [overflow]
*/
overflowX(overflow) {
if (overflow) {
this.documentElement.style.overflowX = overflow;
}
@ -234,7 +224,6 @@ class Contents {
* @param {string} [overflow]
*/
overflowY(overflow) {
if (overflow) {
this.documentElement.style.overflowY = overflow;
}
@ -271,16 +260,15 @@ class Contents {
* @param {string} [options.scalable]
*/
viewport(options) {
var _width, _height, _scale, _minimum, _maximum, _scalable;
// var width, height, scale, minimum, maximum, scalable;
var $viewport = this.document.querySelector("meta[name='viewport']");
var parsed = {
"width": undefined,
"height": undefined,
"scale": undefined,
"minimum": undefined,
"maximum": undefined,
"scalable": undefined
width: undefined,
height: undefined,
scale: undefined,
minimum: undefined,
maximum: undefined,
scalable: undefined,
};
var newContent = [];
var settings = {};
@ -289,7 +277,7 @@ class Contents {
* check for the viewport size
* <meta name="viewport" content="width=1024,height=697" />
*/
if($viewport && $viewport.hasAttribute("content")) {
if ($viewport && $viewport.hasAttribute("content")) {
let content = $viewport.getAttribute("content");
let _width = content.match(/width\s*=\s*([^,]*)/);
let _height = content.match(/height\s*=\s*([^,]*)/);
@ -298,22 +286,26 @@ class Contents {
let _maximum = content.match(/maximum-scale\s*=\s*([^,]*)/);
let _scalable = content.match(/user-scalable\s*=\s*([^,]*)/);
if(_width && _width.length && typeof _width[1] !== "undefined"){
if (_width && _width.length && typeof _width[1] !== "undefined") {
parsed.width = _width[1];
}
if(_height && _height.length && typeof _height[1] !== "undefined"){
if (_height && _height.length && typeof _height[1] !== "undefined") {
parsed.height = _height[1];
}
if(_scale && _scale.length && typeof _scale[1] !== "undefined"){
if (_scale && _scale.length && typeof _scale[1] !== "undefined") {
parsed.scale = _scale[1];
}
if(_minimum && _minimum.length && typeof _minimum[1] !== "undefined"){
if (_minimum && _minimum.length && typeof _minimum[1] !== "undefined") {
parsed.minimum = _minimum[1];
}
if(_maximum && _maximum.length && typeof _maximum[1] !== "undefined"){
if (_maximum && _maximum.length && typeof _maximum[1] !== "undefined") {
parsed.maximum = _maximum[1];
}
if(_scalable && _scalable.length && typeof _scalable[1] !== "undefined"){
if (
_scalable &&
_scalable.length &&
typeof _scalable[1] !== "undefined"
) {
parsed.scalable = _scalable[1];
}
}
@ -338,7 +330,6 @@ class Contents {
newContent.push("maximum-scale=" + settings.scale);
newContent.push("user-scalable=" + settings.scalable);
} else {
if (settings.scalable) {
newContent.push("user-scalable=" + settings.scalable);
}
@ -363,7 +354,6 @@ class Contents {
this.window.scrollTo(0, 0);
}
return settings;
}
@ -409,7 +399,6 @@ class Contents {
* @private
*/
removeListeners() {
this.removeEventListeners();
this.removeSelectionListeners();
@ -431,10 +420,9 @@ class Contents {
let height = this.textHeight();
if (width != this._size.width || height != this._size.height) {
this._size = {
width: width,
height: height
height: height,
};
this.onResize && this.onResize(this._size);
@ -447,7 +435,6 @@ class Contents {
* @private
*/
resizeListeners() {
var width, height;
// Test size again
clearTimeout(this.expanding);
requestAnimationFrame(this.resizeCheck.bind(this));
@ -477,13 +464,14 @@ class Contents {
transitionListeners() {
let body = this.content;
body.style['transitionProperty'] = "font, font-size, font-size-adjust, font-stretch, font-variation-settings, font-weight, width, height";
body.style['transitionDuration'] = "0.001ms";
body.style['transitionTimingFunction'] = "linear";
body.style['transitionDelay'] = "0";
body.style["transitionProperty"] =
"font, font-size, font-size-adjust, font-stretch, font-variation-settings, font-weight, width, height";
body.style["transitionDuration"] = "0.001ms";
body.style["transitionTimingFunction"] = "linear";
body.style["transitionDelay"] = "0";
this._resizeCheck = this.resizeCheck.bind(this);
this.document.addEventListener('transitionend', this._resizeCheck);
this.document.addEventListener("transitionend", this._resizeCheck);
}
/**
@ -493,8 +481,8 @@ class Contents {
*/
mediaQueryListeners() {
var sheets = this.document.styleSheets;
var mediaChangeHandler = function(m){
if(m.matches && !this._expanding) {
var mediaChangeHandler = function (m) {
if (m.matches && !this._expanding) {
setTimeout(this.expand.bind(this), 1);
}
}.bind(this);
@ -507,10 +495,10 @@ class Contents {
} catch (e) {
return;
}
if(!rules) return; // Stylesheets changed
if (!rules) return; // Stylesheets changed
for (var j = 0; j < rules.length; j += 1) {
//if (rules[j].constructor === CSSMediaRule) {
if(rules[j].media){
if (rules[j].media) {
var mql = this.window.matchMedia(rules[j].media.mediaText);
mql.addListener(mediaChangeHandler);
//mql.onchange = mediaChangeHandler;
@ -544,7 +532,12 @@ class Contents {
});
// configuration of the observer:
let config = { attributes: true, childList: true, characterData: true, subtree: true };
let config = {
attributes: true,
childList: true,
characterData: true,
subtree: true,
};
// pass in the target node, as well as the observer options
this.observer.observe(this.document, config);
@ -560,8 +553,7 @@ class Contents {
for (var i = 0; i < images.length; i++) {
img = images[i];
if (typeof img.naturalWidth !== "undefined" &&
img.naturalWidth === 0) {
if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) {
img.onload = this.expand.bind(this);
}
}
@ -576,10 +568,11 @@ class Contents {
return;
}
this.document.fonts.ready.then(function () {
this.document.fonts.ready.then(
function () {
this.resizeCheck();
}.bind(this));
}.bind(this)
);
}
/**
@ -587,7 +580,7 @@ class Contents {
* @returns {element} documentElement
*/
root() {
if(!this.document) return null;
if (!this.document) return null;
return this.document.documentElement;
}
@ -599,30 +592,38 @@ class Contents {
*/
locationOf(target, ignoreClass) {
var position;
var targetPos = {"left": 0, "top": 0};
var targetPos = { left: 0, top: 0 };
if(!this.document) return targetPos;
if (!this.document) return targetPos;
if(this.epubcfi.isCfiString(target)) {
if (this.epubcfi.isCfiString(target)) {
let range = new EpubCFI(target).toRange(this.document, ignoreClass);
if(range) {
if (range) {
try {
if (!range.endContainer ||
(range.startContainer == range.endContainer
&& range.startOffset == range.endOffset)) {
if (
!range.endContainer ||
(range.startContainer == range.endContainer &&
range.startOffset == range.endOffset)
) {
// If the end for the range is not set, it results in collapsed becoming
// true. This in turn leads to inconsistent behaviour when calling
// getBoundingRect. Wrong bounds lead to the wrong page being displayed.
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15684911/
let pos = range.startContainer.textContent.indexOf(" ", range.startOffset);
let pos = range.startContainer.textContent.indexOf(
" ",
range.startOffset
);
if (pos == -1) {
pos = range.startContainer.textContent.length;
}
range.setEnd(range.startContainer, pos);
}
} catch (e) {
console.error("setting end offset to start container length failed", e);
console.error(
"setting end offset to start container length failed",
e
);
}
if (range.startContainer.nodeType === Node.ELEMENT_NODE) {
@ -648,7 +649,8 @@ class Contents {
newRange.setStart(container, range.startOffset - 2);
newRange.setEnd(container, range.startOffset);
position = newRange.getBoundingClientRect();
} else { // empty, return the parent element
} else {
// empty, return the parent element
position = container.parentNode.getBoundingClientRect();
}
} catch (e) {
@ -659,13 +661,10 @@ class Contents {
}
}
}
} else if(typeof target === "string" &&
target.indexOf("#") > -1) {
let id = target.substring(target.indexOf("#")+1);
} else if (typeof target === "string" && target.indexOf("#") > -1) {
let id = target.substring(target.indexOf("#") + 1);
let el = this.document.getElementById(id);
if(el) {
if (el) {
if (isWebkit) {
// Webkit reports incorrect bounding rects in Columns
let newRange = new Range();
@ -690,17 +689,18 @@ class Contents {
* @param {string} src url
*/
addStylesheet(src) {
return new Promise(function(resolve, reject){
return new Promise(
function (resolve, reject) {
var $stylesheet;
var ready = false;
if(!this.document) {
if (!this.document) {
resolve(false);
return;
}
// Check if link already exists
$stylesheet = this.document.querySelector("link[href='"+src+"']");
$stylesheet = this.document.querySelector("link[href='" + src + "']");
if ($stylesheet) {
resolve(true);
return; // already present
@ -710,8 +710,8 @@ class Contents {
$stylesheet.type = "text/css";
$stylesheet.rel = "stylesheet";
$stylesheet.href = src;
$stylesheet.onload = $stylesheet.onreadystatechange = function() {
if ( !ready && (!this.readyState || this.readyState == "complete") ) {
$stylesheet.onload = $stylesheet.onreadystatechange = function () {
if (!ready && (!this.readyState || this.readyState == "complete")) {
ready = true;
// Let apply
setTimeout(() => {
@ -721,15 +721,15 @@ class Contents {
};
this.document.head.appendChild($stylesheet);
}.bind(this));
}.bind(this)
);
}
_getStylesheetNode(key) {
var styleEl;
key = "epubjs-inserted-css-" + (key || '');
key = "epubjs-inserted-css-" + (key || "");
if(!this.document) return false;
if (!this.document) return false;
// Check if link already exists
styleEl = this.document.getElementById(key);
@ -748,7 +748,7 @@ class Contents {
* @param {string} key If the key is the same, the CSS will be replaced instead of inserted
*/
addStylesheetCss(serializedCss, key) {
if(!this.document || !serializedCss) return false;
if (!this.document || !serializedCss) return false;
var styleEl;
styleEl = this._getStylesheetNode(key);
@ -767,14 +767,17 @@ class Contents {
addStylesheetRules(rules, key) {
var styleSheet;
if(!this.document || !rules || rules.length === 0) return;
if (!this.document || !rules || rules.length === 0) return;
// Grab style sheet
styleSheet = this._getStylesheetNode(key).sheet;
if (Object.prototype.toString.call(rules) === "[object Array]") {
for (var i = 0, rl = rules.length; i < rl; i++) {
var j = 1, rule = rules[i], selector = rules[i][0], propStr = "";
var j = 1,
rule = rules[i],
selector = rules[i][0],
propStr = "";
// If the second argument of a rule is an array of arrays, correct our variables.
if (Object.prototype.toString.call(rule[1][0]) === "[object Array]") {
rule = rule[1];
@ -783,11 +786,15 @@ class Contents {
for (var pl = rule.length; j < pl; j++) {
var prop = rule[j];
propStr += prop[0] + ":" + prop[1] + (prop[2] ? " !important" : "") + ";\n";
propStr +=
prop[0] + ":" + prop[1] + (prop[2] ? " !important" : "") + ";\n";
}
// Insert CSS Rule
styleSheet.insertRule(selector + "{" + propStr + "}", styleSheet.cssRules.length);
styleSheet.insertRule(
selector + "{" + propStr + "}",
styleSheet.cssRules.length
);
}
} else {
const selectors = Object.keys(rules);
@ -796,17 +803,27 @@ class Contents {
if (Array.isArray(definition)) {
definition.forEach((item) => {
const _rules = Object.keys(item);
const result = _rules.map((rule) => {
const result = _rules
.map((rule) => {
return `${rule}:${item[rule]}`;
}).join(';');
styleSheet.insertRule(`${selector}{${result}}`, styleSheet.cssRules.length);
})
.join(";");
styleSheet.insertRule(
`${selector}{${result}}`,
styleSheet.cssRules.length
);
});
} else {
const _rules = Object.keys(definition);
const result = _rules.map((rule) => {
const result = _rules
.map((rule) => {
return `${rule}:${definition[rule]}`;
}).join(';');
styleSheet.insertRule(`${selector}{${result}}`, styleSheet.cssRules.length);
})
.join(";");
styleSheet.insertRule(
`${selector}{${result}}`,
styleSheet.cssRules.length
);
}
});
}
@ -818,12 +835,12 @@ class Contents {
* @returns {Promise} loaded
*/
addScript(src) {
return new Promise(function(resolve, reject){
return new Promise(
function (resolve, reject) {
var $script;
var ready = false;
if(!this.document) {
if (!this.document) {
resolve(false);
return;
}
@ -832,18 +849,18 @@ class Contents {
$script.type = "text/javascript";
$script.async = true;
$script.src = src;
$script.onload = $script.onreadystatechange = function() {
if ( !ready && (!this.readyState || this.readyState == "complete") ) {
$script.onload = $script.onreadystatechange = function () {
if (!ready && (!this.readyState || this.readyState == "complete")) {
ready = true;
setTimeout(function(){
setTimeout(function () {
resolve(true);
}, 1);
}
};
this.document.head.appendChild($script);
}.bind(this));
}.bind(this)
);
}
/**
@ -853,14 +870,13 @@ class Contents {
addClass(className) {
var content;
if(!this.document) return;
if (!this.document) return;
content = this.content || this.document.body;
if (content) {
content.classList.add(className);
}
}
/**
@ -870,43 +886,45 @@ class Contents {
removeClass(className) {
var content;
if(!this.document) return;
if (!this.document) return;
content = this.content || this.document.body;
if (content) {
content.classList.remove(className);
}
}
/**
* Add DOM event listeners
* @private
*/
addEventListeners(){
if(!this.document) {
addEventListeners() {
if (!this.document) {
return;
}
this._triggerEvent = this.triggerEvent.bind(this);
DOM_EVENTS.forEach(function(eventName){
this.document.addEventListener(eventName, this._triggerEvent, { passive: true });
DOM_EVENTS.forEach(function (eventName) {
this.document.addEventListener(eventName, this._triggerEvent, {
passive: true,
});
}, this);
}
/**
* Remove DOM event listeners
* @private
*/
removeEventListeners(){
if(!this.document) {
removeEventListeners() {
if (!this.document) {
return;
}
DOM_EVENTS.forEach(function(eventName){
this.document.removeEventListener(eventName, this._triggerEvent, { passive: true });
DOM_EVENTS.forEach(function (eventName) {
this.document.removeEventListener(eventName, this._triggerEvent, {
passive: true,
});
}, this);
this._triggerEvent = undefined;
}
@ -915,7 +933,7 @@ class Contents {
* Emit passed browser events
* @private
*/
triggerEvent(e){
triggerEvent(e) {
this.emit(e.type, e);
}
@ -923,23 +941,29 @@ class Contents {
* Add listener for text selection
* @private
*/
addSelectionListeners(){
if(!this.document) {
addSelectionListeners() {
if (!this.document) {
return;
}
this._onSelectionChange = this.onSelectionChange.bind(this);
this.document.addEventListener("selectionchange", this._onSelectionChange, { passive: true });
this.document.addEventListener("selectionchange", this._onSelectionChange, {
passive: true,
});
}
/**
* Remove listener for text selection
* @private
*/
removeSelectionListeners(){
if(!this.document) {
removeSelectionListeners() {
if (!this.document) {
return;
}
this.document.removeEventListener("selectionchange", this._onSelectionChange, { passive: true });
this.document.removeEventListener(
"selectionchange",
this._onSelectionChange,
{ passive: true }
);
this._onSelectionChange = undefined;
}
@ -947,26 +971,29 @@ class Contents {
* Handle getting text on selection
* @private
*/
onSelectionChange(e){
onSelectionChange(e) {
if (this.selectionEndTimeout) {
clearTimeout(this.selectionEndTimeout);
}
this.selectionEndTimeout = setTimeout(function() {
this.selectionEndTimeout = setTimeout(
function () {
var selection = this.window.getSelection();
this.triggerSelectedEvent(selection);
}.bind(this), 250);
}.bind(this),
250
);
}
/**
* Emit event on text selection
* @private
*/
triggerSelectedEvent(selection){
triggerSelectedEvent(selection) {
var range, cfirange;
if (selection && selection.rangeCount > 0) {
range = selection.getRangeAt(0);
if(!range.collapsed) {
if (!range.collapsed) {
// cfirange = this.section.cfiFromRange(range);
cfirange = new EpubCFI(range, this.cfiBase).toString();
this.emit(EVENTS.CONTENTS.SELECTED, cfirange);
@ -981,7 +1008,7 @@ class Contents {
* @param {string} [ignoreClass]
* @returns {Range} range
*/
range(_cfi, ignoreClass){
range(_cfi, ignoreClass) {
var cfi = new EpubCFI(_cfi);
return cfi.toRange(this.document, ignoreClass);
}
@ -992,7 +1019,7 @@ class Contents {
* @param {string} [ignoreClass]
* @returns {EpubCFI} cfi
*/
cfiFromRange(range, ignoreClass){
cfiFromRange(range, ignoreClass) {
return new EpubCFI(range, this.cfiBase, ignoreClass).toString();
}
@ -1002,12 +1029,12 @@ class Contents {
* @param {string} [ignoreClass]
* @returns {EpubCFI} cfi
*/
cfiFromNode(node, ignoreClass){
cfiFromNode(node, ignoreClass) {
return new EpubCFI(node, this.cfiBase, ignoreClass).toString();
}
// TODO: find where this is used - remove?
map(layout){
map(layout) {
var map = new Mapping(layout);
return map.section();
}
@ -1017,7 +1044,7 @@ class Contents {
* @param {number} [width]
* @param {number} [height]
*/
size(width, height){
size(width, height) {
var viewport = { scale: 1.0, scalable: "no" };
this.layoutStyle("scrolling");
@ -1025,7 +1052,7 @@ class Contents {
if (width >= 0) {
this.width(width);
viewport.width = width;
this.css("padding", "0 "+(width/12)+"px");
this.css("padding", "0 " + width / 12 + "px");
}
if (height >= 0) {
@ -1036,7 +1063,6 @@ class Contents {
this.css("margin", "0");
this.css("box-sizing", "border-box");
this.viewport(viewport);
}
@ -1047,14 +1073,15 @@ class Contents {
* @param {number} columnWidth
* @param {number} gap
*/
columns(width, height, columnWidth, gap, dir){
columns(width, height, columnWidth, gap, dir) {
let COLUMN_AXIS = prefixed("column-axis");
let COLUMN_GAP = prefixed("column-gap");
let COLUMN_WIDTH = prefixed("column-width");
let COLUMN_FILL = prefixed("column-fill");
let writingMode = this.writingMode();
let axis = (writingMode.indexOf("vertical") === 0) ? "vertical" : "horizontal";
let axis =
writingMode.indexOf("vertical") === 0 ? "vertical" : "horizontal";
this.layoutStyle("paginated");
@ -1076,16 +1103,16 @@ class Contents {
this.css("margin", "0", true);
if (axis === "vertical") {
this.css("padding-top", (gap / 2) + "px", true);
this.css("padding-bottom", (gap / 2) + "px", true);
this.css("padding-top", gap / 2 + "px", true);
this.css("padding-bottom", gap / 2 + "px", true);
this.css("padding-left", "20px");
this.css("padding-right", "20px");
this.css(COLUMN_AXIS, "vertical");
} else {
this.css("padding-top", "20px");
this.css("padding-bottom", "20px");
this.css("padding-left", (gap / 2) + "px", true);
this.css("padding-right", (gap / 2) + "px", true);
this.css("padding-left", gap / 2 + "px", true);
this.css("padding-right", gap / 2 + "px", true);
this.css(COLUMN_AXIS, "horizontal");
}
@ -1094,8 +1121,8 @@ class Contents {
this.css(COLUMN_FILL, "auto");
this.css(COLUMN_GAP, gap+"px");
this.css(COLUMN_WIDTH, columnWidth+"px");
this.css(COLUMN_GAP, gap + "px");
this.css(COLUMN_WIDTH, columnWidth + "px");
// Fix glyph clipping in WebKit
// https://github.com/futurepress/epub.js/issues/983
@ -1108,14 +1135,15 @@ class Contents {
* @param {number} offsetX
* @param {number} offsetY
*/
scaler(scale, offsetX, offsetY){
scaler(scale, offsetX, offsetY) {
var scaleStr = "scale(" + scale + ")";
var translateStr = "";
// this.css("position", "absolute"));
this.css("transform-origin", "top left");
if (offsetX >= 0 || offsetY >= 0) {
translateStr = " translate(" + (offsetX || 0 )+ "px, " + (offsetY || 0 )+ "px )";
translateStr =
" translate(" + (offsetX || 0) + "px, " + (offsetY || 0) + "px )";
}
this.css("transform", scaleStr + translateStr);
@ -1126,7 +1154,7 @@ class Contents {
* @param {number} width
* @param {number} height
*/
fit(width, height, section){
fit(width, height, section) {
var viewport = this.viewport();
var viewportWidth = parseInt(viewport.width);
var viewportHeight = parseInt(viewport.height);
@ -1153,12 +1181,15 @@ class Contents {
// this.scaler(scale, offsetX > 0 ? offsetX : 0, offsetY);
// background images are not scaled by transform
this.css("background-size", viewportWidth * scale + "px " + viewportHeight * scale + "px");
this.css(
"background-size",
viewportWidth * scale + "px " + viewportHeight * scale + "px"
);
this.css("background-color", "transparent");
if (section && section.properties.includes("page-spread-left")) {
// set margin since scale is weird
var marginLeft = width - (viewportWidth * scale);
var marginLeft = width - viewportWidth * scale;
this.css("margin-left", marginLeft + "px");
}
}
@ -1200,7 +1231,9 @@ class Contents {
this.documentElement.style[WRITING_MODE] = mode;
}
return this.window.getComputedStyle(this.documentElement)[WRITING_MODE] || '';
return (
this.window.getComputedStyle(this.documentElement)[WRITING_MODE] || ""
);
}
/**
@ -1209,7 +1242,6 @@ class Contents {
* @private
*/
layoutStyle(style) {
if (style) {
this._layoutStyle = style;
navigator.epubReadingSystem.layoutStyle = this._layoutStyle;
@ -1246,7 +1278,7 @@ class Contents {
default:
return false;
}
}
},
};
return navigator.epubReadingSystem;
}
@ -1255,7 +1287,6 @@ class Contents {
// this.document.removeEventListener('transitionend', this._resizeCheck);
this.removeListeners();
}
}

View file

@ -1,4 +1,4 @@
import {qs, qsa } from "./utils/core";
import { qs, qsa } from "./utils/core";
/**
* Open DisplayOptions Format Parser
@ -23,12 +23,12 @@ class DisplayOptions {
* @return {DisplayOptions} self
*/
parse(displayOptionsDocument) {
if(!displayOptionsDocument) {
if (!displayOptionsDocument) {
return this;
}
const displayOptionsNode = qs(displayOptionsDocument, "display_options");
if(!displayOptionsNode) {
if (!displayOptionsNode) {
return this;
}

View file

@ -1,13 +1,9 @@
import Book from "./book";
import Rendition from "./rendition";
import CFI from "./epubcfi";
import Contents from "./contents";
import * as utils from "./utils/core";
import CFI from "./epubcfi";
import Rendition from "./rendition";
import { EPUBJS_VERSION } from "./utils/constants";
import IframeView from "./managers/views/iframe";
import DefaultViewManager from "./managers/default";
import ContinuousViewManager from "./managers/continuous";
import * as utils from "./utils/core";
/**
* Creates a new Book
@ -22,7 +18,7 @@ function ePub(url, options) {
ePub.VERSION = EPUBJS_VERSION;
if (typeof(global) !== "undefined") {
if (typeof global !== "undefined") {
global.EPUBJS_VERSION = EPUBJS_VERSION;
}

View file

@ -1,8 +1,13 @@
import {extend, type, findChildren, RangeObject, isNumber} from "./utils/core";
import {
RangeObject,
extend,
findChildren,
isNumber,
type,
} from "./utils/core";
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
const COMMENT_NODE = 8;
const DOCUMENT_NODE = 9;
/**
@ -23,7 +28,7 @@ const DOCUMENT_NODE = 9;
@param {string} [ignoreClass] class to ignore when parsing DOM
*/
class EpubCFI {
constructor(cfiFrom, base, ignoreClass){
constructor(cfiFrom, base, ignoreClass) {
var type;
this.str = "";
@ -42,16 +47,15 @@ class EpubCFI {
return new EpubCFI(cfiFrom, base, ignoreClass);
}
if(typeof base === "string") {
if (typeof base === "string") {
this.base = this.parseComponent(base);
} else if(typeof base === "object" && base.steps) {
} else if (typeof base === "object" && base.steps) {
this.base = base;
}
type = this.checkType(cfiFrom);
if(type === "string") {
if (type === "string") {
this.str = cfiFrom;
return extend(this, this.parse(cfiFrom));
} else if (type === "range") {
@ -65,7 +69,6 @@ class EpubCFI {
} else {
throw new TypeError("not a valid argument for EpubCFI");
}
}
/**
@ -73,15 +76,23 @@ class EpubCFI {
* @private
*/
checkType(cfi) {
if (this.isCfiString(cfi)) {
return "string";
// Is a range object
} else if (cfi && typeof cfi === "object" && (type(cfi) === "Range" || typeof(cfi.startContainer) != "undefined")){
} else if (
cfi &&
typeof cfi === "object" &&
(type(cfi) === "Range" || typeof cfi.startContainer != "undefined")
) {
return "range";
} else if (cfi && typeof cfi === "object" && typeof(cfi.nodeType) != "undefined" ){ // || typeof cfi === "function"
} else if (
cfi &&
typeof cfi === "object" &&
typeof cfi.nodeType != "undefined"
) {
// || typeof cfi === "function"
return "node";
} else if (cfi && typeof cfi === "object" && cfi instanceof EpubCFI){
} else if (cfi && typeof cfi === "object" && cfi instanceof EpubCFI) {
return "EpubCFI";
} else {
return false;
@ -100,24 +111,24 @@ class EpubCFI {
base: {},
path: {},
start: null,
end: null
end: null,
};
var baseComponent, pathComponent, range;
if(typeof cfiStr !== "string") {
return {spinePos: -1};
if (typeof cfiStr !== "string") {
return { spinePos: -1 };
}
if(cfiStr.indexOf("epubcfi(") === 0 && cfiStr[cfiStr.length-1] === ")") {
if (cfiStr.indexOf("epubcfi(") === 0 && cfiStr[cfiStr.length - 1] === ")") {
// Remove initial epubcfi( and ending )
cfiStr = cfiStr.slice(8, cfiStr.length-1);
cfiStr = cfiStr.slice(8, cfiStr.length - 1);
}
baseComponent = this.getChapterComponent(cfiStr);
// Make sure this is a valid cfi or return
if(!baseComponent) {
return {spinePos: -1};
if (!baseComponent) {
return { spinePos: -1 };
}
cfi.base = this.parseComponent(baseComponent);
@ -127,7 +138,7 @@ class EpubCFI {
range = this.getRange(cfiStr);
if(range) {
if (range) {
cfi.range = true;
cfi.start = this.parseComponent(range[0]);
cfi.end = this.parseComponent(range[1]);
@ -142,19 +153,19 @@ class EpubCFI {
return cfi;
}
parseComponent(componentStr){
parseComponent(componentStr) {
var component = {
steps: [],
terminal: {
offset: null,
assertion: null
}
assertion: null,
},
};
var parts = componentStr.split(":");
var steps = parts[0].split("/");
var terminal;
if(parts.length > 1) {
if (parts.length > 1) {
terminal = parts[1];
component.terminal = this.parseTerminal(terminal);
}
@ -163,48 +174,51 @@ class EpubCFI {
steps.shift(); // Ignore the first slash
}
component.steps = steps.map(function(step){
component.steps = steps.map(
function (step) {
return this.parseStep(step);
}.bind(this));
}.bind(this)
);
return component;
}
parseStep(stepStr){
parseStep(stepStr) {
var type, num, index, has_brackets, id;
has_brackets = stepStr.match(/\[(.*)\]/);
if(has_brackets && has_brackets[1]){
if (has_brackets && has_brackets[1]) {
id = has_brackets[1];
}
//-- Check if step is a text node or element
num = parseInt(stepStr);
if(isNaN(num)) {
if (isNaN(num)) {
return;
}
if(num % 2 === 0) { // Even = is an element
if (num % 2 === 0) {
// Even = is an element
type = "element";
index = num / 2 - 1;
} else {
type = "text";
index = (num - 1 ) / 2;
index = (num - 1) / 2;
}
return {
"type" : type,
"index" : index,
"id" : id || null
type: type,
index: index,
id: id || null,
};
}
parseTerminal(termialStr){
parseTerminal(termialStr) {
var characterOffset, textLocationAssertion;
var assertion = termialStr.match(/\[(.*)\]/);
if(assertion && assertion[1]){
if (assertion && assertion[1]) {
characterOffset = parseInt(termialStr.split("[")[0]);
textLocationAssertion = assertion[1];
} else {
@ -216,39 +230,31 @@ class EpubCFI {
}
return {
"offset": characterOffset,
"assertion": textLocationAssertion
offset: characterOffset,
assertion: textLocationAssertion,
};
}
getChapterComponent(cfiStr) {
var indirection = cfiStr.split("!");
return indirection[0];
}
getPathComponent(cfiStr) {
var indirection = cfiStr.split("!");
if(indirection[1]) {
if (indirection[1]) {
let ranges = indirection[1].split(",");
return ranges[0];
}
}
getRange(cfiStr) {
var ranges = cfiStr.split(",");
if(ranges.length === 3){
return [
ranges[1],
ranges[2]
];
if (ranges.length === 3) {
return [ranges[1], ranges[2]];
}
return false;
@ -260,29 +266,29 @@ class EpubCFI {
}
joinSteps(steps) {
if(!steps) {
if (!steps) {
return "";
}
return steps.map(function(part){
return steps
.map(function (part) {
var segment = "";
if(part.type === "element") {
if (part.type === "element") {
segment += (part.index + 1) * 2;
}
if(part.type === "text") {
segment += 1 + (2 * part.index); // TODO: double check that this is odd
if (part.type === "text") {
segment += 1 + 2 * part.index; // TODO: double check that this is odd
}
if(part.id) {
if (part.id) {
segment += "[" + part.id + "]";
}
return segment;
}).join("/");
})
.join("/");
}
segmentString(segment) {
@ -290,11 +296,11 @@ class EpubCFI {
segmentString += this.joinSteps(segment.steps);
if(segment.terminal && segment.terminal.offset != null){
if (segment.terminal && segment.terminal.offset != null) {
segmentString += ":" + segment.terminal.offset;
}
if(segment.terminal && segment.terminal.assertion != null){
if (segment.terminal && segment.terminal.assertion != null) {
segmentString += "[" + segment.terminal.assertion + "]";
}
@ -314,12 +320,12 @@ class EpubCFI {
cfiString += this.segmentString(this.path);
// Add Range, if present
if(this.range && this.start) {
if (this.range && this.start) {
cfiString += ",";
cfiString += this.segmentString(this.start);
}
if(this.range && this.end) {
if (this.range && this.end) {
cfiString += ",";
cfiString += this.segmentString(this.end);
}
@ -329,7 +335,6 @@ class EpubCFI {
return cfiString;
}
/**
* Compare which of two CFIs is earlier in the text
* @returns {number} First is earlier = -1, Second is earlier = 1, They are equal = 0
@ -338,22 +343,17 @@ class EpubCFI {
var stepsA, stepsB;
var terminalA, terminalB;
var rangeAStartSteps, rangeAEndSteps;
var rangeBEndSteps, rangeBEndSteps;
var rangeAStartTerminal, rangeAEndTerminal;
var rangeBStartTerminal, rangeBEndTerminal;
if(typeof cfiOne === "string") {
if (typeof cfiOne === "string") {
cfiOne = new EpubCFI(cfiOne);
}
if(typeof cfiTwo === "string") {
if (typeof cfiTwo === "string") {
cfiTwo = new EpubCFI(cfiTwo);
}
// Compare Spine Positions
if(cfiOne.spinePos > cfiTwo.spinePos) {
if (cfiOne.spinePos > cfiTwo.spinePos) {
return 1;
}
if(cfiOne.spinePos < cfiTwo.spinePos) {
if (cfiOne.spinePos < cfiTwo.spinePos) {
return -1;
}
@ -375,31 +375,31 @@ class EpubCFI {
// Compare Each Step in the First item
for (var i = 0; i < stepsA.length; i++) {
if(!stepsA[i]) {
if (!stepsA[i]) {
return -1;
}
if(!stepsB[i]) {
if (!stepsB[i]) {
return 1;
}
if(stepsA[i].index > stepsB[i].index) {
if (stepsA[i].index > stepsB[i].index) {
return 1;
}
if(stepsA[i].index < stepsB[i].index) {
if (stepsA[i].index < stepsB[i].index) {
return -1;
}
// Otherwise continue checking
}
// All steps in First equal to Second and First is Less Specific
if(stepsA.length < stepsB.length) {
if (stepsA.length < stepsB.length) {
return -1;
}
// Compare the character offset of the text node
if(terminalA.offset > terminalB.offset) {
if (terminalA.offset > terminalB.offset) {
return 1;
}
if(terminalA.offset < terminalB.offset) {
if (terminalA.offset < terminalB.offset) {
return -1;
}
@ -408,13 +408,13 @@ class EpubCFI {
}
step(node) {
var nodeType = (node.nodeType === TEXT_NODE) ? "text" : "element";
var nodeType = node.nodeType === TEXT_NODE ? "text" : "element";
return {
"id" : node.id,
"tagName" : node.tagName,
"type" : nodeType,
"index" : this.position(node)
id: node.id,
tagName: node.tagName,
type: nodeType,
index: this.position(node),
};
}
@ -428,13 +428,13 @@ class EpubCFI {
}
// Otherwise add the filter node in
nodeType = (filteredNode.nodeType === TEXT_NODE) ? "text" : "element";
nodeType = filteredNode.nodeType === TEXT_NODE ? "text" : "element";
return {
"id" : filteredNode.id,
"tagName" : filteredNode.tagName,
"type" : nodeType,
"index" : this.filteredPosition(filteredNode, ignoreClass)
id: filteredNode.id,
tagName: filteredNode.tagName,
type: nodeType,
index: this.filteredPosition(filteredNode, ignoreClass),
};
}
@ -443,15 +443,17 @@ class EpubCFI {
steps: [],
terminal: {
offset: null,
assertion: null
}
assertion: null,
},
};
var currentNode = node;
var step;
while(currentNode && currentNode.parentNode &&
currentNode.parentNode.nodeType != DOCUMENT_NODE) {
while (
currentNode &&
currentNode.parentNode &&
currentNode.parentNode.nodeType != DOCUMENT_NODE
) {
if (ignoreClass) {
step = this.filteredStep(currentNode, ignoreClass);
} else {
@ -463,24 +465,20 @@ class EpubCFI {
}
currentNode = currentNode.parentNode;
}
if (offset != null && offset >= 0) {
segment.terminal.offset = offset;
// Make sure we are getting to a textNode if there is an offset
if(segment.steps[segment.steps.length-1].type != "text") {
if (segment.steps[segment.steps.length - 1].type != "text") {
segment.steps.push({
"type" : "text",
"index" : 0
type: "text",
index: 0,
});
}
}
return segment;
}
@ -489,9 +487,11 @@ class EpubCFI {
return false;
}
if(stepA.index === stepB.index &&
if (
stepA.index === stepB.index &&
stepA.id === stepB.id &&
stepA.type === stepB.type) {
stepA.type === stepB.type
) {
return true;
}
@ -511,7 +511,7 @@ class EpubCFI {
base: {},
path: {},
start: null,
end: null
end: null,
};
var start = range.startContainer;
@ -524,10 +524,10 @@ class EpubCFI {
if (ignoreClass) {
// Tell pathTo if / what to ignore
needsIgnoring = (start.ownerDocument.querySelector("." + ignoreClass) != null);
needsIgnoring =
start.ownerDocument.querySelector("." + ignoreClass) != null;
}
if (typeof base === "string") {
cfi.base = this.parseComponent(base);
cfi.spinePos = cfi.base.steps[1].index;
@ -557,7 +557,7 @@ class EpubCFI {
// Create a new empty path
cfi.path = {
steps: [],
terminal: null
terminal: null,
};
// Push steps that are shared between start and end to the common path
@ -566,9 +566,9 @@ class EpubCFI {
for (i = 0; i < len; i++) {
if (this.equalStep(cfi.start.steps[i], cfi.end.steps[i])) {
if(i === len-1) {
if (i === len - 1) {
// Last step is equal, check terminals
if(cfi.start.terminal === cfi.end.terminal) {
if (cfi.start.terminal === cfi.end.terminal) {
// CFI's are equal
cfi.path.steps.push(cfi.start.steps[i]);
// Not a range
@ -577,7 +577,6 @@ class EpubCFI {
} else {
cfi.path.steps.push(cfi.start.steps[i]);
}
} else {
break;
}
@ -605,7 +604,7 @@ class EpubCFI {
base: {},
path: {},
start: null,
end: null
end: null,
};
if (typeof base === "string") {
@ -652,7 +651,6 @@ class EpubCFI {
// Parent will be ignored on next step
return anchor;
}
} else if (needsIgnoring && !isText) {
// Otherwise just skip the element node
return false;
@ -660,7 +658,6 @@ class EpubCFI {
// No need to filter
return anchor;
}
}
patchOffset(anchor, offset, ignoreClass) {
@ -677,9 +674,9 @@ class EpubCFI {
}
while (curr.previousSibling) {
if(curr.previousSibling.nodeType === ELEMENT_NODE) {
if (curr.previousSibling.nodeType === ELEMENT_NODE) {
// Originally a text node, so join
if(curr.previousSibling.classList.contains(ignoreClass)){
if (curr.previousSibling.classList.contains(ignoreClass)) {
totalOffset += curr.previousSibling.textContent.length;
} else {
break; // Normal node, dont join
@ -693,38 +690,36 @@ class EpubCFI {
}
return totalOffset;
}
normalizedMap(children, nodeType, ignoreClass) {
var output = {};
var prevIndex = -1;
var i, len = children.length;
var i,
len = children.length;
var currNodeType;
var prevNodeType;
for (i = 0; i < len; i++) {
currNodeType = children[i].nodeType;
// Check if needs ignoring
if (currNodeType === ELEMENT_NODE &&
children[i].classList.contains(ignoreClass)) {
if (
currNodeType === ELEMENT_NODE &&
children[i].classList.contains(ignoreClass)
) {
currNodeType = TEXT_NODE;
}
if (i > 0 &&
currNodeType === TEXT_NODE &&
prevNodeType === TEXT_NODE) {
if (i > 0 && currNodeType === TEXT_NODE && prevNodeType === TEXT_NODE) {
// join text nodes
output[i] = prevIndex;
} else if (nodeType === currNodeType){
} else if (nodeType === currNodeType) {
prevIndex = prevIndex + 1;
output[i] = prevIndex;
}
prevNodeType = currNodeType;
}
return output;
@ -755,14 +750,13 @@ class EpubCFI {
} else {
children = anchor.parentNode.childNodes;
// Inside an ignored node
if(anchor.parentNode.classList.contains(ignoreClass)) {
if (anchor.parentNode.classList.contains(ignoreClass)) {
anchor = anchor.parentNode;
children = anchor.parentNode.childNodes;
}
map = this.normalizedMap(children, TEXT_NODE, ignoreClass);
}
index = Array.prototype.indexOf.call(children, anchor);
return map[index];
@ -771,12 +765,12 @@ class EpubCFI {
stepsToXpath(steps) {
var xpath = [".", "*"];
steps.forEach(function(step){
steps.forEach(function (step) {
var position = step.index + 1;
if(step.id){
if (step.id) {
xpath.push("*[position()=" + position + " and @id='" + step.id + "']");
} else if(step.type === "text") {
} else if (step.type === "text") {
xpath.push("text()[" + position + "]");
} else {
xpath.push("*[" + position + "]");
@ -786,7 +780,6 @@ class EpubCFI {
return xpath.join("/");
}
/*
To get the last step if needed:
@ -805,12 +798,12 @@ class EpubCFI {
stepsToQuerySelector(steps) {
var query = ["html"];
steps.forEach(function(step){
steps.forEach(function (step) {
var position = step.index + 1;
if(step.id){
if (step.id) {
query.push("#" + step.id);
} else if(step.type === "text") {
} else if (step.type === "text") {
// unsupported in querySelector
// query.push("text()[" + position + "]");
} else {
@ -819,12 +812,12 @@ class EpubCFI {
});
return query.join(">");
}
textNodes(container, ignoreClass) {
return Array.prototype.slice.call(container.childNodes).
filter(function (node) {
return Array.prototype.slice
.call(container.childNodes)
.filter(function (node) {
if (node.nodeType === TEXT_NODE) {
return true;
} else if (ignoreClass && node.classList.contains(ignoreClass)) {
@ -845,26 +838,24 @@ class EpubCFI {
for (i = 0; i < len; i++) {
step = steps[i];
if(step.type === "element") {
if (step.type === "element") {
//better to get a container using id as some times step.index may not be correct
//For ex.https://github.com/futurepress/epub.js/issues/561
if(step.id) {
if (step.id) {
container = doc.getElementById(step.id);
}
else {
} else {
children = container.children || findChildren(container);
container = children[step.index];
}
} else if(step.type === "text") {
} else if (step.type === "text") {
container = this.textNodes(container, ignoreClass)[step.index];
}
if(!container) {
if (!container) {
//Break the for loop as due to incorrect index we can get error if
//container is undefined so that other functionailties works fine
//like navigation
break;
}
}
return container;
@ -875,10 +866,16 @@ class EpubCFI {
var container;
var xpath;
if(!ignoreClass && typeof doc.evaluate != "undefined") {
if (!ignoreClass && typeof doc.evaluate != "undefined") {
xpath = this.stepsToXpath(steps);
container = doc.evaluate(xpath, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
} else if(ignoreClass) {
container = doc.evaluate(
xpath,
doc,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
} else if (ignoreClass) {
container = this.walkToNode(steps, doc, ignoreClass);
} else {
container = this.walkToNode(steps, doc);
@ -888,20 +885,20 @@ class EpubCFI {
}
fixMiss(steps, offset, _doc, ignoreClass) {
var container = this.findNode(steps.slice(0,-1), _doc, ignoreClass);
var container = this.findNode(steps.slice(0, -1), _doc, ignoreClass);
var children = container.childNodes;
var map = this.normalizedMap(children, TEXT_NODE, ignoreClass);
var child;
var len;
var lastStepIndex = steps[steps.length-1].index;
var lastStepIndex = steps[steps.length - 1].index;
for (let childIndex in map) {
if (!map.hasOwnProperty(childIndex)) return;
if(map[childIndex] === lastStepIndex) {
if (map[childIndex] === lastStepIndex) {
child = children[childIndex];
len = child.textContent.length;
if(offset > len) {
if (offset > len) {
offset = offset - len;
} else {
if (child.nodeType === ELEMENT_NODE) {
@ -916,9 +913,8 @@ class EpubCFI {
return {
container: container,
offset: offset
offset: offset,
};
}
/**
@ -933,10 +929,12 @@ class EpubCFI {
var start, end, startContainer, endContainer;
var cfi = this;
var startSteps, endSteps;
var needsIgnoring = ignoreClass ? (doc.querySelector("." + ignoreClass) != null) : false;
var needsIgnoring = ignoreClass
? doc.querySelector("." + ignoreClass) != null
: false;
var missed;
if (typeof(doc.createRange) !== "undefined") {
if (typeof doc.createRange !== "undefined") {
range = doc.createRange();
} else {
range = new RangeObject();
@ -945,27 +943,42 @@ class EpubCFI {
if (cfi.range) {
start = cfi.start;
startSteps = cfi.path.steps.concat(start.steps);
startContainer = this.findNode(startSteps, doc, needsIgnoring ? ignoreClass : null);
startContainer = this.findNode(
startSteps,
doc,
needsIgnoring ? ignoreClass : null
);
end = cfi.end;
endSteps = cfi.path.steps.concat(end.steps);
endContainer = this.findNode(endSteps, doc, needsIgnoring ? ignoreClass : null);
endContainer = this.findNode(
endSteps,
doc,
needsIgnoring ? ignoreClass : null
);
} else {
start = cfi.path;
startSteps = cfi.path.steps;
startContainer = this.findNode(cfi.path.steps, doc, needsIgnoring ? ignoreClass : null);
startContainer = this.findNode(
cfi.path.steps,
doc,
needsIgnoring ? ignoreClass : null
);
}
if(startContainer) {
if (startContainer) {
try {
if(start.terminal.offset != null) {
if (start.terminal.offset != null) {
range.setStart(startContainer, start.terminal.offset);
} else {
range.setStart(startContainer, 0);
}
} catch (e) {
missed = this.fixMiss(startSteps, start.terminal.offset, doc, needsIgnoring ? ignoreClass : null);
missed = this.fixMiss(
startSteps,
start.terminal.offset,
doc,
needsIgnoring ? ignoreClass : null
);
range.setStart(missed.container, missed.offset);
}
} else {
@ -976,20 +989,22 @@ class EpubCFI {
if (endContainer) {
try {
if(end.terminal.offset != null) {
if (end.terminal.offset != null) {
range.setEnd(endContainer, end.terminal.offset);
} else {
range.setEnd(endContainer, 0);
}
} catch (e) {
missed = this.fixMiss(endSteps, cfi.end.terminal.offset, doc, needsIgnoring ? ignoreClass : null);
missed = this.fixMiss(
endSteps,
cfi.end.terminal.offset,
doc,
needsIgnoring ? ignoreClass : null
);
range.setEnd(missed.container, missed.offset);
}
}
// doc.defaultView.getSelection().addRange(range);
return range;
}
@ -1000,9 +1015,11 @@ class EpubCFI {
* @returns {boolean}
*/
isCfiString(str) {
if(typeof str === "string" &&
if (
typeof str === "string" &&
str.indexOf("epubcfi(") === 0 &&
str[str.length-1] === ")") {
str[str.length - 1] === ")"
) {
return true;
}
@ -1012,11 +1029,11 @@ class EpubCFI {
generateChapterComponent(_spineNodeIndex, _pos, id) {
var pos = parseInt(_pos),
spineNodeIndex = (_spineNodeIndex + 1) * 2,
cfi = "/"+spineNodeIndex+"/";
cfi = "/" + spineNodeIndex + "/";
cfi += (pos + 1) * 2;
if(id) {
if (id) {
cfi += "[" + id + "]";
}
@ -1041,7 +1058,6 @@ class EpubCFI {
this.path.steps = this.path.steps.concat(this.end.steps);
this.path.terminal = this.end.terminal;
}
}
}

View file

@ -1,15 +1,9 @@
import Book from "./book";
import EpubCFI from "./epubcfi";
import Rendition from "./rendition";
import Contents from "./contents";
import Layout from "./layout";
import ePub from "./epub";
import EpubCFI from "./epubcfi";
import Layout from "./layout";
import Rendition from "./rendition";
export default ePub;
export {
Book,
EpubCFI,
Rendition,
Contents,
Layout
};
export { Book, Contents, EpubCFI, Layout, Rendition };

View file

@ -1,6 +1,6 @@
import { extend } from "./utils/core";
import { EVENTS } from "./utils/constants";
import EventEmitter from "event-emitter";
import { EVENTS } from "./utils/constants";
import { extend } from "./utils/core";
/**
* Figures out the CSS values to apply for a layout
@ -15,19 +15,20 @@ class Layout {
constructor(settings) {
this.settings = settings;
this.name = settings.layout || "reflowable";
this._spread = (settings.spread === "none") ? false : true;
this._spread = settings.spread === "none" ? false : true;
this._minSpreadWidth = settings.minSpreadWidth || 800;
this._evenSpreads = settings.evenSpreads || false;
if (settings.flow === "scrolled" ||
if (
settings.flow === "scrolled" ||
settings.flow === "scrolled-continuous" ||
settings.flow === "scrolled-doc") {
settings.flow === "scrolled-doc"
) {
this._flow = "scrolled";
} else {
this._flow = "paginated";
}
this.width = 0;
this.height = 0;
this.spreadWidth = 0;
@ -47,9 +48,8 @@ class Layout {
delta: 0,
columnWidth: 0,
gap: 0,
divisor: 1
divisor: 1,
};
}
/**
@ -58,16 +58,18 @@ class Layout {
* @return {string} simplified flow
*/
flow(flow) {
if (typeof(flow) != "undefined") {
if (flow === "scrolled" ||
if (typeof flow != "undefined") {
if (
flow === "scrolled" ||
flow === "scrolled-continuous" ||
flow === "scrolled-doc") {
flow === "scrolled-doc"
) {
this._flow = "scrolled";
} else {
this._flow = "paginated";
}
// this.props.flow = this._flow;
this.update({flow: this._flow});
this.update({ flow: this._flow });
}
return this._flow;
}
@ -80,11 +82,10 @@ class Layout {
* @return {boolean} spread true | false
*/
spread(spread, min) {
if (spread) {
this._spread = (spread === "none") ? false : true;
this._spread = spread === "none" ? false : true;
// this.props.spread = this._spread;
this.update({spread: this._spread});
this.update({ spread: this._spread });
}
if (min >= 0) {
@ -100,8 +101,7 @@ class Layout {
* @param {number} _height height of the rendering
* @param {number} _gap width of the gap between columns
*/
calculate(_width, _height, _gap){
calculate(_width, _height, _gap) {
var divisor = 1;
var gap = _gap || 0;
@ -123,20 +123,24 @@ class Layout {
divisor = 1;
}
if (this.name === "reflowable" && this._flow === "paginated" && !(_gap >= 0)) {
gap = ((section % 2 === 0) ? section : section - 1);
if (
this.name === "reflowable" &&
this._flow === "paginated" &&
!(_gap >= 0)
) {
gap = section % 2 === 0 ? section : section - 1;
}
if (this.name === "pre-paginated" ) {
if (this.name === "pre-paginated") {
gap = 0;
}
//-- Double Page
if(divisor > 1) {
if (divisor > 1) {
// width = width - gap;
// columnWidth = (width - gap) / divisor;
// gap = gap / divisor;
columnWidth = (width / divisor) - gap;
columnWidth = width / divisor - gap;
pageWidth = columnWidth + gap;
} else {
columnWidth = width;
@ -147,7 +151,7 @@ class Layout {
width = columnWidth;
}
spreadWidth = (columnWidth * divisor) + gap;
spreadWidth = columnWidth * divisor + gap;
delta = width;
@ -179,9 +183,8 @@ class Layout {
delta,
columnWidth,
gap,
divisor
divisor,
});
}
/**
@ -189,13 +192,19 @@ class Layout {
* @param {Contents} contents
* @return {Promise}
*/
format(contents, section, axis){
format(contents, section, axis) {
var formating;
if (this.name === "pre-paginated") {
formating = contents.fit(this.columnWidth, this.height, section);
} else if (this._flow === "paginated") {
formating = contents.columns(this.width, this.height, this.columnWidth, this.gap, this.settings.direction);
formating = contents.columns(
this.width,
this.height,
this.columnWidth,
this.gap,
this.settings.direction
);
} else if (axis && axis === "horizontal") {
formating = contents.size(null, this.height);
} else {
@ -212,7 +221,6 @@ class Layout {
* @return {{spreads: Number, pages: Number}}
*/
count(totalLength, pageLength) {
let spreads, pages;
if (this.name === "pre-paginated") {
@ -220,19 +228,19 @@ class Layout {
pages = 1;
} else if (this._flow === "paginated") {
pageLength = pageLength || this.delta;
spreads = Math.ceil( totalLength / pageLength);
spreads = Math.ceil(totalLength / pageLength);
pages = spreads * this.divisor;
} else { // scrolled
} else {
// scrolled
pageLength = pageLength || this.height;
spreads = Math.ceil( totalLength / pageLength);
spreads = Math.ceil(totalLength / pageLength);
pages = spreads;
}
return {
spreads,
pages
pages,
};
}
/**
@ -248,7 +256,7 @@ class Layout {
}
});
if(Object.keys(props).length > 0) {
if (Object.keys(props).length > 0) {
let newProps = extend(this.props, props);
this.emit(EVENTS.LAYOUT.UPDATED, newProps, props);
}

View file

@ -1,8 +1,8 @@
import {qs, sprint, locationOf, defer} from "./utils/core";
import Queue from "./utils/queue";
import EventEmitter from "event-emitter";
import EpubCFI from "./epubcfi";
import { EVENTS } from "./utils/constants";
import EventEmitter from "event-emitter";
import { defer, locationOf, qs, sprint } from "./utils/core";
import Queue from "./utils/queue";
/**
* Find Locations for a Book
@ -22,15 +22,11 @@ class Locations {
this._locations = [];
this._locationsWords = [];
this.total = 0;
this.break = 150;
this._current = 0;
this._wordCounter = 0;
this.currentLocation = '';
this._currentCfi ='';
this.currentLocation = "";
this._currentCfi = "";
this.processingTimeout = undefined;
}
@ -40,20 +36,22 @@ class Locations {
* @return {Promise<Array<string>>} locations
*/
generate(chars) {
if (chars) {
this.break = chars;
}
this.q.pause();
this.spine.each(function(section) {
this.spine.each(
function (section) {
if (section.linear) {
this.q.enqueue(this.process.bind(this), section);
}
}.bind(this));
}.bind(this)
);
return this.q.run().then(function() {
return this.q.run().then(
function () {
this.total = this._locations.length - 1;
if (this._currentCfi) {
@ -62,33 +60,35 @@ class Locations {
return this._locations;
// console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end));
}.bind(this));
}.bind(this)
);
}
createRange () {
createRange() {
return {
startContainer: undefined,
startOffset: undefined,
endContainer: undefined,
endOffset: undefined
endOffset: undefined,
};
}
process(section) {
return section.load(this.request)
.then(function(contents) {
return section.load(this.request).then(
function (contents) {
var completed = new defer();
var locations = this.parse(contents, section.cfiBase);
this._locations = this._locations.concat(locations);
section.unload();
this.processingTimeout = setTimeout(() => completed.resolve(locations), this.pause);
this.processingTimeout = setTimeout(
() => completed.resolve(locations),
this.pause
);
return completed.promise;
}.bind(this));
}.bind(this)
);
}
parse(contents, cfiBase, chars) {
@ -99,7 +99,7 @@ class Locations {
var counter = 0;
var prev;
var _break = chars || this.break;
var parser = function(node) {
var parser = function (node) {
var len = node.length;
var dist;
var pos = 0;
@ -119,12 +119,11 @@ class Locations {
// Node is smaller than a break,
// skip over it
if(dist > len){
if (dist > len) {
counter += len;
pos = len;
}
while (pos < len) {
dist = _break - counter;
@ -139,7 +138,7 @@ class Locations {
// pos += dist;
// Gone over
if(pos + dist >= len){
if (pos + dist >= len) {
// Continue counter for next node
counter += len - pos;
// break
@ -175,7 +174,6 @@ class Locations {
return locations;
}
/**
* Load all of sections in the book to generate locations
* @param {string} startCfi start position
@ -189,26 +187,41 @@ class Locations {
this._locationsWords = [];
this._wordCounter = 0;
this.spine.each(function(section) {
this.spine.each(
function (section) {
if (section.linear) {
if (start) {
if (section.index >= start.spinePos) {
this.q.enqueue(this.processWords.bind(this), section, wordCount, start, count);
this.q.enqueue(
this.processWords.bind(this),
section,
wordCount,
start,
count
);
}
} else {
this.q.enqueue(this.processWords.bind(this), section, wordCount, start, count);
this.q.enqueue(
this.processWords.bind(this),
section,
wordCount,
start,
count
);
}
}
}.bind(this));
}.bind(this)
);
return this.q.run().then(function() {
return this.q.run().then(
function () {
if (this._currentCfi) {
this.currentLocation = this._currentCfi;
}
return this._locationsWords;
}.bind(this));
}.bind(this)
);
}
processWords(section, wordCount, startCfi, count) {
@ -216,24 +229,32 @@ class Locations {
return Promise.resolve();
}
return section.load(this.request)
.then(function(contents) {
return section.load(this.request).then(
function (contents) {
var completed = new defer();
var locations = this.parseWords(contents, section, wordCount, startCfi);
var remainingCount = count - this._locationsWords.length;
this._locationsWords = this._locationsWords.concat(locations.length >= count ? locations.slice(0, remainingCount) : locations);
this._locationsWords = this._locationsWords.concat(
locations.length >= count
? locations.slice(0, remainingCount)
: locations
);
section.unload();
this.processingTimeout = setTimeout(() => completed.resolve(locations), this.pause);
this.processingTimeout = setTimeout(
() => completed.resolve(locations),
this.pause
);
return completed.promise;
}.bind(this));
}.bind(this)
);
}
//http://stackoverflow.com/questions/18679576/counting-words-in-string
countWords(s) {
s = s.replace(/(^\s*)|(\s*$)/gi, "");//exclude start and end white-space
s = s.replace(/[ ]{2,}/gi, " ");//2 or more space to 1
s = s.replace(/(^\s*)|(\s*$)/gi, ""); //exclude start and end white-space
s = s.replace(/[ ]{2,}/gi, " "); //2 or more space to 1
s = s.replace(/\n /, "\n"); // exclude newline with a start spacing
return s.split(" ").length;
}
@ -243,14 +264,18 @@ class Locations {
var locations = [];
var doc = contents.ownerDocument;
var body = qs(doc, "body");
var prev;
var _break = wordCount;
var foundStartNode = startCfi ? startCfi.spinePos !== section.index : true;
var startNode;
if (startCfi && section.index === startCfi.spinePos) {
startNode = startCfi.findNode(startCfi.range ? startCfi.path.steps.concat(startCfi.start.steps) : startCfi.path.steps, contents.ownerDocument);
startNode = startCfi.findNode(
startCfi.range
? startCfi.path.steps.concat(startCfi.start.steps)
: startCfi.path.steps,
contents.ownerDocument
);
}
var parser = function(node) {
var parser = function (node) {
if (!foundStartNode) {
if (node === startNode) {
foundStartNode = true;
@ -280,7 +305,6 @@ class Locations {
pos = len;
}
while (pos < len) {
dist = _break - this._wordCounter;
@ -300,7 +324,6 @@ class Locations {
this._wordCounter = 0;
}
}
prev = node;
};
sprint(body, parser.bind(this));
@ -313,13 +336,13 @@ class Locations {
* @param {EpubCFI} cfi
* @return {number}
*/
locationFromCfi(cfi){
locationFromCfi(cfi) {
let loc;
if (EpubCFI.prototype.isCfiString(cfi)) {
cfi = new EpubCFI(cfi);
}
// Check if the location has not been set yet
if(this._locations.length === 0) {
if (this._locations.length === 0) {
return -1;
}
@ -338,7 +361,7 @@ class Locations {
* @return {number}
*/
percentageFromCfi(cfi) {
if(this._locations.length === 0) {
if (this._locations.length === 0) {
return null;
}
// Find closest cfi
@ -357,7 +380,7 @@ class Locations {
return 0;
}
return (loc / this.total);
return loc / this.total;
}
/**
@ -365,14 +388,14 @@ class Locations {
* @param {number} loc
* @return {EpubCFI} cfi
*/
cfiFromLocation(loc){
cfiFromLocation(loc) {
var cfi = -1;
// check that pg is an int
if(typeof loc != "number"){
if (typeof loc != "number") {
loc = parseInt(loc);
}
if(loc >= 0 && loc < this._locations.length) {
if (loc >= 0 && loc < this._locations.length) {
cfi = this._locations[loc];
}
@ -384,7 +407,7 @@ class Locations {
* @param {number} percentage
* @return {EpubCFI} cfi
*/
cfiFromPercentage(percentage){
cfiFromPercentage(percentage) {
let loc;
if (percentage > 1) {
console.warn("Normalize cfiFromPercentage value to between 0 - 1");
@ -405,7 +428,7 @@ class Locations {
* Load locations from JSON
* @param {json} locations
*/
load(locations){
load(locations) {
if (typeof locations === "string") {
this._locations = JSON.parse(locations);
} else {
@ -419,18 +442,18 @@ class Locations {
* Save locations to JSON
* @return {json}
*/
save(){
save() {
return JSON.stringify(this._locations);
}
getCurrent(){
getCurrent() {
return this._current;
}
setCurrent(curr){
setCurrent(curr) {
var loc;
if(typeof curr == "string"){
if (typeof curr == "string") {
this._currentCfi = curr;
} else if (typeof curr == "number") {
this._current = curr;
@ -438,11 +461,11 @@ class Locations {
return;
}
if(this._locations.length === 0) {
if (this._locations.length === 0) {
return;
}
if(typeof curr == "string"){
if (typeof curr == "string") {
loc = this.locationFromCfi(curr);
this._current = loc;
} else {
@ -450,7 +473,7 @@ class Locations {
}
this.emit(EVENTS.LOCATIONS.CHANGED, {
percentage: this.percentageFromLocation(loc)
percentage: this.percentageFromLocation(loc),
});
}
@ -471,11 +494,11 @@ class Locations {
/**
* Locations length
*/
length () {
length() {
return this._locations.length;
}
destroy () {
destroy() {
this.spine = undefined;
this.request = undefined;
this.pause = undefined;
@ -484,7 +507,7 @@ class Locations {
this.q = undefined;
this.epubcfi = undefined;
this._locations = undefined
this._locations = undefined;
this.total = undefined;
this.break = undefined;

View file

@ -1,8 +1,8 @@
import {extend, defer, requestAnimationFrame} from "../../utils/core";
import debounce from "lodash/debounce";
import { EVENTS } from "../../utils/constants";
import { defer, extend, requestAnimationFrame } from "../../utils/core";
import DefaultViewManager from "../default";
import Snap from "../helpers/snap";
import { EVENTS } from "../../utils/constants";
import debounce from "lodash/debounce";
class ContinuousViewManager extends DefaultViewManager {
constructor(options) {
@ -23,7 +23,7 @@ class ContinuousViewManager extends DefaultViewManager {
snap: false,
afterScrolledTimeout: 10,
allowScriptedContent: false,
allowPopups: false
allowPopups: false,
});
extend(this.settings, options.settings || {});
@ -42,26 +42,31 @@ class ContinuousViewManager extends DefaultViewManager {
height: 0,
forceEvenPages: false,
allowScriptedContent: this.settings.allowScriptedContent,
allowPopups: this.settings.allowPopups
allowPopups: this.settings.allowPopups,
};
this.scrollTop = 0;
this.scrollLeft = 0;
}
display(section, target){
return DefaultViewManager.prototype.display.call(this, section, target)
.then(function () {
display(section, target) {
return DefaultViewManager.prototype.display
.call(this, section, target)
.then(
function () {
return this.fill();
}.bind(this));
}.bind(this)
);
}
fill(_full){
fill(_full) {
var full = _full || new defer();
this.q.enqueue(() => {
this.q
.enqueue(() => {
return this.check();
}).then((result) => {
})
.then((result) => {
if (result) {
this.fill(full);
} else {
@ -72,21 +77,14 @@ class ContinuousViewManager extends DefaultViewManager {
return full.promise;
}
moveTo(offset){
// var bounds = this.stage.bounds();
// var dist = Math.floor(offset.top / bounds.height) * bounds.height;
moveTo(offset) {
var distX = 0,
distY = 0;
var offsetX = 0,
offsetY = 0;
if(!this.isPaginated) {
if (!this.isPaginated) {
distY = offset.top;
offsetY = offset.top+this.settings.offsetDelta;
} else {
distX = Math.floor(offset.left / this.layout.delta) * this.layout.delta;
offsetX = distX+this.settings.offsetDelta;
}
if (distX > 0 || distY > 0) {
@ -94,20 +92,16 @@ class ContinuousViewManager extends DefaultViewManager {
}
}
afterResized(view){
afterResized(view) {
this.emit(EVENTS.MANAGERS.RESIZE, view.section);
}
// Remove Previous Listeners if present
removeShownListeners(view){
// view.off("shown", this.afterDisplayed);
// view.off("shown", this.afterDisplayedAbove);
view.onDisplayed = function(){};
removeShownListeners(view) {
view.onDisplayed = function () {};
}
add(section){
add(section) {
var view = this.createView(section);
this.views.append(view);
@ -124,14 +118,13 @@ class ContinuousViewManager extends DefaultViewManager {
this.updateWritingMode(mode);
});
// view.on(EVENTS.VIEWS.SHOWN, this.afterDisplayed.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
view.onResize = this.afterResized.bind(this);
return view.display(this.request);
}
append(section){
append(section) {
var view = this.createView(section);
view.on(EVENTS.VIEWS.RESIZED, (bounds) => {
@ -153,7 +146,7 @@ class ContinuousViewManager extends DefaultViewManager {
return view;
}
prepend(section){
prepend(section) {
var view = this.createView(section);
view.on(EVENTS.VIEWS.RESIZED, (bounds) => {
@ -176,20 +169,21 @@ class ContinuousViewManager extends DefaultViewManager {
return view;
}
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);
}
}
update(_offset){
update(_offset) {
var container = this.bounds();
var views = this.views.all();
var viewsLength = views.length;
var visible = [];
var offset = typeof _offset != "undefined" ? _offset : (this.settings.offset || 0);
var offset =
typeof _offset != "undefined" ? _offset : this.settings.offset || 0;
var isVisible;
var view;
@ -200,16 +194,16 @@ class ContinuousViewManager extends DefaultViewManager {
isVisible = this.isVisible(view, offset, offset, container);
if(isVisible === true) {
// console.log("visible " + view.index, view.displayed);
if (isVisible === true) {
if (!view.displayed) {
let displayed = view.display(this.request)
.then(function (view) {
let displayed = view.display(this.request).then(
function (view) {
view.show();
}, (err) => {
},
(err) => {
view.hide();
});
}
);
promises.push(displayed);
} else {
view.show();
@ -217,33 +211,32 @@ class ContinuousViewManager extends DefaultViewManager {
visible.push(view);
} else {
this.q.enqueue(view.destroy.bind(view));
// console.log("hidden " + view.index, view.displayed);
clearTimeout(this.trimTimeout);
this.trimTimeout = setTimeout(function(){
this.trimTimeout = setTimeout(
function () {
this.q.enqueue(this.trim.bind(this));
}.bind(this), 250);
}.bind(this),
250
);
}
}
}
if(promises.length){
return Promise.all(promises)
.catch((err) => {
if (promises.length) {
return Promise.all(promises).catch((err) => {
updating.reject(err);
});
} else {
updating.resolve();
return updating.promise;
}
}
check(_offsetLeft, _offsetTop){
check(_offsetLeft, _offsetTop) {
var checking = new defer();
var newViews = [];
var horizontal = (this.settings.axis === "horizontal");
var horizontal = this.settings.axis === "horizontal";
var delta = this.settings.offset || 0;
if (_offsetLeft && horizontal) {
@ -254,12 +247,18 @@ class ContinuousViewManager extends DefaultViewManager {
delta = _offsetTop;
}
var bounds = this._bounds; // bounds saved this until resize
// bounds saved this until resize
var bounds = this._bounds;
let offset = horizontal ? this.scrollLeft : this.scrollTop;
let visibleLength = horizontal ? Math.floor(bounds.width) : bounds.height;
let contentLength = horizontal ? this.container.scrollWidth : this.container.scrollHeight;
let writingMode = (this.writingMode && this.writingMode.indexOf("vertical") === 0) ? "vertical" : "horizontal";
let contentLength = horizontal
? this.container.scrollWidth
: this.container.scrollHeight;
let writingMode =
this.writingMode && this.writingMode.indexOf("vertical") === 0
? "vertical"
: "horizontal";
let rtlScrollType = this.settings.rtlScrollType;
let rtl = this.settings.direction === "rtl";
@ -274,8 +273,10 @@ class ContinuousViewManager extends DefaultViewManager {
}
} else {
// Scroll offset starts at 0 and goes negative
if ((horizontal && rtl && rtlScrollType === "negative") ||
(!horizontal && rtl && rtlScrollType === "default")) {
if (
(horizontal && rtl && rtlScrollType === "negative") ||
(!horizontal && rtl && rtlScrollType === "default")
) {
offset = offset * -1;
}
}
@ -284,7 +285,7 @@ class ContinuousViewManager extends DefaultViewManager {
let first = this.views.first();
let prev = first && first.section.prev();
if(prev) {
if (prev) {
newViews.push(this.prepend(prev));
}
};
@ -293,10 +294,9 @@ class ContinuousViewManager extends DefaultViewManager {
let last = this.views.last();
let next = last && last.section.next();
if(next) {
if (next) {
newViews.push(this.append(next));
}
};
let end = offset + visibleLength + delta;
@ -310,45 +310,47 @@ class ContinuousViewManager extends DefaultViewManager {
prepend();
}
let promises = newViews.map((view) => {
return view.display(this.request);
});
if(newViews.length){
if (newViews.length) {
return Promise.all(promises)
.then(() => {
return this.check();
})
.then(() => {
.then(
() => {
// Check to see if anything new is on screen after rendering
return this.update(delta);
}, (err) => {
},
(err) => {
return err;
});
}
);
} else {
this.q.enqueue(function(){
this.q.enqueue(
function () {
this.update();
}.bind(this));
}.bind(this)
);
checking.resolve(false);
return checking.promise;
}
}
trim(){
trim() {
var task = new defer();
var displayed = this.views.displayed();
var first = displayed[0];
var last = displayed[displayed.length-1];
var last = displayed[displayed.length - 1];
var firstIndex = this.views.indexOf(first);
var lastIndex = this.views.indexOf(last);
var above = this.views.slice(0, firstIndex);
var below = this.views.slice(lastIndex+1);
var below = this.views.slice(lastIndex + 1);
// Erase all but last above
for (var i = 0; i < above.length-1; i++) {
for (var i = 0; i < above.length - 1; i++) {
this.erase(above[i], above);
}
@ -361,12 +363,11 @@ class ContinuousViewManager extends DefaultViewManager {
return task.promise;
}
erase(view, above){ //Trim
erase(view, above) {
var prevTop;
var prevLeft;
if(!this.settings.fullsize) {
if (!this.settings.fullsize) {
prevTop = this.container.scrollTop;
prevLeft = this.container.scrollLeft;
} else {
@ -378,11 +379,11 @@ class ContinuousViewManager extends DefaultViewManager {
this.views.remove(view);
if(above) {
if (above) {
if (this.settings.axis === "vertical") {
this.scrollTo(0, prevTop - bounds.height, true);
} else {
if(this.settings.direction === 'rtl') {
if (this.settings.direction === "rtl") {
if (!this.settings.fullsize) {
this.scrollTo(prevLeft, 0, true);
} else {
@ -393,35 +394,42 @@ class ContinuousViewManager extends DefaultViewManager {
}
}
}
}
addEventListeners(stage){
window.addEventListener("unload", function(e){
addEventListeners(stage) {
window.addEventListener(
"unload",
function (e) {
this.ignore = true;
// this.scrollTo(0,0);
this.destroy();
}.bind(this));
}.bind(this)
);
this.addScrollListeners();
if (this.isPaginated && this.settings.snap) {
this.snapper = new Snap(this, this.settings.snap && (typeof this.settings.snap === "object") && this.settings.snap);
this.snapper = new Snap(
this,
this.settings.snap &&
typeof this.settings.snap === "object" &&
this.settings.snap
);
}
}
addScrollListeners() {
var scroller;
this.tick = requestAnimationFrame;
let dir = this.settings.direction === "rtl" && this.settings.rtlScrollType === "default" ? -1 : 1;
let dir =
this.settings.direction === "rtl" &&
this.settings.rtlScrollType === "default"
? -1
: 1;
this.scrollDeltaVert = 0;
this.scrollDeltaHorz = 0;
if(!this.settings.fullsize) {
if (!this.settings.fullsize) {
scroller = this.container;
this.scrollTop = this.container.scrollTop;
this.scrollLeft = this.container.scrollLeft;
@ -434,16 +442,13 @@ class ContinuousViewManager extends DefaultViewManager {
this._onScroll = this.onScroll.bind(this);
scroller.addEventListener("scroll", this._onScroll);
this._scrolled = debounce(this.scrolled.bind(this), 30);
// this.tick.call(window, this.onScroll.bind(this));
this.didScroll = false;
}
removeEventListeners(){
removeEventListeners() {
var scroller;
if(!this.settings.fullsize) {
if (!this.settings.fullsize) {
scroller = this.container;
} else {
scroller = window;
@ -453,12 +458,16 @@ class ContinuousViewManager extends DefaultViewManager {
this._onScroll = undefined;
}
onScroll(){
onScroll() {
let scrollTop;
let scrollLeft;
let dir = this.settings.direction === "rtl" && this.settings.rtlScrollType === "default" ? -1 : 1;
let dir =
this.settings.direction === "rtl" &&
this.settings.rtlScrollType === "default"
? -1
: 1;
if(!this.settings.fullsize) {
if (!this.settings.fullsize) {
scrollTop = this.container.scrollTop;
scrollLeft = this.container.scrollLeft;
} else {
@ -469,104 +478,108 @@ class ContinuousViewManager extends DefaultViewManager {
this.scrollTop = scrollTop;
this.scrollLeft = scrollLeft;
if(!this.ignore) {
if (!this.ignore) {
this._scrolled();
} else {
this.ignore = false;
}
this.scrollDeltaVert += Math.abs(scrollTop-this.prevScrollTop);
this.scrollDeltaHorz += Math.abs(scrollLeft-this.prevScrollLeft);
this.scrollDeltaVert += Math.abs(scrollTop - this.prevScrollTop);
this.scrollDeltaHorz += Math.abs(scrollLeft - this.prevScrollLeft);
this.prevScrollTop = scrollTop;
this.prevScrollLeft = scrollLeft;
clearTimeout(this.scrollTimeout);
this.scrollTimeout = setTimeout(function(){
this.scrollTimeout = setTimeout(
function () {
this.scrollDeltaVert = 0;
this.scrollDeltaHorz = 0;
}.bind(this), 150);
}.bind(this),
150
);
clearTimeout(this.afterScrolled);
this.didScroll = false;
}
scrolled() {
this.q.enqueue(function() {
this.q.enqueue(
function () {
return this.check();
}.bind(this));
}.bind(this)
);
this.emit(EVENTS.MANAGERS.SCROLL, {
top: this.scrollTop,
left: this.scrollLeft
left: this.scrollLeft,
});
clearTimeout(this.afterScrolled);
this.afterScrolled = setTimeout(function () {
this.afterScrolled = setTimeout(
function () {
// Don't report scroll if we are about the snap
if (this.snapper && this.snapper.supportsTouch && this.snapper.needsSnap()) {
if (
this.snapper &&
this.snapper.supportsTouch &&
this.snapper.needsSnap()
) {
return;
}
this.emit(EVENTS.MANAGERS.SCROLLED, {
top: this.scrollTop,
left: this.scrollLeft
left: this.scrollLeft,
});
}.bind(this), this.settings.afterScrolledTimeout);
}.bind(this),
this.settings.afterScrolledTimeout
);
}
next(){
next() {
let delta =
this.layout.props.name === "pre-paginated" && this.layout.props.spread
? this.layout.props.delta * 2
: this.layout.props.delta;
let delta = this.layout.props.name === "pre-paginated" &&
this.layout.props.spread ? this.layout.props.delta * 2 : this.layout.props.delta;
if(!this.views.length) return;
if(this.isPaginated && this.settings.axis === "horizontal") {
if (!this.views.length) return;
if (this.isPaginated && this.settings.axis === "horizontal") {
this.scrollBy(delta, 0, true);
} else {
this.scrollBy(0, this.layout.height, true);
}
this.q.enqueue(function() {
this.q.enqueue(
function () {
return this.check();
}.bind(this));
}.bind(this)
);
}
prev(){
prev() {
let delta =
this.layout.props.name === "pre-paginated" && this.layout.props.spread
? this.layout.props.delta * 2
: this.layout.props.delta;
let delta = this.layout.props.name === "pre-paginated" &&
this.layout.props.spread ? this.layout.props.delta * 2 : this.layout.props.delta;
if(!this.views.length) return;
if(this.isPaginated && this.settings.axis === "horizontal") {
if (!this.views.length) return;
if (this.isPaginated && this.settings.axis === "horizontal") {
this.scrollBy(-delta, 0, true);
} else {
this.scrollBy(0, -this.layout.height, true);
}
this.q.enqueue(function() {
this.q.enqueue(
function () {
return this.check();
}.bind(this));
}.bind(this)
);
}
updateFlow(flow){
updateFlow(flow) {
if (this.rendered && this.snapper) {
this.snapper.destroy();
this.snapper = undefined;
@ -575,18 +588,22 @@ class ContinuousViewManager extends DefaultViewManager {
super.updateFlow(flow, "scroll");
if (this.rendered && this.isPaginated && this.settings.snap) {
this.snapper = new Snap(this, this.settings.snap && (typeof this.settings.snap === "object") && this.settings.snap);
this.snapper = new Snap(
this,
this.settings.snap &&
typeof this.settings.snap === "object" &&
this.settings.snap
);
}
}
destroy(){
destroy() {
super.destroy();
if (this.snapper) {
this.snapper.destroy();
}
}
}
export default ContinuousViewManager;

File diff suppressed because it is too large Load diff

View file

@ -1,36 +1,38 @@
import {extend, defer, requestAnimationFrame, prefixed} from "../../utils/core";
import { EVENTS, DOM_EVENTS } from "../../utils/constants";
import EventEmitter from "event-emitter";
import { EVENTS } from "../../utils/constants";
import { defer, extend } from "../../utils/core";
// easing equations from https://github.com/danro/easing-js/blob/master/easing.js
const PI_D2 = (Math.PI / 2);
const PI_D2 = Math.PI / 2;
const EASING_EQUATIONS = {
easeOutSine: function (pos) {
return Math.sin(pos * PI_D2);
},
easeInOutSine: function (pos) {
return (-0.5 * (Math.cos(Math.PI * pos) - 1));
return -0.5 * (Math.cos(Math.PI * pos) - 1);
},
easeInOutQuint: function (pos) {
if ((pos /= 0.5) < 1) {
return 0.5 * Math.pow(pos, 5);
}
return 0.5 * (Math.pow((pos - 2), 5) + 2);
return 0.5 * (Math.pow(pos - 2, 5) + 2);
},
easeInCubic: function(pos) {
easeInCubic: function (pos) {
return Math.pow(pos, 3);
}
},
};
class Snap {
constructor(manager, options) {
this.settings = extend({
this.settings = extend(
{
duration: 80,
minVelocity: 0.2,
minDistance: 10,
easing: EASING_EQUATIONS['easeInCubic']
}, options || {});
easing: EASING_EQUATIONS["easeInCubic"],
},
options || {}
);
this.supportsTouch = this.supportsTouch();
@ -55,8 +57,6 @@ class Snap {
this.element.style["WebkitOverflowScrolling"] = "touch";
}
// this.overflow = this.manager.overflow;
// set lookahead offset to page width
this.manager.settings.offset = this.layout.width;
this.manager.settings.afterScrolledTimeout = this.settings.duration * 2;
@ -72,7 +72,6 @@ class Snap {
this.resizeCanceler = false;
this.snapping = false;
this.scrollLeft;
this.scrollTop;
@ -87,7 +86,10 @@ class Snap {
}
supportsTouch() {
if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
if (
"ontouchstart" in window ||
(window.DocumentTouch && document instanceof DocumentTouch)
) {
return true;
}
@ -104,44 +106,56 @@ class Snap {
addListeners() {
this._onResize = this.onResize.bind(this);
window.addEventListener('resize', this._onResize);
window.addEventListener("resize", this._onResize);
this._onScroll = this.onScroll.bind(this);
this.scroller.addEventListener('scroll', this._onScroll);
this.scroller.addEventListener("scroll", this._onScroll);
this._onTouchStart = this.onTouchStart.bind(this);
this.scroller.addEventListener('touchstart', this._onTouchStart, { passive: true });
this.on('touchstart', this._onTouchStart);
this.scroller.addEventListener("touchstart", this._onTouchStart, {
passive: true,
});
this.on("touchstart", this._onTouchStart);
this._onTouchMove = this.onTouchMove.bind(this);
this.scroller.addEventListener('touchmove', this._onTouchMove, { passive: true });
this.on('touchmove', this._onTouchMove);
this.scroller.addEventListener("touchmove", this._onTouchMove, {
passive: true,
});
this.on("touchmove", this._onTouchMove);
this._onTouchEnd = this.onTouchEnd.bind(this);
this.scroller.addEventListener('touchend', this._onTouchEnd, { passive: true });
this.on('touchend', this._onTouchEnd);
this.scroller.addEventListener("touchend", this._onTouchEnd, {
passive: true,
});
this.on("touchend", this._onTouchEnd);
this._afterDisplayed = this.afterDisplayed.bind(this);
this.manager.on(EVENTS.MANAGERS.ADDED, this._afterDisplayed);
}
removeListeners() {
window.removeEventListener('resize', this._onResize);
window.removeEventListener("resize", this._onResize);
this._onResize = undefined;
this.scroller.removeEventListener('scroll', this._onScroll);
this.scroller.removeEventListener("scroll", this._onScroll);
this._onScroll = undefined;
this.scroller.removeEventListener('touchstart', this._onTouchStart, { passive: true });
this.off('touchstart', this._onTouchStart);
this.scroller.removeEventListener("touchstart", this._onTouchStart, {
passive: true,
});
this.off("touchstart", this._onTouchStart);
this._onTouchStart = undefined;
this.scroller.removeEventListener('touchmove', this._onTouchMove, { passive: true });
this.off('touchmove', this._onTouchMove);
this.scroller.removeEventListener("touchmove", this._onTouchMove, {
passive: true,
});
this.off("touchmove", this._onTouchMove);
this._onTouchMove = undefined;
this.scroller.removeEventListener('touchend', this._onTouchEnd, { passive: true });
this.off('touchend', this._onTouchEnd);
this.scroller.removeEventListener("touchend", this._onTouchEnd, {
passive: true,
});
this.off("touchend", this._onTouchEnd);
this._onTouchEnd = undefined;
this.manager.off(EVENTS.MANAGERS.ADDED, this._afterDisplayed);
@ -155,7 +169,7 @@ class Snap {
});
}
triggerViewEvent(e, contents){
triggerViewEvent(e, contents) {
this.emit(e.type, e, contents);
}
@ -194,7 +208,6 @@ class Snap {
this.touchCanceler = true;
if (!this.fullsize && deltaY < 10) {
this.element.scrollLeft -= screenX - this.endTouchX;
}
@ -229,10 +242,10 @@ class Snap {
wasSwiped() {
let snapWidth = this.layout.pageWidth * this.layout.divisor;
let distance = (this.endTouchX - this.startTouchX);
let distance = this.endTouchX - this.startTouchX;
let absolute = Math.abs(distance);
let time = this.endTime - this.startTime;
let velocity = (distance / time);
let velocity = distance / time;
let minVelocity = this.settings.minVelocity;
if (absolute <= this.settings.minDistance || absolute >= snapWidth) {
@ -251,16 +264,16 @@ class Snap {
needsSnap() {
let left = this.scrollLeft;
let snapWidth = this.layout.pageWidth * this.layout.divisor;
return (left % snapWidth) !== 0;
return left % snapWidth !== 0;
}
snap(howMany=0) {
snap(howMany = 0) {
let left = this.scrollLeft;
let snapWidth = this.layout.pageWidth * this.layout.divisor;
let snapTo = Math.round(left / snapWidth) * snapWidth;
if (howMany) {
snapTo += (howMany * snapWidth);
snapTo += howMany * snapWidth;
}
return this.smoothScrollTo(snapTo);
@ -272,16 +285,13 @@ class Snap {
const startTime = this.now();
const duration = this.settings.duration;
const easing = this.settings.easing;
this.snapping = true;
// add animation loop
function tick() {
const now = this.now();
const time = Math.min(1, ((now - startTime) / duration));
const timeFunction = easing(time);
const time = Math.min(1, (now - startTime) / duration);
if (this.touchCanceler || this.resizeCanceler) {
this.resizeCanceler = false;
@ -292,7 +302,7 @@ class Snap {
if (time < 1) {
window.requestAnimationFrame(tick.bind(this));
this.scrollTo(start + ((destination - start) * time), 0);
this.scrollTo(start + (destination - start) * time, 0);
} else {
this.scrollTo(destination, 0);
this.snapping = false;
@ -305,7 +315,7 @@ class Snap {
return deferred.promise;
}
scrollTo(left=0, top=0) {
scrollTo(left = 0, top = 0) {
if (this.fullsize) {
window.scroll(left, top);
} else {
@ -315,7 +325,9 @@ class Snap {
}
now() {
return ('now' in window.performance) ? performance.now() : new Date().getTime();
return "now" in window.performance
? performance.now()
: new Date().getTime();
}
destroy() {

View file

@ -1,5 +1,11 @@
import {uuid, isNumber, isElement, windowBounds, extend} from "../../utils/core";
import throttle from 'lodash/throttle'
import throttle from "lodash/throttle";
import {
extend,
isElement,
isNumber,
uuid,
windowBounds,
} from "../../utils/core";
class Stage {
constructor(_options) {
@ -8,30 +14,29 @@ class Stage {
this.container = this.create(this.settings);
if(this.settings.hidden) {
if (this.settings.hidden) {
this.wrapper = this.wrap(this.container);
}
}
/*
* Creates an element to render to.
* Resizes to passed width and height or to the elements size
*/
create(options){
let height = options.height;// !== false ? options.height : "100%";
let width = options.width;// !== false ? options.width : "100%";
create(options) {
let height = options.height;
let width = options.width;
let overflow = options.overflow || false;
let axis = options.axis || "vertical";
let direction = options.direction;
extend(this.settings, options);
if(options.height && isNumber(options.height)) {
if (options.height && isNumber(options.height)) {
height = options.height + "px";
}
if(options.width && isNumber(options.width)) {
if (options.width && isNumber(options.width)) {
width = options.width + "px";
}
@ -42,24 +47,22 @@ class Stage {
container.classList.add("epub-container");
// Style Element
// container.style.fontSize = "0";
container.style.wordSpacing = "0";
container.style.lineHeight = "0";
container.style.verticalAlign = "top";
container.style.position = "relative";
if(axis === "horizontal") {
// container.style.whiteSpace = "nowrap";
if (axis === "horizontal") {
container.style.display = "flex";
container.style.flexDirection = "row";
container.style.flexWrap = "nowrap";
}
if(width){
if (width) {
container.style.width = width;
}
if(height){
if (height) {
container.style.height = height;
}
@ -99,33 +102,31 @@ class Stage {
return wrapper;
}
getElement(_element){
getElement(_element) {
var element;
if(isElement(_element)) {
if (isElement(_element)) {
element = _element;
} else if (typeof _element === "string") {
element = document.getElementById(_element);
}
if(!element){
if (!element) {
throw new Error("Not an Element");
}
return element;
}
attachTo(what){
attachTo(what) {
var element = this.getElement(what);
var base;
if(!element){
if (!element) {
return;
}
if(this.settings.hidden) {
if (this.settings.hidden) {
base = this.wrapper;
} else {
base = this.container;
@ -136,39 +137,40 @@ class Stage {
this.element = element;
return element;
}
getContainer() {
return this.container;
}
onResize(func){
onResize(func) {
// Only listen to window for resize event if width and height are not fixed.
// This applies if it is set to a percent or auto.
if(!isNumber(this.settings.width) ||
!isNumber(this.settings.height) ) {
if (!isNumber(this.settings.width) || !isNumber(this.settings.height)) {
this.resizeFunc = throttle(func, 50);
window.addEventListener("resize", this.resizeFunc, false);
}
}
onOrientationChange(func){
onOrientationChange(func) {
this.orientationChangeFunc = func;
window.addEventListener("orientationchange", this.orientationChangeFunc, false);
window.addEventListener(
"orientationchange",
this.orientationChangeFunc,
false
);
}
size(width, height){
size(width, height) {
var bounds;
let _width = width || this.settings.width;
let _height = height || this.settings.height;
// If width or height are set to false, inherit them from containing element
if(width === null) {
if (width === null) {
bounds = this.element.getBoundingClientRect();
if(bounds.width) {
if (bounds.width) {
width = Math.floor(bounds.width);
this.container.style.width = width + "px";
}
@ -180,14 +182,13 @@ class Stage {
}
}
if(height === null) {
if (height === null) {
bounds = bounds || this.element.getBoundingClientRect();
if(bounds.height) {
if (bounds.height) {
height = bounds.height;
this.container.style.height = height + "px";
}
} else {
if (isNumber(height)) {
this.container.style.height = height + "px";
@ -196,11 +197,11 @@ class Stage {
}
}
if(!isNumber(width)) {
if (!isNumber(width)) {
width = this.container.clientWidth;
}
if(!isNumber(height)) {
if (!isNumber(height)) {
height = this.container.clientHeight;
}
@ -210,7 +211,7 @@ class Stage {
left: parseFloat(this.containerStyles["padding-left"]) || 0,
right: parseFloat(this.containerStyles["padding-right"]) || 0,
top: parseFloat(this.containerStyles["padding-top"]) || 0,
bottom: parseFloat(this.containerStyles["padding-bottom"]) || 0
bottom: parseFloat(this.containerStyles["padding-bottom"]) || 0,
};
// Bounds not set, get them from window
@ -220,47 +221,37 @@ class Stage {
left: parseFloat(bodyStyles["padding-left"]) || 0,
right: parseFloat(bodyStyles["padding-right"]) || 0,
top: parseFloat(bodyStyles["padding-top"]) || 0,
bottom: parseFloat(bodyStyles["padding-bottom"]) || 0
bottom: parseFloat(bodyStyles["padding-bottom"]) || 0,
};
if (!_width) {
width = _windowBounds.width -
bodyPadding.left -
bodyPadding.right;
width = _windowBounds.width - bodyPadding.left - bodyPadding.right;
}
if ((this.settings.fullsize && !_height) || !_height) {
height = _windowBounds.height -
bodyPadding.top -
bodyPadding.bottom;
height = _windowBounds.height - bodyPadding.top - bodyPadding.bottom;
}
return {
width: width -
this.containerPadding.left -
this.containerPadding.right,
height: height -
this.containerPadding.top -
this.containerPadding.bottom
width: width - this.containerPadding.left - this.containerPadding.right,
height: height - this.containerPadding.top - this.containerPadding.bottom,
};
}
bounds(){
bounds() {
let box;
if (this.container.style.overflow !== "visible") {
box = this.container && this.container.getBoundingClientRect();
}
if(!box || !box.width || !box.height) {
if (!box || !box.width || !box.height) {
return windowBounds();
} else {
return box;
}
}
getSheet(){
getSheet() {
var style = document.createElement("style");
// WebKit hack --> https://davidwalsh.name/add-rules-stylesheets
@ -271,17 +262,17 @@ class Stage {
return style.sheet;
}
addStyleRules(selector, rulesArray){
addStyleRules(selector, rulesArray) {
var scope = "#" + this.id + " ";
var rules = "";
if(!this.sheet){
if (!this.sheet) {
this.sheet = this.getSheet();
}
rulesArray.forEach(function(set) {
rulesArray.forEach(function (set) {
for (var prop in set) {
if(set.hasOwnProperty(prop)) {
if (set.hasOwnProperty(prop)) {
rules += prop + ":" + set[prop] + ";";
}
}
@ -291,7 +282,7 @@ class Stage {
}
axis(axis) {
if(axis === "horizontal") {
if (axis === "horizontal") {
this.container.style.display = "flex";
this.container.style.flexDirection = "row";
this.container.style.flexWrap = "nowrap";
@ -301,16 +292,6 @@ class Stage {
this.settings.axis = axis;
}
// orientation(orientation) {
// if (orientation === "landscape") {
//
// } else {
//
// }
//
// this.orientation = orientation;
// }
direction(dir) {
if (this.container) {
this.container.dir = dir;
@ -339,23 +320,16 @@ class Stage {
}
destroy() {
var base;
if (this.element) {
if(this.settings.hidden) {
base = this.wrapper;
} else {
base = this.container;
}
if(this.element.contains(this.container)) {
if (this.element.contains(this.container)) {
this.element.removeChild(this.container);
}
window.removeEventListener("resize", this.resizeFunc);
window.removeEventListener("orientationChange", this.orientationChangeFunc);
window.removeEventListener(
"orientationChange",
this.orientationChangeFunc
);
}
}
}

View file

@ -15,7 +15,7 @@ class Views {
}
last() {
return this._views[this._views.length-1];
return this._views[this._views.length - 1];
}
indexOf(view) {
@ -30,18 +30,18 @@ class Views {
return this._views[i];
}
append(view){
append(view) {
this._views.push(view);
if(this.container){
if (this.container) {
this.container.appendChild(view.element);
}
this.length++;
return view;
}
prepend(view){
prepend(view) {
this._views.unshift(view);
if(this.container){
if (this.container) {
this.container.insertBefore(view.element, this.container.firstChild);
}
this.length++;
@ -51,9 +51,12 @@ class Views {
insert(view, index) {
this._views.splice(index, 0, view);
if(this.container){
if(index < this.container.children.length){
this.container.insertBefore(view.element, this.container.children[index]);
if (this.container) {
if (index < this.container.children.length) {
this.container.insertBefore(
view.element,
this.container.children[index]
);
} else {
this.container.appendChild(view.element);
}
@ -66,22 +69,21 @@ class Views {
remove(view) {
var index = this._views.indexOf(view);
if(index > -1) {
if (index > -1) {
this._views.splice(index, 1);
}
this.destroy(view);
this.length--;
}
destroy(view) {
if(view.displayed){
if (view.displayed) {
view.destroy();
}
if(this.container){
if (this.container) {
this.container.removeChild(view.element);
}
view = null;
@ -93,12 +95,12 @@ class Views {
return this._views.forEach.apply(this._views, arguments);
}
clear(){
clear() {
// Remove all views
var view;
var len = this.length;
if(!this.length) return;
if (!this.length) return;
for (var i = 0; i < len; i++) {
view = this._views[i];
@ -109,54 +111,52 @@ class Views {
this.length = 0;
}
find(section){
find(section) {
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed && view.section.index == section.index) {
if (view.displayed && view.section.index == section.index) {
return view;
}
}
}
displayed(){
displayed() {
var displayed = [];
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed){
if (view.displayed) {
displayed.push(view);
}
}
return displayed;
}
show(){
show() {
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed){
if (view.displayed) {
view.show();
}
}
this.hidden = false;
}
hide(){
hide() {
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed){
if (view.displayed) {
view.hide();
}
}

View file

@ -1,14 +1,24 @@
import EventEmitter from "event-emitter";
import {extend, borders, uuid, isNumber, bounds, defer, createBlobUrl, revokeBlobUrl} from "../../utils/core";
import EpubCFI from "../../epubcfi";
import { Highlight, Pane, Underline } from "marks-pane";
import Contents from "../../contents";
import EpubCFI from "../../epubcfi";
import { EVENTS } from "../../utils/constants";
import { Pane, Highlight, Underline } from "marks-pane";
import {
borders,
bounds,
createBlobUrl,
defer,
extend,
isNumber,
revokeBlobUrl,
uuid,
} from "../../utils/core";
class IframeView {
constructor(section, options) {
this.settings = extend({
ignoreClass : "",
this.settings = extend(
{
ignoreClass: "",
axis: undefined, //options.layout && options.layout.props.flow === "scrolled" ? "vertical" : "horizontal",
direction: undefined,
width: 0,
@ -18,8 +28,10 @@ class IframeView {
method: undefined,
forceRight: false,
allowScriptedContent: false,
allowPopups: false
}, options || {});
allowPopups: false,
},
options || {}
);
this.id = "epubjs-view-" + uuid();
this.section = section;
@ -48,7 +60,6 @@ class IframeView {
this.highlights = {};
this.underlines = {};
this.marks = {};
}
container(axis) {
@ -63,7 +74,7 @@ class IframeView {
element.style.position = "relative";
element.style.display = "block";
if(axis && axis == "horizontal"){
if (axis && axis == "horizontal") {
element.style.flex = "none";
} else {
element.style.flex = "initial";
@ -73,12 +84,11 @@ class IframeView {
}
create() {
if(this.iframe) {
if (this.iframe) {
return this.iframe;
}
if(!this.element) {
if (!this.element) {
this.element = this.createContainer();
}
@ -100,34 +110,18 @@ class IframeView {
}
this.iframe.setAttribute("enable-annotation", "true");
this.resizing = true;
// this.iframe.style.display = "none";
this.element.style.visibility = "hidden";
this.iframe.style.visibility = "hidden";
this.iframe.style.width = "0";
this.iframe.style.height = "0";
this._width = 0;
this._height = 0;
this.element.setAttribute("ref", this.index);
this.added = true;
this.elementBounds = bounds(this.element);
// if(width || height){
// this.resize(width, height);
// } else if(this.width && this.height){
// this.resize(this.width, this.height);
// } else {
// this.iframeBounds = bounds(this.iframe);
// }
if(("srcdoc" in this.iframe)) {
if ("srcdoc" in this.iframe) {
this.supportsSrcdoc = true;
} else {
this.supportsSrcdoc = false;
@ -141,36 +135,40 @@ class IframeView {
}
render(request, show) {
// view.onLayout = this.layout.format.bind(this.layout);
this.create();
// Fit to size of the container, apply padding
this.size();
if(!this.sectionRender) {
if (!this.sectionRender) {
this.sectionRender = this.section.render(request);
}
// Render Chain
return this.sectionRender
.then(function(contents){
.then(
function (contents) {
return this.load(contents);
}.bind(this))
.then(function(){
}.bind(this)
)
.then(
function () {
// find and report the writingMode axis
let writingMode = this.contents.writingMode();
// Set the axis based on the flow and writing mode
let axis;
if (this.settings.flow === "scrolled") {
axis = (writingMode.indexOf("vertical") === 0) ? "horizontal" : "vertical";
axis =
writingMode.indexOf("vertical") === 0 ? "horizontal" : "vertical";
} else {
axis = (writingMode.indexOf("vertical") === 0) ? "vertical" : "horizontal";
axis =
writingMode.indexOf("vertical") === 0 ? "vertical" : "horizontal";
}
if (writingMode.indexOf("vertical") === 0 && this.settings.flow === "paginated") {
if (
writingMode.indexOf("vertical") === 0 &&
this.settings.flow === "paginated"
) {
this.layout.delta = this.layout.height;
}
@ -180,7 +178,6 @@ class IframeView {
this.setWritingMode(writingMode);
this.emit(EVENTS.VIEWS.WRITING_MODE, writingMode);
// apply the layout function to the contents
this.layout.format(this.contents, this.section, this.axis);
@ -196,20 +193,22 @@ class IframeView {
}
resolve();
});
}.bind(this), function(e){
}.bind(this),
function (e) {
this.emit(EVENTS.VIEWS.LOAD_ERROR, e);
return new Promise((resolve, reject) => {
reject(e);
});
}.bind(this))
.then(function() {
}.bind(this)
)
.then(
function () {
this.emit(EVENTS.VIEWS.RENDERED, this.section);
}.bind(this));
}.bind(this)
);
}
reset () {
reset() {
if (this.iframe) {
this.iframe.style.width = "0";
this.iframe.style.height = "0";
@ -228,9 +227,9 @@ class IframeView {
var width = _width || this.settings.width;
var height = _height || this.settings.height;
if(this.layout.name === "pre-paginated") {
if (this.layout.name === "pre-paginated") {
this.lock("both", width, height);
} else if(this.settings.axis === "horizontal") {
} else if (this.settings.axis === "horizontal") {
this.lock("height", width, height);
} else {
this.lock("width", width, height);
@ -245,39 +244,28 @@ class IframeView {
var elBorders = borders(this.element);
var iframeBorders;
if(this.iframe) {
if (this.iframe) {
iframeBorders = borders(this.iframe);
} else {
iframeBorders = {width: 0, height: 0};
iframeBorders = { width: 0, height: 0 };
}
if(what == "width" && isNumber(width)){
if (what == "width" && isNumber(width)) {
this.lockedWidth = width - elBorders.width - iframeBorders.width;
// this.resize(this.lockedWidth, width); // width keeps ratio correct
}
if(what == "height" && isNumber(height)){
if (what == "height" && isNumber(height)) {
this.lockedHeight = height - elBorders.height - iframeBorders.height;
// this.resize(width, this.lockedHeight);
}
if(what === "both" &&
isNumber(width) &&
isNumber(height)){
if (what === "both" && isNumber(width) && isNumber(height)) {
this.lockedWidth = width - elBorders.width - iframeBorders.width;
this.lockedHeight = height - elBorders.height - iframeBorders.height;
// this.resize(this.lockedWidth, this.lockedHeight);
}
if(this.displayed && this.iframe) {
// this.contents.layout();
if (this.displayed && this.iframe) {
this.expand();
}
}
// Resize a single axis based on content dimensions
@ -286,47 +274,49 @@ class IframeView {
var height = this.lockedHeight;
var columns;
var textWidth, textHeight;
if(!this.iframe || this._expanding) return;
if (!this.iframe || this._expanding) return;
this._expanding = true;
if(this.layout.name === "pre-paginated") {
if (this.layout.name === "pre-paginated") {
width = this.layout.columnWidth;
height = this.layout.height;
}
// Expand Horizontally
else if(this.settings.axis === "horizontal") {
else if (this.settings.axis === "horizontal") {
// Get the width of the text
width = this.contents.textWidth();
if (width % this.layout.pageWidth > 0) {
width = Math.ceil(width / this.layout.pageWidth) * this.layout.pageWidth;
width =
Math.ceil(width / this.layout.pageWidth) * this.layout.pageWidth;
}
if (this.settings.forceEvenPages) {
columns = (width / this.layout.pageWidth);
if ( this.layout.divisor > 1 &&
columns = width / this.layout.pageWidth;
if (
this.layout.divisor > 1 &&
this.layout.name === "reflowable" &&
(columns % 2 > 0)) {
columns % 2 > 0
) {
// add a blank page
width += this.layout.pageWidth;
}
}
} // Expand Vertically
else if(this.settings.axis === "vertical") {
else if (this.settings.axis === "vertical") {
height = this.contents.textHeight();
if (this.settings.flow === "paginated" &&
height % this.layout.height > 0) {
if (
this.settings.flow === "paginated" &&
height % this.layout.height > 0
) {
height = Math.ceil(height / this.layout.height) * this.layout.height;
}
}
// Only Resize if dimensions have changed or
// if Frame is still hidden, so needs reframing
if(this._needsReframe || width != this._width || height != this._height){
if (this._needsReframe || width != this._width || height != this._height) {
this.reframe(width, height);
}
@ -336,20 +326,22 @@ class IframeView {
reframe(width, height) {
var size;
if(isNumber(width)){
if (isNumber(width)) {
this.element.style.width = width + "px";
this.iframe.style.width = width + "px";
this._width = width;
}
if(isNumber(height)){
if (isNumber(height)) {
this.element.style.height = height + "px";
this.iframe.style.height = height + "px";
this._height = height;
}
let widthDelta = this.prevBounds ? width - this.prevBounds.width : width;
let heightDelta = this.prevBounds ? height - this.prevBounds.height : height;
let heightDelta = this.prevBounds
? height - this.prevBounds.height
: height;
size = {
width: width,
@ -377,46 +369,41 @@ class IframeView {
this.prevBounds = size;
this.elementBounds = bounds(this.element);
}
load(contents) {
var loading = new defer();
var loaded = loading.promise;
if(!this.iframe) {
if (!this.iframe) {
loading.reject(new Error("No Iframe Available"));
return loaded;
}
this.iframe.onload = function(event) {
this.iframe.onload = function (event) {
this.onLoad(event, loading);
}.bind(this);
if (this.settings.method === "blobUrl") {
this.blobUrl = createBlobUrl(contents, "application/xhtml+xml");
this.iframe.src = this.blobUrl;
this.element.appendChild(this.iframe);
} else if(this.settings.method === "srcdoc"){
} else if (this.settings.method === "srcdoc") {
this.iframe.srcdoc = contents;
this.element.appendChild(this.iframe);
} else {
this.element.appendChild(this.iframe);
this.document = this.iframe.contentDocument;
if(!this.document) {
if (!this.document) {
loading.reject(new Error("No Document Available"));
return loaded;
}
this.iframe.contentDocument.open();
// For Cordova windows platform
if(window.MSApp && MSApp.execUnsafeLocalFunction) {
if (window.MSApp && MSApp.execUnsafeLocalFunction) {
var outerThis = this;
MSApp.execUnsafeLocalFunction(function () {
outerThis.iframe.contentDocument.write(contents);
@ -425,18 +412,21 @@ class IframeView {
this.iframe.contentDocument.write(contents);
}
this.iframe.contentDocument.close();
}
return loaded;
}
onLoad(event, promise) {
this.window = this.iframe.contentWindow;
this.document = this.iframe.contentDocument;
this.contents = new Contents(this.document, this.document.body, this.section.cfiBase, this.section.index);
this.contents = new Contents(
this.document,
this.document.body,
this.section.cfiBase,
this.section.index
);
this.rendering = false;
@ -451,7 +441,7 @@ class IframeView {
}
this.contents.on(EVENTS.CONTENTS.EXPAND, () => {
if(this.displayed && this.iframe) {
if (this.displayed && this.iframe) {
this.expand();
if (this.contents) {
this.layout.format(this.contents);
@ -460,7 +450,7 @@ class IframeView {
});
this.contents.on(EVENTS.CONTENTS.RESIZE, (e) => {
if(this.displayed && this.iframe) {
if (this.displayed && this.iframe) {
this.expand();
if (this.contents) {
this.layout.format(this.contents);
@ -481,21 +471,18 @@ class IframeView {
}
setAxis(axis) {
this.settings.axis = axis;
if(axis == "horizontal"){
if (axis == "horizontal") {
this.element.style.flex = "none";
} else {
this.element.style.flex = "initial";
}
this.size();
}
setWritingMode(mode) {
// this.element.style.writingMode = writingMode;
this.writingMode = mode;
}
@ -511,33 +498,29 @@ class IframeView {
var displayed = new defer();
if (!this.displayed) {
this.render(request)
.then(function () {
this.render(request).then(
function () {
this.emit(EVENTS.VIEWS.DISPLAYED, this);
this.onDisplayed(this);
this.displayed = true;
displayed.resolve(this);
}.bind(this), function (err) {
}.bind(this),
function (err) {
displayed.reject(err, this);
});
}
);
} else {
displayed.resolve(this);
}
return displayed.promise;
}
show() {
this.element.style.visibility = "visible";
if(this.iframe){
if (this.iframe) {
this.iframe.style.visibility = "visible";
// Remind Safari to redraw the iframe
@ -550,7 +533,6 @@ class IframeView {
}
hide() {
// this.iframe.style.display = "none";
this.element.style.visibility = "hidden";
this.iframe.style.visibility = "hidden";
@ -561,8 +543,8 @@ class IframeView {
offset() {
return {
top: this.element.offsetTop,
left: this.element.offsetLeft
}
left: this.element.offsetLeft,
};
}
width() {
@ -578,12 +560,11 @@ class IframeView {
}
locationOf(target) {
var parentPos = this.iframe.getBoundingClientRect();
var targetPos = this.contents.locationOf(target, this.settings.ignoreClass);
return {
"left": targetPos.left,
"top": targetPos.top
left: targetPos.left,
top: targetPos.top,
};
}
@ -596,18 +577,21 @@ class IframeView {
}
bounds(force) {
if(force || !this.elementBounds) {
if (force || !this.elementBounds) {
this.elementBounds = bounds(this.element);
}
return this.elementBounds;
}
highlight(cfiRange, data={}, cb, className = "epubjs-hl", styles = {}) {
highlight(cfiRange, data = {}, cb, className = "epubjs-hl", styles = {}) {
if (!this.contents) {
return;
}
const attributes = Object.assign({"fill": "yellow", "fill-opacity": "0.3", "mix-blend-mode": "multiply"}, styles);
const attributes = Object.assign(
{ fill: "yellow", "fill-opacity": "0.3", "mix-blend-mode": "multiply" },
styles
);
let range = this.contents.range(cfiRange);
let emitter = () => {
@ -623,7 +607,11 @@ class IframeView {
let m = new Highlight(range, className, data, attributes);
let h = this.pane.addMark(m);
this.highlights[cfiRange] = { "mark": h, "element": h.element, "listeners": [emitter, cb] };
this.highlights[cfiRange] = {
mark: h,
element: h.element,
listeners: [emitter, cb],
};
h.element.setAttribute("ref", className);
h.element.addEventListener("click", emitter);
@ -636,11 +624,18 @@ class IframeView {
return h;
}
underline(cfiRange, data={}, cb, className = "epubjs-ul", styles = {}) {
underline(cfiRange, data = {}, cb, className = "epubjs-ul", styles = {}) {
if (!this.contents) {
return;
}
const attributes = Object.assign({"stroke": "black", "stroke-opacity": "0.3", "mix-blend-mode": "multiply"}, styles);
const attributes = Object.assign(
{
stroke: "black",
"stroke-opacity": "0.3",
"mix-blend-mode": "multiply",
},
styles
);
let range = this.contents.range(cfiRange);
let emitter = () => {
this.emit(EVENTS.VIEWS.MARK_CLICKED, cfiRange, data);
@ -655,7 +650,11 @@ class IframeView {
let m = new Underline(range, className, data, attributes);
let h = this.pane.addMark(m);
this.underlines[cfiRange] = { "mark": h, "element": h.element, "listeners": [emitter, cb] };
this.underlines[cfiRange] = {
mark: h,
element: h.element,
listeners: [emitter, cb],
};
h.element.setAttribute("ref", className);
h.element.addEventListener("click", emitter);
@ -668,7 +667,7 @@ class IframeView {
return h;
}
mark(cfiRange, data={}, cb) {
mark(cfiRange, data = {}, cb) {
if (!this.contents) {
return;
}
@ -683,7 +682,7 @@ class IframeView {
return;
}
let container = range.commonAncestorContainer;
let parent = (container.nodeType === 1) ? container : container.parentNode;
let parent = container.nodeType === 1 ? container : container.parentNode;
let emitter = (e) => {
this.emit(EVENTS.VIEWS.MARK_CLICKED, cfiRange, data);
@ -692,7 +691,8 @@ class IframeView {
if (range.collapsed && container.nodeType === 1) {
range = new Range();
range.selectNodeContents(container);
} else if (range.collapsed) { // Webkit doesn't like collapsed ranges
} else if (range.collapsed) {
// Webkit doesn't like collapsed ranges
range = new Range();
range.selectNodeContents(parent);
}
@ -721,7 +721,11 @@ class IframeView {
this.element.appendChild(mark);
this.marks[cfiRange] = { "element": mark, "range": range, "listeners": [emitter, cb] };
this.marks[cfiRange] = {
element: mark,
range: range,
listeners: [emitter, cb],
};
return parent;
}
@ -729,8 +733,10 @@ class IframeView {
placeMark(element, range) {
let top, right, left;
if(this.layout.name === "pre-paginated" ||
this.settings.axis !== "horizontal") {
if (
this.layout.name === "pre-paginated" ||
this.settings.axis !== "horizontal"
) {
let pos = range.getBoundingClientRect();
top = pos.top;
right = pos.right;
@ -743,8 +749,10 @@ class IframeView {
rect = rects[i];
if (!left || rect.left < left) {
left = rect.left;
// right = rect.right;
right = Math.ceil(left / this.layout.props.pageWidth) * this.layout.props.pageWidth - (this.layout.gap / 2);
right =
Math.ceil(left / this.layout.props.pageWidth) *
this.layout.props.pageWidth -
this.layout.gap / 2;
top = rect.top;
}
}
@ -764,7 +772,7 @@ class IframeView {
if (l) {
item.element.removeEventListener("click", l);
item.element.removeEventListener("touchstart", l);
};
}
});
delete this.highlights[cfiRange];
}
@ -779,7 +787,7 @@ class IframeView {
if (l) {
item.element.removeEventListener("click", l);
item.element.removeEventListener("touchstart", l);
};
}
});
delete this.underlines[cfiRange];
}
@ -794,14 +802,13 @@ class IframeView {
if (l) {
item.element.removeEventListener("click", l);
item.element.removeEventListener("touchstart", l);
};
}
});
delete this.marks[cfiRange];
}
}
destroy() {
for (let cfiRange in this.highlights) {
this.unhighlight(cfiRange);
}
@ -818,7 +825,7 @@ class IframeView {
revokeBlobUrl(this.blobUrl);
}
if(this.displayed){
if (this.displayed) {
this.displayed = false;
this.removeListeners();
@ -840,9 +847,6 @@ class IframeView {
this._width = null;
this._height = null;
}
// this.element.style.height = "0px";
// this.element.style.width = "0px";
}
}

View file

@ -1,19 +1,31 @@
import EventEmitter from "event-emitter";
import {extend, borders, uuid, isNumber, bounds, defer, qs, parse} from "../../utils/core";
import EpubCFI from "../../epubcfi";
import Contents from "../../contents";
import EpubCFI from "../../epubcfi";
import { EVENTS } from "../../utils/constants";
import {
borders,
bounds,
defer,
extend,
isNumber,
parse,
qs,
uuid,
} from "../../utils/core";
class InlineView {
constructor(section, options) {
this.settings = extend({
ignoreClass : "",
this.settings = extend(
{
ignoreClass: "",
axis: "vertical",
width: 0,
height: 0,
layout: undefined,
globalLayoutProperties: {},
}, options || {});
},
options || {}
);
this.id = "epubjs-view:" + uuid();
this.section = section;
@ -35,27 +47,14 @@ class InlineView {
this.epubcfi = new EpubCFI();
this.layout = this.settings.layout;
// Dom events to listen for
// this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"];
}
container(axis) {
var element = document.createElement("div");
element.classList.add("epub-view");
// if(this.settings.axis === "horizontal") {
// element.style.width = "auto";
// element.style.height = "0";
// } else {
// element.style.width = "0";
// element.style.height = "auto";
// }
element.style.overflow = "hidden";
if(axis && axis == "horizontal"){
if (axis && axis == "horizontal") {
element.style.display = "inline-block";
} else {
element.style.display = "block";
@ -65,12 +64,11 @@ class InlineView {
}
create() {
if(this.frame) {
if (this.frame) {
return this.frame;
}
if(!this.element) {
if (!this.element) {
this.element = this.createContainer();
}
@ -82,11 +80,10 @@ class InlineView {
this.resizing = true;
// this.frame.style.display = "none";
this.element.style.visibility = "hidden";
this.frame.style.visibility = "hidden";
if(this.settings.axis === "horizontal") {
if (this.settings.axis === "horizontal") {
this.frame.style.width = "auto";
this.frame.style.height = "0";
} else {
@ -106,56 +103,37 @@ class InlineView {
}
render(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.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(){
}.bind(this)
)
.then(function () {}.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){
if (show !== false) {
this.show();
//}, view);
}
// this.map = new Map(view, this.layout);
//this.hooks.show.trigger(view, this);
this.emit(EVENTS.VIEWS.RENDERED, this.section);
}.bind(this))
.catch(function(e){
}.bind(this)
)
.catch(
function (e) {
this.emit(EVENTS.VIEWS.LOAD_ERROR, e);
}.bind(this));
}.bind(this)
);
}
// Determine locks base on settings
@ -163,15 +141,14 @@ class InlineView {
var width = _width || this.settings.width;
var height = _height || this.settings.height;
if(this.layout.name === "pre-paginated") {
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") {
} 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
@ -179,32 +156,28 @@ class InlineView {
var elBorders = borders(this.element);
var iframeBorders;
if(this.frame) {
if (this.frame) {
iframeBorders = borders(this.frame);
} else {
iframeBorders = {width: 0, height: 0};
iframeBorders = { width: 0, height: 0 };
}
if(what == "width" && isNumber(width)){
if (what == "width" && isNumber(width)) {
this.lockedWidth = width - elBorders.width - iframeBorders.width;
this.resize(this.lockedWidth, false); // width keeps ratio correct
}
if(what == "height" && isNumber(height)){
if (what == "height" && isNumber(height)) {
this.lockedHeight = height - elBorders.height - iframeBorders.height;
this.resize(false, this.lockedHeight);
}
if(what === "both" &&
isNumber(width) &&
isNumber(height)){
if (what === "both" && isNumber(width) && isNumber(height)) {
this.lockedWidth = width - elBorders.width - iframeBorders.width;
this.lockedHeight = height - elBorders.height - iframeBorders.height;
this.resize(this.lockedWidth, this.lockedHeight);
}
}
// Resize a single axis based on content dimensions
@ -214,21 +187,21 @@ class InlineView {
var textWidth, textHeight;
if(!this.frame || this._expanding) return;
if (!this.frame || this._expanding) return;
this._expanding = true;
// Expand Horizontally
if(this.settings.axis === "horizontal") {
if (this.settings.axis === "horizontal") {
width = this.contentWidth(textWidth);
} // Expand Vertically
else if(this.settings.axis === "vertical") {
else if (this.settings.axis === "vertical") {
height = this.contentHeight(textHeight);
}
// Only Resize if dimensions have changed or
// if Frame is still hidden, so needs reframing
if(this._needsReframe || width != this._width || height != this._height){
if (this._needsReframe || width != this._width || height != this._height) {
this.resize(width, height);
}
@ -243,17 +216,15 @@ class InlineView {
return this.frame.scrollHeight;
}
resize(width, height) {
if (!this.frame) return;
if(!this.frame) return;
if(isNumber(width)){
if (isNumber(width)) {
this.frame.style.width = width + "px";
this._width = width;
}
if(isNumber(height)){
if (isNumber(height)) {
this.frame.style.height = height + "px";
this._height = height;
}
@ -272,32 +243,13 @@ class InlineView {
this.onResize(this, size);
this.emit(EVENTS.VIEWS.RESIZED, size);
}
load(contents) {
var loading = new defer();
var loaded = loading.promise;
var doc = parse(contents, "text/html");
var body = qs(doc, "body");
/*
var srcs = doc.querySelectorAll("[src]");
Array.prototype.slice.call(srcs)
.forEach(function(item) {
var src = item.getAttribute("src");
var assetUri = URI(src);
var origin = assetUri.origin();
var absoluteUri;
if (!origin) {
absoluteUri = assetUri.absoluteTo(this.section.url);
item.src = absoluteUri;
}
}.bind(this));
*/
this.frame.innerHTML = body.innerHTML;
this.document = this.frame.ownerDocument;
@ -309,7 +261,6 @@ class InlineView {
loading.resolve(this.contents);
return loaded;
}
@ -317,12 +268,7 @@ class InlineView {
this.layout = layout;
}
resizeListenters() {
// Test size again
// clearTimeout(this.expanding);
// this.expanding = setTimeout(this.expand.bind(this), 350);
}
resizeListenters() {}
addListeners() {
//TODO: Add content listeners for expanding
@ -336,31 +282,27 @@ class InlineView {
var displayed = new defer();
if (!this.displayed) {
this.render(request).then(function () {
this.render(request).then(
function () {
this.emit(EVENTS.VIEWS.DISPLAYED, this);
this.onDisplayed(this);
this.displayed = true;
displayed.resolve(this);
}.bind(this));
}.bind(this)
);
} else {
displayed.resolve(this);
}
return displayed.promise;
}
show() {
this.element.style.visibility = "visible";
if(this.frame){
if (this.frame) {
this.frame.style.visibility = "visible";
}
@ -368,7 +310,6 @@ class InlineView {
}
hide() {
// this.frame.style.display = "none";
this.element.style.visibility = "hidden";
this.frame.style.visibility = "hidden";
@ -385,8 +326,8 @@ class InlineView {
var targetPos = this.contents.locationOf(target, this.settings.ignoreClass);
return {
"left": window.scrollX + parentPos.left + targetPos.left,
"top": window.scrollY + parentPos.top + targetPos.top
left: window.scrollX + parentPos.left + targetPos.left,
top: window.scrollY + parentPos.top + targetPos.top,
};
}
@ -399,15 +340,14 @@ class InlineView {
}
bounds() {
if(!this.elementBounds) {
if (!this.elementBounds) {
this.elementBounds = bounds(this.element);
}
return this.elementBounds;
}
destroy() {
if(this.displayed){
if (this.displayed) {
this.displayed = false;
this.removeListeners();
@ -422,8 +362,6 @@ class InlineView {
this._width = null;
this._height = null;
}
// this.element.style.height = "0px";
// this.element.style.width = "0px";
}
}

View file

@ -10,9 +10,9 @@ import { nodeBounds } from "./utils/core";
* @param {boolean} [dev] toggle developer highlighting
*/
class Mapping {
constructor(layout, direction, axis, dev=false) {
constructor(layout, direction, axis, dev = false) {
this.layout = layout;
this.horizontal = (axis === "horizontal") ? true : false;
this.horizontal = axis === "horizontal" ? true : false;
this.direction = direction || "ltr";
this._dev = dev;
}
@ -44,7 +44,7 @@ class Mapping {
result = this.rangePairToCfiPair(cfiBase, {
start: this.findStart(root, start, end),
end: this.findEnd(root, start, end)
end: this.findEnd(root, start, end),
});
if (this._dev === true) {
@ -74,38 +74,43 @@ class Mapping {
// IE11 has strange issue, if root is text node IE throws exception on
// calling treeWalker.nextNode(), saying
// Unexpected call to method or property access instead of returning null value
if(root && root.nodeType === Node.TEXT_NODE) {
if (root && root.nodeType === Node.TEXT_NODE) {
return;
}
// safeFilter is required so that it can work in IE as filter is a function for IE
// and for other browser filter is an object.
var filter = {
acceptNode: function(node) {
acceptNode: function (node) {
if (node.data.trim().length > 0) {
return NodeFilter.FILTER_ACCEPT;
} else {
return NodeFilter.FILTER_REJECT;
}
}
},
};
var safeFilter = filter.acceptNode;
safeFilter.acceptNode = filter.acceptNode;
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, safeFilter, false);
var treeWalker = document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
safeFilter,
false
);
var node;
var result;
while ((node = treeWalker.nextNode())) {
result = func(node);
if(result) break;
if (result) break;
}
return result;
}
findRanges(view){
findRanges(view) {
var columns = [];
var scrollWidth = view.contents.scrollWidth();
var spreads = Math.ceil( scrollWidth / this.layout.spreadWidth);
var spreads = Math.ceil(scrollWidth / this.layout.spreadWidth);
var count = spreads * this.layout.divisor;
var columnWidth = this.layout.columnWidth;
var gap = this.layout.gap;
@ -113,10 +118,10 @@ class Mapping {
for (var i = 0; i < count.pages; i++) {
start = (columnWidth + gap) * i;
end = (columnWidth * (i+1)) + (gap * i);
end = columnWidth * (i + 1) + gap * i;
columns.push({
start: this.findStart(view.document.body, start, end),
end: this.findEnd(view.document.body, start, end)
end: this.findEnd(view.document.body, start, end),
});
}
@ -131,30 +136,26 @@ class Mapping {
* @param {number} end position to end at
* @return {Range}
*/
findStart(root, start, end){
findStart(root, start, end) {
var stack = [root];
var $el;
var found;
var $prev = root;
while (stack.length) {
$el = stack.shift();
found = this.walk($el, (node) => {
var left, right, top, bottom;
var elPos;
var elRange;
elPos = nodeBounds(node);
if (this.horizontal && this.direction === "ltr") {
left = this.horizontal ? elPos.left : elPos.top;
right = this.horizontal ? elPos.right : elPos.bottom;
if( left >= start && left <= end ) {
if (left >= start && left <= end) {
return node;
} else if (right > start) {
return node;
@ -162,13 +163,11 @@ class Mapping {
$prev = node;
stack.push(node);
}
} else if (this.horizontal && this.direction === "rtl") {
left = elPos.left;
right = elPos.right;
if( right <= end && right >= start ) {
if (right <= end && right >= start) {
return node;
} else if (left < end) {
return node;
@ -176,13 +175,11 @@ class Mapping {
$prev = node;
stack.push(node);
}
} else {
top = elPos.top;
bottom = elPos.bottom;
if( top >= start && top <= end ) {
if (top >= start && top <= end) {
return node;
} else if (bottom > start) {
return node;
@ -190,16 +187,12 @@ class Mapping {
$prev = node;
stack.push(node);
}
}
});
if(found) {
if (found) {
return this.findTextStartRange(found, start, end);
}
}
// Return last element
@ -214,75 +207,63 @@ class Mapping {
* @param {number} end position to end at
* @return {Range}
*/
findEnd(root, start, end){
findEnd(root, start, end) {
var stack = [root];
var $el;
var $prev = root;
var found;
while (stack.length) {
$el = stack.shift();
found = this.walk($el, (node) => {
var left, right, top, bottom;
var elPos;
var elRange;
elPos = nodeBounds(node);
if (this.horizontal && this.direction === "ltr") {
left = Math.round(elPos.left);
right = Math.round(elPos.right);
if(left > end && $prev) {
if (left > end && $prev) {
return $prev;
} else if(right > end) {
} else if (right > end) {
return node;
} else {
$prev = node;
stack.push(node);
}
} else if (this.horizontal && this.direction === "rtl") {
left = Math.round(this.horizontal ? elPos.left : elPos.top);
right = Math.round(this.horizontal ? elPos.right : elPos.bottom);
if(right < start && $prev) {
if (right < start && $prev) {
return $prev;
} else if(left < start) {
} else if (left < start) {
return node;
} else {
$prev = node;
stack.push(node);
}
} else {
top = Math.round(elPos.top);
bottom = Math.round(elPos.bottom);
if(top > end && $prev) {
if (top > end && $prev) {
return $prev;
} else if(bottom > end) {
} else if (bottom > end) {
return node;
} else {
$prev = node;
stack.push(node);
}
}
});
if(found){
if (found) {
return this.findTextEndRange(found, start, end);
}
}
// end of chapter
@ -297,7 +278,7 @@ class Mapping {
* @param {number} end position to end at
* @return {Range}
*/
findTextStartRange(node, start, end){
findTextStartRange(node, start, end) {
var ranges = this.splitTextNodeIntoRanges(node);
var range;
var pos;
@ -309,30 +290,21 @@ class Mapping {
pos = range.getBoundingClientRect();
if (this.horizontal && this.direction === "ltr") {
left = pos.left;
if( left >= start ) {
if (left >= start) {
return range;
}
} else if (this.horizontal && this.direction === "rtl") {
right = pos.right;
if( right <= end ) {
if (right <= end) {
return range;
}
} else {
top = pos.top;
if( top >= start ) {
if (top >= start) {
return range;
}
}
// prev = range;
}
return ranges[0];
@ -346,7 +318,7 @@ class Mapping {
* @param {number} end position to end at
* @return {Range}
*/
findTextEndRange(node, start, end){
findTextEndRange(node, start, end) {
var ranges = this.splitTextNodeIntoRanges(node);
var prev;
var range;
@ -359,48 +331,39 @@ class Mapping {
pos = range.getBoundingClientRect();
if (this.horizontal && this.direction === "ltr") {
left = pos.left;
right = pos.right;
if(left > end && prev) {
if (left > end && prev) {
return prev;
} else if(right > end) {
} else if (right > end) {
return range;
}
} else if (this.horizontal && this.direction === "rtl") {
left = pos.left
left = pos.left;
right = pos.right;
if(right < start && prev) {
if (right < start && prev) {
return prev;
} else if(left < start) {
} else if (left < start) {
return range;
}
} else {
top = pos.top;
bottom = pos.bottom;
if(top > end && prev) {
if (top > end && prev) {
return prev;
} else if(bottom > end) {
} else if (bottom > end) {
return range;
}
}
prev = range;
}
// Ends before limit
return ranges[ranges.length-1];
return ranges[ranges.length - 1];
}
/**
@ -410,7 +373,7 @@ class Mapping {
* @param {string} [_splitter] what to split on
* @return {Range[]}
*/
splitTextNodeIntoRanges(node, _splitter){
splitTextNodeIntoRanges(node, _splitter) {
var ranges = [];
var textContent = node.textContent || "";
var text = textContent.trim();
@ -420,7 +383,7 @@ class Mapping {
var pos = text.indexOf(splitter);
if(pos === -1 || node.nodeType != Node.TEXT_NODE) {
if (pos === -1 || node.nodeType != Node.TEXT_NODE) {
range = doc.createRange();
range.selectNodeContents(node);
return [range];
@ -432,22 +395,20 @@ class Mapping {
ranges.push(range);
range = false;
while ( pos != -1 ) {
while (pos != -1) {
pos = text.indexOf(splitter, pos + 1);
if(pos > 0) {
if(range) {
if (pos > 0) {
if (range) {
range.setEnd(node, pos);
ranges.push(range);
}
range = doc.createRange();
range.setStart(node, pos+1);
range.setStart(node, pos + 1);
}
}
if(range) {
if (range) {
range.setEnd(node, text.length);
ranges.push(range);
}
@ -455,7 +416,6 @@ class Mapping {
return ranges;
}
/**
* Turn a pair of ranges into a pair of CFIs
* @private
@ -463,8 +423,7 @@ class Mapping {
* @param {object} rangePair { start: Range, end: Range }
* @return {object} { start: "epubcfi(...)", end: "epubcfi(...)" }
*/
rangePairToCfiPair(cfiBase, rangePair){
rangePairToCfiPair(cfiBase, rangePair) {
var startRange = rangePair.start;
var endRange = rangePair.end;
@ -476,12 +435,11 @@ class Mapping {
return {
start: startCfi,
end: endCfi
end: endCfi,
};
}
rangeListToCfiList(cfiBase, columns){
rangeListToCfiList(cfiBase, columns) {
var map = [];
var cifPair;
@ -489,7 +447,6 @@ class Mapping {
cifPair = this.rangePairToCfiPair(cfiBase, columns[i]);
map.push(cifPair);
}
return map;
@ -502,7 +459,7 @@ class Mapping {
*/
axis(axis) {
if (axis) {
this.horizontal = (axis === "horizontal") ? true : false;
this.horizontal = axis === "horizontal" ? true : false;
}
return this.horizontal;
}

View file

@ -1,4 +1,4 @@
import {qs, qsa, querySelectorByType, filterChildren, getParentByTagName} from "./utils/core";
import { filterChildren, qs, qsa, querySelectorByType } from "./utils/core";
/**
* Navigation Parser
@ -35,10 +35,10 @@ class Navigation {
if (!isXml) {
this.toc = this.load(xml);
} else if(html) {
} else if (html) {
this.toc = this.parseNav(xml);
this.landmarks = this.parseLandmarks(xml);
} else if(ncx){
} else if (ncx) {
this.toc = this.parseNcx(xml);
}
@ -72,7 +72,6 @@ class Navigation {
this.unpack(item.subitems);
}
}
}
/**
@ -83,13 +82,13 @@ class Navigation {
get(target) {
var index;
if(!target) {
if (!target) {
return this.toc;
}
if(target.indexOf("#") === 0) {
if (target.indexOf("#") === 0) {
index = this.tocById[target.substring(1)];
} else if(target in this.tocByHref){
} else if (target in this.tocByHref) {
index = this.tocByHref[target];
}
@ -132,7 +131,7 @@ class Navigation {
landmark(type) {
var index;
if(!type) {
if (!type) {
return this.landmarks;
}
@ -147,7 +146,7 @@ class Navigation {
* @param {document} navHtml
* @return {array} navigation list
*/
parseNav(navHtml){
parseNav(navHtml) {
var navElement = querySelectorByType(navHtml, "nav", "toc");
var list = [];
@ -191,8 +190,8 @@ class Navigation {
*/
navItem(item, parent) {
let id = item.getAttribute("id") || undefined;
let content = filterChildren(item, "a", true)
|| filterChildren(item, "span", true);
let content =
filterChildren(item, "a", true) || filterChildren(item, "span", true);
if (!content) {
return;
@ -212,11 +211,11 @@ class Navigation {
}
return {
"id": id,
"href": src,
"label": text,
"subitems" : subitems,
"parent" : parent
id: id,
href: src,
label: text,
subitems: subitems,
parent: parent,
};
}
@ -226,7 +225,7 @@ class Navigation {
* @param {document} navHtml
* @return {array} landmarks list
*/
parseLandmarks(navHtml){
parseLandmarks(navHtml) {
var navElement = querySelectorByType(navHtml, "nav", "landmarks");
var navItems = navElement ? qsa(navElement, "li") : [];
var length = navItems.length;
@ -234,7 +233,7 @@ class Navigation {
var list = [];
var item;
if(!navItems || length === 0) return list;
if (!navItems || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.landmarkItem(navItems[i]);
@ -253,21 +252,23 @@ class Navigation {
* @param {element} item
* @return {object} landmarkItem
*/
landmarkItem(item){
landmarkItem(item) {
let content = filterChildren(item, "a", true);
if (!content) {
return;
}
let type = content.getAttributeNS("http://www.idpf.org/2007/ops", "type") || undefined;
let type =
content.getAttributeNS("http://www.idpf.org/2007/ops", "type") ||
undefined;
let href = content.getAttribute("href") || "";
let text = content.textContent || "";
return {
"href": href,
"label": text,
"type" : type
href: href,
label: text,
type: type,
};
}
@ -277,7 +278,7 @@ class Navigation {
* @param {document} navHtml
* @return {array} navigation list
*/
parseNcx(tocXml){
parseNcx(tocXml) {
var navPoints = qsa(tocXml, "navPoint");
var length = navPoints.length;
var i;
@ -285,12 +286,12 @@ class Navigation {
var list = [];
var item, parent;
if(!navPoints || length === 0) return list;
if (!navPoints || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.ncxItem(navPoints[i]);
toc[item.id] = item;
if(!item.parent) {
if (!item.parent) {
list.push(item);
} else {
parent = toc[item.parent];
@ -307,7 +308,7 @@ class Navigation {
* @param {element} item
* @return {object} ncxItem
*/
ncxItem(item){
ncxItem(item) {
var id = item.getAttribute("id") || false,
content = qs(item, "content"),
src = content.getAttribute("src"),
@ -317,17 +318,20 @@ class Navigation {
parentNode = item.parentNode,
parent;
if(parentNode && (parentNode.nodeName === "navPoint" || parentNode.nodeName.split(':').slice(-1)[0] === "navPoint")) {
if (
parentNode &&
(parentNode.nodeName === "navPoint" ||
parentNode.nodeName.split(":").slice(-1)[0] === "navPoint")
) {
parent = parentNode.getAttribute("id");
}
return {
"id": id,
"href": src,
"label": text,
"subitems" : subitems,
"parent" : parent
id: id,
href: src,
label: text,
subitems: subitems,
parent: parent,
};
}
@ -337,7 +341,7 @@ class Navigation {
* @return {Array} navItems
*/
load(json) {
return json.map(item => {
return json.map((item) => {
item.label = item.title;
item.subitems = item.children ? this.load(item.children) : [];
return item;

View file

@ -1,4 +1,4 @@
import {qs, qsa, qsp, indexOfElementNode} from "./utils/core";
import { indexOfElementNode, qs, qsa, qsp } from "./utils/core";
/**
* Open Packaging Format Parser
@ -8,9 +8,9 @@ import {qs, qsa, qsp, indexOfElementNode} from "./utils/core";
class Packaging {
constructor(packageDocument) {
this.manifest = {};
this.navPath = '';
this.ncxPath = '';
this.coverPath = '';
this.navPath = "";
this.ncxPath = "";
this.coverPath = "";
this.spineNodeIndex = 0;
this.spine = [];
this.metadata = {};
@ -25,25 +25,25 @@ class Packaging {
* @param {document} packageDocument OPF XML
* @return {object} parsed package parts
*/
parse(packageDocument){
parse(packageDocument) {
var metadataNode, manifestNode, spineNode;
if(!packageDocument) {
if (!packageDocument) {
throw new Error("Package File Not Found");
}
metadataNode = qs(packageDocument, "metadata");
if(!metadataNode) {
if (!metadataNode) {
throw new Error("No Metadata Found");
}
manifestNode = qs(packageDocument, "manifest");
if(!manifestNode) {
if (!manifestNode) {
throw new Error("No Manifest Found");
}
spineNode = qs(packageDocument, "spine");
if(!spineNode) {
if (!spineNode) {
throw new Error("No Spine Found");
}
@ -59,16 +59,18 @@ class Packaging {
this.uniqueIdentifier = this.findUniqueIdentifier(packageDocument);
this.metadata = this.parseMetadata(metadataNode);
this.metadata.direction = spineNode.getAttribute("page-progression-direction");
this.metadata.direction = spineNode.getAttribute(
"page-progression-direction"
);
return {
"metadata" : this.metadata,
"spine" : this.spine,
"manifest" : this.manifest,
"navPath" : this.navPath,
"ncxPath" : this.ncxPath,
"coverPath": this.coverPath,
"spineNodeIndex" : this.spineNodeIndex
metadata: this.metadata,
spine: this.spine,
manifest: this.manifest,
navPath: this.navPath,
ncxPath: this.ncxPath,
coverPath: this.coverPath,
spineNodeIndex: this.spineNodeIndex,
};
}
@ -78,7 +80,7 @@ class Packaging {
* @param {node} xml
* @return {object} metadata
*/
parseMetadata(xml){
parseMetadata(xml) {
var metadata = {};
metadata.title = this.getElementText(xml, "title");
@ -99,7 +101,10 @@ class Packaging {
metadata.orientation = this.getPropertyText(xml, "rendition:orientation");
metadata.flow = this.getPropertyText(xml, "rendition:flow");
metadata.viewport = this.getPropertyText(xml, "rendition:viewport");
metadata.media_active_class = this.getPropertyText(xml, "media:active-class");
metadata.media_active_class = this.getPropertyText(
xml,
"media:active-class"
);
metadata.spread = this.getPropertyText(xml, "rendition:spread");
// metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction");
@ -112,7 +117,7 @@ class Packaging {
* @param {node} manifestXml
* @return {object} manifest
*/
parseManifest(manifestXml){
parseManifest(manifestXml) {
var manifest = {};
//-- Turn items into an array
@ -121,7 +126,7 @@ class Packaging {
var items = Array.prototype.slice.call(selected);
//-- Create an object with the id as key
items.forEach(function(item){
items.forEach(function (item) {
var id = item.getAttribute("id"),
href = item.getAttribute("href") || "",
type = item.getAttribute("media-type") || "",
@ -129,17 +134,15 @@ class Packaging {
properties = item.getAttribute("properties") || "";
manifest[id] = {
"href" : href,
href: href,
// "url" : href,
"type" : type,
"overlay" : overlay,
"properties" : properties.length ? properties.split(" ") : []
type: type,
overlay: overlay,
properties: properties.length ? properties.split(" ") : [],
};
});
return manifest;
}
/**
@ -149,7 +152,7 @@ class Packaging {
* @param {Packaging.manifest} manifest
* @return {object} spine
*/
parseSpine(spineXml, manifest){
parseSpine(spineXml, manifest) {
var spine = [];
var selected = qsa(spineXml, "itemref");
@ -158,7 +161,7 @@ class Packaging {
// var epubcfi = new EpubCFI();
//-- Add to array to maintain ordering and cross reference with manifest
items.forEach(function(item, index){
items.forEach(function (item, index) {
var idref = item.getAttribute("idref");
// var cfiBase = epubcfi.generateChapterComponent(spineNodeIndex, index, Id);
var props = item.getAttribute("properties") || "";
@ -167,13 +170,13 @@ class Packaging {
// var manifestPropArray = manifestProps.length ? manifestProps.split(" ") : [];
var itemref = {
"id" : item.getAttribute("id"),
"idref" : idref,
"linear" : item.getAttribute("linear") || "yes",
"properties" : propArray,
id: item.getAttribute("id"),
idref: idref,
linear: item.getAttribute("linear") || "yes",
properties: propArray,
// "href" : manifest[Id].href,
// "url" : manifest[Id].url,
"index" : index
index: index,
// "cfiBase" : cfiBase
};
spine.push(itemref);
@ -188,18 +191,24 @@ class Packaging {
* @param {node} packageXml
* @return {string} Unique Identifier text
*/
findUniqueIdentifier(packageXml){
var uniqueIdentifierId = packageXml.documentElement.getAttribute("unique-identifier");
if (! uniqueIdentifierId) {
findUniqueIdentifier(packageXml) {
var uniqueIdentifierId =
packageXml.documentElement.getAttribute("unique-identifier");
if (!uniqueIdentifierId) {
return "";
}
var identifier = packageXml.getElementById(uniqueIdentifierId);
if (! identifier) {
if (!identifier) {
return "";
}
if (identifier.localName === "identifier" && identifier.namespaceURI === "http://purl.org/dc/elements/1.1/") {
return identifier.childNodes.length > 0 ? identifier.childNodes[0].nodeValue.trim() : "";
if (
identifier.localName === "identifier" &&
identifier.namespaceURI === "http://purl.org/dc/elements/1.1/"
) {
return identifier.childNodes.length > 0
? identifier.childNodes[0].nodeValue.trim()
: "";
}
return "";
@ -211,11 +220,11 @@ class Packaging {
* @param {element} manifestNode
* @return {string}
*/
findNavPath(manifestNode){
findNavPath(manifestNode) {
// Find item with property "nav"
// Should catch nav regardless of order
// var node = manifestNode.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']");
var node = qsp(manifestNode, "item", {"properties":"nav"});
var node = qsp(manifestNode, "item", { properties: "nav" });
return node ? node.getAttribute("href") : false;
}
@ -227,9 +236,11 @@ class Packaging {
* @param {element} spineNode
* @return {string}
*/
findNcxPath(manifestNode, spineNode){
findNcxPath(manifestNode, spineNode) {
// var node = manifestNode.querySelector("item[media-type='application/x-dtbncx+xml']");
var node = qsp(manifestNode, "item", {"media-type":"application/x-dtbncx+xml"});
var node = qsp(manifestNode, "item", {
"media-type": "application/x-dtbncx+xml",
});
var tocId;
// If we can't find the toc by media-type then try to look for id of the item in the spine attributes as
@ -237,7 +248,7 @@ class Packaging {
// "The item that describes the NCX must be referenced by the spine toc attribute."
if (!node) {
tocId = spineNode.getAttribute("toc");
if(tocId) {
if (tocId) {
// node = manifestNode.querySelector("item[id='" + tocId + "']");
node = manifestNode.querySelector(`#${tocId}`);
}
@ -254,25 +265,19 @@ class Packaging {
* @param {node} packageXml
* @return {string} href
*/
findCoverPath(packageXml){
var pkg = qs(packageXml, "package");
var epubVersion = pkg.getAttribute("version");
findCoverPath(packageXml) {
// Try parsing cover with epub 3.
// var node = packageXml.querySelector("item[properties='cover-image']");
var node = qsp(packageXml, "item", {"properties":"cover-image"});
var node = qsp(packageXml, "item", { properties: "cover-image" });
if (node) return node.getAttribute("href");
// Fallback to epub 2.
var metaCover = qsp(packageXml, "meta", {"name":"cover"});
var metaCover = qsp(packageXml, "meta", { name: "cover" });
if (metaCover) {
var coverId = metaCover.getAttribute("content");
// var cover = packageXml.querySelector("item[id='" + coverId + "']");
var cover = packageXml.getElementById(coverId);
return cover ? cover.getAttribute("href") : "";
}
else {
} else {
return false;
}
}
@ -284,20 +289,22 @@ class Packaging {
* @param {string} tag
* @return {string} text
*/
getElementText(xml, tag){
var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag);
getElementText(xml, tag) {
var found = xml.getElementsByTagNameNS(
"http://purl.org/dc/elements/1.1/",
tag
);
var el;
if(!found || found.length === 0) return "";
if (!found || found.length === 0) return "";
el = found[0];
if(el.childNodes.length){
if (el.childNodes.length) {
return el.childNodes[0].nodeValue;
}
return "";
}
/**
@ -307,10 +314,10 @@ class Packaging {
* @param {string} property
* @return {string} text
*/
getPropertyText(xml, property){
var el = qsp(xml, "meta", {"property":property});
getPropertyText(xml, property) {
var el = qsp(xml, "meta", { property: property });
if(el && el.childNodes.length){
if (el && el.childNodes.length) {
return el.childNodes[0].nodeValue;
}
@ -326,7 +333,7 @@ class Packaging {
this.metadata = json.metadata;
let spine = json.readingOrder || json.spine;
this.spine = spine.map((item, index) =>{
this.spine = spine.map((item, index) => {
item.index = index;
item.linear = item.linear || "yes";
return item;
@ -342,20 +349,20 @@ class Packaging {
this.spineNodeIndex = 0;
this.toc = json.toc.map((item, index) =>{
this.toc = json.toc.map((item, index) => {
item.label = item.title;
return item;
});
return {
"metadata" : this.metadata,
"spine" : this.spine,
"manifest" : this.manifest,
"navPath" : this.navPath,
"ncxPath" : this.ncxPath,
"coverPath": this.coverPath,
"spineNodeIndex" : this.spineNodeIndex,
"toc" : this.toc
metadata: this.metadata,
spine: this.spine,
manifest: this.manifest,
navPath: this.navPath,
ncxPath: this.ncxPath,
coverPath: this.coverPath,
spineNodeIndex: this.spineNodeIndex,
toc: this.toc,
};
}

View file

@ -1,10 +1,10 @@
import EpubCFI from "./epubcfi";
import {
indexOfSorted,
locationOf,
qs,
qsa,
querySelectorByType,
indexOfSorted,
locationOf
} from "./utils/core";
/**
@ -28,7 +28,7 @@ class PageList {
this.pageList = this.parse(xml);
}
if(this.pageList && this.pageList.length) {
if (this.pageList && this.pageList.length) {
this.process(this.pageList);
}
}
@ -41,12 +41,11 @@ class PageList {
var html = qs(xml, "html");
var ncx = qs(xml, "ncx");
if(html) {
if (html) {
return this.parseNav(xml);
} else if(ncx){
} else if (ncx) {
return this.parseNcx(xml);
}
}
/**
@ -55,7 +54,7 @@ class PageList {
* @param {node} navHtml
* @return {PageList.item[]} list
*/
parseNav(navHtml){
parseNav(navHtml) {
var navElement = querySelectorByType(navHtml, "nav", "page-list");
var navItems = navElement ? qsa(navElement, "li") : [];
var length = navItems.length;
@ -63,7 +62,7 @@ class PageList {
var list = [];
var item;
if(!navItems || length === 0) return list;
if (!navItems || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.item(navItems[i]);
@ -109,8 +108,8 @@ class PageList {
var page = parseInt(pageText, 10);
return {
"href": href,
"page": page,
href: href,
page: page,
};
}
@ -120,7 +119,7 @@ class PageList {
* @param {node} item
* @return {object} pageListItem
*/
item(item){
item(item) {
var content = qs(item, "a"),
href = content.getAttribute("href") || "",
text = content.textContent || "",
@ -130,20 +129,20 @@ class PageList {
packageUrl,
cfi;
if(isCfi != -1) {
if (isCfi != -1) {
split = href.split("#");
packageUrl = split[0];
cfi = split.length > 1 ? split[1] : false;
return {
"cfi" : cfi,
"href" : href,
"packageUrl" : packageUrl,
"page" : page
cfi: cfi,
href: href,
packageUrl: packageUrl,
page: page,
};
} else {
return {
"href" : href,
"page" : page
href: href,
page: page,
};
}
}
@ -153,15 +152,15 @@ class PageList {
* @private
* @param {array} pageList
*/
process(pageList){
pageList.forEach(function(item){
process(pageList) {
pageList.forEach(function (item) {
this.pages.push(item.page);
if (item.cfi) {
this.locations.push(item.cfi);
}
}, this);
this.firstPage = parseInt(this.pages[0]);
this.lastPage = parseInt(this.pages[this.pages.length-1]);
this.lastPage = parseInt(this.pages[this.pages.length - 1]);
this.totalPages = this.lastPage - this.firstPage;
}
@ -170,11 +169,11 @@ class PageList {
* @param {string} cfi EpubCFI String
* @return {number} page
*/
pageFromCfi(cfi){
pageFromCfi(cfi) {
var pg = -1;
// Check if the pageList has not been set yet
if(this.locations.length === 0) {
if (this.locations.length === 0) {
return -1;
}
@ -183,7 +182,7 @@ class PageList {
// check if the cfi is in the location list
// var index = this.locations.indexOf(cfi);
var index = indexOfSorted(cfi, this.locations, this.epubcfi.compare);
if(index != -1) {
if (index != -1) {
pg = this.pages[index];
} else {
// Otherwise add it to the list of locations
@ -191,14 +190,13 @@ class PageList {
//index = EPUBJS.core.insert(cfi, this.locations, this.epubcfi.compare);
index = locationOf(cfi, this.locations, this.epubcfi.compare);
// Get the page at the location just before the new one, or return the first
pg = index-1 >= 0 ? this.pages[index-1] : this.pages[0];
if(pg !== undefined) {
pg = index - 1 >= 0 ? this.pages[index - 1] : this.pages[0];
if (pg !== undefined) {
// Add the new page in so that the locations and page array match up
//this.pages.splice(index, 0, pg);
} else {
pg = -1;
}
}
return pg;
}
@ -208,17 +206,17 @@ class PageList {
* @param {string | number} pg
* @return {string} cfi
*/
cfiFromPage(pg){
cfiFromPage(pg) {
var cfi = -1;
// check that pg is an int
if(typeof pg != "number"){
if (typeof pg != "number") {
pg = parseInt(pg);
}
// check if the cfi is in the page list
// Pages could be unsorted.
var index = this.pages.indexOf(pg);
if(index != -1) {
if (index != -1) {
cfi = this.locations[index];
}
// TODO: handle pages not in the list
@ -230,7 +228,7 @@ class PageList {
* @param {number} percent
* @return {number} page
*/
pageFromPercentage(percent){
pageFromPercentage(percent) {
var pg = Math.round(this.totalPages * percent);
return pg;
}
@ -240,7 +238,7 @@ class PageList {
* @param {number} pg the page
* @return {number} percentage
*/
percentageFromPage(pg){
percentageFromPage(pg) {
var percentage = (pg - this.firstPage) / this.totalPages;
return Math.round(percentage * 1000) / 1000;
}
@ -250,7 +248,7 @@ class PageList {
* @param {string} cfi EpubCFI String
* @return {number} percentage
*/
percentageFromCfi(cfi){
percentageFromCfi(cfi) {
var pg = this.pageFromCfi(cfi);
var percentage = this.percentageFromPage(pg);
return percentage;

View file

@ -1,21 +1,15 @@
import EventEmitter from "event-emitter";
import { extend, defer, isFloat } from "./utils/core";
import Hook from "./utils/hook";
import EpubCFI from "./epubcfi";
import Queue from "./utils/queue";
import Layout from "./layout";
// import Mapping from "./mapping";
import Themes from "./themes";
import Contents from "./contents";
import Annotations from "./annotations";
import { EVENTS, DOM_EVENTS } from "./utils/constants";
// Default Views
import IframeView from "./managers/views/iframe";
// Default View Managers
import DefaultViewManager from "./managers/default/index";
import EpubCFI from "./epubcfi";
import Layout from "./layout";
import ContinuousViewManager from "./managers/continuous/index";
import DefaultViewManager from "./managers/default/index";
import IframeView from "./managers/views/iframe";
import Themes from "./themes";
import { DOM_EVENTS, EVENTS } from "./utils/constants";
import { defer, extend, isFloat } from "./utils/core";
import Hook from "./utils/hook";
import Queue from "./utils/queue";
/**
* Displays an Epub as a series of Views for each Section.
@ -42,7 +36,6 @@ import ContinuousViewManager from "./managers/continuous/index";
*/
class Rendition {
constructor(book, options) {
this.settings = extend(this.settings || {}, {
width: null,
height: null,
@ -59,12 +52,12 @@ class Rendition {
snap: false,
defaultDirection: "ltr",
allowScriptedContent: false,
allowPopups: false
allowPopups: false,
});
extend(this.settings, options);
if (typeof(this.settings.manager) === "object") {
if (typeof this.settings.manager === "object") {
this.manager = this.settings.manager;
}
@ -209,20 +202,24 @@ class Rendition {
* Start the rendering
* @return {Promise} rendering has started
*/
start(){
if (!this.settings.layout && (this.book.package.metadata.layout === "pre-paginated" || this.book.displayOptions.fixedLayout === "true")) {
start() {
if (
!this.settings.layout &&
(this.book.package.metadata.layout === "pre-paginated" ||
this.book.displayOptions.fixedLayout === "true")
) {
this.settings.layout = "pre-paginated";
}
switch(this.book.package.metadata.spread) {
case 'none':
this.settings.spread = 'none';
switch (this.book.package.metadata.spread) {
case "none":
this.settings.spread = "none";
break;
case 'both':
case "both":
this.settings.spread = true;
break;
}
if(!this.manager) {
if (!this.manager) {
this.ViewManager = this.requireManager(this.settings.manager);
this.View = this.requireView(this.settings.view);
@ -230,14 +227,18 @@ class Rendition {
view: this.View,
queue: this.q,
request: this.book.load.bind(this.book),
settings: this.settings
settings: this.settings,
});
}
this.direction(this.book.package.metadata.direction || this.settings.defaultDirection);
this.direction(
this.book.package.metadata.direction || this.settings.defaultDirection
);
// Parse metadata to get layout props
this.settings.globalLayoutProperties = this.determineLayoutProperties(this.book.package.metadata);
this.settings.globalLayoutProperties = this.determineLayoutProperties(
this.book.package.metadata
);
this.flow(this.settings.globalLayoutProperties.flow);
@ -251,7 +252,10 @@ class Rendition {
this.manager.on(EVENTS.MANAGERS.RESIZED, this.onResized.bind(this));
// Listen for rotation
this.manager.on(EVENTS.MANAGERS.ORIENTATION_CHANGE, this.onOrientationChange.bind(this));
this.manager.on(
EVENTS.MANAGERS.ORIENTATION_CHANGE,
this.onOrientationChange.bind(this)
);
// Listen for scroll changes
this.manager.on(EVENTS.MANAGERS.SCROLLED, this.reportLocation.bind(this));
@ -273,14 +277,13 @@ class Rendition {
* @param {element} element to attach to
* @return {Promise}
*/
attachTo(element){
return this.q.enqueue(function () {
attachTo(element) {
return this.q.enqueue(
function () {
// Start rendering
this.manager.render(element, {
"width" : this.settings.width,
"height" : this.settings.height
width: this.settings.width,
height: this.settings.height,
});
/**
@ -289,9 +292,8 @@ class Rendition {
* @memberof Rendition
*/
this.emit(EVENTS.RENDITION.ATTACHED);
}.bind(this));
}.bind(this)
);
}
/**
@ -302,7 +304,7 @@ class Rendition {
* @param {string} target Url or EpubCFI
* @return {Promise}
*/
display(target){
display(target) {
if (this.displaying) {
this.displaying.resolve();
}
@ -315,15 +317,13 @@ class Rendition {
* @param {string} target Url or EpubCFI
* @return {Promise}
*/
_display(target){
_display(target) {
if (!this.book) {
return;
}
var isCfiString = this.epubcfi.isCfiString(target);
var displaying = new defer();
var displayed = displaying.promise;
var section;
var moveTo;
this.displaying = displaying;
@ -334,13 +334,13 @@ class Rendition {
section = this.book.spine.get(target);
if(!section){
if (!section) {
displaying.reject(new Error("No Section Found"));
return displayed;
}
this.manager.display(section, target)
.then(() => {
this.manager.display(section, target).then(
() => {
displaying.resolve(section);
this.displaying = undefined;
@ -352,7 +352,8 @@ class Rendition {
*/
this.emit(EVENTS.RENDITION.DISPLAYED, section);
this.reportLocation();
}, (err) => {
},
(err) => {
/**
* Emit that has been an error displaying
* @event displayError
@ -360,67 +361,23 @@ class Rendition {
* @memberof Rendition
*/
this.emit(EVENTS.RENDITION.DISPLAY_ERROR, err);
});
}
);
return displayed;
}
/*
render(view, show) {
// view.onLayout = this.layout.format.bind(this.layout);
view.create();
// Fit to size of the container, apply padding
this.manager.resizeView(view);
// Render Chain
return view.section.render(this.book.request)
.then(function(contents){
return view.load(contents);
}.bind(this))
.then(function(doc){
return this.hooks.content.trigger(view, this);
}.bind(this))
.then(function(){
this.layout.format(view.contents);
return this.hooks.layout.trigger(view, this);
}.bind(this))
.then(function(){
return view.display();
}.bind(this))
.then(function(){
return this.hooks.render.trigger(view, this);
}.bind(this))
.then(function(){
if(show !== false) {
this.q.enqueue(function(view){
view.show();
}, view);
}
// this.map = new Map(view, this.layout);
this.hooks.show.trigger(view, this);
this.trigger("rendered", view.section);
}.bind(this))
.catch(function(e){
this.trigger("loaderror", e);
}.bind(this));
}
*/
/**
* Report what section has been displayed
* @private
* @param {*} view
*/
afterDisplayed(view){
afterDisplayed(view) {
view.on(EVENTS.VIEWS.MARK_CLICKED, (cfiRange, data) =>
this.triggerMarkEvent(cfiRange, data, view.contents)
);
view.on(EVENTS.VIEWS.MARK_CLICKED, (cfiRange, data) => this.triggerMarkEvent(cfiRange, data, view.contents));
this.hooks.render.trigger(view, this)
.then(() => {
this.hooks.render.trigger(view, this).then(() => {
if (view.contents) {
this.hooks.content.trigger(view.contents, this).then(() => {
/**
@ -436,7 +393,6 @@ class Rendition {
this.emit(EVENTS.RENDITION.RENDERED, view.section, view);
}
});
}
/**
@ -444,7 +400,7 @@ class Rendition {
* @private
* @param {*} view
*/
afterRemoved(view){
afterRemoved(view) {
this.hooks.unloaded.trigger(view, this).then(() => {
/**
* Emit that a section has been removed
@ -461,8 +417,7 @@ class Rendition {
* Report resize events and display the last seen location
* @private
*/
onResized(size, epubcfi){
onResized(size, epubcfi) {
/**
* Emit that the rendition has been resized
* @event resized
@ -471,22 +426,25 @@ class Rendition {
* @param {string} epubcfi (optional)
* @memberof Rendition
*/
this.emit(EVENTS.RENDITION.RESIZED, {
this.emit(
EVENTS.RENDITION.RESIZED,
{
width: size.width,
height: size.height
}, epubcfi);
height: size.height,
},
epubcfi
);
if (this.location && this.location.start) {
this.display(epubcfi || this.location.start.cfi);
}
}
/**
* Report orientation events and display the last seen location
* @private
*/
onOrientationChange(orientation){
onOrientationChange(orientation) {
/**
* Emit that the rendition has been rotated
* @event orientationchange
@ -501,7 +459,7 @@ class Rendition {
* Usually you would be better off calling display()
* @param {object} offset
*/
moveTo(offset){
moveTo(offset) {
this.manager.moveTo(offset);
}
@ -511,7 +469,7 @@ class Rendition {
* @param {number} [height]
* @param {string} [epubcfi] (optional)
*/
resize(width, height, epubcfi){
resize(width, height, epubcfi) {
if (width) {
this.settings.width = width;
}
@ -524,7 +482,7 @@ class Rendition {
/**
* Clear all rendered views
*/
clear(){
clear() {
this.manager.clear();
}
@ -532,8 +490,9 @@ class Rendition {
* Go to the next "page" in the rendition
* @return {Promise}
*/
next(){
return this.q.enqueue(this.manager.next.bind(this.manager))
next() {
return this.q
.enqueue(this.manager.next.bind(this.manager))
.then(this.reportLocation.bind(this));
}
@ -541,8 +500,9 @@ class Rendition {
* Go to the previous "page" in the rendition
* @return {Promise}
*/
prev(){
return this.q.enqueue(this.manager.prev.bind(this.manager))
prev() {
return this.q
.enqueue(this.manager.prev.bind(this.manager))
.then(this.reportLocation.bind(this));
}
@ -553,29 +513,33 @@ class Rendition {
* @param {object} metadata
* @return {object} properties
*/
determineLayoutProperties(metadata){
determineLayoutProperties(metadata) {
var properties;
var layout = this.settings.layout || metadata.layout || "reflowable";
var spread = this.settings.spread || metadata.spread || "auto";
var orientation = this.settings.orientation || metadata.orientation || "auto";
var orientation =
this.settings.orientation || metadata.orientation || "auto";
var flow = this.settings.flow || metadata.flow || "auto";
var viewport = metadata.viewport || "";
var minSpreadWidth = this.settings.minSpreadWidth || metadata.minSpreadWidth || 800;
var minSpreadWidth =
this.settings.minSpreadWidth || metadata.minSpreadWidth || 800;
var direction = this.settings.direction || metadata.direction || "ltr";
if ((this.settings.width === 0 || this.settings.width > 0) &&
(this.settings.height === 0 || this.settings.height > 0)) {
if (
(this.settings.width === 0 || this.settings.width > 0) &&
(this.settings.height === 0 || this.settings.height > 0)
) {
// viewport = "width="+this.settings.width+", height="+this.settings.height+"";
}
properties = {
layout : layout,
spread : spread,
orientation : orientation,
flow : flow,
viewport : viewport,
minSpreadWidth : minSpreadWidth,
direction: direction
layout: layout,
spread: spread,
orientation: orientation,
flow: flow,
viewport: viewport,
minSpreadWidth: minSpreadWidth,
direction: direction,
};
return properties;
@ -586,11 +550,13 @@ class Rendition {
* (scrolled-continuous vs scrolled-doc are handled by different view managers)
* @param {string} flow
*/
flow(flow){
flow(flow) {
var _flow = flow;
if (flow === "scrolled" ||
if (
flow === "scrolled" ||
flow === "scrolled-doc" ||
flow === "scrolled-continuous") {
flow === "scrolled-continuous"
) {
_flow = "scrolled";
}
@ -622,7 +588,7 @@ class Rendition {
* Adjust the layout of the rendition to reflowable or pre-paginated
* @param {object} settings
*/
layout(settings){
layout(settings) {
if (settings) {
this._layout = new Layout(settings);
this._layout.spread(settings.spread, this.settings.minSpreadWidth);
@ -631,7 +597,7 @@ class Rendition {
this._layout.on(EVENTS.LAYOUT.UPDATED, (props, changed) => {
this.emit(EVENTS.RENDITION.LAYOUT, props, changed);
})
});
}
if (this.manager && this._layout) {
@ -646,8 +612,7 @@ class Rendition {
* @param {string} spread none | auto (TODO: implement landscape, portrait, both)
* @param {int} [min] min width to use spreads at
*/
spread(spread, min){
spread(spread, min) {
this.settings.spread = spread;
if (min) {
@ -667,8 +632,7 @@ class Rendition {
* Adjust the direction of the rendition
* @param {string} dir
*/
direction(dir){
direction(dir) {
this.settings.direction = dir || "ltr";
if (this.manager) {
@ -686,12 +650,19 @@ class Rendition {
* @fires relocated
* @fires locationChanged
*/
reportLocation(){
return this.q.enqueue(function reportedLocation(){
requestAnimationFrame(function reportedLocationAfterRAF() {
reportLocation() {
return this.q.enqueue(
function reportedLocation() {
requestAnimationFrame(
function reportedLocationAfterRAF() {
var location = this.manager.currentLocation();
if (location && location.then && typeof location.then === "function") {
location.then(function(result) {
if (
location &&
location.then &&
typeof location.then === "function"
) {
location.then(
function (result) {
let located = this.located(result);
if (!located || !located.start || !located.end) {
@ -705,11 +676,12 @@ class Rendition {
href: this.location.start.href,
start: this.location.start.cfi,
end: this.location.end.cfi,
percentage: this.location.start.percentage
percentage: this.location.start.percentage,
});
this.emit(EVENTS.RENDITION.RELOCATED, this.location);
}.bind(this));
}.bind(this)
);
} else if (location) {
let located = this.located(location);
@ -735,7 +707,7 @@ class Rendition {
href: this.location.start.href,
start: this.location.start.cfi,
end: this.location.end.cfi,
percentage: this.location.start.percentage
percentage: this.location.start.percentage,
});
/**
@ -745,21 +717,25 @@ class Rendition {
*/
this.emit(EVENTS.RENDITION.RELOCATED, this.location);
}
}.bind(this));
}.bind(this));
}.bind(this)
);
}.bind(this)
);
}
/**
* Get the Current Location object
* @return {displayedLocation | promise} location (may be a promise)
*/
currentLocation(){
currentLocation() {
var location = this.manager.currentLocation();
if (location && location.then && typeof location.then === "function") {
location.then(function(result) {
location.then(
function (result) {
let located = this.located(result);
return located;
}.bind(this));
}.bind(this)
);
} else if (location) {
let located = this.located(location);
return located;
@ -772,12 +748,12 @@ class Rendition {
* @returns {displayedLocation}
* @private
*/
located(location){
located(location) {
if (!location.length) {
return {};
}
let start = location[0];
let end = location[location.length-1];
let end = location[location.length - 1];
let located = {
start: {
@ -786,30 +762,34 @@ class Rendition {
cfi: start.mapping.start,
displayed: {
page: start.pages[0] || 1,
total: start.totalPages
}
total: start.totalPages,
},
},
end: {
index: end.index,
href: end.href,
cfi: end.mapping.end,
displayed: {
page: end.pages[end.pages.length-1] || 1,
total: end.totalPages
}
}
page: end.pages[end.pages.length - 1] || 1,
total: end.totalPages,
},
},
};
let locationStart = this.book.locations.locationFromCfi(start.mapping.start);
let locationStart = this.book.locations.locationFromCfi(
start.mapping.start
);
let locationEnd = this.book.locations.locationFromCfi(end.mapping.end);
if (locationStart != null) {
located.start.location = locationStart;
located.start.percentage = this.book.locations.percentageFromLocation(locationStart);
located.start.percentage =
this.book.locations.percentageFromLocation(locationStart);
}
if (locationEnd != null) {
located.end.location = locationEnd;
located.end.percentage = this.book.locations.percentageFromLocation(locationEnd);
located.end.percentage =
this.book.locations.percentageFromLocation(locationEnd);
}
let pageStart = this.book.pageList.pageFromCfi(start.mapping.start);
@ -822,13 +802,17 @@ class Rendition {
located.end.page = pageEnd;
}
if (end.index === this.book.spine.last().index &&
located.end.displayed.page >= located.end.displayed.total) {
if (
end.index === this.book.spine.last().index &&
located.end.displayed.page >= located.end.displayed.total
) {
located.atEnd = true;
}
if (start.index === this.book.spine.first().index &&
located.start.displayed.page === 1) {
if (
start.index === this.book.spine.first().index &&
located.start.displayed.page === 1
) {
located.atStart = true;
}
@ -838,7 +822,7 @@ class Rendition {
/**
* Remove and Clean Up the Rendition
*/
destroy(){
destroy() {
// Clear the queue
// this.q.clear();
// this.q = undefined;
@ -864,8 +848,6 @@ class Rendition {
// this.starting = undefined;
// this.started = undefined;
}
/**
@ -873,12 +855,14 @@ class Rendition {
* @private
* @param {Contents} view contents
*/
passEvents(contents){
passEvents(contents) {
DOM_EVENTS.forEach((e) => {
contents.on(e, (ev) => this.triggerViewEvent(ev, contents));
});
contents.on(EVENTS.CONTENTS.SELECTED, (e) => this.triggerSelectedEvent(e, contents));
contents.on(EVENTS.CONTENTS.SELECTED, (e) =>
this.triggerSelectedEvent(e, contents)
);
}
/**
@ -886,7 +870,7 @@ class Rendition {
* @private
* @param {event} e
*/
triggerViewEvent(e, contents){
triggerViewEvent(e, contents) {
this.emit(e.type, e, contents);
}
@ -895,7 +879,7 @@ class Rendition {
* @private
* @param {string} cfirange
*/
triggerSelectedEvent(cfirange, contents){
triggerSelectedEvent(cfirange, contents) {
/**
* Emit that a text selection has occurred
* @event selected
@ -911,7 +895,7 @@ class Rendition {
* @private
* @param {EpubCFI} cfirange
*/
triggerMarkEvent(cfiRange, data, contents){
triggerMarkEvent(cfiRange, data, contents) {
/**
* Emit that a mark was clicked
* @event markClicked
@ -929,10 +913,10 @@ class Rendition {
* @param {string} ignoreClass
* @return {range}
*/
getRange(cfi, ignoreClass){
getRange(cfi, ignoreClass) {
var _cfi = new EpubCFI(cfi);
var found = this.manager.visible().filter(function (view) {
if(_cfi.spinePos === view.index) return true;
if (_cfi.spinePos === view.index) return true;
});
// Should only every return 1 item
@ -947,37 +931,47 @@ class Rendition {
* @private
*/
adjustImages(contents) {
if (this._layout.name === "pre-paginated") {
return new Promise(function(resolve){
return new Promise(function (resolve) {
resolve();
});
}
let computed = contents.window.getComputedStyle(contents.content, null);
let height = (contents.content.offsetHeight - (parseFloat(computed.paddingTop) + parseFloat(computed.paddingBottom))) * .95;
let horizontalPadding = parseFloat(computed.paddingLeft) + parseFloat(computed.paddingRight);
let height =
(contents.content.offsetHeight -
(parseFloat(computed.paddingTop) +
parseFloat(computed.paddingBottom))) *
0.95;
let horizontalPadding =
parseFloat(computed.paddingLeft) + parseFloat(computed.paddingRight);
contents.addStylesheetRules({
"img" : {
"max-width": (this._layout.columnWidth ? (this._layout.columnWidth - horizontalPadding) + "px" : "100%") + "!important",
img: {
"max-width":
(this._layout.columnWidth
? this._layout.columnWidth - horizontalPadding + "px"
: "100%") + "!important",
"max-height": height + "px" + "!important",
"object-fit": "contain",
"page-break-inside": "avoid",
"break-inside": "avoid",
"box-sizing": "border-box"
"box-sizing": "border-box",
},
"svg" : {
"max-width": (this._layout.columnWidth ? (this._layout.columnWidth - horizontalPadding) + "px" : "100%") + "!important",
svg: {
"max-width":
(this._layout.columnWidth
? this._layout.columnWidth - horizontalPadding + "px"
: "100%") + "!important",
"max-height": height + "px" + "!important",
"page-break-inside": "avoid",
"break-inside": "avoid"
}
"break-inside": "avoid",
},
});
return new Promise(function(resolve, reject){
return new Promise(function (resolve, reject) {
// Wait to apply
setTimeout(function() {
setTimeout(function () {
resolve();
}, 1);
});
@ -987,7 +981,7 @@ class Rendition {
* Get the Contents object of each rendered view
* @returns {Contents[]}
*/
getContents () {
getContents() {
return this.manager ? this.manager.getContents() : [];
}
@ -995,7 +989,7 @@ class Rendition {
* Get the views member from the manager
* @returns {Views}
*/
views () {
views() {
let views = this.manager ? this.manager.views : undefined;
return views || [];
}
@ -1060,7 +1054,6 @@ class Rendition {
}
doc.getElementsByTagName("head")[0].appendChild(meta);
}
}
//-- Enable binding events to Renderer

View file

@ -1,9 +1,9 @@
import {substitute} from "./utils/replacements";
import {createBase64Url, createBlobUrl, blob2base64} from "./utils/core";
import Url from "./utils/url";
import path from "path-webpack";
import { blob2base64, createBase64Url, createBlobUrl } from "./utils/core";
import mime from "./utils/mime";
import Path from "./utils/path";
import path from "path-webpack";
import { substitute } from "./utils/replacements";
import Url from "./utils/url";
/**
* Handle Package Resources
@ -18,9 +18,9 @@ class Resources {
constructor(manifest, options) {
this.settings = {
replacements: (options && options.replacements) || "base64",
archive: (options && options.archive),
resolver: (options && options.resolver),
request: (options && options.request)
archive: options && options.archive,
resolver: options && options.resolver,
request: options && options.request,
};
this.process(manifest);
@ -30,10 +30,9 @@ class Resources {
* Process resources
* @param {Manifest} manifest
*/
process(manifest){
process(manifest) {
this.manifest = manifest;
this.resources = Object.keys(manifest).
map(function (key){
this.resources = Object.keys(manifest).map(function (key) {
return manifest[key];
});
@ -54,29 +53,23 @@ class Resources {
* Split resources by type
* @private
*/
split(){
split() {
// HTML
this.html = this.resources.
filter(function (item){
if (item.type === "application/xhtml+xml" ||
item.type === "text/html") {
this.html = this.resources.filter(function (item) {
if (item.type === "application/xhtml+xml" || item.type === "text/html") {
return true;
}
});
// Exclude HTML
this.assets = this.resources.
filter(function (item){
if (item.type !== "application/xhtml+xml" &&
item.type !== "text/html") {
this.assets = this.resources.filter(function (item) {
if (item.type !== "application/xhtml+xml" && item.type !== "text/html") {
return true;
}
});
// Only CSS
this.css = this.resources.
filter(function (item){
this.css = this.resources.filter(function (item) {
if (item.type === "text/css") {
return true;
}
@ -87,19 +80,18 @@ class Resources {
* Convert split resources into Urls
* @private
*/
splitUrls(){
splitUrls() {
// All Assets Urls
this.urls = this.assets.
map(function(item) {
this.urls = this.assets.map(
function (item) {
return item.href;
}.bind(this));
}.bind(this)
);
// Css Urls
this.cssUrls = this.css.map(function(item) {
this.cssUrls = this.css.map(function (item) {
return item.href;
});
}
/**
@ -107,15 +99,18 @@ class Resources {
* @param {string} url
* @return {Promise<string>} Promise resolves with url string
*/
createUrl (url) {
createUrl(url) {
var parsedUrl = new Url(url);
var mimeType = mime.lookup(parsedUrl.filename);
if (this.settings.archive) {
return this.settings.archive.createUrl(url, {"base64": (this.settings.replacements === "base64")});
return this.settings.archive.createUrl(url, {
base64: this.settings.replacements === "base64",
});
} else {
if (this.settings.replacements === "base64") {
return this.settings.request(url, 'blob')
return this.settings
.request(url, "blob")
.then((blob) => {
return blob2base64(blob);
})
@ -123,9 +118,9 @@ class Resources {
return createBase64Url(blob, mimeType);
});
} else {
return this.settings.request(url, 'blob').then((blob) => {
return this.settings.request(url, "blob").then((blob) => {
return createBlobUrl(blob, mimeType);
})
});
}
}
}
@ -134,27 +129,26 @@ class Resources {
* Create blob urls for all the assets
* @return {Promise} returns replacement urls
*/
replacements(){
replacements() {
if (this.settings.replacements === "none") {
return new Promise(function(resolve) {
return new Promise(
function (resolve) {
resolve(this.urls);
}.bind(this));
}.bind(this)
);
}
var replacements = this.urls.map( (url) => {
var replacements = this.urls.map((url) => {
var absolute = this.settings.resolver(url);
return this.createUrl(absolute).
catch((err) => {
console.error(err);
return this.createUrl(absolute).catch(() => {
return null;
});
});
return Promise.all(replacements)
.then( (replacementUrls) => {
return Promise.all(replacements).then((replacementUrls) => {
this.replacementUrls = replacementUrls.filter((url) => {
return (typeof(url) === "string");
return typeof url === "string";
});
return replacementUrls;
});
@ -167,23 +161,25 @@ class Resources {
* @param {method} [resolver]
* @return {Promise}
*/
replaceCss(archive, resolver){
replaceCss(archive, resolver) {
var replaced = [];
archive = archive || this.settings.archive;
resolver = resolver || this.settings.resolver;
this.cssUrls.forEach(function(href) {
var replacement = this.createCssFile(href, archive, resolver)
.then(function (replacementUrl) {
this.cssUrls.forEach(
function (href) {
var replacement = this.createCssFile(href, archive, resolver).then(
function (replacementUrl) {
// switch the url in the replacementUrls
var indexInUrls = this.urls.indexOf(href);
if (indexInUrls > -1) {
this.replacementUrls[indexInUrls] = replacementUrl;
}
}.bind(this))
}.bind(this)
);
replaced.push(replacement);
}.bind(this));
}.bind(this)
);
return Promise.all(replaced);
}
@ -193,11 +189,11 @@ class Resources {
* @param {string} href the original css file
* @return {Promise} returns a BlobUrl to the new CSS file or a data url
*/
createCssFile(href){
createCssFile(href) {
var newUrl;
if (path.isAbsolute(href)) {
return new Promise(function(resolve){
return new Promise(function (resolve) {
resolve();
});
}
@ -214,7 +210,7 @@ class Resources {
}
// Get asset links relative to css file
var relUrls = this.urls.map( (assetHref) => {
var relUrls = this.urls.map((assetHref) => {
var resolved = this.settings.resolver(assetHref);
var relative = new Path(absolute).relative(resolved);
@ -223,12 +219,13 @@ class Resources {
if (!textResponse) {
// file not found, don't replace
return new Promise(function(resolve){
return new Promise(function (resolve) {
resolve();
});
}
return textResponse.then( (text) => {
return textResponse.then(
(text) => {
// Replacements in the css text
text = substitute(text, relUrls, this.replacementUrls);
@ -240,13 +237,14 @@ class Resources {
}
return newUrl;
}, (err) => {
},
(err) => {
// handle response errors
return new Promise(function(resolve){
return new Promise(function (resolve) {
resolve();
});
});
}
);
}
/**
@ -255,16 +253,17 @@ class Resources {
* @param {resolver} [resolver]
* @return {string[]} array with relative Urls
*/
relativeTo(absolute, resolver){
relativeTo(absolute, resolver) {
resolver = resolver || this.settings.resolver;
// Get Urls relative to current sections
return this.urls.
map(function(href) {
return this.urls.map(
function (href) {
var resolved = resolver(href);
var relative = new Path(absolute).relative(resolved);
return relative;
}.bind(this));
}.bind(this)
);
}
/**
@ -278,9 +277,11 @@ class Resources {
return;
}
if (this.replacementUrls.length) {
return new Promise(function(resolve, reject) {
return new Promise(
function (resolve, reject) {
resolve(this.replacementUrls[indexInUrls]);
}.bind(this));
}.bind(this)
);
} else {
return this.createUrl(path);
}

View file

@ -1,10 +1,9 @@
import { defer } from "./utils/core";
import { DOMParser as XMLDOMSerializer } from "@xmldom/xmldom";
import EpubCFI from "./epubcfi";
import { defer, sprint } from "./utils/core";
import Hook from "./utils/hook";
import { sprint } from "./utils/core";
import { replaceBase } from "./utils/replacements";
import Request from "./utils/request";
import { DOMParser as XMLDOMSerializer } from "@xmldom/xmldom";
/**
* Represents a Section of the Book
@ -14,7 +13,7 @@ import { DOMParser as XMLDOMSerializer } from "@xmldom/xmldom";
* @param {object} hooks hooks for serialize and content
*/
class Section {
constructor(item, hooks){
constructor(item, hooks) {
this.idref = item.idref;
this.linear = item.linear === "yes";
this.properties = item.properties;
@ -45,27 +44,31 @@ class Section {
* @param {method} [_request] a request method to use for loading
* @return {document} a promise with the xml document
*/
load(_request){
load(_request) {
var request = _request || this.request || Request;
var loading = new defer();
var loaded = loading.promise;
if(this.contents) {
if (this.contents) {
loading.resolve(this.contents);
} else {
request(this.url)
.then(function(xml){
.then(
function (xml) {
// var directory = new Url(this.url).directory;
this.document = xml;
this.contents = xml.documentElement;
return this.hooks.content.trigger(this.document, this);
}.bind(this))
.then(function(){
}.bind(this)
)
.then(
function () {
loading.resolve(this.contents);
}.bind(this))
.catch(function(error){
}.bind(this)
)
.catch(function (error) {
loading.reject(error);
});
}
@ -77,7 +80,7 @@ class Section {
* Adds a base tag for resolving urls in the section
* @private
*/
base(){
base() {
return replaceBase(this.document, this);
}
@ -86,15 +89,17 @@ class Section {
* @param {method} [_request] a request method to use for loading
* @return {string} output a serialized XML Document
*/
render(_request){
render(_request) {
var rendering = new defer();
var rendered = rendering.promise;
this.output; // TODO: better way to return this from hooks?
this.load(_request).
then(function(contents){
var userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
var isIE = userAgent.indexOf('Trident') >= 0;
this.load(_request)
.then(
function (contents) {
var userAgent =
(typeof navigator !== "undefined" && navigator.userAgent) || "";
var isIE = userAgent.indexOf("Trident") >= 0;
var Serializer;
if (typeof XMLSerializer === "undefined" || isIE) {
Serializer = XMLDOMSerializer;
@ -104,14 +109,19 @@ class Section {
var serializer = new Serializer();
this.output = serializer.serializeToString(contents);
return this.output;
}.bind(this)).
then(function(){
}.bind(this)
)
.then(
function () {
return this.hooks.serialize.trigger(this.output, this);
}.bind(this)).
then(function(){
}.bind(this)
)
.then(
function () {
rendering.resolve(this.output);
}.bind(this))
.catch(function(error){
}.bind(this)
)
.catch(function (error) {
rendering.reject(error);
});
@ -123,11 +133,11 @@ class Section {
* @param {string} _query The query string to find
* @return {object[]} A list of matches, with form {cfi, excerpt}
*/
find(_query){
find(_query) {
var section = this;
var matches = [];
var query = _query.toLowerCase();
var find = function(node){
var find = function (node) {
var text = node.textContent.toLowerCase();
var range = section.document.createRange();
var cfi;
@ -151,16 +161,18 @@ class Section {
// Generate the excerpt
if (node.textContent.length < limit) {
excerpt = node.textContent;
}
else {
excerpt = node.textContent.substring(pos - limit/2, pos + limit/2);
} else {
excerpt = node.textContent.substring(
pos - limit / 2,
pos + limit / 2
);
excerpt = "..." + excerpt + "...";
}
// Add the CFI to the matches list
matches.push({
cfi: cfi,
excerpt: excerpt
excerpt: excerpt,
});
}
@ -168,13 +180,12 @@ class Section {
}
};
sprint(section.document, function(node) {
sprint(section.document, function (node) {
find(node);
});
return matches;
};
}
/**
* Search a string in multiple sequential Element of the section. If the document.createTreeWalker api is missed(eg: IE8), use `find` as a fallback.
@ -182,63 +193,88 @@ class Section {
* @param {int} maxSeqEle The maximum number of Element that are combined for search, default value is 5.
* @return {object[]} A list of matches, with form {cfi, excerpt}
*/
search(_query , maxSeqEle = 5){
if (typeof(document.createTreeWalker) == "undefined") {
search(_query, maxSeqEle = 5) {
if (typeof document.createTreeWalker == "undefined") {
return this.find(_query);
}
let matches = [];
const excerptLimit = 150;
const section = this;
const query = _query.toLowerCase();
const search = function(nodeList){
const textWithCase = nodeList.reduce((acc ,current)=>{
const search = function (nodeList) {
const textWithCase = nodeList.reduce((acc, current) => {
return acc + current.textContent;
},"");
}, "");
const text = textWithCase.toLowerCase();
const pos = text.indexOf(query);
if (pos != -1){
const startNodeIndex = 0 , endPos = pos + query.length;
let endNodeIndex = 0 , l = 0;
if (pos < nodeList[startNodeIndex].length){
if (pos != -1) {
const startNodeIndex = 0,
endPos = pos + query.length;
let endNodeIndex = 0,
l = 0;
if (pos < nodeList[startNodeIndex].length) {
let cfi;
while( endNodeIndex < nodeList.length - 1 ){
while (endNodeIndex < nodeList.length - 1) {
l += nodeList[endNodeIndex].length;
if ( endPos <= l){
if (endPos <= l) {
break;
}
endNodeIndex += 1;
}
let startNode = nodeList[startNodeIndex] , endNode = nodeList[endNodeIndex];
let startNode = nodeList[startNodeIndex],
endNode = nodeList[endNodeIndex];
let range = section.document.createRange();
range.setStart(startNode,pos);
let beforeEndLengthCount = nodeList.slice(0, endNodeIndex).reduce((acc,current)=>{return acc+current.textContent.length;},0) ;
range.setEnd(endNode, beforeEndLengthCount > endPos ? endPos : endPos - beforeEndLengthCount );
range.setStart(startNode, pos);
let beforeEndLengthCount = nodeList
.slice(0, endNodeIndex)
.reduce((acc, current) => {
return acc + current.textContent.length;
}, 0);
range.setEnd(
endNode,
beforeEndLengthCount > endPos
? endPos
: endPos - beforeEndLengthCount
);
cfi = section.cfiFromRange(range);
let excerpt = nodeList.slice(0, endNodeIndex+1).reduce((acc,current)=>{return acc+current.textContent ;},"");
if (excerpt.length > excerptLimit){
excerpt = excerpt.substring(pos - excerptLimit/2, pos + excerptLimit/2);
let excerpt = nodeList
.slice(0, endNodeIndex + 1)
.reduce((acc, current) => {
return acc + current.textContent;
}, "");
if (excerpt.length > excerptLimit) {
excerpt = excerpt.substring(
pos - excerptLimit / 2,
pos + excerptLimit / 2
);
excerpt = "..." + excerpt + "...";
}
matches.push({
cfi: cfi,
excerpt: excerpt
excerpt: excerpt,
});
}
}
}
};
const treeWalker = document.createTreeWalker(section.document, NodeFilter.SHOW_TEXT, null, false);
let node , nodeList = [];
while (node = treeWalker.nextNode()) {
const treeWalker = document.createTreeWalker(
section.document,
NodeFilter.SHOW_TEXT,
null,
false
);
let node,
nodeList = [];
while ((node = treeWalker.nextNode())) {
nodeList.push(node);
if (nodeList.length == maxSeqEle){
search(nodeList.slice(0 , maxSeqEle));
if (nodeList.length == maxSeqEle) {
search(nodeList.slice(0, maxSeqEle));
nodeList = nodeList.slice(1, maxSeqEle);
}
}
if (nodeList.length > 0){
if (nodeList.length > 0) {
search(nodeList);
}
return matches;
@ -250,23 +286,23 @@ class Section {
* @param {object} globalLayout The global layout settings object, chapter properties string
* @return {object} layoutProperties Object with layout properties
*/
reconcileLayoutSettings(globalLayout){
reconcileLayoutSettings(globalLayout) {
//-- Get the global defaults
var settings = {
layout : globalLayout.layout,
spread : globalLayout.spread,
orientation : globalLayout.orientation
layout: globalLayout.layout,
spread: globalLayout.spread,
orientation: globalLayout.orientation,
};
//-- Get the chapter's display type
this.properties.forEach(function(prop){
this.properties.forEach(function (prop) {
var rendition = prop.replace("rendition:", "");
var split = rendition.indexOf("-");
var property, value;
if(split != -1){
if (split != -1) {
property = rendition.slice(0, split);
value = rendition.slice(split+1);
value = rendition.slice(split + 1);
settings[property] = value;
}

View file

@ -1,7 +1,11 @@
import EpubCFI from "./epubcfi";
import Hook from "./utils/hook";
import Section from "./section";
import {replaceBase, replaceCanonical, replaceMeta} from "./utils/replacements";
import Hook from "./utils/hook";
import {
replaceBase,
replaceCanonical,
replaceMeta,
} from "./utils/replacements";
/**
* A collection of Spine Items
@ -22,9 +26,7 @@ class Spine {
this.hooks.content.register(replaceMeta);
this.epubcfi = new EpubCFI();
this.loaded = false;
this.items = undefined;
this.manifest = undefined;
this.spineNodeIndex = undefined;
@ -39,40 +41,43 @@ class Spine {
* @param {method} canonical Resolve canonical url
*/
unpack(_package, resolver, canonical) {
this.items = _package.spine;
this.manifest = _package.manifest;
this.spineNodeIndex = _package.spineNodeIndex;
this.baseUrl = _package.baseUrl || _package.basePath || "";
this.length = this.items.length;
this.items.forEach( (item, index) => {
this.items.forEach((item, index) => {
var manifestItem = this.manifest[item.idref];
var spineItem;
item.index = index;
item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.id);
item.cfiBase = this.epubcfi.generateChapterComponent(
this.spineNodeIndex,
item.index,
item.id
);
if (item.href) {
item.url = resolver(item.href, true);
item.canonical = canonical(item.href);
}
if(manifestItem) {
if (manifestItem) {
item.href = manifestItem.href;
item.url = resolver(item.href, true);
item.canonical = canonical(item.href);
if(manifestItem.properties.length){
if (manifestItem.properties.length) {
item.properties.push.apply(item.properties, manifestItem.properties);
}
}
if (item.linear === "yes") {
item.prev = function() {
item.prev = function () {
let prevIndex = item.index;
while (prevIndex > 0) {
let prev = this.get(prevIndex-1);
let prev = this.get(prevIndex - 1);
if (prev && prev.linear) {
return prev;
}
@ -80,10 +85,10 @@ class Spine {
}
return;
}.bind(this);
item.next = function() {
item.next = function () {
let nextIndex = item.index;
while (nextIndex < this.spineItems.length-1) {
let next = this.get(nextIndex+1);
while (nextIndex < this.spineItems.length - 1) {
let next = this.get(nextIndex + 1);
if (next && next.linear) {
return next;
}
@ -92,20 +97,17 @@ class Spine {
return;
}.bind(this);
} else {
item.prev = function() {
item.prev = function () {
return;
}
item.next = function() {
};
item.next = function () {
return;
};
}
}
spineItem = new Section(item, this.hooks);
this.append(spineItem);
});
this.loaded = true;
@ -131,14 +133,14 @@ class Spine {
}
index += 1;
}
} else if(this.epubcfi.isCfiString(target)) {
} else if (this.epubcfi.isCfiString(target)) {
let cfi = new EpubCFI(target);
index = cfi.spinePos;
} else if(typeof target === "number" || isNaN(target) === false){
} else if (typeof target === "number" || isNaN(target) === false) {
index = target;
} else if(typeof target === "string" && target.indexOf("#") === 0) {
} else if (typeof target === "string" && target.indexOf("#") === 0) {
index = this.spineById[target.substring(1)];
} else if(typeof target === "string") {
} else if (typeof target === "string") {
// Remove fragments
target = target.split("#")[0];
index = this.spineByHref[target] || this.spineByHref[encodeURI(target)];
@ -180,7 +182,7 @@ class Spine {
this.spineById[section.idref] = 0;
// Re-index
this.spineItems.forEach(function(item, index){
this.spineItems.forEach(function (item, index) {
item.index = index;
});
@ -199,7 +201,7 @@ class Spine {
remove(section) {
var index = this.spineItems.indexOf(section);
if(index > -1) {
if (index > -1) {
delete this.spineByHref[section.href];
delete this.spineById[section.idref];
@ -229,7 +231,7 @@ class Spine {
return next;
}
index += 1;
} while (index < this.spineItems.length) ;
} while (index < this.spineItems.length);
}
/**
@ -237,7 +239,7 @@ class Spine {
* @return {Section} last section
*/
last() {
let index = this.spineItems.length-1;
let index = this.spineItems.length - 1;
do {
let prev = this.get(index);
@ -251,9 +253,9 @@ class Spine {
destroy() {
this.each((section) => section.destroy());
this.spineItems = undefined
this.spineByHref = undefined
this.spineById = undefined
this.spineItems = undefined;
this.spineByHref = undefined;
this.spineById = undefined;
this.hooks.serialize.clear();
this.hooks.content.clear();

View file

@ -1,9 +1,9 @@
import {defer, isXml, parse} from "./utils/core";
import httpRequest from "./utils/request";
import mime from "./utils/mime";
import Path from "./utils/path";
import EventEmitter from "event-emitter";
import localforage from "localforage";
import { defer, isXml, parse } from "./utils/core";
import mime from "./utils/mime";
import Path from "./utils/path";
import httpRequest from "./utils/request";
/**
* Handles saving and requesting files from local storage
@ -13,20 +13,15 @@ import localforage from "localforage";
* @param {function} [resolver]
*/
class Store {
constructor(name, requester, resolver) {
this.urlCache = {};
this.storage = undefined;
this.name = name;
this.requester = requester || httpRequest;
this.resolver = resolver;
this.online = true;
this.checkRequirements();
this.addListeners();
}
@ -35,14 +30,14 @@ class Store {
* Requires localForage if it isn't there
* @private
*/
checkRequirements(){
checkRequirements() {
try {
let store;
if (typeof localforage === "undefined") {
store = localforage;
}
this.storage = store.createInstance({
name: this.name
name: this.name,
});
} catch (e) {
throw new Error("localForage lib not loaded");
@ -55,8 +50,8 @@ class Store {
*/
addListeners() {
this._status = this.status.bind(this);
window.addEventListener('online', this._status);
window.addEventListener('offline', this._status);
window.addEventListener("online", this._status);
window.addEventListener("offline", this._status);
}
/**
@ -64,8 +59,8 @@ class Store {
* @private
*/
removeListeners() {
window.removeEventListener('online', this._status);
window.removeEventListener('offline', this._status);
window.removeEventListener("online", this._status);
window.removeEventListener("offline", this._status);
this._status = undefined;
}
@ -97,15 +92,13 @@ class Store {
return this.storage.getItem(encodedUrl).then((item) => {
if (!item || force) {
return this.requester(url, "binary")
.then((data) => {
return this.requester(url, "binary").then((data) => {
return this.storage.setItem(encodedUrl, data);
});
} else {
return item;
}
});
});
return Promise.all(mapped);
}
@ -122,9 +115,11 @@ class Store {
return this.storage.getItem(encodedUrl).then((result) => {
if (!result) {
return this.requester(url, "binary", withCredentials, headers).then((data) => {
return this.requester(url, "binary", withCredentials, headers).then(
(data) => {
return this.storage.setItem(encodedUrl, data);
});
}
);
}
return result;
});
@ -138,19 +133,20 @@ class Store {
* @param {object} [headers]
* @return {Promise<Blob | string | JSON | Document | XMLDocument>}
*/
request(url, type, withCredentials, headers){
request(url, type, withCredentials, headers) {
if (this.online) {
// From network
return this.requester(url, type, withCredentials, headers).then((data) => {
return this.requester(url, type, withCredentials, headers).then(
(data) => {
// save to store if not present
this.put(url);
return data;
})
}
);
} else {
// From store
return this.retrieve(url, type);
}
}
/**
@ -160,22 +156,20 @@ class Store {
* @return {Promise<Blob | string | JSON | Document | XMLDocument>}
*/
retrieve(url, type) {
var deferred = new defer();
var response;
var path = new Path(url);
// If type isn't set, determine it from the file extension
if(!type) {
if (!type) {
type = path.extension;
}
if(type == "blob"){
if (type == "blob") {
response = this.getBlob(url);
} else {
response = this.getText(url);
}
return response.then((r) => {
var deferred = new defer();
var result;
@ -184,8 +178,8 @@ class Store {
deferred.resolve(result);
} else {
deferred.reject({
message : "File not found in storage: " + url,
stack : new Error().stack
message: "File not found in storage: " + url,
stack: new Error().stack,
});
}
return deferred.promise;
@ -199,22 +193,16 @@ class Store {
* @param {string} [type]
* @return {any} the parsed result
*/
handleResponse(response, type){
handleResponse(response, type) {
var r;
if(type == "json") {
if (type == "json") {
r = JSON.parse(response);
}
else
if(isXml(type)) {
} else if (isXml(type)) {
r = parse(response, "text/xml");
}
else
if(type == "xhtml") {
} else if (type == "xhtml") {
r = parse(response, "application/xhtml+xml");
}
else
if(type == "html" || type == "htm") {
} else if (type == "html" || type == "htm") {
r = parse(response, "text/html");
} else {
r = response;
@ -229,17 +217,16 @@ class Store {
* @param {string} [mimeType]
* @return {Blob}
*/
getBlob(url, mimeType){
getBlob(url, mimeType) {
let encodedUrl = window.encodeURIComponent(url);
return this.storage.getItem(encodedUrl).then(function(uint8array) {
if(!uint8array) return;
return this.storage.getItem(encodedUrl).then(function (uint8array) {
if (!uint8array) return;
mimeType = mimeType || mime.lookup(url);
return new Blob([uint8array], {type : mimeType});
return new Blob([uint8array], { type: mimeType });
});
}
/**
@ -248,19 +235,19 @@ class Store {
* @param {string} [mimeType]
* @return {string}
*/
getText(url, mimeType){
getText(url, mimeType) {
let encodedUrl = window.encodeURIComponent(url);
mimeType = mimeType || mime.lookup(url);
return this.storage.getItem(encodedUrl).then(function(uint8array) {
return this.storage.getItem(encodedUrl).then(function (uint8array) {
var deferred = new defer();
var reader = new FileReader();
var blob;
if(!uint8array) return;
if (!uint8array) return;
blob = new Blob([uint8array], {type : mimeType});
blob = new Blob([uint8array], { type: mimeType });
reader.addEventListener("loadend", () => {
deferred.resolve(reader.result);
@ -278,7 +265,7 @@ class Store {
* @param {string} [mimeType]
* @return {string} base64 encoded
*/
getBase64(url, mimeType){
getBase64(url, mimeType) {
let encodedUrl = window.encodeURIComponent(url);
mimeType = mimeType || mime.lookup(url);
@ -288,9 +275,9 @@ class Store {
var reader = new FileReader();
var blob;
if(!uint8array) return;
if (!uint8array) return;
blob = new Blob([uint8array], {type : mimeType});
blob = new Blob([uint8array], { type: mimeType });
reader.addEventListener("loadend", () => {
deferred.resolve(reader.result);
@ -307,14 +294,14 @@ class Store {
* @param {object} [options.base64] use base64 encoding or blob url
* @return {Promise} url promise with Url string
*/
createUrl(url, options){
createUrl(url, options) {
var deferred = new defer();
var _URL = window.URL || window.webkitURL || window.mozURL;
var tempUrl;
var response;
var useBase64 = options && options.base64;
if(url in this.urlCache) {
if (url in this.urlCache) {
deferred.resolve(this.urlCache[url]);
return deferred.promise;
}
@ -323,36 +310,31 @@ class Store {
response = this.getBase64(url);
if (response) {
response.then(function(tempUrl) {
response.then(
function (tempUrl) {
this.urlCache[url] = tempUrl;
deferred.resolve(tempUrl);
}.bind(this));
}.bind(this)
);
}
} else {
response = this.getBlob(url);
if (response) {
response.then(function(blob) {
response.then(
function (blob) {
tempUrl = _URL.createObjectURL(blob);
this.urlCache[url] = tempUrl;
deferred.resolve(tempUrl);
}.bind(this));
}.bind(this)
);
}
}
if (!response) {
deferred.reject({
message : "File not found in storage: " + url,
stack : new Error().stack
message: "File not found in storage: " + url,
stack: new Error().stack,
});
}
@ -363,10 +345,10 @@ class Store {
* Revoke Temp Url for a archive item
* @param {string} url url of the item in the store
*/
revokeUrl(url){
revokeUrl(url) {
var _URL = window.URL || window.webkitURL || window.mozURL;
var fromCache = this.urlCache[url];
if(fromCache) _URL.revokeObjectURL(fromCache);
if (fromCache) _URL.revokeObjectURL(fromCache);
}
destroy() {

View file

@ -9,18 +9,17 @@ class Themes {
constructor(rendition) {
this.rendition = rendition;
this._themes = {
"default" : {
"rules" : {},
"url" : "",
"serialized" : ""
}
default: {
rules: {},
url: "",
serialized: "",
},
};
this._overrides = {};
this._current = "default";
this._injected = [];
this.rendition.hooks.content.register(this.inject.bind(this));
this.rendition.hooks.content.register(this.overrides.bind(this));
}
/**
@ -30,20 +29,20 @@ class Themes {
* @example themes.register("light", { "body": { "color": "purple"}})
* @example themes.register({ "light" : {...}, "dark" : {...}})
*/
register () {
register() {
if (arguments.length === 0) {
return;
}
if (arguments.length === 1 && typeof(arguments[0]) === "object") {
if (arguments.length === 1 && typeof arguments[0] === "object") {
return this.registerThemes(arguments[0]);
}
if (arguments.length === 1 && typeof(arguments[0]) === "string") {
if (arguments.length === 1 && typeof arguments[0] === "string") {
return this.default(arguments[0]);
}
if (arguments.length === 2 && typeof(arguments[1]) === "string") {
if (arguments.length === 2 && typeof arguments[1] === "string") {
return this.registerUrl(arguments[0], arguments[1]);
}
if (arguments.length === 2 && typeof(arguments[1]) === "object") {
if (arguments.length === 2 && typeof arguments[1] === "object") {
return this.registerRules(arguments[0], arguments[1]);
}
}
@ -54,14 +53,14 @@ class Themes {
* @example themes.register("http://example.com/default.css")
* @example themes.register({ "body": { "color": "purple"}})
*/
default (theme) {
default(theme) {
if (!theme) {
return;
}
if (typeof(theme) === "string") {
if (typeof theme === "string") {
return this.registerUrl("default", theme);
}
if (typeof(theme) === "object") {
if (typeof theme === "object") {
return this.registerRules("default", theme);
}
}
@ -70,10 +69,10 @@ class Themes {
* Register themes object
* @param {object} themes
*/
registerThemes (themes) {
registerThemes(themes) {
for (var theme in themes) {
if (themes.hasOwnProperty(theme)) {
if (typeof(themes[theme]) === "string") {
if (typeof themes[theme] === "string") {
this.registerUrl(theme, themes[theme]);
} else {
this.registerRules(theme, themes[theme]);
@ -87,9 +86,9 @@ class Themes {
* @param {string} name
* @param {string} css
*/
registerCss (name, css) {
this._themes[name] = { "serialized" : css };
if (this._injected[name] || name == 'default') {
registerCss(name, css) {
this._themes[name] = { serialized: css };
if (this._injected[name] || name == "default") {
this.update(name);
}
}
@ -99,10 +98,10 @@ class Themes {
* @param {string} name
* @param {string} input
*/
registerUrl (name, input) {
registerUrl(name, input) {
var url = new Url(input);
this._themes[name] = { "url": url.toString() };
if (this._injected[name] || name == 'default') {
this._themes[name] = { url: url.toString() };
if (this._injected[name] || name == "default") {
this.update(name);
}
}
@ -112,10 +111,10 @@ class Themes {
* @param {string} name
* @param {object} rules
*/
registerRules (name, rules) {
this._themes[name] = { "rules": rules };
registerRules(name, rules) {
this._themes[name] = { rules: rules };
// TODO: serialize css rules
if (this._injected[name] || name == 'default') {
if (this._injected[name] || name == "default") {
this.update(name);
}
}
@ -124,7 +123,7 @@ class Themes {
* Select a theme
* @param {string} name
*/
select (name) {
select(name) {
var prev = this._current;
var contents;
@ -132,7 +131,7 @@ class Themes {
this.update(name);
contents = this.rendition.getContents();
contents.forEach( (content) => {
contents.forEach((content) => {
content.removeClass(prev);
content.addClass(name);
});
@ -142,9 +141,9 @@ class Themes {
* Update a theme
* @param {string} name
*/
update (name) {
update(name) {
var contents = this.rendition.getContents();
contents.forEach( (content) => {
contents.forEach((content) => {
this.add(name, content);
});
}
@ -153,22 +152,28 @@ class Themes {
* Inject all themes into contents
* @param {Contents} contents
*/
inject (contents) {
inject(contents) {
var links = [];
var themes = this._themes;
var theme;
for (var name in themes) {
if (themes.hasOwnProperty(name) && (name === this._current || name === "default")) {
if (
themes.hasOwnProperty(name) &&
(name === this._current || name === "default")
) {
theme = themes[name];
if((theme.rules && Object.keys(theme.rules).length > 0) || (theme.url && links.indexOf(theme.url) === -1)) {
if (
(theme.rules && Object.keys(theme.rules).length > 0) ||
(theme.url && links.indexOf(theme.url) === -1)
) {
this.add(name, contents);
}
this._injected.push(name);
}
}
if(this._current != "default") {
if (this._current != "default") {
contents.addClass(this._current);
}
}
@ -178,7 +183,7 @@ class Themes {
* @param {string} name
* @param {Contents} contents
*/
add (name, contents) {
add(name, contents) {
var theme = this._themes[name];
if (!theme || !contents) {
@ -202,25 +207,29 @@ class Themes {
* @param {string} value
* @param {boolean} priority
*/
override (name, value, priority) {
override(name, value, priority) {
var contents = this.rendition.getContents();
this._overrides[name] = {
value: value,
priority: priority === true
priority: priority === true,
};
contents.forEach( (content) => {
content.css(name, this._overrides[name].value, this._overrides[name].priority);
contents.forEach((content) => {
content.css(
name,
this._overrides[name].value,
this._overrides[name].priority
);
});
}
removeOverride (name) {
removeOverride(name) {
var contents = this.rendition.getContents();
delete this._overrides[name];
contents.forEach( (content) => {
contents.forEach((content) => {
content.css(name);
});
}
@ -229,7 +238,7 @@ class Themes {
* Add all overrides
* @param {Content} content
*/
overrides (contents) {
overrides(contents) {
var overrides = this._overrides;
for (var rule in overrides) {
@ -243,7 +252,7 @@ class Themes {
* Adjust the font size of a rendition
* @param {number} size
*/
fontSize (size) {
fontSize(size) {
this.override("font-size", size);
}
@ -251,7 +260,7 @@ class Themes {
* Adjust the font-family of a rendition
* @param {string} f
*/
font (f) {
font(f) {
this.override("font-family", f, true);
}
@ -262,7 +271,6 @@ class Themes {
this._current = undefined;
this._injected = undefined;
}
}
export default Themes;

View file

@ -1,62 +1,73 @@
export const EPUBJS_VERSION = "0.3";
// Dom events to listen for
export const DOM_EVENTS = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "mousemove", "click", "touchend", "touchstart", "touchmove"];
export const DOM_EVENTS = [
"keydown",
"keyup",
"keypressed",
"mouseup",
"mousedown",
"mousemove",
"click",
"touchend",
"touchstart",
"touchmove",
];
export const EVENTS = {
BOOK : {
OPEN_FAILED : "openFailed"
BOOK: {
OPEN_FAILED: "openFailed",
},
CONTENTS : {
EXPAND : "expand",
RESIZE : "resize",
SELECTED : "selected",
SELECTED_RANGE : "selectedRange",
LINK_CLICKED : "linkClicked"
CONTENTS: {
EXPAND: "expand",
RESIZE: "resize",
SELECTED: "selected",
SELECTED_RANGE: "selectedRange",
LINK_CLICKED: "linkClicked",
},
LOCATIONS : {
CHANGED : "changed"
LOCATIONS: {
CHANGED: "changed",
},
MANAGERS : {
RESIZE : "resize",
RESIZED : "resized",
ORIENTATION_CHANGE : "orientationchange",
ADDED : "added",
SCROLL : "scroll",
SCROLLED : "scrolled",
REMOVED : "removed",
MANAGERS: {
RESIZE: "resize",
RESIZED: "resized",
ORIENTATION_CHANGE: "orientationchange",
ADDED: "added",
SCROLL: "scroll",
SCROLLED: "scrolled",
REMOVED: "removed",
},
VIEWS : {
VIEWS: {
AXIS: "axis",
WRITING_MODE: "writingMode",
LOAD_ERROR : "loaderror",
RENDERED : "rendered",
RESIZED : "resized",
DISPLAYED : "displayed",
SHOWN : "shown",
HIDDEN : "hidden",
MARK_CLICKED : "markClicked"
LOAD_ERROR: "loaderror",
RENDERED: "rendered",
RESIZED: "resized",
DISPLAYED: "displayed",
SHOWN: "shown",
HIDDEN: "hidden",
MARK_CLICKED: "markClicked",
},
RENDITION : {
STARTED : "started",
ATTACHED : "attached",
DISPLAYED : "displayed",
DISPLAY_ERROR : "displayerror",
RENDERED : "rendered",
REMOVED : "removed",
RESIZED : "resized",
ORIENTATION_CHANGE : "orientationchange",
LOCATION_CHANGED : "locationChanged",
RELOCATED : "relocated",
MARK_CLICKED : "markClicked",
SELECTED : "selected",
LAYOUT: "layout"
RENDITION: {
STARTED: "started",
ATTACHED: "attached",
DISPLAYED: "displayed",
DISPLAY_ERROR: "displayerror",
RENDERED: "rendered",
REMOVED: "removed",
RESIZED: "resized",
ORIENTATION_CHANGE: "orientationchange",
LOCATION_CHANGED: "locationChanged",
RELOCATED: "relocated",
MARK_CLICKED: "markClicked",
SELECTED: "selected",
LAYOUT: "layout",
},
LAYOUT : {
UPDATED : "updated"
LAYOUT: {
UPDATED: "updated",
},
ANNOTATION : {
ATTACH : "attach",
DETACH : "detach"
}
}
ANNOTATION: {
ATTACH: "attach",
DETACH: "detach",
},
};

View file

@ -1,7 +1,7 @@
/**
* Core Utilities and Helpers
* @module Core
*/
*/
import { DOMParser as XMLDOMParser } from "@xmldom/xmldom";
/**
@ -9,12 +9,21 @@ import { DOMParser as XMLDOMParser } from "@xmldom/xmldom";
* @returns {function} requestAnimationFrame
* @memberof Core
*/
export const requestAnimationFrame = (typeof window != "undefined") ? (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame) : false;
export const requestAnimationFrame =
typeof window != "undefined"
? window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame
: false;
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
const COMMENT_NODE = 8;
const DOCUMENT_NODE = 9;
const _URL = typeof URL != "undefined" ? URL : (typeof window != "undefined" ? (window.URL || window.webkitURL || window.mozURL) : undefined);
const _URL =
typeof URL != "undefined"
? URL
: typeof window != "undefined"
? window.URL || window.webkitURL || window.mozURL
: undefined;
/**
* Generates a UUID
@ -24,11 +33,14 @@ const _URL = typeof URL != "undefined" ? URL : (typeof window != "undefined" ? (
*/
export function uuid() {
var d = new Date().getTime();
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=="x" ? r : (r&0x7|0x8)).toString(16);
});
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
/[xy]/g,
function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == "x" ? r : (r & 0x7) | 0x8).toString(16);
}
);
return uuid;
}
@ -92,17 +104,20 @@ export function isFloat(n) {
* @memberof Core
*/
export function prefixed(unprefixed) {
var vendors = ["Webkit", "webkit", "Moz", "O", "ms" ];
var vendors = ["Webkit", "webkit", "Moz", "O", "ms"];
var prefixes = ["-webkit-", "-webkit-", "-moz-", "-o-", "-ms-"];
var lower = unprefixed.toLowerCase();
var length = vendors.length;
if (typeof(document) === "undefined" || typeof(document.body.style[lower]) != "undefined") {
if (
typeof document === "undefined" ||
typeof document.body.style[lower] != "undefined"
) {
return unprefixed;
}
for (var i = 0; i < length; i++) {
if (typeof(document.body.style[prefixes[i] + lower]) != "undefined") {
if (typeof document.body.style[prefixes[i] + lower] != "undefined") {
return prefixes[i] + lower;
}
}
@ -135,9 +150,13 @@ export function defaults(obj) {
export function extend(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function (source) {
if(!source) return;
Object.getOwnPropertyNames(source).forEach(function(propName) {
Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName));
if (!source) return;
Object.getOwnPropertyNames(source).forEach(function (propName) {
Object.defineProperty(
target,
propName,
Object.getOwnPropertyDescriptor(source, propName)
);
});
});
return target;
@ -174,27 +193,27 @@ export function locationOf(item, array, compareFunction, _start, _end) {
var end = _end || array.length;
var pivot = parseInt(start + (end - start) / 2);
var compared;
if(!compareFunction){
compareFunction = function(a, b) {
if(a > b) return 1;
if(a < b) return -1;
if(a == b) return 0;
if (!compareFunction) {
compareFunction = function (a, b) {
if (a > b) return 1;
if (a < b) return -1;
if (a == b) return 0;
};
}
if(end-start <= 0) {
if (end - start <= 0) {
return pivot;
}
compared = compareFunction(array[pivot], item);
if(end-start === 1) {
if (end - start === 1) {
return compared >= 0 ? pivot : pivot + 1;
}
if(compared === 0) {
if (compared === 0) {
return pivot;
}
if(compared === -1) {
if (compared === -1) {
return locationOf(item, array, compareFunction, pivot, end);
} else{
} else {
return locationOf(item, array, compareFunction, start, pivot);
}
}
@ -215,27 +234,27 @@ export function indexOfSorted(item, array, compareFunction, _start, _end) {
var end = _end || array.length;
var pivot = parseInt(start + (end - start) / 2);
var compared;
if(!compareFunction){
compareFunction = function(a, b) {
if(a > b) return 1;
if(a < b) return -1;
if(a == b) return 0;
if (!compareFunction) {
compareFunction = function (a, b) {
if (a > b) return 1;
if (a < b) return -1;
if (a == b) return 0;
};
}
if(end-start <= 0) {
if (end - start <= 0) {
return -1; // Not found
}
compared = compareFunction(array[pivot], item);
if(end-start === 1) {
if (end - start === 1) {
return compared === 0 ? pivot : -1;
}
if(compared === 0) {
if (compared === 0) {
return pivot; // Found
}
if(compared === -1) {
if (compared === -1) {
return indexOfSorted(item, array, compareFunction, pivot, end);
} else{
} else {
return indexOfSorted(item, array, compareFunction, start, pivot);
}
}
@ -247,27 +266,41 @@ export function indexOfSorted(item, array, compareFunction, _start, _end) {
* @memberof Core
*/
export function bounds(el) {
var style = window.getComputedStyle(el);
var widthProps = ["width", "paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"];
var heightProps = ["height", "paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"];
var widthProps = [
"width",
"paddingRight",
"paddingLeft",
"marginRight",
"marginLeft",
"borderRightWidth",
"borderLeftWidth",
];
var heightProps = [
"height",
"paddingTop",
"paddingBottom",
"marginTop",
"marginBottom",
"borderTopWidth",
"borderBottomWidth",
];
var width = 0;
var height = 0;
widthProps.forEach(function(prop){
widthProps.forEach(function (prop) {
width += parseFloat(style[prop]) || 0;
});
heightProps.forEach(function(prop){
heightProps.forEach(function (prop) {
height += parseFloat(style[prop]) || 0;
});
return {
height: height,
width: width
width: width,
};
}
/**
@ -278,27 +311,39 @@ export function bounds(el) {
* @memberof Core
*/
export function borders(el) {
var style = window.getComputedStyle(el);
var widthProps = ["paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"];
var heightProps = ["paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"];
var widthProps = [
"paddingRight",
"paddingLeft",
"marginRight",
"marginLeft",
"borderRightWidth",
"borderLeftWidth",
];
var heightProps = [
"paddingTop",
"paddingBottom",
"marginTop",
"marginBottom",
"borderTopWidth",
"borderBottomWidth",
];
var width = 0;
var height = 0;
widthProps.forEach(function(prop){
widthProps.forEach(function (prop) {
width += parseFloat(style[prop]) || 0;
});
heightProps.forEach(function(prop){
heightProps.forEach(function (prop) {
height += parseFloat(style[prop]) || 0;
});
return {
height: height,
width: width
width: width,
};
}
/**
@ -311,7 +356,7 @@ export function borders(el) {
export function nodeBounds(node) {
let elPos;
let doc = node.ownerDocument;
if(node.nodeType == Node.TEXT_NODE){
if (node.nodeType == Node.TEXT_NODE) {
let elRange = doc.createRange();
elRange.selectNodeContents(node);
elPos = elRange.getBoundingClientRect();
@ -327,7 +372,6 @@ export function nodeBounds(node) {
* @memberof Core
*/
export function windowBounds() {
var width = window.innerWidth;
var height = window.innerHeight;
@ -337,9 +381,8 @@ export function windowBounds() {
right: width,
bottom: height,
width: width,
height: height
height: height,
};
}
/**
@ -402,8 +445,8 @@ export function isXml(ext) {
* @returns {Blob}
* @memberof Core
*/
export function createBlob(content, mime){
return new Blob([content], {type : mime });
export function createBlob(content, mime) {
return new Blob([content], { type: mime });
}
/**
@ -413,7 +456,7 @@ export function createBlob(content, mime){
* @returns {string} url
* @memberof Core
*/
export function createBlobUrl(content, mime){
export function createBlobUrl(content, mime) {
var tempUrl;
var blob = createBlob(content, mime);
@ -427,7 +470,7 @@ export function createBlobUrl(content, mime){
* @param {string} url
* @memberof Core
*/
export function revokeBlobUrl(url){
export function revokeBlobUrl(url) {
return _URL.revokeObjectURL(url);
}
@ -438,11 +481,11 @@ export function revokeBlobUrl(url){
* @returns {string} url
* @memberof Core
*/
export function createBase64Url(content, mime){
export function createBase64Url(content, mime) {
var data;
var datauri;
if (typeof(content) !== "string") {
if (typeof content !== "string") {
// Only handles strings
return;
}
@ -460,7 +503,7 @@ export function createBase64Url(content, mime){
* @returns {string} type
* @memberof Core
*/
export function type(obj){
export function type(obj) {
return Object.prototype.toString.call(obj).slice(8, -1);
}
@ -484,7 +527,7 @@ export function parse(markup, mime, forceXMLDom) {
// Remove byte order mark before parsing
// https://www.w3.org/International/questions/qa-byte-order-mark
if(markup.charCodeAt(0) === 0xFEFF) {
if (markup.charCodeAt(0) === 0xfeff) {
markup = markup.slice(1);
}
@ -524,7 +567,6 @@ export function qs(el, sel) {
* @memberof Core
*/
export function qsa(el, sel) {
if (typeof el.querySelector != "undefined") {
return el.querySelectorAll(sel);
} else {
@ -551,9 +593,9 @@ export function qsp(el, sel, props) {
return el.querySelector(sel);
} else {
q = el.getElementsByTagName(sel);
filtered = Array.prototype.slice.call(q, 0).filter(function(el) {
filtered = Array.prototype.slice.call(q, 0).filter(function (el) {
for (var prop in props) {
if(el.getAttribute(prop) === props[prop]){
if (el.getAttribute(prop) === props[prop]) {
return true;
}
}
@ -574,14 +616,19 @@ export function qsp(el, sel, props) {
*/
export function sprint(root, func) {
var doc = root.ownerDocument || root;
if (typeof(doc.createTreeWalker) !== "undefined") {
if (typeof doc.createTreeWalker !== "undefined") {
treeWalker(root, func, NodeFilter.SHOW_TEXT);
} else {
walk(root, function(node) {
if (node && node.nodeType === 3) { // Node.TEXT_NODE
walk(
root,
function (node) {
if (node && node.nodeType === 3) {
// Node.TEXT_NODE
func(node);
}
}, true);
},
true
);
}
}
@ -605,19 +652,19 @@ export function treeWalker(root, func, filter) {
* @param {node} node
* @param {callback} return false for continue,true for break inside callback
*/
export function walk(node,callback){
if(callback(node)){
export function walk(node, callback) {
if (callback(node)) {
return true;
}
node = node.firstChild;
if(node){
do{
let walked = walk(node,callback);
if(walked){
if (node) {
do {
let walked = walk(node, callback);
if (walked) {
return true;
}
node = node.nextSibling;
} while(node);
} while (node);
}
}
@ -628,16 +675,15 @@ export function walk(node,callback){
* @memberof Core
*/
export function blob2base64(blob) {
return new Promise(function(resolve, reject) {
return new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
reader.onloadend = function () {
resolve(reader.result);
};
});
}
/**
* Creates a new pending promise and provides methods to resolve or reject it.
* From: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred#backwards_forwards_compatible
@ -682,17 +728,20 @@ export function defer() {
* @returns {element[]} elements
* @memberof Core
*/
export function querySelectorByType(html, element, type){
export function querySelectorByType(html, element, type) {
var query;
if (typeof html.querySelector != "undefined") {
query = html.querySelector(`${element}[*|type="${type}"]`);
}
// Handle IE not supporting namespaced epub:type in querySelector
if(!query || query.length === 0) {
if (!query || query.length === 0) {
query = qsa(html, element);
for (var i = 0; i < query.length; i++) {
if(query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type ||
query[i].getAttribute("epub:type") === type) {
if (
query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") ===
type ||
query[i].getAttribute("epub:type") === type
) {
return query[i];
}
}
@ -730,7 +779,7 @@ export function parents(node) {
for (; node; node = node.parentNode) {
nodes.unshift(node);
}
return nodes
return nodes;
}
/**
@ -768,7 +817,7 @@ export function filterChildren(el, nodeName, single) {
*/
export function getParentByTagName(node, tagname) {
let parent;
if (node === null || tagname === '') return;
if (node === null || tagname === "") return;
parent = node.parentNode;
while (parent.nodeType === 1) {
if (parent.tagName.toLowerCase() === tagname) {
@ -841,9 +890,10 @@ export class RangeObject {
}
selectNodeContents(referenceNode) {
let end = referenceNode.childNodes[referenceNode.childNodes - 1];
let endIndex = (referenceNode.nodeType === 3) ?
referenceNode.textContent.length : parent.childNodes.length;
let endIndex =
referenceNode.nodeType === 3
? referenceNode.textContent.length
: parent.childNodes.length;
this.setStart(referenceNode, 0);
this.setEnd(referenceNode, endIndex);
}
@ -862,8 +912,10 @@ export class RangeObject {
}
_checkCollapsed() {
if (this.startContainer === this.endContainer &&
this.startOffset === this.endOffset) {
if (
this.startContainer === this.endContainer &&
this.startOffset === this.endOffset
) {
this.collapsed = true;
} else {
this.collapsed = false;

View file

@ -6,7 +6,7 @@
* @example this.content = new EPUBJS.Hook(this);
*/
class Hook {
constructor(context){
constructor(context) {
this.context = context || this;
this.hooks = [];
}
@ -15,13 +15,13 @@ class Hook {
* Adds a function to be run before a hook completes
* @example this.content.register(function(){...});
*/
register(){
for(var i = 0; i < arguments.length; ++i) {
register() {
for (var i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === "function") {
this.hooks.push(arguments[i]);
} else {
// unpack array
for(var j = 0; j < arguments[i].length; ++j) {
for (var j = 0; j < arguments[i].length; ++j) {
this.hooks.push(arguments[i][j]);
}
}
@ -32,7 +32,7 @@ class Hook {
* Removes a function
* @example this.content.deregister(function(){...});
*/
deregister(func){
deregister(func) {
let hook;
for (let i = 0; i < this.hooks.length; i++) {
hook = this.hooks[i];
@ -47,36 +47,35 @@ class Hook {
* Triggers a hook to run all functions
* @example this.content.trigger(args).then(function(){...});
*/
trigger(){
trigger() {
var args = arguments;
var context = this.context;
var promises = [];
this.hooks.forEach(function(task) {
this.hooks.forEach(function (task) {
try {
var executing = task.apply(context, args);
} catch (err) {
console.log(err);
console.error(err);
}
if(executing && typeof executing["then"] === "function") {
if (executing && typeof executing["then"] === "function") {
// Task is a function that returns a promise
promises.push(executing);
}
// Otherwise Task resolves immediately, continue
});
return Promise.all(promises);
}
// Adds a function to be run before a hook completes
list(){
list() {
return this.hooks;
}
clear(){
return this.hooks = [];
clear() {
return (this.hooks = []);
}
}
export default Hook;

View file

@ -4,143 +4,147 @@ edited down
*/
var table = {
"application" : {
"ecmascript" : [ "es", "ecma" ],
"javascript" : "js",
"ogg" : "ogx",
"pdf" : "pdf",
"postscript" : [ "ps", "ai", "eps", "epsi", "epsf", "eps2", "eps3" ],
"rdf+xml" : "rdf",
"smil" : [ "smi", "smil" ],
"xhtml+xml" : [ "xhtml", "xht" ],
"xml" : [ "xml", "xsl", "xsd", "opf", "ncx" ],
"zip" : "zip",
"x-httpd-eruby" : "rhtml",
"x-latex" : "latex",
"x-maker" : [ "frm", "maker", "frame", "fm", "fb", "book", "fbdoc" ],
"x-object" : "o",
"x-shockwave-flash" : [ "swf", "swfl" ],
"x-silverlight" : "scr",
"epub+zip" : "epub",
"font-tdpfr" : "pfr",
"inkml+xml" : [ "ink", "inkml" ],
"json" : "json",
"jsonml+json" : "jsonml",
"mathml+xml" : "mathml",
"metalink+xml" : "metalink",
"mp4" : "mp4s",
application: {
ecmascript: ["es", "ecma"],
javascript: "js",
ogg: "ogx",
pdf: "pdf",
postscript: ["ps", "ai", "eps", "epsi", "epsf", "eps2", "eps3"],
"rdf+xml": "rdf",
smil: ["smi", "smil"],
"xhtml+xml": ["xhtml", "xht"],
xml: ["xml", "xsl", "xsd", "opf", "ncx"],
zip: "zip",
"x-httpd-eruby": "rhtml",
"x-latex": "latex",
"x-maker": ["frm", "maker", "frame", "fm", "fb", "book", "fbdoc"],
"x-object": "o",
"x-shockwave-flash": ["swf", "swfl"],
"x-silverlight": "scr",
"epub+zip": "epub",
"font-tdpfr": "pfr",
"inkml+xml": ["ink", "inkml"],
json: "json",
"jsonml+json": "jsonml",
"mathml+xml": "mathml",
"metalink+xml": "metalink",
mp4: "mp4s",
// "oebps-package+xml" : "opf",
"omdoc+xml" : "omdoc",
"oxps" : "oxps",
"vnd.amazon.ebook" : "azw",
"widget" : "wgt",
"omdoc+xml": "omdoc",
oxps: "oxps",
"vnd.amazon.ebook": "azw",
widget: "wgt",
// "x-dtbncx+xml" : "ncx",
"x-dtbook+xml" : "dtb",
"x-dtbresource+xml" : "res",
"x-font-bdf" : "bdf",
"x-font-ghostscript" : "gsf",
"x-font-linux-psf" : "psf",
"x-font-otf" : "otf",
"x-font-pcf" : "pcf",
"x-font-snf" : "snf",
"x-font-ttf" : [ "ttf", "ttc" ],
"x-font-type1" : [ "pfa", "pfb", "pfm", "afm" ],
"x-font-woff" : "woff",
"x-mobipocket-ebook" : [ "prc", "mobi" ],
"x-mspublisher" : "pub",
"x-nzb" : "nzb",
"x-tgif" : "obj",
"xaml+xml" : "xaml",
"xml-dtd" : "dtd",
"xproc+xml" : "xpl",
"xslt+xml" : "xslt",
"internet-property-stream" : "acx",
"x-compress" : "z",
"x-compressed" : "tgz",
"x-gzip" : "gz",
"x-dtbook+xml": "dtb",
"x-dtbresource+xml": "res",
"x-font-bdf": "bdf",
"x-font-ghostscript": "gsf",
"x-font-linux-psf": "psf",
"x-font-otf": "otf",
"x-font-pcf": "pcf",
"x-font-snf": "snf",
"x-font-ttf": ["ttf", "ttc"],
"x-font-type1": ["pfa", "pfb", "pfm", "afm"],
"x-font-woff": "woff",
"x-mobipocket-ebook": ["prc", "mobi"],
"x-mspublisher": "pub",
"x-nzb": "nzb",
"x-tgif": "obj",
"xaml+xml": "xaml",
"xml-dtd": "dtd",
"xproc+xml": "xpl",
"xslt+xml": "xslt",
"internet-property-stream": "acx",
"x-compress": "z",
"x-compressed": "tgz",
"x-gzip": "gz",
},
"audio" : {
"flac" : "flac",
"midi" : [ "mid", "midi", "kar", "rmi" ],
"mpeg" : [ "mpga", "mpega", "mp2", "mp3", "m4a", "mp2a", "m2a", "m3a" ],
"mpegurl" : "m3u",
"ogg" : [ "oga", "ogg", "spx" ],
"x-aiff" : [ "aif", "aiff", "aifc" ],
"x-ms-wma" : "wma",
"x-wav" : "wav",
"adpcm" : "adp",
"mp4" : "mp4a",
"webm" : "weba",
"x-aac" : "aac",
"x-caf" : "caf",
"x-matroska" : "mka",
"x-pn-realaudio-plugin" : "rmp",
"xm" : "xm",
"mid" : [ "mid", "rmi" ]
audio: {
flac: "flac",
midi: ["mid", "midi", "kar", "rmi"],
mpeg: ["mpga", "mpega", "mp2", "mp3", "m4a", "mp2a", "m2a", "m3a"],
mpegurl: "m3u",
ogg: ["oga", "ogg", "spx"],
"x-aiff": ["aif", "aiff", "aifc"],
"x-ms-wma": "wma",
"x-wav": "wav",
adpcm: "adp",
mp4: "mp4a",
webm: "weba",
"x-aac": "aac",
"x-caf": "caf",
"x-matroska": "mka",
"x-pn-realaudio-plugin": "rmp",
xm: "xm",
mid: ["mid", "rmi"],
},
"image" : {
"gif" : "gif",
"ief" : "ief",
"jpeg" : [ "jpeg", "jpg", "jpe" ],
"pcx" : "pcx",
"png" : "png",
"svg+xml" : [ "svg", "svgz" ],
"tiff" : [ "tiff", "tif" ],
"x-icon" : "ico",
"bmp" : "bmp",
"webp" : "webp",
"x-pict" : [ "pic", "pct" ],
"x-tga" : "tga",
"cis-cod" : "cod"
image: {
gif: "gif",
ief: "ief",
jpeg: ["jpeg", "jpg", "jpe"],
pcx: "pcx",
png: "png",
"svg+xml": ["svg", "svgz"],
tiff: ["tiff", "tif"],
"x-icon": "ico",
bmp: "bmp",
webp: "webp",
"x-pict": ["pic", "pct"],
"x-tga": "tga",
"cis-cod": "cod",
},
"text" : {
"cache-manifest" : [ "manifest", "appcache" ],
"css" : "css",
"csv" : "csv",
"html" : [ "html", "htm", "shtml", "stm" ],
"mathml" : "mml",
"plain" : [ "txt", "text", "brf", "conf", "def", "list", "log", "in", "bas" ],
"richtext" : "rtx",
"tab-separated-values" : "tsv",
"x-bibtex" : "bib"
text: {
"cache-manifest": ["manifest", "appcache"],
css: "css",
csv: "csv",
html: ["html", "htm", "shtml", "stm"],
mathml: "mml",
plain: ["txt", "text", "brf", "conf", "def", "list", "log", "in", "bas"],
richtext: "rtx",
"tab-separated-values": "tsv",
"x-bibtex": "bib",
},
video: {
mpeg: ["mpeg", "mpg", "mpe", "m1v", "m2v", "mp2", "mpa", "mpv2"],
mp4: ["mp4", "mp4v", "mpg4"],
quicktime: ["qt", "mov"],
ogg: "ogv",
"vnd.mpegurl": ["mxu", "m4u"],
"x-flv": "flv",
"x-la-asf": ["lsf", "lsx"],
"x-mng": "mng",
"x-ms-asf": ["asf", "asx", "asr"],
"x-ms-wm": "wm",
"x-ms-wmv": "wmv",
"x-ms-wmx": "wmx",
"x-ms-wvx": "wvx",
"x-msvideo": "avi",
"x-sgi-movie": "movie",
"x-matroska": ["mpv", "mkv", "mk3d", "mks"],
"3gpp2": "3g2",
h261: "h261",
h263: "h263",
h264: "h264",
jpeg: "jpgv",
jpm: ["jpm", "jpgm"],
mj2: ["mj2", "mjp2"],
"vnd.ms-playready.media.pyv": "pyv",
"vnd.uvvu.mp4": ["uvu", "uvvu"],
"vnd.vivo": "viv",
webm: "webm",
"x-f4v": "f4v",
"x-m4v": "m4v",
"x-ms-vob": "vob",
"x-smv": "smv",
},
"video" : {
"mpeg" : [ "mpeg", "mpg", "mpe", "m1v", "m2v", "mp2", "mpa", "mpv2" ],
"mp4" : [ "mp4", "mp4v", "mpg4" ],
"quicktime" : [ "qt", "mov" ],
"ogg" : "ogv",
"vnd.mpegurl" : [ "mxu", "m4u" ],
"x-flv" : "flv",
"x-la-asf" : [ "lsf", "lsx" ],
"x-mng" : "mng",
"x-ms-asf" : [ "asf", "asx", "asr" ],
"x-ms-wm" : "wm",
"x-ms-wmv" : "wmv",
"x-ms-wmx" : "wmx",
"x-ms-wvx" : "wvx",
"x-msvideo" : "avi",
"x-sgi-movie" : "movie",
"x-matroska" : [ "mpv", "mkv", "mk3d", "mks" ],
"3gpp2" : "3g2",
"h261" : "h261",
"h263" : "h263",
"h264" : "h264",
"jpeg" : "jpgv",
"jpm" : [ "jpm", "jpgm" ],
"mj2" : [ "mj2", "mjp2" ],
"vnd.ms-playready.media.pyv" : "pyv",
"vnd.uvvu.mp4" : [ "uvu", "uvvu" ],
"vnd.vivo" : "viv",
"webm" : "webm",
"x-f4v" : "f4v",
"x-m4v" : "m4v",
"x-ms-vob" : "vob",
"x-smv" : "smv"
}
};
var mimeTypes = (function() {
var type, subtype, val, index, mimeTypes = {};
var mimeTypes = (function () {
var type,
subtype,
val,
index,
mimeTypes = {};
for (type in table) {
if (table.hasOwnProperty(type)) {
for (subtype in table[type]) {
@ -160,10 +164,13 @@ var mimeTypes = (function() {
return mimeTypes;
})();
var defaultValue = "text/plain";//"application/octet-stream";
var defaultValue = "text/plain"; //"application/octet-stream";
function lookup(filename) {
return filename && mimeTypes[filename.split(".").pop().toLowerCase()] || defaultValue;
};
return (
(filename && mimeTypes[filename.split(".").pop().toLowerCase()]) ||
defaultValue
);
}
export default { lookup };

View file

@ -29,7 +29,6 @@ class Path {
this.filename = parsed.base;
this.extension = parsed.ext.slice(1);
}
/**
@ -37,7 +36,7 @@ class Path {
* @param {string} what
* @returns {object}
*/
parse (what) {
parse(what) {
return path.parse(what);
}
@ -45,7 +44,7 @@ class Path {
* @param {string} what
* @returns {boolean}
*/
isAbsolute (what) {
isAbsolute(what) {
return path.isAbsolute(what || this.path);
}
@ -54,8 +53,8 @@ class Path {
* @param {string} what
* @returns {boolean}
*/
isDirectory (what) {
return (what.charAt(what.length-1) === "/");
isDirectory(what) {
return what.charAt(what.length - 1) === "/";
}
/**
@ -65,7 +64,7 @@ class Path {
* @param {string} what
* @returns {string} resolved
*/
resolve (what) {
resolve(what) {
return path.resolve(this.directory, what);
}
@ -76,8 +75,8 @@ class Path {
* @param {string} what
* @returns {string} relative
*/
relative (what) {
var isAbsolute = what && (what.indexOf("://") > -1);
relative(what) {
var isAbsolute = what && what.indexOf("://") > -1;
if (isAbsolute) {
return what;
@ -94,7 +93,7 @@ class Path {
* Return the path string
* @returns {string} path
*/
toString () {
toString() {
return this.path;
}
}

View file

@ -1,4 +1,4 @@
import {defer, requestAnimationFrame} from "./core";
import { defer, requestAnimationFrame } from "./core";
/**
* Queue for handling tasks one at a time
@ -6,7 +6,7 @@ import {defer, requestAnimationFrame} from "./core";
* @param {scope} context what this will resolve to in the tasks
*/
class Queue {
constructor(context){
constructor(context) {
this._q = [];
this.context = context;
this.tick = requestAnimationFrame;
@ -28,29 +28,26 @@ class Queue {
// if(args && !Array.isArray(args)) {
// args = [args];
// }
if(!task) {
if (!task) {
throw new Error("No Task Provided");
}
if(typeof task === "function"){
if (typeof task === "function") {
deferred = new defer();
promise = deferred.promise;
queued = {
"task" : task,
"args" : args,
task: task,
args: args,
//"context" : context,
"deferred" : deferred,
"promise" : promise
deferred: deferred,
promise: promise,
};
} else {
// Task is a promise
queued = {
"promise" : task
promise: task,
};
}
this._q.push(queued);
@ -69,48 +66,46 @@ class Queue {
* Run one item
* @return {Promise}
*/
dequeue(){
dequeue() {
var inwait, task, result;
if(this._q.length && !this.paused) {
if (this._q.length && !this.paused) {
inwait = this._q.shift();
task = inwait.task;
if(task){
if (task) {
// console.log(task)
result = task.apply(this.context, inwait.args);
if(result && typeof result["then"] === "function") {
if (result && typeof result["then"] === "function") {
// Task is a function that returns a promise
return result.then(function(){
return result.then(
function () {
inwait.deferred.resolve.apply(this.context, arguments);
}.bind(this), function() {
}.bind(this),
function () {
inwait.deferred.reject.apply(this.context, arguments);
}.bind(this));
}.bind(this)
);
} else {
// Task resolves immediately
inwait.deferred.resolve.apply(this.context, result);
return inwait.promise;
}
} else if(inwait.promise) {
} else if (inwait.promise) {
// Task is a promise
return inwait.promise;
}
} else {
inwait = new defer();
inwait.deferred.resolve();
return inwait.promise;
}
}
// Run All Immediately
dump(){
while(this._q.length) {
dump() {
while (this._q.length) {
this.dequeue();
}
}
@ -119,31 +114,27 @@ class Queue {
* Run all tasks sequentially, at convince
* @return {Promise}
*/
run(){
if(!this.running){
run() {
if (!this.running) {
this.running = true;
this.defered = new defer();
}
this.tick.call(window, () => {
if(this._q.length) {
this.dequeue()
.then(function(){
if (this._q.length) {
this.dequeue().then(
function () {
this.run();
}.bind(this));
}.bind(this)
);
} else {
this.defered.resolve();
this.running = undefined;
}
});
// Unpause
if(this.paused == true) {
if (this.paused == true) {
this.paused = false;
}
@ -154,28 +145,27 @@ class Queue {
* Flush all, as quickly as possible
* @return {Promise}
*/
flush(){
if(this.running){
flush() {
if (this.running) {
return this.running;
}
if(this._q.length) {
this.running = this.dequeue()
.then(function(){
if (this._q.length) {
this.running = this.dequeue().then(
function () {
this.running = undefined;
return this.flush();
}.bind(this));
}.bind(this)
);
return this.running;
}
}
/**
* Clear all items in wait
*/
clear(){
clear() {
this._q = [];
}
@ -183,28 +173,27 @@ class Queue {
* Get the number of tasks in the queue
* @return {number} tasks
*/
length(){
length() {
return this._q.length;
}
/**
* Pause a running queue
*/
pause(){
pause() {
this.paused = true;
}
/**
* End the queue
*/
stop(){
stop() {
this._q = [];
this.running = false;
this.paused = true;
}
}
/**
* Create a new task from a callback
* @class
@ -215,13 +204,12 @@ class Queue {
* @return {function} task
*/
class Task {
constructor(task, args, context){
return function(){
constructor(task, args, context) {
return function () {
var toApply = arguments || [];
return new Promise( (resolve, reject) => {
var callback = function(value, err){
return new Promise((resolve, reject) => {
var callback = function (value, err) {
if (!value && err) {
reject(err);
} else {
@ -233,14 +221,10 @@ class Task {
// Apply all arguments to the functions
task.apply(context || this, toApply);
});
};
}
}
export default Queue;
export { Task };

View file

@ -1,21 +1,20 @@
import { qs, qsa } from "./core";
import { qs } from "./core";
import Url from "./url";
import Path from "./path";
export function replaceBase(doc, section){
export function replaceBase(doc, section) {
var base;
var head;
var url = section.url;
var absolute = (url.indexOf("://") > -1);
var absolute = url.indexOf("://") > -1;
if(!doc){
if (!doc) {
return;
}
head = qs(doc, "head");
base = qs(head, "base");
if(!base) {
if (!base) {
base = doc.createElement("base");
head.insertBefore(base, head.firstChild);
}
@ -28,12 +27,12 @@ export function replaceBase(doc, section){
base.setAttribute("href", url);
}
export function replaceCanonical(doc, section){
export function replaceCanonical(doc, section) {
var head;
var link;
var url = section.canonical;
if(!doc){
if (!doc) {
return;
}
@ -50,11 +49,11 @@ export function replaceCanonical(doc, section){
}
}
export function replaceMeta(doc, section){
export function replaceMeta(doc, section) {
var head;
var meta;
var id = section.idref;
if(!doc){
if (!doc) {
return;
}
@ -73,7 +72,6 @@ export function replaceMeta(doc, section){
// TODO: move me to Contents
export function replaceLinks(contents, fn) {
var links = contents.querySelectorAll("a[href]");
if (!links.length) {
@ -82,32 +80,29 @@ export function replaceLinks(contents, fn) {
var base = qs(contents.ownerDocument, "base");
var location = base ? base.getAttribute("href") : undefined;
var replaceLink = function(link){
var replaceLink = function (link) {
var href = link.getAttribute("href");
if(href.indexOf("mailto:") === 0){
if (href.indexOf("mailto:") === 0) {
return;
}
var absolute = (href.indexOf("://") > -1);
if(absolute){
var absolute = href.indexOf("://") > -1;
if (absolute) {
link.setAttribute("target", "_blank");
}else{
} else {
var linkUrl;
try {
linkUrl = new Url(href, location);
} catch(error) {
} catch (error) {
// NOOP
}
link.onclick = function(){
if(linkUrl && linkUrl.hash) {
link.onclick = function () {
if (linkUrl && linkUrl.hash) {
fn(linkUrl.Path.path + linkUrl.hash);
} else if(linkUrl){
} else if (linkUrl) {
fn(linkUrl.Path.path);
} else {
fn(href);
@ -121,12 +116,10 @@ export function replaceLinks(contents, fn) {
for (var i = 0; i < links.length; i++) {
replaceLink(links[i]);
}
}
export function substitute(content, urls, replacements) {
urls.forEach(function(url, i){
urls.forEach(function (url, i) {
if (url && replacements[i]) {
// Account for special characters in the file name.
// See https://stackoverflow.com/a/6318729.

View file

@ -1,8 +1,8 @@
import {defer, isXml, parse} from "./core";
import { defer, isXml, parse } from "./core";
import Path from "./path";
function request(url, type, withCredentials, headers) {
var supportsURL = (typeof window != "undefined") ? window.URL : false; // TODO: fallback for url if window isn't defined
var supportsURL = typeof window != "undefined" ? window.URL : false; // TODO: fallback for url if window isn't defined
var BLOB_RESPONSE = supportsURL ? "blob" : "arraybuffer";
var deferred = new defer();
@ -18,11 +18,11 @@ function request(url, type, withCredentials, headers) {
if (!("overrideMimeType" in xhrPrototype)) {
// IE10 might have response, but not overrideMimeType
Object.defineProperty(xhrPrototype, "overrideMimeType", {
value: function xmlHttpRequestOverrideMimeType() {}
value: function xmlHttpRequestOverrideMimeType() {},
});
}
if(withCredentials) {
if (withCredentials) {
xhr.withCredentials = true;
}
@ -31,38 +31,28 @@ function request(url, type, withCredentials, headers) {
xhr.open("GET", url, true);
for(header in headers) {
for (header in headers) {
xhr.setRequestHeader(header, headers[header]);
}
if(type == "json") {
if (type == "json") {
xhr.setRequestHeader("Accept", "application/json");
}
// If type isn"t set, determine it from the file extension
if(!type) {
if (!type) {
type = new Path(url).extension;
}
if(type == "blob"){
if (type == "blob") {
xhr.responseType = BLOB_RESPONSE;
}
if(isXml(type)) {
// xhr.responseType = "document";
if (isXml(type)) {
xhr.overrideMimeType("text/xml"); // for OPF parsing
}
if(type == "xhtml") {
// xhr.responseType = "document";
}
if(type == "html" || type == "htm") {
// xhr.responseType = "document";
}
if(type == "binary") {
if (type == "binary") {
xhr.responseType = "arraybuffer";
}
@ -76,18 +66,19 @@ function request(url, type, withCredentials, headers) {
if (this.readyState === XMLHttpRequest.DONE) {
var responseXML = false;
if(this.responseType === "" || this.responseType === "document") {
if (this.responseType === "" || this.responseType === "document") {
responseXML = this.responseXML;
}
if (this.status === 200 || this.status === 0 || responseXML) { //-- Firefox is reporting 0 for blob urls
if (this.status === 200 || this.status === 0 || responseXML) {
//-- Firefox is reporting 0 for blob urls
var r;
if (!this.response && !responseXML) {
deferred.reject({
status: this.status,
message : "Empty Response",
stack : new Error().stack
message: "Empty Response",
stack: new Error().stack,
});
return deferred.promise;
}
@ -96,50 +87,41 @@ function request(url, type, withCredentials, headers) {
deferred.reject({
status: this.status,
response: this.response,
message : "Forbidden",
stack : new Error().stack
message: "Forbidden",
stack: new Error().stack,
});
return deferred.promise;
}
if(responseXML){
if (responseXML) {
r = this.responseXML;
} else
if(isXml(type)){
} else if (isXml(type)) {
// xhr.overrideMimeType("text/xml"); // for OPF parsing
// If this.responseXML wasn't set, try to parse using a DOMParser from text
r = parse(this.response, "text/xml");
}else
if(type == "xhtml"){
} else if (type == "xhtml") {
r = parse(this.response, "application/xhtml+xml");
}else
if(type == "html" || type == "htm"){
} else if (type == "html" || type == "htm") {
r = parse(this.response, "text/html");
}else
if(type == "json"){
} else if (type == "json") {
r = JSON.parse(this.response);
}else
if(type == "blob"){
if(supportsURL) {
} else if (type == "blob") {
if (supportsURL) {
r = this.response;
} else {
//-- Safari doesn't support responseType blob, so create a blob from arraybuffer
r = new Blob([this.response]);
}
}else{
} else {
r = this.response;
}
deferred.resolve(r);
} else {
deferred.reject({
status: this.status,
message : this.response,
stack : new Error().stack
message: this.response,
stack: new Error().stack,
});
}
}
}

View file

@ -8,7 +8,7 @@ export default function scrollType() {
if (definer.scrollLeft > 0) {
type = "default";
} else {
if (typeof Element !== 'undefined' && Element.prototype.scrollIntoView) {
if (typeof Element !== "undefined" && Element.prototype.scrollIntoView) {
definer.children[0].children[1].scrollIntoView();
if (definer.scrollLeft < 0) {
type = "negative";
@ -26,8 +26,8 @@ export default function scrollType() {
}
export function createDefiner() {
var definer = document.createElement('div');
definer.dir="rtl";
var definer = document.createElement("div");
definer.dir = "rtl";
definer.style.position = "fixed";
definer.style.width = "1px";
@ -36,14 +36,14 @@ export function createDefiner() {
definer.style.left = "0px";
definer.style.overflow = "hidden";
var innerDiv = document.createElement('div');
var innerDiv = document.createElement("div");
innerDiv.style.width = "2px";
var spanA = document.createElement('span');
var spanA = document.createElement("span");
spanA.style.width = "1px";
spanA.style.display = "inline-block";
var spanB = document.createElement('span');
var spanB = document.createElement("span");
spanB.style.width = "1px";
spanB.style.display = "inline-block";

View file

@ -1,5 +1,5 @@
import Path from "./path";
import path from "path-webpack";
import Path from "./path";
/**
* creates a Url object for parsing and manipulation of a url string
@ -9,7 +9,7 @@ import path from "path-webpack";
*/
class Url {
constructor(urlString, baseString) {
var absolute = (urlString.indexOf("://") > -1);
var absolute = urlString.indexOf("://") > -1;
var pathname = urlString;
var basePath;
@ -22,17 +22,21 @@ class Url {
this.search = "";
this.base = baseString;
if (!absolute &&
if (
!absolute &&
baseString !== false &&
typeof(baseString) !== "string" &&
window && window.location) {
typeof baseString !== "string" &&
window &&
window.location
) {
this.base = window.location.href;
}
// URL Polyfill doesn't throw an error if base is empty
if (absolute || this.base) {
try {
if (this.base) { // Safari doesn't like an undefined base
if (this.base) {
// Safari doesn't like an undefined base
this.Url = new URL(urlString, this.base);
} else {
this.Url = new URL(urlString);
@ -44,7 +48,7 @@ class Url {
this.hash = this.Url.hash;
this.search = this.Url.search;
pathname = this.Url.pathname + (this.Url.search ? this.Url.search : '');
pathname = this.Url.pathname + (this.Url.search ? this.Url.search : "");
} catch (e) {
// Skip URL parsing
this.Url = undefined;
@ -61,13 +65,12 @@ class Url {
this.directory = this.Path.directory;
this.filename = this.Path.filename;
this.extension = this.Path.extension;
}
/**
* @returns {Path}
*/
path () {
path() {
return this.Path;
}
@ -76,8 +79,8 @@ class Url {
* @param {string} what
* @returns {string} url
*/
resolve (what) {
var isAbsolute = (what.indexOf("://") > -1);
resolve(what) {
var isAbsolute = what.indexOf("://") > -1;
var fullpath;
if (isAbsolute) {
@ -93,14 +96,14 @@ class Url {
* @param {string} what
* @returns {string} path
*/
relative (what) {
relative(what) {
return path.relative(what, this.directory);
}
/**
* @returns {string}
*/
toString () {
toString() {
return this.href;
}
}

View file

@ -1,371 +0,0 @@
var domain = window.location.origin;
module('Core');
test("EPUBJS.core.resolveUrl", 1, function() {
var a = "http://example.com/fred/chasen/";
var b = "/chasen/derf.html";
var resolved = EPUBJS.core.resolveUrl(a, b);
equal( resolved, "http://example.com/fred/chasen/derf.html", "resolved" );
});
test("EPUBJS.core.resolveUrl ../", 1, function() {
var a = "http://example.com/fred/chasen/";
var b = "../derf.html";
var resolved = EPUBJS.core.resolveUrl(a, b);
equal( resolved, "http://example.com/fred/derf.html", "resolved" );
});
test("EPUBJS.core.resolveUrl folders", 1, function() {
var a = "/fred/chasen/";
var b = "/fred/chasen/derf.html";
var resolved = EPUBJS.core.resolveUrl(a, b);
equal( resolved, "/fred/chasen/derf.html", "resolved" );
});
test("EPUBJS.core.resolveUrl ../folders", 1, function() {
var a = "/fred/chasen/";
var b = "../../derf.html";
var resolved = EPUBJS.core.resolveUrl(a, b);
equal( resolved, "/derf.html", "resolved" );
});
module('Create');
asyncTest("Create new ePub(/path/to/epub/)", 1, function() {
var book = ePub("../books/moby-dick/");
book.opened.then(function(){
equal( book.url, "../books/moby-dick/OPS/", "book url is passed to new EPUBJS.Book" );
start();
});
});
asyncTest("Create new ePub(/path/to/epub/package.opf)", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.url, domain + "/books/moby-dick/OPS/", "bookPath is passed to new EPUBJS.Book" );
start();
});
});
asyncTest("Open using ePub(/path/to/epub/package.opf)", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.packageUrl, "../books/moby-dick/OPS/package.opf", "packageUrl is set" );
start();
});
});
asyncTest("Open Remote ePub", 1, function() {
var book = ePub("https://s3.amazonaws.com/moby-dick/");
book.opened.then(function(){
equal( book.packageUrl, "https://s3.amazonaws.com/moby-dick/OPS/package.opf", "packageUrl is set" );
start();
});
});
asyncTest("Open Remote ePub from Package", 1, function() {
var book = ePub("https://s3.amazonaws.com/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.packageUrl, "https://s3.amazonaws.com/moby-dick/OPS/package.opf", "packageUrl is set" );
start();
});
});
asyncTest("Find Epub Package", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.packageUrl, "../books/moby-dick/OPS/package.opf", "packageUrl is set" );
start();
});
});
module('Parse');
//TODO: add mocked tests for parser
asyncTest("Manifest", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( Object.keys(book.package.manifest).length, 152, "Manifest is parsed" );
start();
});
});
asyncTest("Metadata", 3, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.package.metadata.creator, "Herman Melville", "Creator metadata is parsed" );
equal( book.package.metadata.title, "Moby-Dick", "Title metadata is parsed" );
equal( book.package.metadata.identifier, "code.google.com.epub-samples.moby-dick-basic", "Identifier metadata is parsed" );
start();
});
});
asyncTest("Spine", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.package.spine.length, 144, "Spine is parsed" );
start();
});
});
asyncTest("Cover", 1, function() {
var book = ePub("../books/moby-dick/");
book.opened.then(function(){
equal( book.cover, "../books/moby-dick/OPS/images/9780316000000.jpg", "Cover is set" );
start();
});
});
module('Spine');
asyncTest("Length", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.spine.length, 144, "All spine items present" );
start();
});
});
asyncTest("Items", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
equal( book.spine.spineItems.length, 144, "All spine items added" );
start();
});
});
asyncTest("First Item", 2, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
var section = book.spine.get(0);
equal( section.href, "cover.xhtml", "First spine item href found" );
equal( section.cfiBase, "/6/2[cover]", "First spine item cfi found" );
start();
});
});
asyncTest("Find Item by Href", 2, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
var section = book.spine.get("chapter_001.xhtml");
equal( section.href, "chapter_001.xhtml", "chap 1 spine item href found" );
equal( section.cfiBase, "/6/14[xchapter_001]", "chap 1 spine item cfi found" );
start();
});
});
asyncTest("Find Item by ID", 2, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
var section = book.spine.get("#xchapter_050");
equal( section.href, "chapter_050.xhtml", "chap 50 spine item href found" );
equal( section.cfiBase, "/6/112[xchapter_050]", "chap 50 spine item cfi found" );
start();
});
});
asyncTest("Render Spine Item", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
var section = book.spine.get("#xchapter_050");
section.render().then(function(content){
equal( content.substring(377, 429), "<h1>Chapter 50. Ahabs Boat and Crew. Fedallah.</h1>", "Chapter text rendered as string" );
});
start();
});
});
module('Navigation');
asyncTest("NCX & Nav", 2, function() {
var book = ePub("../books/moby-dick/");
book.opened.then(function(){
equal( book.navigation.navUrl, "../books/moby-dick/OPS/toc.xhtml", "Nav URL found" );
equal( book.navigation.ncxUrl, "../books/moby-dick/OPS/toc.ncx", "NCX URL found" );
start();
});
});
asyncTest("Load TOC Auto Pick", 1, function() {
var book = ePub("../books/moby-dick/");
book.opened.then(function(){
book.navigation.load().then(function(toc){
equal( toc.length, 141, "Full Nav toc parsed" );
start();
});
});
});
asyncTest("Load TOC from Nav", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
var nav = book.navigation.nav.load();
nav.then(function(toc){
equal( toc.length, 141, "Full Nav toc parsed" );
start();
});
});
});
asyncTest("Load TOC from NCX", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.opened.then(function(){
var ncx = book.navigation.ncx.load();
ncx.then(function(toc){
equal( toc.length, 14, "Full NCX toc parsed" );
start();
});
});
});
asyncTest("Get all TOC", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.loaded.navigation.then(function(){
equal( book.navigation.get().length, 141, "Full Nav toc parsed" );
start();
});
});
asyncTest("Get TOC item by href", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.loaded.navigation.then(function(){
var item = book.navigation.get("chapter_001.xhtml");
equal( item.id, "toc-chapter_001", "Found TOC item" );
start();
});
});
asyncTest("Get TOC item by ID", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
book.loaded.navigation.then(function(){
var item = book.navigation.get("#toc-chapter_001");
equal( item.href, "chapter_001.xhtml", "Found TOC item" );
start();
});
});
module('Hooks');
asyncTest("Register a new hook", 1, function() {
var beforeDisplay = new EPUBJS.Hook();
beforeDisplay.register(function(args){
var defer = new RSVP.defer();
console.log("ran", 1);
defer.resolve();
return defer.promise;
});
equal( beforeDisplay.hooks.length, 1, "Registered a hook" );
start();
// this.beforeDisplay.trigger(args).then(function(){});
});
asyncTest("Trigger all new hook", 4, function() {
var beforeDisplay = new EPUBJS.Hook(this);
this.testerObject = {tester: 1};
beforeDisplay.register(function(testerObject){
var defer = new RSVP.defer();
start();
equal( testerObject.tester, 1, "tester is 1" );
stop();
testerObject.tester += 1;
defer.resolve();
return defer.promise;
});
beforeDisplay.register(function(testerObject){
var defer = new RSVP.defer();
start();
equal(testerObject.tester, 2, "tester is 2" );
stop();
testerObject.tester += 1;
defer.resolve();
return defer.promise;
});
start();
equal( beforeDisplay.hooks.length, 2, "Added two hooks" );
stop();
beforeDisplay.trigger(this.testerObject).then(function(){
start();
equal( this.testerObject.tester, 3, "tester is 3" );
}.bind(this));
});

View file

@ -1,16 +0,0 @@
module('Rendering');
/*
asyncTest("Render To", 1, function() {
var book = ePub("../books/moby-dick/OPS/package.opf");
var rendition = book.renderTo("qunit-fixture", {width:400, height:600});
var displayed = rendition.display(0);
displayed.then(function(){
equal( $( "iframe", "#qunit-fixture" ).length, 1, "iframe added successfully" );
start();
});
});
*/

View file

@ -1,53 +1,60 @@
import Rendition from "./rendition";
import View from "./managers/view";
import Rendition from "./rendition";
export default class Annotations {
constructor(rendition: Rendition);
add(type: string, cfiRange: string, data?: object, cb?: Function, className?: string, styles?: object): Annotation;
add(
type: string,
cfiRange: string,
data?: object,
cb?: Function,
className?: string,
styles?: object
): Annotation;
remove(cfiRange: string, type: string): void;
highlight(cfiRange: string, data?: object, cb?: Function, className?: string, styles?: object): void;
underline(cfiRange: string, data?: object, cb?: Function, className?: string, styles?: object): void;
highlight(
cfiRange: string,
data?: object,
cb?: Function,
className?: string,
styles?: object
): void;
underline(
cfiRange: string,
data?: object,
cb?: Function,
className?: string,
styles?: object
): void;
mark(cfiRange: string, data?: object, cb?: Function): void;
each(): Array<Annotation>;
each(): Array<Annotation>
private _removeFromAnnotationBySectionIndex(sectionIndex: number, hash: string): void;
private _removeFromAnnotationBySectionIndex(
sectionIndex: number,
hash: string
): void;
private _annotationsAt(index: number): void;
private inject(view: View): void;
private clear(view: View): void;
}
declare class Annotation {
constructor(options: {
type: string,
cfiRange: string,
data?: object,
sectionIndex?: number,
cb?: Function,
className?: string,
styles?: object
type: string;
cfiRange: string;
data?: object;
sectionIndex?: number;
cb?: Function;
className?: string;
styles?: object;
});
update(data: object): void;
attach(view: View): any;
detach(view: View): any;
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
}

20
types/archive.d.ts vendored
View file

@ -1,27 +1,25 @@
import JSZip = require('jszip');
import JSZip = require("jszip");
export default class Archive {
constructor();
open(input: BinaryType, isBase64?: boolean): Promise<JSZip>;
openUrl(zipUrl: string, isBase64?: boolean): Promise<JSZip>;
request(url: string, type?: string): Promise<Blob | string | JSON | Document | XMLDocument>;
request(
url: string,
type?: string
): Promise<Blob | string | JSON | Document | XMLDocument>;
getBlob(url: string, mimeType?: string): Promise<Blob>;
getText(url: string): Promise<string>;
getBase64(url: string, mimeType?: string): Promise<string>;
createUrl(url: string, options: { base64: boolean }): Promise<string>;
revokeUrl(url: string): void;
destroy(): void;
private checkRequirements(): void;
private handleResponse(response: any, type?: string): Blob | string | JSON | Document | XMLDocument;
private handleResponse(
response: any,
type?: string
): Blob | string | JSON | Document | XMLDocument;
}

90
types/book.d.ts vendored
View file

@ -1,32 +1,34 @@
import {
import Archive from "./archive";
import Container from "./container";
import Locations from "./locations";
import Navigation from "./navigation";
import Packaging, {
PackagingManifestObject,
PackagingMetadataObject,
PackagingSpineItem,
PackagingObject
} from "./packaging";
import PageList, { PageListItem } from "./pagelist";
import Rendition, { RenditionOptions } from "./rendition";
import Section, { SpineItem } from "./section";
import Archive from "./archive";
import Navigation from "./navigation";
import PageList, {PageListItem} from "./pagelist";
import Spine from "./spine";
import Locations from "./locations";
import Url from "./utils/url";
import Path from "./utils/path";
import Resources from "./resources";
import Container from "./container";
import Packaging from "./packaging";
import Section, { SpineItem } from "./section";
import Spine from "./spine";
import Store from "./store";
import Path from "./utils/path";
import Url from "./utils/url";
export interface BookOptions {
requestMethod?: (url: string, type: string, withCredentials: object, headers: object) => Promise<object>;
requestCredentials?: object,
requestHeaders?: object,
encoding?: string,
replacements?: string,
canonical?: (path: string) => string,
openAs?: string,
store?: string
requestMethod?: (
url: string,
type: string,
withCredentials: object,
headers: object
) => Promise<object>;
requestCredentials?: object;
requestHeaders?: object;
encoding?: string;
replacements?: string;
canonical?: (path: string) => string;
openAs?: string;
store?: string;
}
export default class Book {
@ -38,14 +40,14 @@ export default class Book {
opened: Promise<Book>;
isOpen: boolean;
loaded: {
metadata: Promise<PackagingMetadataObject>,
spine: Promise<SpineItem[]>,
manifest: Promise<PackagingManifestObject>,
cover: Promise<string>,
navigation: Promise<Navigation>,
pageList: Promise<PageListItem[]>,
resources: Promise<string[]>,
}
metadata: Promise<PackagingMetadataObject>;
spine: Promise<SpineItem[]>;
manifest: Promise<PackagingManifestObject>;
cover: Promise<string>;
navigation: Promise<Navigation>;
pageList: Promise<PageListItem[]>;
resources: Promise<string[]>;
};
ready: Promise<void>;
request: Function;
spine: Spine;
@ -57,66 +59,40 @@ export default class Book {
archived: boolean;
archive: Archive;
resources: Resources;
rendition: Rendition
rendition: Rendition;
container: Container;
packaging: Packaging;
storage: Store;
canonical(path: string): string;
coverUrl(): Promise<string | null>;
destroy(): void;
determineType(input: string): string;
getRange(cfiRange: string): Promise<Range>;
key(identifier?: string): string;
load(path: string): Promise<object>;
loadNavigation(opf: XMLDocument): Promise<Navigation>;
open(input: string, what?: string): Promise<object>;
open(input: ArrayBuffer, what?: string): Promise<object>;
openContainer(url: string): Promise<string>;
openEpub(data: BinaryType, encoding?: string): Promise<Book>;
openManifest(url: string): Promise<Book>;
openPackaging(url: string): Promise<Book>;
renderTo(element: Element, options?: RenditionOptions): Rendition;
renderTo(element: string, options?: RenditionOptions): Rendition;
private replacements(): Promise<void>;
resolve(path: string, absolute?: boolean): string;
section(target: string): Section;
section(target: number): Section;
setRequestCredentials(credentials: object): void;
setRequestHeaders(headers: object): void;
unarchive(input: BinaryType, encoding?: string): Promise<Archive>;
store(name: string): Store;
unpack(opf: XMLDocument): Promise<Book>;
private replacements(): Promise<void>;
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
}

View file

@ -2,6 +2,5 @@ export default class Container {
constructor(containerDocument: Document);
parse(containerDocument: Document): void;
destroy(): void;
}

100
types/contents.d.ts vendored
View file

@ -1,16 +1,21 @@
import EpubCFI from "./epubcfi";
export interface ViewportSettings {
width: string,
height: string,
scale: string,
scalable: string,
minimum: string,
maximum: string
width: string;
height: string;
scale: string;
scalable: string;
minimum: string;
maximum: string;
}
export default class Contents {
constructor(doc: Document, content: Element, cfiBase: string, sectionIndex: number);
constructor(
doc: Document,
content: Element,
cfiBase: string,
sectionIndex: number
);
epubcfi: EpubCFI;
document: Document;
@ -23,117 +28,80 @@ export default class Contents {
static listenedEvents: string[];
addClass(className: string): void;
addScript(src: string): Promise<boolean>;
addStylesheet(src: string): Promise<boolean>;
addStylesheetRules(rules: Array<object> | object, key: string): Promise<boolean>;
addStylesheetRules(
rules: Array<object> | object,
key: string
): Promise<boolean>;
addStylesheetCss(serializedCss: string, key: string): Promise<boolean>;
cfiFromNode(node: Node, ignoreClass?: string): string;
cfiFromRange(range: Range, ignoreClass?: string): string;
columns(width: number, height: number, columnWidth: number, gap: number, dir: string): void;
columns(
width: number,
height: number,
columnWidth: number,
gap: number,
dir: string
): void;
contentHeight(h: number): number;
contentWidth(w: number): number;
css(property: string, value: string, priority?: boolean): string;
destroy(): void;
direction(dir: string): void;
fit(width: number, height: number): void;
height(h: number): number;
locationOf(target: string | EpubCFI, ignoreClass?: string): Promise<{ top: number, left: number }>;
locationOf(
target: string | EpubCFI,
ignoreClass?: string
): Promise<{ top: number; left: number }>;
map(layout: any): any;
mapPage(cfiBase: string, layout: object, start: number, end: number, dev: boolean): any;
mapPage(
cfiBase: string,
layout: object,
start: number,
end: number,
dev: boolean
): any;
overflow(overflow: string): string;
overflowX(overflow: string): string;
overflowY(overflow: string): string;
range(cfi: string, ignoreClass?: string): Range;
removeClass(className: any): void;
root(): Element;
scaler(scale: number, offsetX: number, offsetY: number): void;
scrollHeight(): number;
scrollWidth(): number;
size(width: number, height: number): void;
textHeight(): number;
textWidth(): number;
viewport(options: ViewportSettings): ViewportSettings;
width(w: number): number;
writingMode(mode: string): string;
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
private addEventListeners(): void;
private addSelectionListeners(): void;
private epubReadingSystem(name: string, version: string): object;
private expand(): void;
private fontLoadListeners(): void;
private imageLoadListeners(): void;
private layoutStyle(style: string): string;
private linksHandler(): void;
private listeners(): void;
private mediaQueryListeners(): void;
private onSelectionChange(e: Event): void;
private removeEventListeners(): void;
private removeListeners(): void;
private removeSelectionListeners(): void;
private resizeCheck(): void;
private resizeListeners(): void;
private resizeObservers(): void;
private transitionListeners(): void;
private triggerEvent(e: Event): void;
private triggerSelectedEvent(selection: Selection): void;
}

116
types/core.d.ts vendored
View file

@ -1,83 +1,87 @@
export module Core {
export function uuid(): string;
export function documentHeight(): number;
export function isElement(obj: object): boolean;
export function isNumber(n: any): boolean;
export function isFloat(n: any): boolean;
export function prefixed(unprefixed: string): string;
export function defaults(obj: object): object;
export function extend(target: object): object;
export function insert(item: any, array: Array<any>, compareFunction: Function): number;
export function locationOf(item: any, array: Array<any>, compareFunction: Function, _start: Function, _end: Function): number;
export function indexOfSorted(item: any, array: Array<any>, compareFunction: Function, _start: Function, _end: Function): number;
export function bounds(el: Element): { width: Number, height: Number};
export function borders(el: Element): { width: Number, height: Number};
export function insert(
item: any,
array: Array<any>,
compareFunction: Function
): number;
export function locationOf(
item: any,
array: Array<any>,
compareFunction: Function,
_start: Function,
_end: Function
): number;
export function indexOfSorted(
item: any,
array: Array<any>,
compareFunction: Function,
_start: Function,
_end: Function
): number;
export function bounds(el: Element): { width: Number; height: Number };
export function borders(el: Element): { width: Number; height: Number };
export function nodeBounds(node: Node): object;
export function windowBounds(): { width: Number, height: Number, top: Number, left: Number, right: Number, bottom: Number };
export function windowBounds(): {
width: Number;
height: Number;
top: Number;
left: Number;
right: Number;
bottom: Number;
};
export function indexOfNode(node: Node, typeId: string): number;
export function indexOfTextNode(textNode: Node): number;
export function indexOfElementNode(elementNode: Element): number;
export function isXml(ext: string): boolean;
export function createBlob(content: any, mime: string): Blob;
export function createBlobUrl(content: any, mime: string): string;
export function revokeBlobUrl(url: string): void;
export function createBase64Url(content: any, mime: string): string
export function createBase64Url(content: any, mime: string): string;
export function type(obj: object): string;
export function parse(markup: string, mime: string, forceXMLDom: boolean): Document;
export function parse(
markup: string,
mime: string,
forceXMLDom: boolean
): Document;
export function qs(el: Element, sel: string): Element;
export function qsa(el: Element, sel: string): ArrayLike<Element>;
export function qsp(el: Element, sel: string, props: Array<object>): ArrayLike<Element>;
export function qsp(
el: Element,
sel: string,
props: Array<object>
): ArrayLike<Element>;
export function sprint(root: Node, func: Function): void;
export function treeWalker(root: Node, func: Function, filter: object | Function): void;
export function treeWalker(
root: Node,
func: Function,
filter: object | Function
): void;
export function walk(node: Node, callback: Function): void;
export function blob2base64(blob: Blob): string;
export function defer(): Promise<any>;
export function querySelectorByType(html: Element, element: string, type: string): Array<Element>;
export function querySelectorByType(
html: Element,
element: string,
type: string
): Array<Element>;
export function findChildren(el: Element): Array<Element>;
export function parents(node: Element): Array<Element>;
export function filterChildren(el: Element, nodeName: string, single: boolean): Array<Element>;
export function getParentByTagName(node: Element, tagname: string): Array<Element>;
export class RangeObject extends Range {
}
export function filterChildren(
el: Element,
nodeName: string,
single: boolean
): Array<Element>;
export function getParentByTagName(
node: Element,
tagname: string
): Array<Element>;
export class RangeObject extends Range {}
}

7
types/epub.d.ts vendored
View file

@ -2,5 +2,8 @@ import Book, { BookOptions } from "./book";
export default Epub;
declare function Epub(urlOrData: string | ArrayBuffer, options?: BookOptions) : Book;
declare function Epub(options?: BookOptions) : Book;
declare function Epub(
urlOrData: string | ArrayBuffer,
options?: BookOptions
): Book;
declare function Epub(options?: BookOptions): Book;

105
types/epubcfi.d.ts vendored
View file

@ -1,97 +1,98 @@
interface EpubCFISegment {
steps: Array<object>,
steps: Array<object>;
terminal: {
offset: number,
assertion: string
}
offset: number;
assertion: string;
};
}
interface EpubCFIStep {
id: string,
tagName: string,
type: number,
index: number
id: string;
tagName: string;
type: number;
index: number;
}
interface EpubCFIComponent {
steps: Array<EpubCFIStep>,
steps: Array<EpubCFIStep>;
terminal: {
offset: number,
assertion: string
}
offset: number;
assertion: string;
};
}
export default class EpubCFI {
constructor(cfiFrom?: string | Range | Node, base?: string | object, ignoreClass?: string);
constructor(
cfiFrom?: string | Range | Node,
base?: string | object,
ignoreClass?: string
);
base: EpubCFIComponent;
spinePos: number;
range: boolean;
isCfiString(str: string): boolean;
fromNode(anchor: Node, base: string | object, ignoreClass?: string): EpubCFI;
fromRange(range: Range, base: string | object, ignoreClass?: string): EpubCFI;
parse(cfiStr: string): EpubCFI;
collapse(toStart?: boolean): void;
compare(cfiOne: string | EpubCFI, cfiTwo: string | EpubCFI): number;
equalStep(stepA: object, stepB: object): boolean;
filter(anchor: Element, ignoreClass?: string): Element | false;
toRange(_doc?: Document, ignoreClass?: string): Range;
toString(): string;
private filteredStep(node: Node, ignoreClass?: string): any;
private findNode(steps: Array<EpubCFIStep>, _doc?: Document, ignoreClass?: string): Node;
private fixMiss(steps: Array<EpubCFIStep>, offset: number, _doc?: Document, ignoreClass?: string): any;
private findNode(
steps: Array<EpubCFIStep>,
_doc?: Document,
ignoreClass?: string
): Node;
private fixMiss(
steps: Array<EpubCFIStep>,
offset: number,
_doc?: Document,
ignoreClass?: string
): any;
private checkType(cfi: string | Range | Node): string | false;
private generateChapterComponent(_spineNodeIndex: number, _pos: number, id: string): string;
private generateChapterComponent(
_spineNodeIndex: number,
_pos: number,
id: string
): string;
private getChapterComponent(cfiStr: string): string;
private getCharecterOffsetComponent(cfiStr: string): string;
private getPathComponent(cfiStr: string): string;
private getRange(cfiStr: string): string;
private joinSteps(steps: Array<EpubCFIStep>): Array<EpubCFIStep>;
private normalizedMap(children: Array<Node>, nodeType: number, ignoreClass?: string): object;
private normalizedMap(
children: Array<Node>,
nodeType: number,
ignoreClass?: string
): object;
private parseComponent(componentStr: string): object;
private parseStep(stepStr: string): object;
private parseTerminal(termialStr: string): object;
private patchOffset(anchor: Node, offset: number, ignoreClass?: string): number;
private pathTo(node: Node, offset: number, ignoreClass?: string): EpubCFISegment;
private patchOffset(
anchor: Node,
offset: number,
ignoreClass?: string
): number;
private pathTo(
node: Node,
offset: number,
ignoreClass?: string
): EpubCFISegment;
private position(anchor: Node): number;
private segmentString(segment: EpubCFISegment): string;
private step(node: Node): EpubCFIStep;
private stepsToQuerySelector(steps: Array<EpubCFIStep>): string;
private stepsToXpath(steps: Array<EpubCFIStep>): string;
private textNodes(container: Node, ignoreClass?: string): Array<Node>;
private walkToNode(steps: Array<EpubCFIStep>, _doc?: Document, ignoreClass?: string): Node;
private walkToNode(
steps: Array<EpubCFIStep>,
_doc?: Document,
ignoreClass?: string
): Node;
}

View file

@ -1,9 +0,0 @@
import ePub, { Book } from '../';
function testEpub() {
const epub = ePub("https://s3.amazonaws.com/moby-dick/moby-dick.epub");
const book = new Book("https://s3.amazonaws.com/moby-dick/moby-dick.epub", {});
}
testEpub();

16
types/index.d.ts vendored
View file

@ -8,13 +8,9 @@ export as namespace ePub;
export default Epub;
export { default as Book } from './book';
export { default as EpubCFI } from './epubcfi';
export { default as Rendition, Location } from './rendition';
export { default as Contents } from './contents';
export { default as Layout } from './layout';
export { NavItem } from './navigation';
declare namespace ePub {
}
export { default as Book } from "./book";
export { default as Contents } from "./contents";
export { default as EpubCFI } from "./epubcfi";
export { default as Layout } from "./layout";
export { NavItem } from "./navigation";
export { Location, default as Rendition } from "./rendition";

43
types/layout.d.ts vendored
View file

@ -1,10 +1,10 @@
import Contents from "./contents";
interface LayoutSettings {
layout: string,
spread: string,
minSpreadWidth: number,
evenSpreads: boolean
layout: string;
spread: string;
minSpreadWidth: number;
evenSpreads: boolean;
}
export default class Layout {
@ -13,35 +13,30 @@ export default class Layout {
settings: LayoutSettings;
name: string;
props: {
name: string,
spread: string,
flow: string,
width: number,
height: number,
spreadWidth: number,
delta: number,
columnWidth: number,
gap: number,
divisor: number
name: string;
spread: string;
flow: string;
width: number;
height: number;
spreadWidth: number;
delta: number;
columnWidth: number;
gap: number;
divisor: number;
};
flow(flow: string): string;
spread(spread: string, min: number): boolean;
calculate(_width:number, _height:number, _gap?:number): void;
calculate(_width: number, _height: number, _gap?: number): void;
format(contents: Contents): void | Promise<void>;
count(totalLength: number, pageLength: number): {spreads: Number, pages: Number};
count(
totalLength: number,
pageLength: number
): { spreads: Number; pages: Number };
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
private update(props: object): void;

26
types/locations.d.ts vendored
View file

@ -1,41 +1,29 @@
import Spine from "./spine";
import Section from "./section";
import EpubCFI from "./epubcfi";
import Section from "./section";
import Spine from "./spine";
export default class Locations {
constructor(spine: Spine, request?: Function, pause?: number);
generate(chars: number): Promise<Array<string>>;
process(section: Section): Promise<Array<string>>;
locationFromCfi(cfi: string | EpubCFI): Location;
percentageFromCfi(cfi: string | EpubCFI): number;
percentageFromLocation(loc: number): number;
cfiFromLocation(loc: number): string;
cfiFromPercentage(percentage: number): string;
load(locations: string): Array<string>;
save(): string;
currentLocation(): Location;
currentLocation(curr: string | number): void;
length(): number;
destroy(): void;
private createRange(): {
startContainer: Element,
startOffset: number,
endContainer: Element,
endOffset: number
startContainer: Element;
startOffset: number;
endContainer: Element;
endOffset: number;
};
private parse(contents: Node, cfiBase: string, chars: number) : Array<string>;
private parse(contents: Node, cfiBase: string, chars: number): Array<string>;
}

View file

@ -1,90 +1,59 @@
import Section from "../section";
import Layout from "../layout";
import Contents from "../contents";
import View, { ViewSettings } from "./view";
import Layout from "../layout";
import { EpubCFIPair } from "../mapping";
import Section from "../section";
import View, { ViewSettings } from "./view";
export interface ViewLocation {
index: number,
href: string,
pages: number[],
totalPages: number,
mapping: EpubCFIPair
index: number;
href: string;
pages: number[];
totalPages: number;
mapping: EpubCFIPair;
}
export interface ManagerOptions extends ViewSettings {
infinite?: boolean,
overflow?: string,
[key: string]: any
infinite?: boolean;
overflow?: string;
[key: string]: any;
}
export default class Manager {
constructor(options: object);
render(element: Element, size?: { width: Number, height: Number }): void;
resize(width: Number, height: Number): void;
onOrientationChange(e: Event): void;
private createView(section: Section): View;
display(section: Section, target: string | number): Promise<void>;
private afterDisplayed(view: View): void;
private afterResized(view: View): void;
private moveTo(offset: {top: Number, left: Number}): void;
private append(section: Section): Promise<void>;
private prepend(section: Section): Promise<void>;
next(): Promise<void>;
prev(): Promise<void>;
current(): View;
applyLayout(layout: Layout): void;
bounds(): object;
clear(): void;
current(): View;
currentLocation(): ViewLocation[];
destroy(): void;
direction(dir: string): void;
display(section: Section, target: string | number): Promise<void>;
getContents(): Contents[];
isRendered(): boolean;
next(): Promise<void>;
onOrientationChange(e: Event): void;
prev(): Promise<void>;
render(element: Element, size?: { width: Number; height: Number }): void;
resize(width: Number, height: Number): void;
setLayout(layout: Layout): void;
updateAxis(axis: string, forceUpdate: boolean): void;
updateFlow(flow: string): void;
updateLayout(): void;
visible(): View[];
private createView(section: Section): View;
private afterDisplayed(view: View): void;
private afterResized(view: View): void;
private moveTo(offset: { top: Number; left: Number }): void;
private append(section: Section): Promise<void>;
private prepend(section: Section): Promise<void>;
private scrollBy(x: number, y: number, silent: boolean): void;
private scrollTo(x: number, y: number, silent: boolean): void;
private onScroll(): void;
bounds(): object;
applyLayout(layout: Layout): void;
updateLayout(): void;
setLayout(layout: Layout): void;
updateAxis(axis: string, forceUpdate: boolean): void;
updateFlow(flow: string): void;
getContents(): Contents[];
direction(dir: string): void;
isRendered(): boolean;
destroy(): void;
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
}

View file

@ -1,80 +1,65 @@
import Section from "../section";
import Contents from "../contents";
import Layout from "../layout";
import Section from "../section";
export interface ViewSettings {
ignoreClass?: string,
axis?: string,
flow?: string,
layout?: Layout,
method?: string,
width?: number,
height?: number,
forceEvenPages?: boolean,
allowScriptedContent?: boolean
ignoreClass?: string;
axis?: string;
flow?: string;
layout?: Layout;
method?: string;
width?: number;
height?: number;
forceEvenPages?: boolean;
allowScriptedContent?: boolean;
}
export default class View {
constructor(section: Section, options: ViewSettings);
create(): any;
render(request?: Function, show?: boolean): Promise<void>;
reset(): void;
size(_width: Number, _height: Number): void;
load(content: Contents): Promise<any>;
setLayout(layout: Layout): void;
setAxis(axis: string): void;
display(request?: Function): Promise<any>;
show(): void;
hide(): void;
offset(): { top: Number, left: Number };
offset(): { top: Number; left: Number };
width(): Number;
height(): Number;
position(): object;
locationOf(target: string): { top: Number, left: Number };
locationOf(target: string): { top: Number; left: Number };
onDisplayed(view: View): void;
onResize(view: View): void;
bounds(force?: boolean): object;
highlight(
cfiRange: string,
data?: object,
cb?: Function,
className?: string,
styles?: object
): void;
highlight(cfiRange: string, data?: object, cb?: Function, className?: string, styles?: object): void;
underline(cfiRange: string, data?: object, cb?: Function, className?: string, styles?: object): void;
underline(
cfiRange: string,
data?: object,
cb?: Function,
className?: string,
styles?: object
): void;
mark(cfiRange: string, data?: object, cb?: Function): void;
unhighlight(cfiRange: string): void;
ununderline(cfiRange: string): void;
unmark(cfiRange: string): void;
destroy(): void;
private onLoad(event: Event, promise: Promise<any>): void;
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
}

29
types/mapping.d.ts vendored
View file

@ -1,34 +1,35 @@
import Layout from "./layout";
import Contents from "./contents";
import Layout from "./layout";
export interface EpubCFIPair {
start: string,
end: string
start: string;
end: string;
}
export interface RangePair {
start: Range,
end: Range
start: Range;
end: Range;
}
export default class Mapping {
constructor(layout: Layout, direction?: string, axis?: string, dev?: boolean);
page(contents: Contents, cfiBase: string, start: number, end: number): EpubCFIPair;
page(
contents: Contents,
cfiBase: string,
start: number,
end: number
): EpubCFIPair;
axis(axis: string): boolean;
private walk(root: Node, func: Function);
private findStart(root: Node, start: number, end: number): Range;
private findEnd(root: Node, start: number, end: number): Range;
private findTextStartRange(node: Node, start: number, end: number): Range;
private findTextEndRange(node: Node, start: number, end: number): Range;
private splitTextNodeIntoRanges(node: Node, _splitter?: string): Array<Range>;
private rangePairToCfiPair(cfiBase: string, rangePair: RangePair): EpubCFIPair;
private rangePairToCfiPair(
cfiBase: string,
rangePair: RangePair
): EpubCFIPair;
}

37
types/navigation.d.ts vendored
View file

@ -1,15 +1,15 @@
export interface NavItem {
id: string,
href: string,
label: string,
subitems?: Array<NavItem>,
parent?: string
id: string;
href: string;
label: string;
subitems?: Array<NavItem>;
parent?: string;
}
export interface LandmarkItem {
href?: string,
label?: string,
type?: string
href?: string;
label?: string;
type?: string;
}
export default class Navigation {
@ -19,28 +19,21 @@ export default class Navigation {
landmarks: Array<LandmarkItem>;
parse(xml: XMLDocument): void;
get(target: string) : NavItem;
landmark(type: string) : LandmarkItem;
get(target: string): NavItem;
landmark(type: string): LandmarkItem;
load(json: string): Array<NavItem>;
forEach(fn: (item: NavItem) => {}): any;
private unpack(toc: Array<NavItem>): void;
private parseNav(navHtml: XMLDocument): Array<NavItem>;
private navItem(item: Element): NavItem;
private parseLandmarks(navHtml: XMLDocument): Array<LandmarkItem>;
private landmarkItem(item: Element): LandmarkItem;
private parseNcx(navHtml: XMLDocument): Array<NavItem>;
private ncxItem(item: Element): NavItem;
private getByIndex(target: string, index: number, navItems: NavItem[]): NavItem;
private getByIndex(
target: string,
index: number,
navItems: NavItem[]
): NavItem;
}

76
types/packaging.d.ts vendored
View file

@ -1,47 +1,47 @@
import { SpineItem } from "./section";
export interface PackagingObject {
metadata: PackagingMetadataObject,
spine: Array<SpineItem>,
manifest: PackagingManifestObject,
navPath: string,
ncxPath: string,
coverPath: string,
spineNodeIndex: number
metadata: PackagingMetadataObject;
spine: Array<SpineItem>;
manifest: PackagingManifestObject;
navPath: string;
ncxPath: string;
coverPath: string;
spineNodeIndex: number;
}
export interface PackagingMetadataObject {
title: string,
creator: string,
description: string,
pubdate: string,
publisher: string,
identifier: string,
language: string,
rights: string,
modified_date: string,
layout: string,
orientation: string,
flow: string,
viewport: string,
spread: string,
direction: string,
title: string;
creator: string;
description: string;
pubdate: string;
publisher: string;
identifier: string;
language: string;
rights: string;
modified_date: string;
layout: string;
orientation: string;
flow: string;
viewport: string;
spread: string;
direction: string;
}
export interface PackagingSpineItem {
idref: string,
properties: Array<string>,
index: number
idref: string;
properties: Array<string>;
index: number;
}
export interface PackagingManifestItem {
href: string,
type: string,
properties: Array<string>
href: string;
type: string;
properties: Array<string>;
}
export interface PackagingManifestObject {
[key: string]: PackagingManifestItem
[key: string]: PackagingManifestItem;
}
export default class Packaging {
@ -56,24 +56,18 @@ export default class Packaging {
metadata: PackagingMetadataObject;
parse(packageDocument: XMLDocument): PackagingObject;
load(json: string): PackagingObject;
destroy(): void;
private parseMetadata(xml: Node): PackagingMetadataObject;
private parseManifest(xml: Node): PackagingManifestObject;
private parseSpine(xml: Node, manifest: PackagingManifestObject): Array<PackagingSpineItem>;
private parseSpine(
xml: Node,
manifest: PackagingManifestObject
): Array<PackagingSpineItem>;
private findNavPath(manifestNode: Node): string | false;
private findNcxPath(manifestNode: Node, spineNode: Node): string | false;
private findCoverPath(packageXml: Node): string;
private getElementText(xml: Node, tag: string): string
private getPropertyText(xml: Node, property: string): string
private getElementText(xml: Node, tag: string): string;
private getPropertyText(xml: Node, property: string): string;
}

16
types/pagelist.d.ts vendored
View file

@ -1,29 +1,21 @@
export interface PageListItem {
href: string,
page: string,
cfi?: string,
packageUrl?: string
href: string;
page: string;
cfi?: string;
packageUrl?: string;
}
export default class Pagelist {
constructor(xml: XMLDocument);
parse(xml: XMLDocument): Array<PageListItem>;
pageFromCfi(cfi: string): number;
cfiFromPage(pg: string | number): string;
pageFromPercentage(percent: number): number;
percentageFromPage(pg: number): number;
destroy(): void;
private parseNav(navHtml: Node): Array<PageListItem>;
private item(item: Node): PageListItem;
private process(pageList: Array<PageListItem>): void;
}

134
types/rendition.d.ts vendored
View file

@ -1,67 +1,66 @@
import Annotations from "./annotations";
import Book from "./book";
import Contents from "./contents";
import Section from "./section";
import View from "./managers/view";
import Hook from "./utils/hook";
import Themes from "./themes";
import EpubCFI from "./epubcfi";
import Annotations from "./annotations";
import View from "./managers/view";
import Section from "./section";
import Themes from "./themes";
import Hook from "./utils/hook";
import Queue from "./utils/queue";
export interface RenditionOptions {
width?: number | string,
height?: number | string,
ignoreClass?: string,
manager?: string | Function | object,
view?: string | Function | object,
flow?: string,
layout?: string,
spread?: string,
minSpreadWidth?: number,
stylesheet?: string,
resizeOnOrientationChange?: boolean,
script?: string,
infinite?: boolean,
overflow?: string,
snap?: boolean | object,
defaultDirection?: string,
allowScriptedContent?: boolean,
allowPopups?: boolean
width?: number | string;
height?: number | string;
ignoreClass?: string;
manager?: string | Function | object;
view?: string | Function | object;
flow?: string;
layout?: string;
spread?: string;
minSpreadWidth?: number;
stylesheet?: string;
resizeOnOrientationChange?: boolean;
script?: string;
infinite?: boolean;
overflow?: string;
snap?: boolean | object;
defaultDirection?: string;
allowScriptedContent?: boolean;
allowPopups?: boolean;
}
export interface DisplayedLocation {
index: number,
href: string,
cfi: string,
location: number,
percentage: number,
index: number;
href: string;
cfi: string;
location: number;
percentage: number;
displayed: {
page: number,
total: number
}
page: number;
total: number;
};
}
export interface Location {
start: DisplayedLocation,
end: DisplayedLocation,
atStart: boolean,
atEnd: boolean
start: DisplayedLocation;
end: DisplayedLocation;
atStart: boolean;
atEnd: boolean;
}
export default class Rendition {
constructor(book: Book, options: RenditionOptions);
settings: RenditionOptions;
book: Book;
hooks: {
display: Hook,
serialize: Hook,
content: Hook,
unloaded: Hook,
layout: Hook,
render: Hook,
show: Hook
}
display: Hook;
serialize: Hook;
content: Hook;
unloaded: Hook;
layout: Hook;
render: Hook;
show: Hook;
};
themes: Themes;
annotations: Annotations;
epubcfi: EpubCFI;
@ -70,86 +69,51 @@ export default class Rendition {
started: Promise<void>;
adjustImages(contents: Contents): Promise<void>;
attachTo(element: Element): Promise<void>;
clear(): void;
currentLocation(): DisplayedLocation;
currentLocation(): Promise<DisplayedLocation>;
destroy(): void;
determineLayoutProperties(metadata: object): object;
direction(dir: string): void;
display(target?: string): Promise<void>;
display(target?: number): Promise<void>;
flow(flow: string): void;
getContents(): Contents;
getRange(cfi: string, ignoreClass?: string): Range;
handleLinks(contents: Contents): void;
injectIdentifier(doc: Document, section: Section): void;
injectScript(doc: Document, section: Section): void;
injectStylesheet(doc: Document, section: Section): void;
layout(settings: any): any;
located(location: Location): DisplayedLocation;
moveTo(offset: number): void;
next(): Promise<void>;
onOrientationChange(orientation: string): void;
passEvents(contents: Contents): void;
prev(): Promise<void>;
reportLocation(): Promise<void>;
requireManager(manager: string | Function | object): any;
requireView(view: string | Function | object): any;
resize(width: number, height: number): void;
setManager(manager: Function): void;
spread(spread: string, min?: number): void;
start(): void;
views(): Array<View>;
// Event emitters
emit(type: any, ...args: any[]): void;
off(type: any, listener: any): any;
on(type: any, listener: any): any;
once(type: any, listener: any, ...args: any[]): any;
private triggerMarkEvent(cfiRange: string, data: object, contents: Contents): void;
private triggerMarkEvent(
cfiRange: string,
data: object,
contents: Contents
): void;
private triggerSelectedEvent(cfirange: string, contents: Contents): void;
private triggerViewEvent(e: Event, contents: Contents): void;
private onResized(size: { width: number, height: number }): void;
private onResized(size: { width: number; height: number }): void;
private afterDisplayed(view: any): void;
private afterRemoved(view: any): void;
}

32
types/resources.d.ts vendored
View file

@ -1,33 +1,29 @@
import { PackagingManifestObject } from "./packaging";
import Archive from "./archive";
import { PackagingManifestObject } from "./packaging";
export default class Resources {
constructor(manifest: PackagingManifestObject, options: {
replacements?: string,
archive?: Archive,
resolver?: Function,
request?: Function
});
constructor(
manifest: PackagingManifestObject,
options: {
replacements?: string;
archive?: Archive;
resolver?: Function;
request?: Function;
}
);
process(manifest: PackagingManifestObject): void;
createUrl(url: string): Promise<string>;
replacements(): Promise<Array<string>>;
relativeTo(absolute: boolean, resolver?: Function): Array<string>;
get(path: string): string;
substitute(content: string, url?: string): string;
destroy(): void;
private split(): void;
private splitUrls(): void;
private replaceCss(archive: Archive, resolver?: Function): Promise<Array<string>>;
private replaceCss(
archive: Archive,
resolver?: Function
): Promise<Array<string>>;
private createCssFile(href: string): Promise<string>;
}

42
types/section.d.ts vendored
View file

@ -1,32 +1,31 @@
import { HooksObject } from "./utils/hook";
export interface GlobalLayout {
layout: string,
spread: string,
orientation: string
layout: string;
spread: string;
orientation: string;
}
export interface LayoutSettings {
layout: string,
spread: string,
orientation: string
layout: string;
spread: string;
orientation: string;
}
export interface SpineItem {
index: number,
cfiBase: string,
href?: string,
url?: string,
canonical?: string,
properties?: Array<string>,
linear?: string,
next: () => SpineItem,
prev: () => SpineItem,
index: number;
cfiBase: string;
href?: string;
url?: string;
canonical?: string;
properties?: Array<string>;
linear?: string;
next: () => SpineItem;
prev: () => SpineItem;
}
export default class Section {
constructor(item: SpineItem, hooks: HooksObject);
idref: string;
linear: boolean;
properties: Array<string>;
@ -37,28 +36,17 @@ export default class Section {
next: () => SpineItem;
prev: () => SpineItem;
cfiBase: string;
document: Document;
contents: Element;
output: string;
hooks: HooksObject;
load(_request?: Function): Document;
render(_request?: Function): string;
find(_query: string): Array<Element>;
reconcileLayoutSettings(globalLayout: GlobalLayout): LayoutSettings;
cfiFromRange(_range: Range): string;
cfiFromElement(el: Element): string;
unload(): void;
destroy(): void;
private base(): void;
}

14
types/spine.d.ts vendored
View file

@ -4,27 +4,17 @@ import Hook from "./utils/hook";
export default class Spine {
constructor();
hooks: {
serialize: Hook,
content: Hook
serialize: Hook;
content: Hook;
};
unpack(_package: Packaging, resolver: Function, canonical: Function): void;
get(target?: string | number): Section;
each(...args: any[]): any;
first(): Section;
last(): Section;
destroy(): void;
private append(section: Section): number;
private prepend(section: Section): number;
private remove(section: Section): number;
}

31
types/store.d.ts vendored
View file

@ -1,30 +1,29 @@
import localForage = require('localforage');
import localForage = require("localforage");
import Resources from "./resources";
export default class Store {
constructor(name: string, request?: Function, resolver?: Function);
add(resources: Resources, force?: boolean): Promise<Array<object>>;
put(url: string, withCredentials?: boolean, headers?: object): Promise<Blob>;
request(url: string, type?: string, withCredentials?: boolean, headers?: object): Promise<Blob | string | JSON | Document | XMLDocument>;
retrieve(url: string, type?: string): Promise<Blob | string | JSON | Document | XMLDocument>;
request(
url: string,
type?: string,
withCredentials?: boolean,
headers?: object
): Promise<Blob | string | JSON | Document | XMLDocument>;
retrieve(
url: string,
type?: string
): Promise<Blob | string | JSON | Document | XMLDocument>;
getBlob(url: string, mimeType?: string): Promise<Blob>;
getText(url: string): Promise<string>;
getBase64(url: string, mimeType?: string): Promise<string>;
createUrl(url: string, options: { base64: boolean }): Promise<string>;
revokeUrl(url: string): void;
destroy(): void;
private checkRequirements(): void;
private handleResponse(response: any, type?: string): Blob | string | JSON | Document | XMLDocument;
private handleResponse(
response: any,
type?: string
): Blob | string | JSON | Document | XMLDocument;
}

43
types/themes.d.ts vendored
View file

@ -1,40 +1,23 @@
import Rendition from "./rendition";
import Contents from "./contents";
import Rendition from "./rendition";
export default class Themes {
constructor(rendition: Rendition);
register( themeObject: object ): void;
register( theme: string, url: string ): void;
register( theme: string, themeObject: object ): void;
default( theme: object | string ): void;
registerThemes( themes: object ): void;
registerCss( name: string, css: string ): void;
registerUrl( name: string, input: string ): void;
registerRules( name: string, rules: object ): void;
select( name: string ): void;
update( name: string ): void;
inject( content: Contents ): void;
add( name: string, contents: Contents ): void;
register(themeObject: object): void;
register(theme: string, url: string): void;
register(theme: string, themeObject: object): void;
default(theme: object | string): void;
registerThemes(themes: object): void;
registerCss(name: string, css: string): void;
registerUrl(name: string, input: string): void;
registerRules(name: string, rules: object): void;
select(name: string): void;
update(name: string): void;
inject(content: Contents): void;
add(name: string, contents: Contents): void;
override(name: string, value: string, priority?: boolean): void;
overrides(contents: Contents): void;
fontSize(size: string): void;
font(f: string): void;
destroy(): void;
}

View file

@ -1,24 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6",
"dom"
],
"lib": ["es6", "dom"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"typeRoots": ["../"],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"epubjs-tests.ts"
]
"files": ["index.d.ts"]
}

View file

@ -4,6 +4,6 @@ export const DOM_EVENTS: Array<string>;
export const EVENTS: {
[key: string]: {
[key: string]: string
}
}
[key: string]: string;
};
};

114
types/utils/core.d.ts vendored
View file

@ -1,79 +1,85 @@
export function uuid(): string;
export function documentHeight(): number;
export function isElement(obj: object): boolean;
export function isNumber(n: any): boolean;
export function isFloat(n: any): boolean;
export function prefixed(unprefixed: string): string;
export function defaults(obj: object): object;
export function extend(target: object): object;
export function insert(item: any, array: Array<any>, compareFunction: Function): number;
export function locationOf(item: any, array: Array<any>, compareFunction: Function, _start: Function, _end: Function): number;
export function indexOfSorted(item: any, array: Array<any>, compareFunction: Function, _start: Function, _end: Function): number;
export function bounds(el: Element): { width: Number, height: Number};
export function borders(el: Element): { width: Number, height: Number};
export function insert(
item: any,
array: Array<any>,
compareFunction: Function
): number;
export function locationOf(
item: any,
array: Array<any>,
compareFunction: Function,
_start: Function,
_end: Function
): number;
export function indexOfSorted(
item: any,
array: Array<any>,
compareFunction: Function,
_start: Function,
_end: Function
): number;
export function bounds(el: Element): { width: Number; height: Number };
export function borders(el: Element): { width: Number; height: Number };
export function nodeBounds(node: Node): object;
export function windowBounds(): { width: Number, height: Number, top: Number, left: Number, right: Number, bottom: Number };
export function windowBounds(): {
width: Number;
height: Number;
top: Number;
left: Number;
right: Number;
bottom: Number;
};
export function indexOfNode(node: Node, typeId: string): number;
export function indexOfTextNode(textNode: Node): number;
export function indexOfElementNode(elementNode: Element): number;
export function isXml(ext: string): boolean;
export function createBlob(content: any, mime: string): Blob;
export function createBlobUrl(content: any, mime: string): string;
export function revokeBlobUrl(url: string): void;
export function createBase64Url(content: any, mime: string): string
export function createBase64Url(content: any, mime: string): string;
export function type(obj: object): string;
export function parse(markup: string, mime: string, forceXMLDom: boolean): Document;
export function parse(
markup: string,
mime: string,
forceXMLDom: boolean
): Document;
export function qs(el: Element, sel: string): Element;
export function qsa(el: Element, sel: string): ArrayLike<Element>;
export function qsp(el: Element, sel: string, props: Array<object>): ArrayLike<Element>;
export function qsp(
el: Element,
sel: string,
props: Array<object>
): ArrayLike<Element>;
export function sprint(root: Node, func: Function): void;
export function treeWalker(root: Node, func: Function, filter: object | Function): void;
export function treeWalker(
root: Node,
func: Function,
filter: object | Function
): void;
export function walk(node: Node, callback: Function): void;
export function blob2base64(blob: Blob): string;
export function defer(): Promise<any>;
export function querySelectorByType(html: Element, element: string, type: string): Array<Element>;
export function querySelectorByType(
html: Element,
element: string,
type: string
): Array<Element>;
export function findChildren(el: Element): Array<Element>;
export function parents(node: Element): Array<Element>;
export function filterChildren(el: Element, nodeName: string, single: boolean): Array<Element>;
export function getParentByTagName(node: Element, tagname: string): Array<Element>;
export class RangeObject extends Range {
}
export function filterChildren(
el: Element,
nodeName: string,
single: boolean
): Array<Element>;
export function getParentByTagName(
node: Element,
tagname: string
): Array<Element>;
export class RangeObject extends Range {}

View file

@ -1,5 +1,5 @@
interface HooksObject {
[key: string]: Hook
[key: string]: Hook;
}
export default class Hook {
@ -7,12 +7,8 @@ export default class Hook {
register(func: Function): void;
register(arr: Array<Function>): void;
deregister(func: Function): void;
trigger(...args: any[]): Promise<any>;
list(): Array<any>;
clear(): void;
}

View file

@ -2,16 +2,10 @@ export default class Path {
constructor(pathString: string);
parse(what: string): object;
isAbsolute(what: string): boolean;
isDirectory(what: string): boolean;
resolve(what: string): string;
relative(what: string): string;
splitPath(filename: string): string;
toString(): string;
}

View file

@ -1,31 +1,21 @@
import { defer } from "./core";
export interface QueuedTask {
task: any | Task,
args: any[],
deferred: any, // should be defer, but not working
promise: Promise<any>
task: any | Task;
args: any[];
deferred: any; // should be defer, but not working
promise: Promise<any>;
}
export default class Queue {
constructor(context: any);
enqueue(func: Promise<Function> | Function, ...args: any[]): Promise<any>;
dequeue(): Promise<QueuedTask>;
dump(): void;
run(): Promise<void>;
flush(): Promise<void>;
clear(): void;
length(): number;
pause(): void;
stop(): void;
}

View file

@ -1,12 +1,12 @@
import Section from "../section";
import Contents from "../contents";
import Section from "../section";
export function replaceBase(doc: Document, section: Section): void;
export function replaceCanonical(doc: Document, section: Section): void;
export function replaceMeta(doc: Document, section: Section): void;
export function replaceLinks(contents: Contents, fn: Function): void;
export function substitute(contents: Contents, urls: string[], replacements: string[]): void;
export function substitute(
contents: Contents,
urls: string[],
replacements: string[]
): void;

View file

@ -1 +1,6 @@
export default function request(url: string, type?: string, withCredentials?: boolean, headers?: object): Promise<Blob | string | JSON | Document | XMLDocument>;
export default function request(
url: string,
type?: string,
withCredentials?: boolean,
headers?: object
): Promise<Blob | string | JSON | Document | XMLDocument>;

View file

@ -1,3 +1,2 @@
export default function scrollType(): string;
export function createDefiner(): Node;

View file

@ -4,10 +4,7 @@ export default class Url {
constructor(urlString: string, baseString: string);
path(): Path;
resolve(what: string): string;
relative(what: string): string;
toString(): string;
}

View file

@ -1,8 +1,6 @@
var webpack = require("webpack");
var path = require("path");
var PROD = (process.env.NODE_ENV === "production")
var LEGACY = (process.env.LEGACY)
var MINIMIZE = (process.env.MINIMIZE === "true")
var LEGACY = process.env.LEGACY;
var MINIMIZE = process.env.MINIMIZE === "true";
var hostname = "localhost";
var port = 8080;
@ -22,9 +20,9 @@ if (MINIMIZE) {
module.exports = {
mode: process.env.NODE_ENV,
entry: {
"epub": "./src/epub.js",
epub: "./src/epub.js",
},
devtool: MINIMIZE ? false : 'source-map',
devtool: MINIMIZE ? false : "source-map",
output: {
path: path.resolve("./dist"),
filename: filename,
@ -32,20 +30,20 @@ module.exports = {
library: "ePub",
libraryTarget: "umd",
libraryExport: "default",
publicPath: "/dist/"
publicPath: "/dist/",
},
optimization: {
minimize: MINIMIZE
minimize: MINIMIZE,
},
externals: {
"jszip/dist/jszip": "JSZip",
"xmldom": "xmldom"
xmldom: "xmldom",
},
plugins: [],
resolve: {
alias: {
path: "path-webpack"
}
path: "path-webpack",
},
},
devServer: {
host: hostname,
@ -54,8 +52,8 @@ module.exports = {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,PUT,POST,DELETE",
"Access-Control-Allow-Headers": "Content-Type"
}
"Access-Control-Allow-Headers": "Content-Type",
},
},
module: {
rules: [
@ -65,19 +63,26 @@ module.exports = {
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env", {
targets: LEGACY ? "defaults" : "last 2 Chrome versions, last 2 Safari versions, last 2 ChromeAndroid versions, last 2 iOS versions, last 2 Firefox versions, last 2 Edge versions",
presets: [
[
"@babel/preset-env",
{
targets: LEGACY
? "defaults"
: "last 2 Chrome versions, last 2 Safari versions, last 2 ChromeAndroid versions, last 2 iOS versions, last 2 Firefox versions, last 2 Edge versions",
corejs: 3,
useBuiltIns: "usage",
bugfixes: true,
modules: false
}]]
}
}
}
]
modules: false,
},
],
],
},
},
},
],
},
performance: {
hints: false
}
}
hints: false,
},
};