mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-03 14:59:18 +02:00
Intial move to ES2015
This commit is contained in:
parent
b0944bdff8
commit
353dfa62fd
46 changed files with 16839 additions and 18742 deletions
15
.babelrc
Normal file
15
.babelrc
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": {
|
||||
"chrome": 54,
|
||||
"safari" : 10,
|
||||
"firefox" : 50,
|
||||
"edge" : 14
|
||||
}
|
||||
}]
|
||||
],
|
||||
"plugins": [
|
||||
"add-module-exports"
|
||||
]
|
||||
}
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,3 +5,6 @@ components
|
|||
node_modules
|
||||
bower_components
|
||||
books
|
||||
lib
|
||||
dist
|
||||
documentation/html
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
"examples"
|
||||
],
|
||||
"dependencies": {
|
||||
"es6-promise": "~4.0.5"
|
||||
"es6-promise": "~4.0.5",
|
||||
"jszip": "^3.1.1",
|
||||
"path-webpack": "^0.0.2",
|
||||
"stream-browserify": "^2.0.1",
|
||||
"xmldom": "^0.1.22"
|
||||
}
|
||||
}
|
||||
|
|
17946
dist/epub.js
vendored
17946
dist/epub.js
vendored
File diff suppressed because it is too large
Load diff
2
dist/epub.js.map
vendored
2
dist/epub.js.map
vendored
File diff suppressed because one or more lines are too long
5
dist/epub.min.js
vendored
5
dist/epub.min.js
vendored
File diff suppressed because one or more lines are too long
2110
dist/polyfills.js
vendored
2110
dist/polyfills.js
vendored
File diff suppressed because it is too large
Load diff
1
dist/polyfills.js.map
vendored
1
dist/polyfills.js.map
vendored
File diff suppressed because one or more lines are too long
8
dist/polyfills.min.js
vendored
8
dist/polyfills.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -24,7 +24,7 @@ module.exports = function(config) {
|
|||
|
||||
{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: 'node_modules/es6-promise/dist/es6-promise.auto.js', watched: false, included: true, served: true},
|
||||
|
||||
{pattern: 'libs/url/url.js', watched: false, included: true, served: true}
|
||||
|
||||
|
@ -53,6 +53,22 @@ module.exports = function(config) {
|
|||
alias: {
|
||||
path: "path-webpack"
|
||||
}
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: "babel-loader",
|
||||
query: {
|
||||
presets: ['es2015'],
|
||||
plugins: [
|
||||
"add-module-exports",
|
||||
"transform-runtime"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
|
29
package.json
29
package.json
|
@ -1,20 +1,34 @@
|
|||
{
|
||||
"name": "epubjs",
|
||||
"version": "0.3.0",
|
||||
"description": "Render Epubs",
|
||||
"main": "src/epub.js",
|
||||
"version": "0.3.1",
|
||||
"description": "Parse and Render Epubs",
|
||||
"main": "lib/index.js",
|
||||
"jsnext:main" : "src/index.js",
|
||||
"repository": "https://github.com/futurepress/epub.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/karma start --single-run --browsers PhantomJS",
|
||||
"start": "webpack-dev-server --inline",
|
||||
"build": "./node_modules/.bin/gulp minify"
|
||||
"documentation": "./node_modules/.bin/gulp docs",
|
||||
"start": "webpack-dev-server --inline --d",
|
||||
"build": "webpack --progress",
|
||||
"minify": "NODE_ENV=production webpack --progress",
|
||||
"legacy": "NODE_ENV=production LEGACY=true webpack --progress",
|
||||
"compile": "babel --optional runtime -d lib/ src/",
|
||||
"prepublish": "npm run compile && npm run build && npm run minify && npm run legacy"
|
||||
},
|
||||
"author": "fchasen@gmail.com",
|
||||
"license": "BSD-2-Clause",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-core": "^6.18.2",
|
||||
"babel-loader": "^6.2.8",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-polyfill": "^6.16.0",
|
||||
"babel-preset-env": "0.0.9",
|
||||
"babili-webpack-plugin": "0.0.7",
|
||||
"colors": "^1.1.2",
|
||||
"connect": "^3.0.1",
|
||||
"express": "^4.5.1",
|
||||
|
@ -52,11 +66,10 @@
|
|||
"webpack-dev-server": "^v2.1.0-beta.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"es6-promise": "^4.0.5",
|
||||
"event-emitter": "^0.3.4",
|
||||
"jszip": "^3.1.1",
|
||||
"xmldom": "^0.1.22",
|
||||
"path-webpack": "^0.0.2",
|
||||
"stream-browserify": "^2.0.1"
|
||||
"stream-browserify": "^2.0.1",
|
||||
"xmldom": "^0.1.22"
|
||||
}
|
||||
}
|
||||
|
|
478
src/archive.js
478
src/archive.js
|
@ -1,245 +1,247 @@
|
|||
var core = require('./core');
|
||||
var request = require('./request');
|
||||
var mime = require('../libs/mime/mime');
|
||||
var Path = require('./core').Path;
|
||||
import {defer, isXml, parse} from './utils/core';
|
||||
import request from './request';
|
||||
import mime from '../libs/mime/mime';
|
||||
import Path from './utils/path';
|
||||
|
||||
/**
|
||||
* Handles Unzipping a requesting files from an Epub Archive
|
||||
* @class
|
||||
*/
|
||||
function Archive() {
|
||||
this.zip = undefined;
|
||||
this.checkRequirements();
|
||||
this.urlCache = {};
|
||||
class Archive {
|
||||
constructor() {
|
||||
this.zip = undefined;
|
||||
this.checkRequirements();
|
||||
this.urlCache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if JSZip exists in global namspace,
|
||||
* Requires JSZip if it isn't there
|
||||
* @private
|
||||
*/
|
||||
checkRequirements(){
|
||||
try {
|
||||
if (typeof JSZip === 'undefined') {
|
||||
JSZip = require('jszip');
|
||||
}
|
||||
this.zip = new JSZip();
|
||||
} catch (e) {
|
||||
console.error("JSZip lib not loaded");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open an archive
|
||||
* @param {binary} input
|
||||
* @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});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load and Open an archive
|
||||
* @param {string} zipUrl
|
||||
* @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));
|
||||
};
|
||||
|
||||
/**
|
||||
* Request
|
||||
* @param {string} url a url to request from the archive
|
||||
* @param {[string]} type specify the type of the returned result
|
||||
* @return {Promise}
|
||||
*/
|
||||
request(url, type){
|
||||
var deferred = new defer();
|
||||
var response;
|
||||
var r;
|
||||
var path = new Path(url);
|
||||
|
||||
// If type isn't set, determine it from the file extension
|
||||
if(!type) {
|
||||
type = path.extension;
|
||||
}
|
||||
|
||||
if(type == 'blob'){
|
||||
response = this.getBlob(url);
|
||||
} else {
|
||||
response = this.getText(url);
|
||||
}
|
||||
|
||||
if (response) {
|
||||
response.then(function (r) {
|
||||
let result = this.handleResponse(r, type);
|
||||
deferred.resolve(result);
|
||||
}.bind(this));
|
||||
} else {
|
||||
deferred.reject({
|
||||
message : "File not found in the epub: " + url,
|
||||
stack : new Error().stack
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the response from request
|
||||
* @private
|
||||
* @param {any} response
|
||||
* @param {[string]} type
|
||||
* @return {any} the parsed result
|
||||
*/
|
||||
handleResponse(response, type){
|
||||
var r;
|
||||
|
||||
if(type == "json") {
|
||||
r = JSON.parse(response);
|
||||
}
|
||||
else
|
||||
if(isXml(type)) {
|
||||
r = parse(response, "text/xml");
|
||||
}
|
||||
else
|
||||
if(type == 'xhtml') {
|
||||
r = parse(response, "application/xhtml+xml");
|
||||
}
|
||||
else
|
||||
if(type == 'html' || type == 'htm') {
|
||||
r = parse(response, "text/html");
|
||||
} else {
|
||||
r = response;
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a Blob from Archive by Url
|
||||
* @param {string} url
|
||||
* @param {[string]} mimeType
|
||||
* @return {Blob}
|
||||
*/
|
||||
getBlob(url, mimeType){
|
||||
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
|
||||
var entry = this.zip.file(decodededUrl);
|
||||
|
||||
if(entry) {
|
||||
mimeType = mimeType || mime.lookup(entry.name);
|
||||
return entry.async("uint8array").then(function(uint8array) {
|
||||
return new Blob([uint8array], {type : mimeType});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Text from Archive by Url
|
||||
* @param {string} url
|
||||
* @param {[string]} encoding
|
||||
* @return {string}
|
||||
*/
|
||||
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) {
|
||||
return text;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a base64 encoded result from Archive by Url
|
||||
* @param {string} url
|
||||
* @param {[string]} mimeType
|
||||
* @return {string} base64 encoded
|
||||
*/
|
||||
getBase64(url, mimeType){
|
||||
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
|
||||
var entry = this.zip.file(decodededUrl);
|
||||
|
||||
if(entry) {
|
||||
mimeType = mimeType || mime.lookup(entry.name);
|
||||
return entry.async("base64").then(function(data) {
|
||||
return "data:" + mimeType + ";base64," + data;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a Url from an unarchived item
|
||||
* @param {string} url
|
||||
* @param {[object]} options.base64 use base64 encoding or blob url
|
||||
* @return {Promise} url promise with Url string
|
||||
*/
|
||||
createUrl(url, options){
|
||||
var deferred = new defer();
|
||||
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||
var tempUrl;
|
||||
var blob;
|
||||
var response;
|
||||
var useBase64 = options && options.base64;
|
||||
|
||||
if(url in this.urlCache) {
|
||||
deferred.resolve(this.urlCache[url]);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
if (useBase64) {
|
||||
response = this.getBase64(url);
|
||||
|
||||
if (response) {
|
||||
response.then(function(tempUrl) {
|
||||
|
||||
this.urlCache[url] = tempUrl;
|
||||
deferred.resolve(tempUrl);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
response = this.getBlob(url);
|
||||
|
||||
if (response) {
|
||||
response.then(function(blob) {
|
||||
|
||||
tempUrl = _URL.createObjectURL(blob);
|
||||
this.urlCache[url] = tempUrl;
|
||||
deferred.resolve(tempUrl);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!response) {
|
||||
deferred.reject({
|
||||
message : "File not found in the epub: " + url,
|
||||
stack : new Error().stack
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Revoke Temp Url for a achive item
|
||||
* @param {string} url url of the item in the archive
|
||||
*/
|
||||
revokeUrl(url){
|
||||
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||
var fromCache = this.urlCache[url];
|
||||
if(fromCache) _URL.revokeObjectURL(fromCache);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if JSZip exists in global namspace,
|
||||
* Requires JSZip if it isn't there
|
||||
* @private
|
||||
*/
|
||||
Archive.prototype.checkRequirements = function(){
|
||||
try {
|
||||
if (typeof JSZip === 'undefined') {
|
||||
JSZip = require('jszip');
|
||||
}
|
||||
this.zip = new JSZip();
|
||||
} catch (e) {
|
||||
console.error("JSZip lib not loaded");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open an archive
|
||||
* @param {binary} input
|
||||
* @param {boolean} isBase64 tells JSZip if the input data is base64 encoded
|
||||
* @return {Promise} zipfile
|
||||
*/
|
||||
Archive.prototype.open = function(input, isBase64){
|
||||
return this.zip.loadAsync(input, {"base64": isBase64});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load and Open an archive
|
||||
* @param {string} zipUrl
|
||||
* @param {boolean} isBase64 tells JSZip if the input data is base64 encoded
|
||||
* @return {Promise} zipfile
|
||||
*/
|
||||
Archive.prototype.openUrl = function(zipUrl, isBase64){
|
||||
return request(zipUrl, "binary")
|
||||
.then(function(data){
|
||||
return this.zip.loadAsync(data, {"base64": isBase64});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Request
|
||||
* @param {string} url a url to request from the archive
|
||||
* @param {[string]} type specify the type of the returned result
|
||||
* @return {Promise}
|
||||
*/
|
||||
Archive.prototype.request = function(url, type){
|
||||
var deferred = new core.defer();
|
||||
var response;
|
||||
var r;
|
||||
var path = new Path(url);
|
||||
|
||||
// If type isn't set, determine it from the file extension
|
||||
if(!type) {
|
||||
type = path.extension;
|
||||
}
|
||||
|
||||
if(type == 'blob'){
|
||||
response = this.getBlob(url);
|
||||
} else {
|
||||
response = this.getText(url);
|
||||
}
|
||||
|
||||
if (response) {
|
||||
response.then(function (r) {
|
||||
result = this.handleResponse(r, type);
|
||||
deferred.resolve(result);
|
||||
}.bind(this));
|
||||
} else {
|
||||
deferred.reject({
|
||||
message : "File not found in the epub: " + url,
|
||||
stack : new Error().stack
|
||||
});
|
||||
}
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the response from request
|
||||
* @private
|
||||
* @param {any} response
|
||||
* @param {[string]} type
|
||||
* @return {any} the parsed result
|
||||
*/
|
||||
Archive.prototype.handleResponse = function(response, type){
|
||||
var r;
|
||||
|
||||
if(type == "json") {
|
||||
r = JSON.parse(response);
|
||||
}
|
||||
else
|
||||
if(core.isXml(type)) {
|
||||
r = core.parse(response, "text/xml");
|
||||
}
|
||||
else
|
||||
if(type == 'xhtml') {
|
||||
r = core.parse(response, "application/xhtml+xml");
|
||||
}
|
||||
else
|
||||
if(type == 'html' || type == 'htm') {
|
||||
r = core.parse(response, "text/html");
|
||||
} else {
|
||||
r = response;
|
||||
}
|
||||
|
||||
return r;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a Blob from Archive by Url
|
||||
* @param {string} url
|
||||
* @param {[string]} mimeType
|
||||
* @return {Blob}
|
||||
*/
|
||||
Archive.prototype.getBlob = function(url, mimeType){
|
||||
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
|
||||
var entry = this.zip.file(decodededUrl);
|
||||
|
||||
if(entry) {
|
||||
mimeType = mimeType || mime.lookup(entry.name);
|
||||
return entry.async("uint8array").then(function(uint8array) {
|
||||
return new Blob([uint8array], {type : mimeType});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Text from Archive by Url
|
||||
* @param {string} url
|
||||
* @param {[string]} encoding
|
||||
* @return {string}
|
||||
*/
|
||||
Archive.prototype.getText = function(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) {
|
||||
return text;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a base64 encoded result from Archive by Url
|
||||
* @param {string} url
|
||||
* @param {[string]} mimeType
|
||||
* @return {string} base64 encoded
|
||||
*/
|
||||
Archive.prototype.getBase64 = function(url, mimeType){
|
||||
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
|
||||
var entry = this.zip.file(decodededUrl);
|
||||
|
||||
if(entry) {
|
||||
mimeType = mimeType || mime.lookup(entry.name);
|
||||
return entry.async("base64").then(function(data) {
|
||||
return "data:" + mimeType + ";base64," + data;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a Url from an unarchived item
|
||||
* @param {string} url
|
||||
* @param {[object]} options.base64 use base64 encoding or blob url
|
||||
* @return {Promise} url promise with Url string
|
||||
*/
|
||||
Archive.prototype.createUrl = function(url, options){
|
||||
var deferred = new core.defer();
|
||||
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||
var tempUrl;
|
||||
var blob;
|
||||
var response;
|
||||
var useBase64 = options && options.base64;
|
||||
|
||||
if(url in this.urlCache) {
|
||||
deferred.resolve(this.urlCache[url]);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
if (useBase64) {
|
||||
response = this.getBase64(url);
|
||||
|
||||
if (response) {
|
||||
response.then(function(tempUrl) {
|
||||
|
||||
this.urlCache[url] = tempUrl;
|
||||
deferred.resolve(tempUrl);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
response = this.getBlob(url);
|
||||
|
||||
if (response) {
|
||||
response.then(function(blob) {
|
||||
|
||||
tempUrl = _URL.createObjectURL(blob);
|
||||
this.urlCache[url] = tempUrl;
|
||||
deferred.resolve(tempUrl);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!response) {
|
||||
deferred.reject({
|
||||
message : "File not found in the epub: " + url,
|
||||
stack : new Error().stack
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Revoke Temp Url for a achive item
|
||||
* @param {string} url url of the item in the archive
|
||||
*/
|
||||
Archive.prototype.revokeUrl = function(url){
|
||||
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||
var fromCache = this.urlCache[url];
|
||||
if(fromCache) _URL.revokeObjectURL(fromCache);
|
||||
};
|
||||
|
||||
module.exports = Archive;
|
||||
export default Archive;
|
||||
|
|
867
src/book.js
867
src/book.js
|
@ -1,22 +1,21 @@
|
|||
var EventEmitter = require('event-emitter');
|
||||
var path = require('path');
|
||||
var core = require('./core');
|
||||
var Url = require('./core').Url;
|
||||
var Path = require('./core').Path;
|
||||
var Spine = require('./spine');
|
||||
var Locations = require('./locations');
|
||||
var Container = require('./container');
|
||||
var Packaging = require('./packaging');
|
||||
var Navigation = require('./navigation');
|
||||
var Resources = require('./resources');
|
||||
var PageList = require('./pagelist');
|
||||
var Rendition = require('./rendition');
|
||||
var Archive = require('./archive');
|
||||
var request = require('./request');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
import EventEmitter from 'event-emitter';
|
||||
// import path from 'path';
|
||||
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 Container from './container';
|
||||
import Packaging from './packaging';
|
||||
import Navigation from './navigation';
|
||||
import Resources from './resources';
|
||||
import PageList from './pagelist';
|
||||
import Rendition from './rendition';
|
||||
import Archive from './archive';
|
||||
import request from './request';
|
||||
import EpubCFI from './epubcfi';
|
||||
|
||||
// Const
|
||||
var CONTAINER_PATH = "META-INF/container.xml";
|
||||
const CONTAINER_PATH = "META-INF/container.xml";
|
||||
|
||||
/**
|
||||
* Creates a new Book
|
||||
|
@ -32,459 +31,461 @@ var CONTAINER_PATH = "META-INF/container.xml";
|
|||
* @example new Book("/path/to/book.epub", {})
|
||||
* @example new Book({ replacements: "blobUrl" })
|
||||
*/
|
||||
function Book(url, options){
|
||||
class Book {
|
||||
constructor(url, options) {
|
||||
// Allow passing just options to the Book
|
||||
if (typeof(options) === "undefined"
|
||||
&& typeof(url) === "object") {
|
||||
options = url;
|
||||
url = undefined;
|
||||
}
|
||||
|
||||
// Allow passing just options to the Book
|
||||
if (typeof(options) === "undefined"
|
||||
&& typeof(url) === "object") {
|
||||
options = url;
|
||||
url = undefined;
|
||||
}
|
||||
this.settings = extend(this.settings || {}, {
|
||||
requestMethod: undefined,
|
||||
requestCredentials: undefined,
|
||||
requestHeaders: undefined,
|
||||
encoding: undefined,
|
||||
replacements: 'base64'
|
||||
});
|
||||
|
||||
this.settings = core.extend(this.settings || {}, {
|
||||
requestMethod: undefined,
|
||||
requestCredentials: undefined,
|
||||
requestHeaders: undefined,
|
||||
encoding: undefined,
|
||||
replacements: 'base64'
|
||||
});
|
||||
|
||||
core.extend(this.settings, options);
|
||||
extend(this.settings, options);
|
||||
|
||||
|
||||
// Promises
|
||||
this.opening = new core.defer();
|
||||
/**
|
||||
* @property {promise} opened returns after the book is loaded
|
||||
*/
|
||||
this.opened = this.opening.promise;
|
||||
this.isOpen = false;
|
||||
// Promises
|
||||
this.opening = new defer();
|
||||
/**
|
||||
* @property {promise} opened returns after the book is loaded
|
||||
*/
|
||||
this.opened = this.opening.promise;
|
||||
this.isOpen = false;
|
||||
|
||||
this.loading = {
|
||||
manifest: new core.defer(),
|
||||
spine: new core.defer(),
|
||||
metadata: new core.defer(),
|
||||
cover: new core.defer(),
|
||||
navigation: new core.defer(),
|
||||
pageList: new core.defer(),
|
||||
resources: new core.defer()
|
||||
this.loading = {
|
||||
manifest: new defer(),
|
||||
spine: new defer(),
|
||||
metadata: new defer(),
|
||||
cover: new defer(),
|
||||
navigation: new defer(),
|
||||
pageList: new defer(),
|
||||
resources: new defer()
|
||||
};
|
||||
|
||||
this.loaded = {
|
||||
manifest: this.loading.manifest.promise,
|
||||
spine: this.loading.spine.promise,
|
||||
metadata: this.loading.metadata.promise,
|
||||
cover: this.loading.cover.promise,
|
||||
navigation: this.loading.navigation.promise,
|
||||
pageList: this.loading.pageList.promise,
|
||||
resources: this.loading.resources.promise
|
||||
};
|
||||
|
||||
// this.ready = RSVP.hash(this.loaded);
|
||||
/**
|
||||
* @property {promise} ready returns after the book is loaded and parsed
|
||||
* @private
|
||||
*/
|
||||
this.ready = Promise.all([this.loaded.manifest,
|
||||
this.loaded.spine,
|
||||
this.loaded.metadata,
|
||||
this.loaded.cover,
|
||||
this.loaded.navigation,
|
||||
this.loaded.resources ]);
|
||||
|
||||
|
||||
// Queue for methods used before opening
|
||||
this.isRendered = false;
|
||||
// this._q = queue(this);
|
||||
|
||||
/**
|
||||
* @property {method} request
|
||||
* @private
|
||||
*/
|
||||
this.request = this.settings.requestMethod || request;
|
||||
|
||||
/**
|
||||
* @property {Spine} spine
|
||||
*/
|
||||
this.spine = new Spine();
|
||||
|
||||
/**
|
||||
* @property {Locations} locations
|
||||
*/
|
||||
this.locations = new Locations(this.spine, this.load.bind(this));
|
||||
|
||||
/**
|
||||
* @property {Navigation} navigation
|
||||
*/
|
||||
this.navigation = undefined;
|
||||
|
||||
/**
|
||||
* @property {PageList} pagelist
|
||||
*/
|
||||
this.pageList = new PageList();
|
||||
|
||||
/**
|
||||
* @property {Url} url
|
||||
* @private
|
||||
*/
|
||||
this.url = undefined;
|
||||
|
||||
/**
|
||||
* @property {Path} path
|
||||
* @private
|
||||
*/
|
||||
this.path = undefined;
|
||||
|
||||
/**
|
||||
* @property {boolean} archived
|
||||
* @private
|
||||
*/
|
||||
this.archived = false;
|
||||
|
||||
if(url) {
|
||||
this.open(url).catch((error) => {
|
||||
var err = new Error("Cannot load book at "+ url );
|
||||
console.error(err);
|
||||
this.emit("openFailed", err);
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.loaded = {
|
||||
manifest: this.loading.manifest.promise,
|
||||
spine: this.loading.spine.promise,
|
||||
metadata: this.loading.metadata.promise,
|
||||
cover: this.loading.cover.promise,
|
||||
navigation: this.loading.navigation.promise,
|
||||
pageList: this.loading.pageList.promise,
|
||||
resources: this.loading.resources.promise
|
||||
/**
|
||||
* Open a epub or url
|
||||
* @param {string} input URL, Path or ArrayBuffer
|
||||
* @param {string} [what] to force opening
|
||||
* @returns {Promise} of when the book has been loaded
|
||||
* @example book.open("/path/to/book.epub")
|
||||
*/
|
||||
open(input, what) {
|
||||
var opening;
|
||||
var type = what || this.determineType(input);
|
||||
|
||||
if (type === "binary") {
|
||||
this.archived = true;
|
||||
this.url = new Url("/", "");
|
||||
opening = this.openEpub(input);
|
||||
} else if (type === "epub") {
|
||||
this.archived = true;
|
||||
this.url = new Url("/", "");
|
||||
opening = this.request(input, 'binary')
|
||||
.then(this.openEpub.bind(this));
|
||||
} else if(type == "opf") {
|
||||
this.url = new Url(input);
|
||||
opening = this.openPackaging(this.url.Path.toString());
|
||||
} else {
|
||||
this.url = new Url(input);
|
||||
opening = this.openContainer(CONTAINER_PATH)
|
||||
.then(this.openPackaging.bind(this));
|
||||
}
|
||||
|
||||
return opening;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an archived epub
|
||||
* @private
|
||||
* @param {binary} data
|
||||
* @param {[string]} encoding
|
||||
* @return {Promise}
|
||||
*/
|
||||
openEpub(data, encoding) {
|
||||
return this.unarchive(data, encoding || this.settings.encoding)
|
||||
.then(() => {
|
||||
return this.openContainer(CONTAINER_PATH);
|
||||
})
|
||||
.then((packagePath) => {
|
||||
return this.openPackaging(packagePath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the epub container
|
||||
* @private
|
||||
* @param {string} url
|
||||
* @return {string} packagePath
|
||||
*/
|
||||
openContainer(url) {
|
||||
return this.load(url)
|
||||
.then((xml) => {
|
||||
this.container = new Container(xml);
|
||||
return this.resolve(this.container.packagePath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the Open Packaging Format Xml
|
||||
* @private
|
||||
* @param {string} url
|
||||
* @return {Promise}
|
||||
*/
|
||||
openPackaging(url) {
|
||||
var packageUrl;
|
||||
this.path = new Path(url);
|
||||
return this.load(url)
|
||||
.then((xml) => {
|
||||
this.packaging = new Packaging(xml);
|
||||
return this.unpack(this.packaging);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a resource from the Book
|
||||
* @param {string} path path to the resource to load
|
||||
* @return {Promise} returns a promise with the requested resource
|
||||
*/
|
||||
load(path) {
|
||||
var resolved;
|
||||
|
||||
if(this.archived) {
|
||||
resolved = this.resolve(path);
|
||||
return this.archive.request(resolved);
|
||||
} else {
|
||||
resolved = this.resolve(path);
|
||||
return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a path to it's absolute position in the Book
|
||||
* @param {string} path
|
||||
* @param {[boolean]} absolute force resolving the full URL
|
||||
* @return {string} the resolved path string
|
||||
*/
|
||||
resolve(path, absolute) {
|
||||
var resolved = path;
|
||||
var isAbsolute = (path.indexOf('://') > -1);
|
||||
|
||||
if (isAbsolute) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (this.path) {
|
||||
resolved = this.path.resolve(path);
|
||||
}
|
||||
|
||||
if(absolute != false && this.url) {
|
||||
resolved = this.url.resolve(resolved);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of they input passed to open
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @return {string} binary | directory | epub | opf
|
||||
*/
|
||||
determineType(input) {
|
||||
var url;
|
||||
var path;
|
||||
var extension;
|
||||
|
||||
if (typeof(input) != "string") {
|
||||
return "binary";
|
||||
}
|
||||
|
||||
url = new Url(input);
|
||||
path = url.path();
|
||||
extension = path.extension;
|
||||
|
||||
if (!extension) {
|
||||
return "directory";
|
||||
}
|
||||
|
||||
if(extension === "epub"){
|
||||
return "epub";
|
||||
}
|
||||
|
||||
if(extension === "opf"){
|
||||
return "opf";
|
||||
}
|
||||
};
|
||||
|
||||
// this.ready = RSVP.hash(this.loaded);
|
||||
|
||||
/**
|
||||
* @property {promise} ready returns after the book is loaded and parsed
|
||||
* unpack the contents of the Books packageXml
|
||||
* @private
|
||||
* @param {document} packageXml XML Document
|
||||
*/
|
||||
this.ready = Promise.all([this.loaded.manifest,
|
||||
this.loaded.spine,
|
||||
this.loaded.metadata,
|
||||
this.loaded.cover,
|
||||
this.loaded.navigation,
|
||||
this.loaded.resources ]);
|
||||
unpack(opf) {
|
||||
this.package = opf;
|
||||
|
||||
this.spine.unpack(this.package, this.resolve.bind(this));
|
||||
|
||||
this.resources = new Resources(this.package.manifest, {
|
||||
archive: this.archive,
|
||||
resolver: this.resolve.bind(this),
|
||||
replacements: this.settings.replacements
|
||||
});
|
||||
|
||||
this.loadNavigation(this.package).then(() => {
|
||||
this.toc = this.navigation.toc;
|
||||
this.loading.navigation.resolve(this.navigation);
|
||||
});
|
||||
|
||||
this.cover = this.resolve(this.package.coverPath);
|
||||
|
||||
// Resolve promises
|
||||
this.loading.manifest.resolve(this.package.manifest);
|
||||
this.loading.metadata.resolve(this.package.metadata);
|
||||
this.loading.spine.resolve(this.spine);
|
||||
this.loading.cover.resolve(this.cover);
|
||||
this.loading.resources.resolve(this.resources);
|
||||
this.loading.pageList.resolve(this.pageList);
|
||||
|
||||
|
||||
// Queue for methods used before opening
|
||||
this.isRendered = false;
|
||||
// this._q = core.queue(this);
|
||||
this.isOpen = true;
|
||||
|
||||
/**
|
||||
* @property {method} request
|
||||
* @private
|
||||
*/
|
||||
this.request = this.settings.requestMethod || request;
|
||||
|
||||
/**
|
||||
* @property {Spine} spine
|
||||
*/
|
||||
this.spine = new Spine();
|
||||
|
||||
/**
|
||||
* @property {Locations} locations
|
||||
*/
|
||||
this.locations = new Locations(this.spine, this.load.bind(this));
|
||||
|
||||
/**
|
||||
* @property {Navigation} navigation
|
||||
*/
|
||||
this.navigation = undefined;
|
||||
|
||||
/**
|
||||
* @property {PageList} pagelist
|
||||
*/
|
||||
this.pageList = new PageList();
|
||||
|
||||
/**
|
||||
* @property {Url} url
|
||||
* @private
|
||||
*/
|
||||
this.url = undefined;
|
||||
|
||||
/**
|
||||
* @property {Path} path
|
||||
* @private
|
||||
*/
|
||||
this.path = undefined;
|
||||
|
||||
/**
|
||||
* @property {boolean} archived
|
||||
* @private
|
||||
*/
|
||||
this.archived = false;
|
||||
|
||||
if(url) {
|
||||
this.open(url).catch(function (error) {
|
||||
var err = new Error("Cannot load book at "+ url );
|
||||
console.error(err);
|
||||
this.emit("openFailed", err);
|
||||
console.log(error);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a epub or url
|
||||
* @param {string} input URL, Path or ArrayBuffer
|
||||
* @param {string} [what] to force opening
|
||||
* @returns {Promise} of when the book has been loaded
|
||||
* @example book.open("/path/to/book.epub")
|
||||
*/
|
||||
Book.prototype.open = function(input, what){
|
||||
var opening;
|
||||
var type = what || this.determineType(input);
|
||||
|
||||
if (type === "binary") {
|
||||
this.archived = true;
|
||||
this.url = new Url("/", "");
|
||||
opening = this.openEpub(input);
|
||||
} else if (type === "epub") {
|
||||
this.archived = true;
|
||||
this.url = new Url("/", "");
|
||||
opening = this.request(input, 'binary')
|
||||
.then(this.openEpub.bind(this));
|
||||
} else if(type == "opf") {
|
||||
this.url = new Url(input);
|
||||
opening = this.openPackaging(this.url.Path.toString());
|
||||
} else {
|
||||
this.url = new Url(input);
|
||||
opening = this.openContainer(CONTAINER_PATH)
|
||||
.then(this.openPackaging.bind(this));
|
||||
}
|
||||
|
||||
return opening;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open an archived epub
|
||||
* @private
|
||||
* @param {binary} data
|
||||
* @param {[string]} encoding
|
||||
* @return {Promise}
|
||||
*/
|
||||
Book.prototype.openEpub = function(data, encoding){
|
||||
return this.unarchive(data, encoding || this.settings.encoding)
|
||||
.then(function() {
|
||||
return this.openContainer(CONTAINER_PATH);
|
||||
}.bind(this))
|
||||
.then(function(packagePath) {
|
||||
return this.openPackaging(packagePath);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the epub container
|
||||
* @private
|
||||
* @param {string} url
|
||||
* @return {string} packagePath
|
||||
*/
|
||||
Book.prototype.openContainer = function(url){
|
||||
return this.load(url)
|
||||
.then(function(xml) {
|
||||
this.container = new Container(xml);
|
||||
return this.resolve(this.container.packagePath);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the Open Packaging Format Xml
|
||||
* @private
|
||||
* @param {string} url
|
||||
* @return {Promise}
|
||||
*/
|
||||
Book.prototype.openPackaging = function(url){
|
||||
var packageUrl;
|
||||
this.path = new Path(url);
|
||||
return this.load(url)
|
||||
.then(function(xml) {
|
||||
this.packaging = new Packaging(xml);
|
||||
return this.unpack(this.packaging);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a resource from the Book
|
||||
* @param {string} path path to the resource to load
|
||||
* @return {Promise} returns a promise with the requested resource
|
||||
*/
|
||||
Book.prototype.load = function (path) {
|
||||
var resolved;
|
||||
|
||||
if(this.archived) {
|
||||
resolved = this.resolve(path);
|
||||
return this.archive.request(resolved);
|
||||
} else {
|
||||
resolved = this.resolve(path);
|
||||
return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve a path to it's absolute position in the Book
|
||||
* @param {string} path
|
||||
* @param {[boolean]} absolute force resolving the full URL
|
||||
* @return {string} the resolved path string
|
||||
*/
|
||||
Book.prototype.resolve = function (path, absolute) {
|
||||
var resolved = path;
|
||||
var isAbsolute = (path.indexOf('://') > -1);
|
||||
|
||||
if (isAbsolute) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (this.path) {
|
||||
resolved = this.path.resolve(path);
|
||||
}
|
||||
|
||||
if(absolute != false && this.url) {
|
||||
resolved = this.url.resolve(resolved);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of they input passed to open
|
||||
* @private
|
||||
* @param {string} input
|
||||
* @return {string} binary | directory | epub | opf
|
||||
*/
|
||||
Book.prototype.determineType = function(input) {
|
||||
var url;
|
||||
var path;
|
||||
var extension;
|
||||
|
||||
if (typeof(input) != "string") {
|
||||
return "binary";
|
||||
}
|
||||
|
||||
url = new Url(input);
|
||||
path = url.path();
|
||||
extension = path.extension;
|
||||
|
||||
if (!extension) {
|
||||
return "directory";
|
||||
}
|
||||
|
||||
if(extension === "epub"){
|
||||
return "epub";
|
||||
}
|
||||
|
||||
if(extension === "opf"){
|
||||
return "opf";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* unpack the contents of the Books packageXml
|
||||
* @private
|
||||
* @param {document} packageXml XML Document
|
||||
*/
|
||||
Book.prototype.unpack = function(opf){
|
||||
this.package = opf;
|
||||
|
||||
this.spine.unpack(this.package, this.resolve.bind(this));
|
||||
|
||||
this.resources = new Resources(this.package.manifest, {
|
||||
archive: this.archive,
|
||||
resolver: this.resolve.bind(this),
|
||||
replacements: this.settings.replacements
|
||||
});
|
||||
|
||||
this.loadNavigation(this.package).then(function(){
|
||||
this.toc = this.navigation.toc;
|
||||
this.loading.navigation.resolve(this.navigation);
|
||||
}.bind(this));
|
||||
|
||||
this.cover = this.resolve(this.package.coverPath);
|
||||
|
||||
// Resolve promises
|
||||
this.loading.manifest.resolve(this.package.manifest);
|
||||
this.loading.metadata.resolve(this.package.metadata);
|
||||
this.loading.spine.resolve(this.spine);
|
||||
this.loading.cover.resolve(this.cover);
|
||||
this.loading.resources.resolve(this.resources);
|
||||
this.loading.pageList.resolve(this.pageList);
|
||||
|
||||
|
||||
this.isOpen = true;
|
||||
|
||||
if(this.archived) {
|
||||
this.replacements().then(function() {
|
||||
if(this.archived) {
|
||||
this.replacements().then(() => {
|
||||
this.opening.resolve(this);
|
||||
});
|
||||
} else {
|
||||
// Resolve book opened promise
|
||||
this.opening.resolve(this);
|
||||
}.bind(this));
|
||||
} else {
|
||||
// Resolve book opened promise
|
||||
this.opening.resolve(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
/**
|
||||
* Load Navigation and PageList from package
|
||||
* @private
|
||||
* @param {document} opf XML Document
|
||||
*/
|
||||
loadNavigation(opf) {
|
||||
var navPath = opf.navPath || opf.ncxPath;
|
||||
|
||||
/**
|
||||
* Load Navigation and PageList from package
|
||||
* @private
|
||||
* @param {document} opf XML Document
|
||||
*/
|
||||
Book.prototype.loadNavigation = function(opf){
|
||||
var navPath = opf.navPath || opf.ncxPath;
|
||||
if (!navPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!navPath) {
|
||||
return;
|
||||
return this.load(navPath, 'xml')
|
||||
.then((xml) => {
|
||||
this.navigation = new Navigation(xml);
|
||||
this.pageList = new PageList(xml);
|
||||
return this.navigation;
|
||||
});
|
||||
}
|
||||
|
||||
return this.load(navPath, 'xml')
|
||||
.then(function(xml) {
|
||||
this.navigation = new Navigation(xml);
|
||||
this.pageList = new PageList(xml);
|
||||
return this.navigation;
|
||||
}.bind(this));
|
||||
};
|
||||
/**
|
||||
* Alias for book.spine.get
|
||||
* @param {string} target
|
||||
*/
|
||||
section(target) {
|
||||
return this.spine.get(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for book.spine.get
|
||||
* @param {string} target
|
||||
*/
|
||||
Book.prototype.section = function(target) {
|
||||
return this.spine.get(target);
|
||||
};
|
||||
/**
|
||||
* Sugar to render a book
|
||||
* @param {element} element element to add the views to
|
||||
* @param {[object]} options
|
||||
* @return {Rendition}
|
||||
*/
|
||||
renderTo(element, options) {
|
||||
// var renderMethod = (options && options.method) ?
|
||||
// options.method :
|
||||
// "single";
|
||||
|
||||
/**
|
||||
* Sugar to render a book
|
||||
* @param {element} element element to add the views to
|
||||
* @param {[object]} options
|
||||
* @return {Rendition}
|
||||
*/
|
||||
Book.prototype.renderTo = function(element, options) {
|
||||
// var renderMethod = (options && options.method) ?
|
||||
// options.method :
|
||||
// "single";
|
||||
this.rendition = new Rendition(this, options);
|
||||
this.rendition.attachTo(element);
|
||||
|
||||
this.rendition = new Rendition(this, options);
|
||||
this.rendition.attachTo(element);
|
||||
return this.rendition;
|
||||
};
|
||||
|
||||
return this.rendition;
|
||||
};
|
||||
/**
|
||||
* Set if request should use withCredentials
|
||||
* @param {boolean} credentials
|
||||
*/
|
||||
setRequestCredentials(credentials) {
|
||||
this.settings.requestCredentials = credentials;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set if request should use withCredentials
|
||||
* @param {boolean} credentials
|
||||
*/
|
||||
Book.prototype.setRequestCredentials = function(credentials) {
|
||||
this.settings.requestCredentials = credentials;
|
||||
};
|
||||
/**
|
||||
* Set headers request should use
|
||||
* @param {object} headers
|
||||
*/
|
||||
setRequestHeaders(headers) {
|
||||
this.settings.requestHeaders = headers;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set headers request should use
|
||||
* @param {object} headers
|
||||
*/
|
||||
Book.prototype.setRequestHeaders = function(headers) {
|
||||
this.settings.requestHeaders = headers;
|
||||
};
|
||||
/**
|
||||
* Unarchive a zipped epub
|
||||
* @private
|
||||
* @param {binary} input epub data
|
||||
* @param {[string]} encoding
|
||||
* @return {Archive}
|
||||
*/
|
||||
unarchive(input, encoding) {
|
||||
this.archive = new Archive();
|
||||
return this.archive.open(input, encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unarchive a zipped epub
|
||||
* @private
|
||||
* @param {binary} input epub data
|
||||
* @param {[string]} encoding
|
||||
* @return {Archive}
|
||||
*/
|
||||
Book.prototype.unarchive = function(input, encoding){
|
||||
this.archive = new Archive();
|
||||
return this.archive.open(input, encoding);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the cover url
|
||||
* @return {string} coverUrl
|
||||
*/
|
||||
Book.prototype.coverUrl = function(){
|
||||
var retrieved = this.loaded.cover.
|
||||
then(function(url) {
|
||||
if(this.archived) {
|
||||
// return this.archive.createUrl(this.cover);
|
||||
return this.resources.get(this.cover);
|
||||
}else{
|
||||
return this.cover;
|
||||
}
|
||||
}.bind(this));
|
||||
/**
|
||||
* Get the cover url
|
||||
* @return {string} coverUrl
|
||||
*/
|
||||
coverUrl() {
|
||||
var retrieved = this.loaded.cover.
|
||||
then((url) => {
|
||||
if(this.archived) {
|
||||
// return this.archive.createUrl(this.cover);
|
||||
return this.resources.get(this.cover);
|
||||
}else{
|
||||
return this.cover;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
return retrieved;
|
||||
};
|
||||
return retrieved;
|
||||
}
|
||||
|
||||
/**
|
||||
* load replacement urls
|
||||
* @private
|
||||
* @return {Promise} completed loading urls
|
||||
*/
|
||||
Book.prototype.replacements = function(){
|
||||
this.spine.hooks.serialize.register(function(output, section) {
|
||||
section.output = this.resources.substitute(output, section.url);
|
||||
}.bind(this));
|
||||
/**
|
||||
* load replacement urls
|
||||
* @private
|
||||
* @return {Promise} completed loading urls
|
||||
*/
|
||||
replacements() {
|
||||
this.spine.hooks.serialize.register((output, section) => {
|
||||
section.output = this.resources.substitute(output, section.url);
|
||||
});
|
||||
|
||||
return this.resources.replacements().
|
||||
then(function() {
|
||||
return this.resources.replaceCss();
|
||||
}.bind(this));
|
||||
};
|
||||
return this.resources.replacements().
|
||||
then(() => {
|
||||
return this.resources.replaceCss();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a DOM Range for a given CFI Range
|
||||
* @param {EpubCFI} cfiRange a epub cfi range
|
||||
* @return {Range}
|
||||
*/
|
||||
Book.prototype.range = function(cfiRange) {
|
||||
var cfi = new EpubCFI(cfiRange);
|
||||
var item = this.spine.get(cfi.spinePos);
|
||||
/**
|
||||
* Find a DOM Range for a given CFI Range
|
||||
* @param {EpubCFI} cfiRange a epub cfi range
|
||||
* @return {Range}
|
||||
*/
|
||||
range(cfiRange) {
|
||||
var cfi = new EpubCFI(cfiRange);
|
||||
var item = this.spine.get(cfi.spinePos);
|
||||
|
||||
return item.load().then(function (contents) {
|
||||
var range = cfi.toRange(item.document);
|
||||
return range;
|
||||
})
|
||||
};
|
||||
return item.load().then(function (contents) {
|
||||
var range = cfi.toRange(item.document);
|
||||
return range;
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the Book Key using the identifer in the manifest or other string provided
|
||||
* @param {[string]} identifier to use instead of metadata identifier
|
||||
* @return {string} key
|
||||
*/
|
||||
Book.prototype.key = function(identifier){
|
||||
var ident = identifier || this.package.metadata.identifier || this.url.filename;
|
||||
return "epubjs:" + (EPUBJS_VERSION || ePub.VERSION) + ":" + ident;
|
||||
};
|
||||
/**
|
||||
* Generates the Book Key using the identifer in the manifest or other string provided
|
||||
* @param {[string]} identifier to use instead of metadata identifier
|
||||
* @return {string} key
|
||||
*/
|
||||
key(identifier) {
|
||||
var ident = identifier || this.package.metadata.identifier || this.url.filename;
|
||||
return "epubjs:" + (EPUBJS_VERSION || ePub.VERSION) + ":" + ident;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-- Enable binding events to book
|
||||
EventEmitter(Book.prototype);
|
||||
|
||||
module.exports = Book;
|
||||
export default Book;
|
||||
|
|
|
@ -1,41 +1,43 @@
|
|||
var path = require('path');
|
||||
var core = require('./core');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
import path from 'path-webpack';
|
||||
import {qs} from './utils/core';
|
||||
import EpubCFI from './epubcfi';
|
||||
|
||||
/**
|
||||
* Handles Parsing and Accessing an Epub Container
|
||||
* @class
|
||||
* @param {[document]} containerDocument xml document
|
||||
*/
|
||||
function Container(containerDocument) {
|
||||
if (containerDocument) {
|
||||
this.parse(containerDocument);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the Container XML
|
||||
* @param {document} containerDocument
|
||||
*/
|
||||
Container.prototype.parse = function(containerDocument){
|
||||
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
|
||||
var rootfile, fullpath, folder, encoding;
|
||||
|
||||
if(!containerDocument) {
|
||||
console.error("Container File Not Found");
|
||||
return;
|
||||
class Container {
|
||||
constructor(containerDocument) {
|
||||
if (containerDocument) {
|
||||
this.parse(containerDocument);
|
||||
}
|
||||
};
|
||||
|
||||
rootfile = core.qs(containerDocument, "rootfile");
|
||||
/**
|
||||
* Parse the Container XML
|
||||
* @param {document} containerDocument
|
||||
*/
|
||||
parse(containerDocument){
|
||||
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
|
||||
var rootfile, fullpath, folder, encoding;
|
||||
|
||||
if(!rootfile) {
|
||||
console.error("No RootFile Found");
|
||||
return;
|
||||
}
|
||||
if(!containerDocument) {
|
||||
console.error("Container File Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
this.packagePath = rootfile.getAttribute('full-path');
|
||||
this.directory = path.dirname(this.packagePath);
|
||||
this.encoding = containerDocument.xmlEncoding;
|
||||
};
|
||||
rootfile = qs(containerDocument, "rootfile");
|
||||
|
||||
module.exports = Container;
|
||||
if(!rootfile) {
|
||||
console.error("No RootFile Found");
|
||||
return;
|
||||
}
|
||||
|
||||
this.packagePath = rootfile.getAttribute('full-path');
|
||||
this.directory = path.dirname(this.packagePath);
|
||||
this.encoding = containerDocument.xmlEncoding;
|
||||
};
|
||||
}
|
||||
|
||||
export default Container;
|
||||
|
|
1296
src/contents.js
1296
src/contents.js
File diff suppressed because it is too large
Load diff
10
src/epub.js
10
src/epub.js
|
@ -1,7 +1,7 @@
|
|||
var Book = require('./book');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
var Rendition = require('./rendition');
|
||||
var Contents = require('./contents');
|
||||
import Book from './book';
|
||||
import EpubCFI from './epubcfi';
|
||||
import Rendition from './rendition';
|
||||
import Contents from './contents';
|
||||
|
||||
/**
|
||||
* Creates a new Book
|
||||
|
@ -51,4 +51,4 @@ ePub.register.view("iframe", require('./managers/views/iframe'));
|
|||
ePub.register.manager("default", require('./managers/default'));
|
||||
ePub.register.manager("continuous", require('./managers/continuous'));
|
||||
|
||||
module.exports = ePub;
|
||||
export default ePub;
|
||||
|
|
1752
src/epubcfi.js
1752
src/epubcfi.js
File diff suppressed because it is too large
Load diff
91
src/hook.js
91
src/hook.js
|
@ -5,58 +5,59 @@
|
|||
* @param {any} context scope of this
|
||||
* @example this.content = new EPUBJS.Hook(this);
|
||||
*/
|
||||
function Hook(context){
|
||||
this.context = context || this;
|
||||
this.hooks = [];
|
||||
};
|
||||
class Hook {
|
||||
constructor(context){
|
||||
this.context = context || this;
|
||||
this.hooks = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a function to be run before a hook completes
|
||||
* @example this.content.register(function(){...});
|
||||
*/
|
||||
Hook.prototype.register = function(){
|
||||
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) {
|
||||
this.hooks.push(arguments[i][j]);
|
||||
/**
|
||||
* Adds a function to be run before a hook completes
|
||||
* @example this.content.register(function(){...});
|
||||
*/
|
||||
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) {
|
||||
this.hooks.push(arguments[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggers a hook to run all functions
|
||||
* @example this.content.trigger(args).then(function(){...});
|
||||
*/
|
||||
Hook.prototype.trigger = function(){
|
||||
var args = arguments;
|
||||
var context = this.context;
|
||||
var promises = [];
|
||||
/**
|
||||
* Triggers a hook to run all functions
|
||||
* @example this.content.trigger(args).then(function(){...});
|
||||
*/
|
||||
trigger(){
|
||||
var args = arguments;
|
||||
var context = this.context;
|
||||
var promises = [];
|
||||
|
||||
this.hooks.forEach(function(task, i) {
|
||||
var executing = task.apply(context, args);
|
||||
this.hooks.forEach(function(task, i) {
|
||||
var executing = task.apply(context, args);
|
||||
|
||||
if(executing && typeof executing["then"] === "function") {
|
||||
// Task is a function that returns a promise
|
||||
promises.push(executing);
|
||||
}
|
||||
// Otherwise Task resolves immediately, continue
|
||||
});
|
||||
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);
|
||||
};
|
||||
return Promise.all(promises);
|
||||
};
|
||||
|
||||
// Adds a function to be run before a hook completes
|
||||
Hook.prototype.list = function(){
|
||||
return this.hooks;
|
||||
};
|
||||
// Adds a function to be run before a hook completes
|
||||
list(){
|
||||
return this.hooks;
|
||||
};
|
||||
|
||||
Hook.prototype.clear = function(){
|
||||
return this.hooks = [];
|
||||
};
|
||||
|
||||
module.exports = Hook;
|
||||
clear(){
|
||||
return this.hooks = [];
|
||||
};
|
||||
}
|
||||
export default Hook;
|
||||
|
|
15
src/index.js
Normal file
15
src/index.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import Book from './book';
|
||||
import EpubCFI from './epubcfi';
|
||||
import Rendition from './rendition';
|
||||
import Contents from './contents';
|
||||
import Layout from './layout';
|
||||
import ePub from './epub';
|
||||
|
||||
export default ePub;
|
||||
export {
|
||||
Book,
|
||||
EpubCFI,
|
||||
Rendition,
|
||||
Contents,
|
||||
Layout
|
||||
};
|
278
src/layout.js
278
src/layout.js
|
@ -1,4 +1,4 @@
|
|||
var core = require('./core');
|
||||
import core from './utils/core';
|
||||
|
||||
/**
|
||||
* Figures out the CSS to apply for a layout
|
||||
|
@ -9,146 +9,148 @@ var core = require('./core');
|
|||
* @param {[int=800]} settings.minSpreadWidth
|
||||
* @param {[boolean=false]} settings.evenSpreads
|
||||
*/
|
||||
function Layout(settings){
|
||||
this.name = settings.layout || "reflowable";
|
||||
this._spread = (settings.spread === "none") ? false : true;
|
||||
this._minSpreadWidth = settings.minSpreadWidth || 800;
|
||||
this._evenSpreads = settings.evenSpreads || false;
|
||||
class Layout {
|
||||
constructor(settings) {
|
||||
this.name = settings.layout || "reflowable";
|
||||
this._spread = (settings.spread === "none") ? false : true;
|
||||
this._minSpreadWidth = settings.minSpreadWidth || 800;
|
||||
this._evenSpreads = settings.evenSpreads || false;
|
||||
|
||||
if (settings.flow === "scrolled-continuous" ||
|
||||
settings.flow === "scrolled-doc") {
|
||||
this._flow = "scrolled";
|
||||
} else {
|
||||
this._flow = "paginated";
|
||||
}
|
||||
if (settings.flow === "scrolled-continuous" ||
|
||||
settings.flow === "scrolled-doc") {
|
||||
this._flow = "scrolled";
|
||||
} else {
|
||||
this._flow = "paginated";
|
||||
}
|
||||
|
||||
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.spreadWidth = 0;
|
||||
this.delta = 0;
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.spreadWidth = 0;
|
||||
this.delta = 0;
|
||||
|
||||
this.columnWidth = 0;
|
||||
this.gap = 0;
|
||||
this.divisor = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Switch the flow between paginated and scrolled
|
||||
* @param {string} flow paginated | scrolled
|
||||
*/
|
||||
Layout.prototype.flow = function(flow) {
|
||||
this._flow = (flow === "paginated") ? "paginated" : "scrolled";
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between using spreads or not, and set the
|
||||
* width at which they switch to single.
|
||||
* @param {string} spread true | false
|
||||
* @param {boolean} min integer in pixels
|
||||
*/
|
||||
Layout.prototype.spread = function(spread, min) {
|
||||
|
||||
this._spread = (spread === "none") ? false : true;
|
||||
|
||||
if (min >= 0) {
|
||||
this._minSpreadWidth = min;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the dimensions of the pagination
|
||||
* @param {number} _width [description]
|
||||
* @param {number} _height [description]
|
||||
* @param {number} _gap [description]
|
||||
*/
|
||||
Layout.prototype.calculate = function(_width, _height, _gap){
|
||||
|
||||
var divisor = 1;
|
||||
var gap = _gap || 0;
|
||||
|
||||
//-- Check the width and create even width columns
|
||||
var fullWidth = Math.floor(_width);
|
||||
var width = _width;
|
||||
|
||||
var section = Math.floor(width / 8);
|
||||
|
||||
var colWidth;
|
||||
var spreadWidth;
|
||||
var delta;
|
||||
|
||||
if (this._spread && width >= this._minSpreadWidth) {
|
||||
divisor = 2;
|
||||
} else {
|
||||
divisor = 1;
|
||||
}
|
||||
|
||||
if (this.name === "reflowable" && this._flow === "paginated" && !(_gap >= 0)) {
|
||||
gap = ((section % 2 === 0) ? section : section - 1);
|
||||
}
|
||||
|
||||
if (this.name === "pre-paginated" ) {
|
||||
gap = 0;
|
||||
}
|
||||
|
||||
//-- Double Page
|
||||
if(divisor > 1) {
|
||||
colWidth = (width - gap) / divisor;
|
||||
} else {
|
||||
colWidth = width;
|
||||
}
|
||||
|
||||
if (this.name === "pre-paginated" && divisor > 1) {
|
||||
width = colWidth;
|
||||
}
|
||||
|
||||
spreadWidth = colWidth * divisor;
|
||||
|
||||
delta = (colWidth + gap) * divisor;
|
||||
|
||||
this.width = width;
|
||||
this.height = _height;
|
||||
this.spreadWidth = spreadWidth;
|
||||
this.delta = delta;
|
||||
|
||||
this.columnWidth = colWidth;
|
||||
this.gap = gap;
|
||||
this.divisor = divisor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply Css to a Document
|
||||
* @param {Contents} contents
|
||||
* @return {[Promise]}
|
||||
*/
|
||||
Layout.prototype.format = function(contents){
|
||||
var formating;
|
||||
|
||||
if (this.name === "pre-paginated") {
|
||||
formating = contents.fit(this.columnWidth, this.height);
|
||||
} else if (this._flow === "paginated") {
|
||||
formating = contents.columns(this.width, this.height, this.columnWidth, this.gap);
|
||||
} else { // scrolled
|
||||
formating = contents.size(this.width, null);
|
||||
}
|
||||
|
||||
return formating; // might be a promise in some View Managers
|
||||
};
|
||||
|
||||
/**
|
||||
* Count number of pages
|
||||
* @param {number} totalWidth
|
||||
* @return {number} spreads
|
||||
* @return {number} pages
|
||||
*/
|
||||
Layout.prototype.count = function(totalWidth) {
|
||||
// var totalWidth = contents.scrollWidth();
|
||||
var spreads = Math.ceil( totalWidth / this.spreadWidth);
|
||||
|
||||
return {
|
||||
spreads : spreads,
|
||||
pages : spreads * this.divisor
|
||||
this.columnWidth = 0;
|
||||
this.gap = 0;
|
||||
this.divisor = 1;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = Layout;
|
||||
/**
|
||||
* Switch the flow between paginated and scrolled
|
||||
* @param {string} flow paginated | scrolled
|
||||
*/
|
||||
flow(flow) {
|
||||
this._flow = (flow === "paginated") ? "paginated" : "scrolled";
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between using spreads or not, and set the
|
||||
* width at which they switch to single.
|
||||
* @param {string} spread true | false
|
||||
* @param {boolean} min integer in pixels
|
||||
*/
|
||||
spread(spread, min) {
|
||||
|
||||
this._spread = (spread === "none") ? false : true;
|
||||
|
||||
if (min >= 0) {
|
||||
this._minSpreadWidth = min;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the dimensions of the pagination
|
||||
* @param {number} _width [description]
|
||||
* @param {number} _height [description]
|
||||
* @param {number} _gap [description]
|
||||
*/
|
||||
calculate(_width, _height, _gap){
|
||||
|
||||
var divisor = 1;
|
||||
var gap = _gap || 0;
|
||||
|
||||
//-- Check the width and create even width columns
|
||||
var fullWidth = Math.floor(_width);
|
||||
var width = _width;
|
||||
|
||||
var section = Math.floor(width / 8);
|
||||
|
||||
var colWidth;
|
||||
var spreadWidth;
|
||||
var delta;
|
||||
|
||||
if (this._spread && width >= this._minSpreadWidth) {
|
||||
divisor = 2;
|
||||
} else {
|
||||
divisor = 1;
|
||||
}
|
||||
|
||||
if (this.name === "reflowable" && this._flow === "paginated" && !(_gap >= 0)) {
|
||||
gap = ((section % 2 === 0) ? section : section - 1);
|
||||
}
|
||||
|
||||
if (this.name === "pre-paginated" ) {
|
||||
gap = 0;
|
||||
}
|
||||
|
||||
//-- Double Page
|
||||
if(divisor > 1) {
|
||||
colWidth = (width - gap) / divisor;
|
||||
} else {
|
||||
colWidth = width;
|
||||
}
|
||||
|
||||
if (this.name === "pre-paginated" && divisor > 1) {
|
||||
width = colWidth;
|
||||
}
|
||||
|
||||
spreadWidth = colWidth * divisor;
|
||||
|
||||
delta = (colWidth + gap) * divisor;
|
||||
|
||||
this.width = width;
|
||||
this.height = _height;
|
||||
this.spreadWidth = spreadWidth;
|
||||
this.delta = delta;
|
||||
|
||||
this.columnWidth = colWidth;
|
||||
this.gap = gap;
|
||||
this.divisor = divisor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply Css to a Document
|
||||
* @param {Contents} contents
|
||||
* @return {[Promise]}
|
||||
*/
|
||||
format(contents){
|
||||
var formating;
|
||||
|
||||
if (this.name === "pre-paginated") {
|
||||
formating = contents.fit(this.columnWidth, this.height);
|
||||
} else if (this._flow === "paginated") {
|
||||
formating = contents.columns(this.width, this.height, this.columnWidth, this.gap);
|
||||
} else { // scrolled
|
||||
formating = contents.size(this.width, null);
|
||||
}
|
||||
|
||||
return formating; // might be a promise in some View Managers
|
||||
};
|
||||
|
||||
/**
|
||||
* Count number of pages
|
||||
* @param {number} totalWidth
|
||||
* @return {number} spreads
|
||||
* @return {number} pages
|
||||
*/
|
||||
count(totalWidth) {
|
||||
// var totalWidth = contents.scrollWidth();
|
||||
var spreads = Math.ceil( totalWidth / this.spreadWidth);
|
||||
|
||||
return {
|
||||
spreads : spreads,
|
||||
pages : spreads * this.divisor
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
|
|
437
src/locations.js
437
src/locations.js
|
@ -1,257 +1,258 @@
|
|||
var core = require('./core');
|
||||
var Queue = require('./queue');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
var EventEmitter = require('event-emitter');
|
||||
import {qs, sprint, locationOf} from './utils/core';
|
||||
import Queue from './queue';
|
||||
import EpubCFI from './epubcfi';
|
||||
import EventEmitter from 'event-emitter';
|
||||
|
||||
/**
|
||||
* Find Locations for a Book
|
||||
* @param {Spine} spine
|
||||
* @param {request} request
|
||||
*/
|
||||
function Locations(spine, request) {
|
||||
this.spine = spine;
|
||||
this.request = request;
|
||||
class Locations {
|
||||
constructor(spine, request) {
|
||||
this.spine = spine;
|
||||
this.request = request;
|
||||
|
||||
this.q = new Queue(this);
|
||||
this.epubcfi = new EpubCFI();
|
||||
this.q = new Queue(this);
|
||||
this.epubcfi = new EpubCFI();
|
||||
|
||||
this._locations = [];
|
||||
this.total = 0;
|
||||
this._locations = [];
|
||||
this.total = 0;
|
||||
|
||||
this.break = 150;
|
||||
this.break = 150;
|
||||
|
||||
this._current = 0;
|
||||
this._current = 0;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Load all of sections in the book to generate locations
|
||||
* @param {int} chars how many chars to split on
|
||||
* @return {object} locations
|
||||
*/
|
||||
Locations.prototype.generate = function(chars) {
|
||||
|
||||
if (chars) {
|
||||
this.break = chars;
|
||||
}
|
||||
|
||||
this.q.pause();
|
||||
|
||||
this.spine.each(function(section) {
|
||||
|
||||
this.q.enqueue(this.process.bind(this), section);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
return this.q.run().then(function() {
|
||||
this.total = this._locations.length-1;
|
||||
|
||||
if (this._currentCfi) {
|
||||
this.currentLocation = this._currentCfi;
|
||||
}
|
||||
|
||||
return this._locations;
|
||||
// console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end));
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
Locations.prototype.createRange = function () {
|
||||
return {
|
||||
startContainer: undefined,
|
||||
startOffset: undefined,
|
||||
endContainer: undefined,
|
||||
endOffset: undefined
|
||||
}
|
||||
};
|
||||
|
||||
Locations.prototype.process = function(section) {
|
||||
|
||||
return section.load(this.request)
|
||||
.then(function(contents) {
|
||||
var locations = this.parse(contents, section.cfiBase);
|
||||
this._locations = this._locations.concat(locations);
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
Locations.prototype.parse = function(contents, cfiBase, chars) {
|
||||
var locations = [];
|
||||
var range;
|
||||
var doc = contents.ownerDocument;
|
||||
var body = core.qs(doc, 'body');
|
||||
var counter = 0;
|
||||
var prev;
|
||||
var _break = chars || this.break;
|
||||
var parser = function(node) {
|
||||
var len = node.length;
|
||||
var dist;
|
||||
var pos = 0;
|
||||
|
||||
if (node.textContent.trim().length === 0) {
|
||||
return false; // continue
|
||||
}
|
||||
|
||||
// Start range
|
||||
if (counter == 0) {
|
||||
range = this.createRange();
|
||||
range.startContainer = node;
|
||||
range.startOffset = 0;
|
||||
}
|
||||
|
||||
dist = _break - counter;
|
||||
|
||||
// Node is smaller than a break,
|
||||
// skip over it
|
||||
if(dist > len){
|
||||
counter += len;
|
||||
pos = len;
|
||||
}
|
||||
|
||||
while (pos < len) {
|
||||
// counter = this.break;
|
||||
pos += dist;
|
||||
// Gone over
|
||||
if(pos >= len){
|
||||
// Continue counter for next node
|
||||
counter = len - (pos - _break);
|
||||
// At End
|
||||
} else {
|
||||
// End the previous range
|
||||
range.endContainer = node;
|
||||
range.endOffset = pos;
|
||||
// cfi = section.cfiFromRange(range);
|
||||
cfi = new EpubCFI(range, cfiBase).toString();
|
||||
locations.push(cfi);
|
||||
counter = 0;
|
||||
|
||||
// Start new range
|
||||
pos += 1;
|
||||
range = this.createRange();
|
||||
range.startContainer = node;
|
||||
range.startOffset = pos;
|
||||
|
||||
dist = _break;
|
||||
}
|
||||
}
|
||||
prev = node;
|
||||
};
|
||||
|
||||
core.sprint(body, parser.bind(this));
|
||||
/**
|
||||
* Load all of sections in the book to generate locations
|
||||
* @param {int} chars how many chars to split on
|
||||
* @return {object} locations
|
||||
*/
|
||||
generate(chars) {
|
||||
|
||||
// Close remaining
|
||||
if (range && range.startContainer && prev) {
|
||||
range.endContainer = prev;
|
||||
range.endOffset = prev.length;
|
||||
// cfi = section.cfiFromRange(range);
|
||||
cfi = new EpubCFI(range, cfiBase).toString();
|
||||
locations.push(cfi);
|
||||
counter = 0;
|
||||
}
|
||||
if (chars) {
|
||||
this.break = chars;
|
||||
}
|
||||
|
||||
return locations;
|
||||
};
|
||||
this.q.pause();
|
||||
|
||||
Locations.prototype.locationFromCfi = function(cfi){
|
||||
// Check if the location has not been set yet
|
||||
if(this._locations.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
return core.locationOf(cfi.start, this._locations, this.epubcfi.compare);
|
||||
};
|
||||
this.spine.each(function(section) {
|
||||
|
||||
Locations.prototype.percentageFromCfi = function(cfi) {
|
||||
if(this._locations.length === 0) {
|
||||
return null;
|
||||
}
|
||||
// Find closest cfi
|
||||
var loc = this.locationFromCfi(cfi);
|
||||
// Get percentage in total
|
||||
return this.percentageFromLocation(loc);
|
||||
};
|
||||
this.q.enqueue(this.process.bind(this), section);
|
||||
|
||||
Locations.prototype.percentageFromLocation = function(loc) {
|
||||
if (!loc || !this.total) {
|
||||
return 0;
|
||||
}
|
||||
return (loc / this.total);
|
||||
};
|
||||
}.bind(this));
|
||||
|
||||
Locations.prototype.cfiFromLocation = function(loc){
|
||||
var cfi = -1;
|
||||
// check that pg is an int
|
||||
if(typeof loc != "number"){
|
||||
loc = parseInt(pg);
|
||||
}
|
||||
return this.q.run().then(function() {
|
||||
this.total = this._locations.length-1;
|
||||
|
||||
if(loc >= 0 && loc < this._locations.length) {
|
||||
cfi = this._locations[loc];
|
||||
}
|
||||
if (this._currentCfi) {
|
||||
this.currentLocation = this._currentCfi;
|
||||
}
|
||||
|
||||
return cfi;
|
||||
};
|
||||
return this._locations;
|
||||
// console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end));
|
||||
}.bind(this));
|
||||
|
||||
Locations.prototype.cfiFromPercentage = function(value){
|
||||
var percentage = (value > 1) ? value / 100 : value; // Normalize value to 0-1
|
||||
var loc = Math.ceil(this.total * percentage);
|
||||
};
|
||||
|
||||
return this.cfiFromLocation(loc);
|
||||
};
|
||||
createRange () {
|
||||
return {
|
||||
startContainer: undefined,
|
||||
startOffset: undefined,
|
||||
endContainer: undefined,
|
||||
endOffset: undefined
|
||||
}
|
||||
};
|
||||
|
||||
Locations.prototype.load = function(locations){
|
||||
this._locations = JSON.parse(locations);
|
||||
this.total = this._locations.length-1;
|
||||
return this._locations;
|
||||
};
|
||||
process(section) {
|
||||
|
||||
Locations.prototype.save = function(json){
|
||||
return JSON.stringify(this._locations);
|
||||
};
|
||||
return section.load(this.request)
|
||||
.then(function(contents) {
|
||||
var locations = this.parse(contents, section.cfiBase);
|
||||
this._locations = this._locations.concat(locations);
|
||||
}.bind(this));
|
||||
|
||||
Locations.prototype.getCurrent = function(json){
|
||||
return this._current;
|
||||
};
|
||||
};
|
||||
|
||||
Locations.prototype.setCurrent = function(curr){
|
||||
var loc;
|
||||
parse(contents, cfiBase, chars) {
|
||||
var locations = [];
|
||||
var range;
|
||||
var doc = contents.ownerDocument;
|
||||
var body = qs(doc, 'body');
|
||||
var counter = 0;
|
||||
var prev;
|
||||
var _break = chars || this.break;
|
||||
var parser = function(node) {
|
||||
var len = node.length;
|
||||
var dist;
|
||||
var pos = 0;
|
||||
|
||||
if(typeof curr == "string"){
|
||||
this._currentCfi = curr;
|
||||
} else if (typeof curr == "number") {
|
||||
this._current = curr;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (node.textContent.trim().length === 0) {
|
||||
return false; // continue
|
||||
}
|
||||
|
||||
if(this._locations.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Start range
|
||||
if (counter == 0) {
|
||||
range = this.createRange();
|
||||
range.startContainer = node;
|
||||
range.startOffset = 0;
|
||||
}
|
||||
|
||||
if(typeof curr == "string"){
|
||||
loc = this.locationFromCfi(curr);
|
||||
this._current = loc;
|
||||
} else {
|
||||
loc = curr;
|
||||
}
|
||||
dist = _break - counter;
|
||||
|
||||
this.emit("changed", {
|
||||
percentage: this.percentageFromLocation(loc)
|
||||
});
|
||||
};
|
||||
// Node is smaller than a break,
|
||||
// skip over it
|
||||
if(dist > len){
|
||||
counter += len;
|
||||
pos = len;
|
||||
}
|
||||
|
||||
Object.defineProperty(Locations.prototype, 'currentLocation', {
|
||||
get: function () {
|
||||
while (pos < len) {
|
||||
// counter = this.break;
|
||||
pos += dist;
|
||||
// Gone over
|
||||
if(pos >= len){
|
||||
// Continue counter for next node
|
||||
counter = len - (pos - _break);
|
||||
// At End
|
||||
} else {
|
||||
// End the previous range
|
||||
range.endContainer = node;
|
||||
range.endOffset = pos;
|
||||
// cfi = section.cfiFromRange(range);
|
||||
let cfi = new EpubCFI(range, cfiBase).toString();
|
||||
locations.push(cfi);
|
||||
counter = 0;
|
||||
|
||||
// Start new range
|
||||
pos += 1;
|
||||
range = this.createRange();
|
||||
range.startContainer = node;
|
||||
range.startOffset = pos;
|
||||
|
||||
dist = _break;
|
||||
}
|
||||
}
|
||||
prev = node;
|
||||
};
|
||||
|
||||
sprint(body, parser.bind(this));
|
||||
|
||||
// Close remaining
|
||||
if (range && range.startContainer && prev) {
|
||||
range.endContainer = prev;
|
||||
range.endOffset = prev.length;
|
||||
// cfi = section.cfiFromRange(range);
|
||||
let cfi = new EpubCFI(range, cfiBase).toString();
|
||||
locations.push(cfi);
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
return locations;
|
||||
};
|
||||
|
||||
locationFromCfi(cfi){
|
||||
// Check if the location has not been set yet
|
||||
if(this._locations.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
return locationOf(cfi.start, this._locations, this.epubcfi.compare);
|
||||
};
|
||||
|
||||
percentageFromCfi(cfi) {
|
||||
if(this._locations.length === 0) {
|
||||
return null;
|
||||
}
|
||||
// Find closest cfi
|
||||
var loc = this.locationFromCfi(cfi);
|
||||
// Get percentage in total
|
||||
return this.percentageFromLocation(loc);
|
||||
};
|
||||
|
||||
percentageFromLocation(loc) {
|
||||
if (!loc || !this.total) {
|
||||
return 0;
|
||||
}
|
||||
return (loc / this.total);
|
||||
};
|
||||
|
||||
cfiFromLocation(loc){
|
||||
var cfi = -1;
|
||||
// check that pg is an int
|
||||
if(typeof loc != "number"){
|
||||
loc = parseInt(pg);
|
||||
}
|
||||
|
||||
if(loc >= 0 && loc < this._locations.length) {
|
||||
cfi = this._locations[loc];
|
||||
}
|
||||
|
||||
return cfi;
|
||||
};
|
||||
|
||||
cfiFromPercentage(value){
|
||||
var percentage = (value > 1) ? value / 100 : value; // Normalize value to 0-1
|
||||
var loc = Math.ceil(this.total * percentage);
|
||||
|
||||
return this.cfiFromLocation(loc);
|
||||
};
|
||||
|
||||
load(locations){
|
||||
this._locations = JSON.parse(locations);
|
||||
this.total = this._locations.length-1;
|
||||
return this._locations;
|
||||
};
|
||||
|
||||
save(json){
|
||||
return JSON.stringify(this._locations);
|
||||
};
|
||||
|
||||
getCurrent(json){
|
||||
return this._current;
|
||||
},
|
||||
set: function (curr) {
|
||||
};
|
||||
|
||||
setCurrent(curr){
|
||||
var loc;
|
||||
|
||||
if(typeof curr == "string"){
|
||||
this._currentCfi = curr;
|
||||
} else if (typeof curr == "number") {
|
||||
this._current = curr;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this._locations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof curr == "string"){
|
||||
loc = this.locationFromCfi(curr);
|
||||
this._current = loc;
|
||||
} else {
|
||||
loc = curr;
|
||||
}
|
||||
|
||||
this.emit("changed", {
|
||||
percentage: this.percentageFromLocation(loc)
|
||||
});
|
||||
};
|
||||
|
||||
get currentLocation() {
|
||||
return this._current;
|
||||
}
|
||||
|
||||
set currentLocation(curr) {
|
||||
this.setCurrent(curr);
|
||||
}
|
||||
});
|
||||
|
||||
Locations.prototype.length = function () {
|
||||
return this._locations.length;
|
||||
};
|
||||
length () {
|
||||
return this._locations.length;
|
||||
};
|
||||
}
|
||||
|
||||
EventEmitter(Locations.prototype);
|
||||
|
||||
module.exports = Locations;
|
||||
export default Locations;
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,231 +1,231 @@
|
|||
var core = require('../../core');
|
||||
import {uuid, isNumber, isElement, windowBounds} from '../../utils/core';
|
||||
|
||||
function Stage(_options) {
|
||||
this.settings = _options || {};
|
||||
this.id = "epubjs-container-" + core.uuid();
|
||||
class Stage {
|
||||
constructor(_options) {
|
||||
this.settings = _options || {};
|
||||
this.id = "epubjs-container-" + uuid();
|
||||
|
||||
this.container = this.create(this.settings);
|
||||
this.container = this.create(this.settings);
|
||||
|
||||
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
|
||||
*/
|
||||
Stage.prototype.create = function(options){
|
||||
var height = options.height;// !== false ? options.height : "100%";
|
||||
var width = options.width;// !== false ? options.width : "100%";
|
||||
var overflow = options.overflow || false;
|
||||
var axis = options.axis || "vertical";
|
||||
|
||||
if(options.height && core.isNumber(options.height)) {
|
||||
height = options.height + "px";
|
||||
}
|
||||
|
||||
if(options.width && core.isNumber(options.width)) {
|
||||
width = options.width + "px";
|
||||
}
|
||||
|
||||
// Create new container element
|
||||
container = document.createElement("div");
|
||||
|
||||
container.id = this.id;
|
||||
container.classList.add("epub-container");
|
||||
|
||||
// Style Element
|
||||
// container.style.fontSize = "0";
|
||||
container.style.wordSpacing = "0";
|
||||
container.style.lineHeight = "0";
|
||||
container.style.verticalAlign = "top";
|
||||
|
||||
if(axis === "horizontal") {
|
||||
container.style.whiteSpace = "nowrap";
|
||||
}
|
||||
|
||||
if(width){
|
||||
container.style.width = width;
|
||||
}
|
||||
|
||||
if(height){
|
||||
container.style.height = height;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
container.style.overflow = overflow;
|
||||
}
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
Stage.wrap = function(container) {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
wrapper.style.visibility = "hidden";
|
||||
wrapper.style.overflow = "hidden";
|
||||
wrapper.style.width = "0";
|
||||
wrapper.style.height = "0";
|
||||
|
||||
wrapper.appendChild(container);
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
|
||||
Stage.prototype.getElement = function(_element){
|
||||
var element;
|
||||
|
||||
if(core.isElement(_element)) {
|
||||
element = _element;
|
||||
} else if (typeof _element === "string") {
|
||||
element = document.getElementById(_element);
|
||||
}
|
||||
|
||||
if(!element){
|
||||
console.error("Not an Element");
|
||||
return;
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
Stage.prototype.attachTo = function(what){
|
||||
|
||||
var element = this.getElement(what);
|
||||
var base;
|
||||
|
||||
if(!element){
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.settings.hidden) {
|
||||
base = this.wrapper;
|
||||
} else {
|
||||
base = this.container;
|
||||
}
|
||||
|
||||
element.appendChild(base);
|
||||
|
||||
this.element = element;
|
||||
|
||||
return element;
|
||||
|
||||
};
|
||||
|
||||
Stage.prototype.getContainer = function() {
|
||||
return this.container;
|
||||
};
|
||||
|
||||
Stage.prototype.onResize = function(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(!core.isNumber(this.settings.width) ||
|
||||
!core.isNumber(this.settings.height) ) {
|
||||
window.addEventListener("resize", func, false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Stage.prototype.size = function(width, height){
|
||||
var bounds;
|
||||
// var width = _width || this.settings.width;
|
||||
// var height = _height || this.settings.height;
|
||||
|
||||
// If width or height are set to false, inherit them from containing element
|
||||
if(width === null) {
|
||||
bounds = this.element.getBoundingClientRect();
|
||||
|
||||
if(bounds.width) {
|
||||
width = bounds.width;
|
||||
this.container.style.width = bounds.width + "px";
|
||||
}
|
||||
}
|
||||
|
||||
if(height === null) {
|
||||
bounds = bounds || this.element.getBoundingClientRect();
|
||||
|
||||
if(bounds.height) {
|
||||
height = bounds.height;
|
||||
this.container.style.height = bounds.height + "px";
|
||||
if(this.settings.hidden) {
|
||||
this.wrapper = this.wrap(this.container);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!core.isNumber(width)) {
|
||||
bounds = this.container.getBoundingClientRect();
|
||||
width = bounds.width;
|
||||
//height = bounds.height;
|
||||
}
|
||||
/*
|
||||
* Creates an element to render to.
|
||||
* Resizes to passed width and height or to the elements size
|
||||
*/
|
||||
create(options){
|
||||
var height = options.height;// !== false ? options.height : "100%";
|
||||
var width = options.width;// !== false ? options.width : "100%";
|
||||
var overflow = options.overflow || false;
|
||||
var axis = options.axis || "vertical";
|
||||
|
||||
if(!core.isNumber(height)) {
|
||||
bounds = bounds || this.container.getBoundingClientRect();
|
||||
//width = bounds.width;
|
||||
height = bounds.height;
|
||||
}
|
||||
if(options.height && isNumber(options.height)) {
|
||||
height = options.height + "px";
|
||||
}
|
||||
|
||||
if(options.width && isNumber(options.width)) {
|
||||
width = options.width + "px";
|
||||
}
|
||||
|
||||
this.containerStyles = window.getComputedStyle(this.container);
|
||||
// Create new container element
|
||||
let container = document.createElement("div");
|
||||
|
||||
this.containerPadding = {
|
||||
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
|
||||
container.id = this.id;
|
||||
container.classList.add("epub-container");
|
||||
|
||||
// Style Element
|
||||
// container.style.fontSize = "0";
|
||||
container.style.wordSpacing = "0";
|
||||
container.style.lineHeight = "0";
|
||||
container.style.verticalAlign = "top";
|
||||
|
||||
if(axis === "horizontal") {
|
||||
container.style.whiteSpace = "nowrap";
|
||||
}
|
||||
|
||||
if(width){
|
||||
container.style.width = width;
|
||||
}
|
||||
|
||||
if(height){
|
||||
container.style.height = height;
|
||||
}
|
||||
|
||||
if (overflow) {
|
||||
container.style.overflow = overflow;
|
||||
}
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
return {
|
||||
width: width -
|
||||
this.containerPadding.left -
|
||||
this.containerPadding.right,
|
||||
height: height -
|
||||
this.containerPadding.top -
|
||||
this.containerPadding.bottom
|
||||
wrap(container) {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
wrapper.style.visibility = "hidden";
|
||||
wrapper.style.overflow = "hidden";
|
||||
wrapper.style.width = "0";
|
||||
wrapper.style.height = "0";
|
||||
|
||||
wrapper.appendChild(container);
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
Stage.prototype.bounds = function(){
|
||||
getElement(_element){
|
||||
var element;
|
||||
|
||||
if(!this.container) {
|
||||
return core.windowBounds();
|
||||
} else {
|
||||
return this.container.getBoundingClientRect();
|
||||
}
|
||||
if(isElement(_element)) {
|
||||
element = _element;
|
||||
} else if (typeof _element === "string") {
|
||||
element = document.getElementById(_element);
|
||||
}
|
||||
|
||||
}
|
||||
if(!element){
|
||||
console.error("Not an Element");
|
||||
return;
|
||||
}
|
||||
|
||||
Stage.prototype.getSheet = function(){
|
||||
var style = document.createElement("style");
|
||||
return element;
|
||||
};
|
||||
|
||||
// WebKit hack --> https://davidwalsh.name/add-rules-stylesheets
|
||||
style.appendChild(document.createTextNode(""));
|
||||
attachTo(what){
|
||||
|
||||
document.head.appendChild(style);
|
||||
var element = this.getElement(what);
|
||||
var base;
|
||||
|
||||
return style.sheet;
|
||||
}
|
||||
if(!element){
|
||||
return;
|
||||
}
|
||||
|
||||
Stage.prototype.addStyleRules = function(selector, rulesArray){
|
||||
var scope = "#" + this.id + " ";
|
||||
var rules = "";
|
||||
if(this.settings.hidden) {
|
||||
base = this.wrapper;
|
||||
} else {
|
||||
base = this.container;
|
||||
}
|
||||
|
||||
if(!this.sheet){
|
||||
this.sheet = this.getSheet();
|
||||
}
|
||||
element.appendChild(base);
|
||||
|
||||
rulesArray.forEach(function(set) {
|
||||
for (var prop in set) {
|
||||
if(set.hasOwnProperty(prop)) {
|
||||
rules += prop + ":" + set[prop] + ";";
|
||||
this.element = element;
|
||||
|
||||
return element;
|
||||
|
||||
};
|
||||
|
||||
getContainer() {
|
||||
return this.container;
|
||||
};
|
||||
|
||||
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) ) {
|
||||
window.addEventListener("resize", func, false);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
size(width, height){
|
||||
var bounds;
|
||||
// var width = _width || this.settings.width;
|
||||
// var height = _height || this.settings.height;
|
||||
|
||||
// If width or height are set to false, inherit them from containing element
|
||||
if(width === null) {
|
||||
bounds = this.element.getBoundingClientRect();
|
||||
|
||||
if(bounds.width) {
|
||||
width = bounds.width;
|
||||
this.container.style.width = bounds.width + "px";
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.sheet.insertRule(scope + selector + " {" + rules + "}", 0);
|
||||
if(height === null) {
|
||||
bounds = bounds || this.element.getBoundingClientRect();
|
||||
|
||||
if(bounds.height) {
|
||||
height = bounds.height;
|
||||
this.container.style.height = bounds.height + "px";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!isNumber(width)) {
|
||||
bounds = this.container.getBoundingClientRect();
|
||||
width = bounds.width;
|
||||
//height = bounds.height;
|
||||
}
|
||||
|
||||
if(!isNumber(height)) {
|
||||
bounds = bounds || this.container.getBoundingClientRect();
|
||||
//width = bounds.width;
|
||||
height = bounds.height;
|
||||
}
|
||||
|
||||
|
||||
this.containerStyles = window.getComputedStyle(this.container);
|
||||
|
||||
this.containerPadding = {
|
||||
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
|
||||
};
|
||||
|
||||
return {
|
||||
width: width -
|
||||
this.containerPadding.left -
|
||||
this.containerPadding.right,
|
||||
height: height -
|
||||
this.containerPadding.top -
|
||||
this.containerPadding.bottom
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
bounds(){
|
||||
|
||||
if(!this.container) {
|
||||
return windowBounds();
|
||||
} else {
|
||||
return this.container.getBoundingClientRect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getSheet(){
|
||||
var style = document.createElement("style");
|
||||
|
||||
// WebKit hack --> https://davidwalsh.name/add-rules-stylesheets
|
||||
style.appendChild(document.createTextNode(""));
|
||||
|
||||
document.head.appendChild(style);
|
||||
|
||||
return style.sheet;
|
||||
}
|
||||
|
||||
addStyleRules(selector, rulesArray){
|
||||
var scope = "#" + this.id + " ";
|
||||
var rules = "";
|
||||
|
||||
if(!this.sheet){
|
||||
this.sheet = this.getSheet();
|
||||
}
|
||||
|
||||
rulesArray.forEach(function(set) {
|
||||
for (var prop in set) {
|
||||
if(set.hasOwnProperty(prop)) {
|
||||
rules += prop + ":" + set[prop] + ";";
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.sheet.insertRule(scope + selector + " {" + rules + "}", 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = Stage;
|
||||
export default Stage;
|
||||
|
|
|
@ -1,165 +1,167 @@
|
|||
function Views(container) {
|
||||
this.container = container;
|
||||
this._views = [];
|
||||
this.length = 0;
|
||||
this.hidden = false;
|
||||
};
|
||||
class Views {
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
this._views = [];
|
||||
this.length = 0;
|
||||
this.hidden = false;
|
||||
};
|
||||
|
||||
Views.prototype.all = function() {
|
||||
return this._views;
|
||||
};
|
||||
all() {
|
||||
return this._views;
|
||||
};
|
||||
|
||||
Views.prototype.first = function() {
|
||||
return this._views[0];
|
||||
};
|
||||
first() {
|
||||
return this._views[0];
|
||||
};
|
||||
|
||||
Views.prototype.last = function() {
|
||||
return this._views[this._views.length-1];
|
||||
};
|
||||
last() {
|
||||
return this._views[this._views.length-1];
|
||||
};
|
||||
|
||||
Views.prototype.indexOf = function(view) {
|
||||
return this._views.indexOf(view);
|
||||
};
|
||||
indexOf(view) {
|
||||
return this._views.indexOf(view);
|
||||
};
|
||||
|
||||
Views.prototype.slice = function() {
|
||||
return this._views.slice.apply(this._views, arguments);
|
||||
};
|
||||
slice() {
|
||||
return this._views.slice.apply(this._views, arguments);
|
||||
};
|
||||
|
||||
Views.prototype.get = function(i) {
|
||||
return this._views[i];
|
||||
};
|
||||
get(i) {
|
||||
return this._views[i];
|
||||
};
|
||||
|
||||
Views.prototype.append = function(view){
|
||||
this._views.push(view);
|
||||
if(this.container){
|
||||
this.container.appendChild(view.element);
|
||||
}
|
||||
this.length++;
|
||||
return view;
|
||||
};
|
||||
|
||||
Views.prototype.prepend = function(view){
|
||||
this._views.unshift(view);
|
||||
if(this.container){
|
||||
this.container.insertBefore(view.element, this.container.firstChild);
|
||||
}
|
||||
this.length++;
|
||||
return view;
|
||||
};
|
||||
|
||||
Views.prototype.insert = function(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]);
|
||||
} else {
|
||||
append(view){
|
||||
this._views.push(view);
|
||||
if(this.container){
|
||||
this.container.appendChild(view.element);
|
||||
}
|
||||
}
|
||||
this.length++;
|
||||
return view;
|
||||
};
|
||||
|
||||
this.length++;
|
||||
return view;
|
||||
};
|
||||
prepend(view){
|
||||
this._views.unshift(view);
|
||||
if(this.container){
|
||||
this.container.insertBefore(view.element, this.container.firstChild);
|
||||
}
|
||||
this.length++;
|
||||
return view;
|
||||
};
|
||||
|
||||
Views.prototype.remove = function(view) {
|
||||
var index = this._views.indexOf(view);
|
||||
insert(view, index) {
|
||||
this._views.splice(index, 0, view);
|
||||
|
||||
if(index > -1) {
|
||||
this._views.splice(index, 1);
|
||||
}
|
||||
if(this.container){
|
||||
if(index < this.container.children.length){
|
||||
this.container.insertBefore(view.element, this.container.children[index]);
|
||||
} else {
|
||||
this.container.appendChild(view.element);
|
||||
}
|
||||
}
|
||||
|
||||
this.length++;
|
||||
return view;
|
||||
};
|
||||
|
||||
remove(view) {
|
||||
var index = this._views.indexOf(view);
|
||||
|
||||
if(index > -1) {
|
||||
this._views.splice(index, 1);
|
||||
}
|
||||
|
||||
|
||||
this.destroy(view);
|
||||
|
||||
this.length--;
|
||||
};
|
||||
|
||||
Views.prototype.destroy = function(view) {
|
||||
if(view.displayed){
|
||||
view.destroy();
|
||||
}
|
||||
|
||||
if(this.container){
|
||||
this.container.removeChild(view.element);
|
||||
}
|
||||
view = null;
|
||||
};
|
||||
|
||||
// Iterators
|
||||
|
||||
Views.prototype.each = function() {
|
||||
return this._views.forEach.apply(this._views, arguments);
|
||||
};
|
||||
|
||||
Views.prototype.clear = function(){
|
||||
// Remove all views
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
if(!this.length) return;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
this.destroy(view);
|
||||
}
|
||||
|
||||
this._views = [];
|
||||
this.length = 0;
|
||||
};
|
||||
this.length--;
|
||||
};
|
||||
|
||||
Views.prototype.find = function(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) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Views.prototype.displayed = function(){
|
||||
var displayed = [];
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
destroy(view) {
|
||||
if(view.displayed){
|
||||
displayed.push(view);
|
||||
view.destroy();
|
||||
}
|
||||
}
|
||||
return displayed;
|
||||
};
|
||||
|
||||
Views.prototype.show = function(){
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
if(view.displayed){
|
||||
view.show();
|
||||
if(this.container){
|
||||
this.container.removeChild(view.element);
|
||||
}
|
||||
}
|
||||
this.hidden = false;
|
||||
};
|
||||
view = null;
|
||||
};
|
||||
|
||||
Views.prototype.hide = function(){
|
||||
var view;
|
||||
var len = this.length;
|
||||
// Iterators
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
if(view.displayed){
|
||||
view.hide();
|
||||
each() {
|
||||
return this._views.forEach.apply(this._views, arguments);
|
||||
};
|
||||
|
||||
clear(){
|
||||
// Remove all views
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
if(!this.length) return;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
this.destroy(view);
|
||||
}
|
||||
}
|
||||
this.hidden = true;
|
||||
};
|
||||
|
||||
module.exports = Views;
|
||||
this._views = [];
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
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) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
displayed(){
|
||||
var displayed = [];
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
if(view.displayed){
|
||||
displayed.push(view);
|
||||
}
|
||||
}
|
||||
return displayed;
|
||||
};
|
||||
|
||||
show(){
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
if(view.displayed){
|
||||
view.show();
|
||||
}
|
||||
}
|
||||
this.hidden = false;
|
||||
};
|
||||
|
||||
hide(){
|
||||
var view;
|
||||
var len = this.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
view = this._views[i];
|
||||
if(view.displayed){
|
||||
view.hide();
|
||||
}
|
||||
}
|
||||
this.hidden = true;
|
||||
};
|
||||
}
|
||||
|
||||
export default Views;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,429 +1,431 @@
|
|||
var EventEmitter = require('event-emitter');
|
||||
var core = require('../../core');
|
||||
var EpubCFI = require('../../epubcfi');
|
||||
var Contents = require('../../contents');
|
||||
// var URI = require('urijs');
|
||||
|
||||
function InlineView(section, options) {
|
||||
this.settings = core.extend({
|
||||
ignoreClass : '',
|
||||
axis: 'vertical',
|
||||
width: 0,
|
||||
height: 0,
|
||||
layout: undefined,
|
||||
globalLayoutProperties: {},
|
||||
}, options || {});
|
||||
|
||||
this.id = "epubjs-view:" + core.uuid();
|
||||
this.section = section;
|
||||
this.index = section.index;
|
||||
|
||||
this.element = this.container(this.settings.axis);
|
||||
|
||||
this.added = false;
|
||||
this.displayed = false;
|
||||
this.rendered = false;
|
||||
|
||||
this.width = this.settings.width;
|
||||
this.height = this.settings.height;
|
||||
|
||||
this.fixedWidth = 0;
|
||||
this.fixedHeight = 0;
|
||||
|
||||
// Blank Cfi for Parsing
|
||||
this.epubcfi = new EpubCFI();
|
||||
|
||||
this.layout = this.settings.layout;
|
||||
// Dom events to listen for
|
||||
// this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"];
|
||||
|
||||
};
|
||||
|
||||
InlineView.prototype.container = function(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"){
|
||||
element.style.display = "inline-block";
|
||||
} else {
|
||||
element.style.display = "block";
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
InlineView.prototype.create = function() {
|
||||
|
||||
if(this.frame) {
|
||||
return this.frame;
|
||||
}
|
||||
|
||||
if(!this.element) {
|
||||
this.element = this.createContainer();
|
||||
}
|
||||
|
||||
this.frame = document.createElement('div');
|
||||
this.frame.id = this.id;
|
||||
this.frame.style.overflow = "hidden";
|
||||
this.frame.style.wordSpacing = "initial";
|
||||
this.frame.style.lineHeight = "initial";
|
||||
|
||||
this.resizing = true;
|
||||
|
||||
// this.frame.style.display = "none";
|
||||
this.element.style.visibility = "hidden";
|
||||
this.frame.style.visibility = "hidden";
|
||||
|
||||
if(this.settings.axis === "horizontal") {
|
||||
this.frame.style.width = "auto";
|
||||
this.frame.style.height = "0";
|
||||
} else {
|
||||
this.frame.style.width = "0";
|
||||
this.frame.style.height = "auto";
|
||||
}
|
||||
|
||||
this._width = 0;
|
||||
this._height = 0;
|
||||
|
||||
this.element.appendChild(this.frame);
|
||||
this.added = true;
|
||||
|
||||
this.elementBounds = core.bounds(this.element);
|
||||
|
||||
return this.frame;
|
||||
};
|
||||
|
||||
InlineView.prototype.render = function(request, show) {
|
||||
|
||||
// view.onLayout = this.layout.format.bind(this.layout);
|
||||
this.create();
|
||||
|
||||
// Fit to size of the container, apply padding
|
||||
this.size();
|
||||
|
||||
// Render Chain
|
||||
return this.section.render(request)
|
||||
.then(function(contents){
|
||||
return this.load(contents);
|
||||
}.bind(this))
|
||||
// .then(function(doc){
|
||||
// return this.hooks.content.trigger(view, this);
|
||||
// }.bind(this))
|
||||
.then(function(){
|
||||
// this.settings.layout.format(view.contents);
|
||||
// return this.hooks.layout.trigger(view, this);
|
||||
}.bind(this))
|
||||
// .then(function(){
|
||||
// return this.display();
|
||||
// }.bind(this))
|
||||
// .then(function(){
|
||||
// return this.hooks.render.trigger(view, this);
|
||||
// }.bind(this))
|
||||
.then(function(){
|
||||
|
||||
// apply the layout function to the contents
|
||||
this.settings.layout.format(this.contents);
|
||||
|
||||
// Expand the iframe to the full size of the content
|
||||
// this.expand();
|
||||
|
||||
// Listen for events that require an expansion of the iframe
|
||||
this.addListeners();
|
||||
|
||||
if(show !== false) {
|
||||
//this.q.enqueue(function(view){
|
||||
this.show();
|
||||
//}, view);
|
||||
}
|
||||
// this.map = new Map(view, this.layout);
|
||||
//this.hooks.show.trigger(view, this);
|
||||
this.emit("rendered", this.section);
|
||||
|
||||
}.bind(this))
|
||||
.catch(function(e){
|
||||
this.emit("loaderror", e);
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
// Determine locks base on settings
|
||||
InlineView.prototype.size = function(_width, _height) {
|
||||
var width = _width || this.settings.width;
|
||||
var height = _height || this.settings.height;
|
||||
|
||||
if(this.layout.name === "pre-paginated") {
|
||||
// TODO: check if these are different than the size set in chapter
|
||||
this.lock("both", width, height);
|
||||
} else if(this.settings.axis === "horizontal") {
|
||||
this.lock("height", width, height);
|
||||
} else {
|
||||
this.lock("width", width, height);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Lock an axis to element dimensions, taking borders into account
|
||||
InlineView.prototype.lock = function(what, width, height) {
|
||||
var elBorders = core.borders(this.element);
|
||||
var iframeBorders;
|
||||
|
||||
if(this.frame) {
|
||||
iframeBorders = core.borders(this.frame);
|
||||
} else {
|
||||
iframeBorders = {width: 0, height: 0};
|
||||
}
|
||||
|
||||
if(what == "width" && core.isNumber(width)){
|
||||
this.lockedWidth = width - elBorders.width - iframeBorders.width;
|
||||
this.resize(this.lockedWidth, false); // width keeps ratio correct
|
||||
}
|
||||
|
||||
if(what == "height" && core.isNumber(height)){
|
||||
this.lockedHeight = height - elBorders.height - iframeBorders.height;
|
||||
this.resize(false, this.lockedHeight);
|
||||
}
|
||||
|
||||
if(what === "both" &&
|
||||
core.isNumber(width) &&
|
||||
core.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
|
||||
InlineView.prototype.expand = function(force) {
|
||||
var width = this.lockedWidth;
|
||||
var height = this.lockedHeight;
|
||||
|
||||
var textWidth, textHeight;
|
||||
|
||||
if(!this.frame || this._expanding) return;
|
||||
|
||||
this._expanding = true;
|
||||
|
||||
// Expand Horizontally
|
||||
if(this.settings.axis === "horizontal") {
|
||||
width = this.contentWidth(textWidth);
|
||||
} // Expand Vertically
|
||||
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){
|
||||
this.resize(width, height);
|
||||
}
|
||||
|
||||
this._expanding = false;
|
||||
};
|
||||
|
||||
InlineView.prototype.contentWidth = function(min) {
|
||||
return this.frame.scrollWidth;
|
||||
};
|
||||
|
||||
InlineView.prototype.contentHeight = function(min) {
|
||||
console.log(this.frame.scrollHeight);
|
||||
return this.frame.scrollHeight;
|
||||
};
|
||||
|
||||
|
||||
InlineView.prototype.resize = function(width, height) {
|
||||
|
||||
if(!this.frame) return;
|
||||
|
||||
if(core.isNumber(width)){
|
||||
this.frame.style.width = width + "px";
|
||||
this._width = width;
|
||||
}
|
||||
|
||||
if(core.isNumber(height)){
|
||||
this.frame.style.height = height + "px";
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
this.prevBounds = this.elementBounds;
|
||||
|
||||
this.elementBounds = core.bounds(this.element);
|
||||
|
||||
size = {
|
||||
width: this.elementBounds.width,
|
||||
height: this.elementBounds.height,
|
||||
widthDelta: this.elementBounds.width - this.prevBounds.width,
|
||||
heightDelta: this.elementBounds.height - this.prevBounds.height,
|
||||
};
|
||||
|
||||
this.onResize(this, size);
|
||||
|
||||
this.emit("resized", size);
|
||||
|
||||
};
|
||||
|
||||
|
||||
InlineView.prototype.load = function(contents) {
|
||||
var loading = new core.defer();
|
||||
var loaded = loading.promise;
|
||||
var doc = core.parse(contents, "text/html");
|
||||
var body = core.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;
|
||||
this.window = this.document.defaultView;
|
||||
|
||||
this.contents = new Contents(this.document, this.frame);
|
||||
|
||||
this.rendering = false;
|
||||
|
||||
loading.resolve(this.contents);
|
||||
|
||||
|
||||
return loaded;
|
||||
};
|
||||
|
||||
InlineView.prototype.setLayout = function(layout) {
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
|
||||
InlineView.prototype.resizeListenters = function() {
|
||||
// Test size again
|
||||
// clearTimeout(this.expanding);
|
||||
// this.expanding = setTimeout(this.expand.bind(this), 350);
|
||||
};
|
||||
|
||||
InlineView.prototype.addListeners = function() {
|
||||
//TODO: Add content listeners for expanding
|
||||
};
|
||||
|
||||
InlineView.prototype.removeListeners = function(layoutFunc) {
|
||||
//TODO: remove content listeners for expanding
|
||||
};
|
||||
|
||||
InlineView.prototype.display = function(request) {
|
||||
var displayed = new core.defer();
|
||||
|
||||
if (!this.displayed) {
|
||||
|
||||
this.render(request).then(function () {
|
||||
|
||||
this.emit("displayed", this);
|
||||
this.onDisplayed(this);
|
||||
|
||||
this.displayed = true;
|
||||
|
||||
displayed.resolve(this);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
} else {
|
||||
displayed.resolve(this);
|
||||
}
|
||||
|
||||
|
||||
return displayed.promise;
|
||||
};
|
||||
|
||||
InlineView.prototype.show = function() {
|
||||
|
||||
this.element.style.visibility = "visible";
|
||||
|
||||
if(this.frame){
|
||||
this.frame.style.visibility = "visible";
|
||||
}
|
||||
|
||||
this.emit("shown", this);
|
||||
};
|
||||
|
||||
InlineView.prototype.hide = function() {
|
||||
// this.frame.style.display = "none";
|
||||
this.element.style.visibility = "hidden";
|
||||
this.frame.style.visibility = "hidden";
|
||||
|
||||
this.stopExpanding = true;
|
||||
this.emit("hidden", this);
|
||||
};
|
||||
|
||||
InlineView.prototype.position = function() {
|
||||
return this.element.getBoundingClientRect();
|
||||
};
|
||||
|
||||
InlineView.prototype.locationOf = function(target) {
|
||||
var parentPos = this.frame.getBoundingClientRect();
|
||||
var targetPos = this.contents.locationOf(target, this.settings.ignoreClass);
|
||||
|
||||
return {
|
||||
"left": window.scrollX + parentPos.left + targetPos.left,
|
||||
"top": window.scrollY + parentPos.top + targetPos.top
|
||||
};
|
||||
};
|
||||
|
||||
InlineView.prototype.onDisplayed = function(view) {
|
||||
// Stub, override with a custom functions
|
||||
};
|
||||
|
||||
InlineView.prototype.onResize = function(view, e) {
|
||||
// Stub, override with a custom functions
|
||||
};
|
||||
|
||||
InlineView.prototype.bounds = function() {
|
||||
if(!this.elementBounds) {
|
||||
this.elementBounds = core.bounds(this.element);
|
||||
}
|
||||
return this.elementBounds;
|
||||
};
|
||||
|
||||
InlineView.prototype.destroy = function() {
|
||||
|
||||
if(this.displayed){
|
||||
import EventEmitter from 'event-emitter';
|
||||
import {extend, borders, uuid, isNumber, bounds, defer} from '../../utils/core';
|
||||
import EpubCFI from '../../epubcfi';
|
||||
import Contents from '../../contents';
|
||||
// import URI from 'urijs';
|
||||
|
||||
class InlineView {
|
||||
constructor(section, options) {
|
||||
this.settings = extend({
|
||||
ignoreClass : '',
|
||||
axis: 'vertical',
|
||||
width: 0,
|
||||
height: 0,
|
||||
layout: undefined,
|
||||
globalLayoutProperties: {},
|
||||
}, options || {});
|
||||
|
||||
this.id = "epubjs-view:" + uuid();
|
||||
this.section = section;
|
||||
this.index = section.index;
|
||||
|
||||
this.element = this.container(this.settings.axis);
|
||||
|
||||
this.added = false;
|
||||
this.displayed = false;
|
||||
this.rendered = false;
|
||||
|
||||
this.removeListeners();
|
||||
this.width = this.settings.width;
|
||||
this.height = this.settings.height;
|
||||
|
||||
this.fixedWidth = 0;
|
||||
this.fixedHeight = 0;
|
||||
|
||||
// Blank Cfi for Parsing
|
||||
this.epubcfi = new EpubCFI();
|
||||
|
||||
this.layout = this.settings.layout;
|
||||
// Dom events to listen for
|
||||
// this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"];
|
||||
|
||||
};
|
||||
|
||||
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"){
|
||||
element.style.display = "inline-block";
|
||||
} else {
|
||||
element.style.display = "block";
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
create() {
|
||||
|
||||
if(this.frame) {
|
||||
return this.frame;
|
||||
}
|
||||
|
||||
if(!this.element) {
|
||||
this.element = this.createContainer();
|
||||
}
|
||||
|
||||
this.frame = document.createElement('div');
|
||||
this.frame.id = this.id;
|
||||
this.frame.style.overflow = "hidden";
|
||||
this.frame.style.wordSpacing = "initial";
|
||||
this.frame.style.lineHeight = "initial";
|
||||
|
||||
this.resizing = true;
|
||||
|
||||
// this.frame.style.display = "none";
|
||||
this.element.style.visibility = "hidden";
|
||||
this.frame.style.visibility = "hidden";
|
||||
|
||||
if(this.settings.axis === "horizontal") {
|
||||
this.frame.style.width = "auto";
|
||||
this.frame.style.height = "0";
|
||||
} else {
|
||||
this.frame.style.width = "0";
|
||||
this.frame.style.height = "auto";
|
||||
}
|
||||
|
||||
this._width = 0;
|
||||
this._height = 0;
|
||||
|
||||
this.element.appendChild(this.frame);
|
||||
this.added = true;
|
||||
|
||||
this.elementBounds = bounds(this.element);
|
||||
|
||||
return this.frame;
|
||||
};
|
||||
|
||||
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.load(contents);
|
||||
}.bind(this))
|
||||
// .then(function(doc){
|
||||
// return this.hooks.content.trigger(view, this);
|
||||
// }.bind(this))
|
||||
.then(function(){
|
||||
// this.settings.layout.format(view.contents);
|
||||
// return this.hooks.layout.trigger(view, this);
|
||||
}.bind(this))
|
||||
// .then(function(){
|
||||
// return this.display();
|
||||
// }.bind(this))
|
||||
// .then(function(){
|
||||
// return this.hooks.render.trigger(view, this);
|
||||
// }.bind(this))
|
||||
.then(function(){
|
||||
|
||||
// apply the layout function to the contents
|
||||
this.settings.layout.format(this.contents);
|
||||
|
||||
// Expand the iframe to the full size of the content
|
||||
// this.expand();
|
||||
|
||||
// Listen for events that require an expansion of the iframe
|
||||
this.addListeners();
|
||||
|
||||
if(show !== false) {
|
||||
//this.q.enqueue(function(view){
|
||||
this.show();
|
||||
//}, view);
|
||||
}
|
||||
// this.map = new Map(view, this.layout);
|
||||
//this.hooks.show.trigger(view, this);
|
||||
this.emit("rendered", this.section);
|
||||
|
||||
}.bind(this))
|
||||
.catch(function(e){
|
||||
this.emit("loaderror", e);
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
// Determine locks base on settings
|
||||
size(_width, _height) {
|
||||
var width = _width || this.settings.width;
|
||||
var height = _height || this.settings.height;
|
||||
|
||||
if(this.layout.name === "pre-paginated") {
|
||||
// TODO: check if these are different than the size set in chapter
|
||||
this.lock("both", width, height);
|
||||
} else if(this.settings.axis === "horizontal") {
|
||||
this.lock("height", width, height);
|
||||
} else {
|
||||
this.lock("width", width, height);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Lock an axis to element dimensions, taking borders into account
|
||||
lock(what, width, height) {
|
||||
var elBorders = borders(this.element);
|
||||
var iframeBorders;
|
||||
|
||||
if(this.frame) {
|
||||
iframeBorders = borders(this.frame);
|
||||
} else {
|
||||
iframeBorders = {width: 0, height: 0};
|
||||
}
|
||||
|
||||
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)){
|
||||
this.lockedHeight = height - elBorders.height - iframeBorders.height;
|
||||
this.resize(false, this.lockedHeight);
|
||||
}
|
||||
|
||||
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
|
||||
expand(force) {
|
||||
var width = this.lockedWidth;
|
||||
var height = this.lockedHeight;
|
||||
|
||||
var textWidth, textHeight;
|
||||
|
||||
if(!this.frame || this._expanding) return;
|
||||
|
||||
this._expanding = true;
|
||||
|
||||
// Expand Horizontally
|
||||
if(this.settings.axis === "horizontal") {
|
||||
width = this.contentWidth(textWidth);
|
||||
} // Expand Vertically
|
||||
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){
|
||||
this.resize(width, height);
|
||||
}
|
||||
|
||||
this._expanding = false;
|
||||
};
|
||||
|
||||
contentWidth(min) {
|
||||
return this.frame.scrollWidth;
|
||||
};
|
||||
|
||||
contentHeight(min) {
|
||||
console.log(this.frame.scrollHeight);
|
||||
return this.frame.scrollHeight;
|
||||
};
|
||||
|
||||
|
||||
resize(width, height) {
|
||||
|
||||
if(!this.frame) return;
|
||||
|
||||
if(isNumber(width)){
|
||||
this.frame.style.width = width + "px";
|
||||
this._width = width;
|
||||
}
|
||||
|
||||
if(isNumber(height)){
|
||||
this.frame.style.height = height + "px";
|
||||
this._height = height;
|
||||
}
|
||||
|
||||
this.prevBounds = this.elementBounds;
|
||||
|
||||
this.elementBounds = bounds(this.element);
|
||||
|
||||
size = {
|
||||
width: this.elementBounds.width,
|
||||
height: this.elementBounds.height,
|
||||
widthDelta: this.elementBounds.width - this.prevBounds.width,
|
||||
heightDelta: this.elementBounds.height - this.prevBounds.height,
|
||||
};
|
||||
|
||||
this.onResize(this, size);
|
||||
|
||||
this.emit("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;
|
||||
this.window = this.document.defaultView;
|
||||
|
||||
this.contents = new Contents(this.document, this.frame);
|
||||
|
||||
this.rendering = false;
|
||||
|
||||
loading.resolve(this.contents);
|
||||
|
||||
|
||||
return loaded;
|
||||
};
|
||||
|
||||
setLayout(layout) {
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
|
||||
resizeListenters() {
|
||||
// Test size again
|
||||
// clearTimeout(this.expanding);
|
||||
// this.expanding = setTimeout(this.expand.bind(this), 350);
|
||||
};
|
||||
|
||||
addListeners() {
|
||||
//TODO: Add content listeners for expanding
|
||||
};
|
||||
|
||||
removeListeners(layoutFunc) {
|
||||
//TODO: remove content listeners for expanding
|
||||
};
|
||||
|
||||
display(request) {
|
||||
var displayed = new defer();
|
||||
|
||||
if (!this.displayed) {
|
||||
|
||||
this.render(request).then(function () {
|
||||
|
||||
this.emit("displayed", this);
|
||||
this.onDisplayed(this);
|
||||
|
||||
this.displayed = true;
|
||||
|
||||
displayed.resolve(this);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
} else {
|
||||
displayed.resolve(this);
|
||||
}
|
||||
|
||||
|
||||
return displayed.promise;
|
||||
};
|
||||
|
||||
show() {
|
||||
|
||||
this.element.style.visibility = "visible";
|
||||
|
||||
if(this.frame){
|
||||
this.frame.style.visibility = "visible";
|
||||
}
|
||||
|
||||
this.emit("shown", this);
|
||||
};
|
||||
|
||||
hide() {
|
||||
// this.frame.style.display = "none";
|
||||
this.element.style.visibility = "hidden";
|
||||
this.frame.style.visibility = "hidden";
|
||||
|
||||
this.stopExpanding = true;
|
||||
this.element.removeChild(this.frame);
|
||||
this.displayed = false;
|
||||
this.frame = null;
|
||||
this.emit("hidden", this);
|
||||
};
|
||||
|
||||
this._textWidth = null;
|
||||
this._textHeight = null;
|
||||
this._width = null;
|
||||
this._height = null;
|
||||
}
|
||||
// this.element.style.height = "0px";
|
||||
// this.element.style.width = "0px";
|
||||
};
|
||||
position() {
|
||||
return this.element.getBoundingClientRect();
|
||||
};
|
||||
|
||||
locationOf(target) {
|
||||
var parentPos = this.frame.getBoundingClientRect();
|
||||
var targetPos = this.contents.locationOf(target, this.settings.ignoreClass);
|
||||
|
||||
return {
|
||||
"left": window.scrollX + parentPos.left + targetPos.left,
|
||||
"top": window.scrollY + parentPos.top + targetPos.top
|
||||
};
|
||||
};
|
||||
|
||||
onDisplayed(view) {
|
||||
// Stub, override with a custom functions
|
||||
};
|
||||
|
||||
onResize(view, e) {
|
||||
// Stub, override with a custom functions
|
||||
};
|
||||
|
||||
bounds() {
|
||||
if(!this.elementBounds) {
|
||||
this.elementBounds = bounds(this.element);
|
||||
}
|
||||
return this.elementBounds;
|
||||
};
|
||||
|
||||
destroy() {
|
||||
|
||||
if(this.displayed){
|
||||
this.displayed = false;
|
||||
|
||||
this.removeListeners();
|
||||
|
||||
this.stopExpanding = true;
|
||||
this.element.removeChild(this.frame);
|
||||
this.displayed = false;
|
||||
this.frame = null;
|
||||
|
||||
this._textWidth = null;
|
||||
this._textHeight = null;
|
||||
this._width = null;
|
||||
this._height = null;
|
||||
}
|
||||
// this.element.style.height = "0px";
|
||||
// this.element.style.width = "0px";
|
||||
};
|
||||
}
|
||||
|
||||
EventEmitter(InlineView.prototype);
|
||||
|
||||
module.exports = InlineView;
|
||||
export default InlineView;
|
||||
|
|
578
src/mapping.js
578
src/mapping.js
|
@ -1,297 +1,299 @@
|
|||
var EpubCFI = require('./epubcfi');
|
||||
import EpubCFI from './epubcfi';
|
||||
|
||||
function Mapping(layout){
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
Mapping.prototype.section = function(view) {
|
||||
var ranges = this.findRanges(view);
|
||||
var map = this.rangeListToCfiList(view.section.cfiBase, ranges);
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
Mapping.prototype.page = function(contents, cfiBase, start, end) {
|
||||
var root = contents && contents.document ? contents.document.body : false;
|
||||
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.rangePairToCfiPair(cfiBase, {
|
||||
start: this.findStart(root, start, end),
|
||||
end: this.findEnd(root, start, end)
|
||||
});
|
||||
};
|
||||
|
||||
Mapping.prototype.walk = function(root, func) {
|
||||
//var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, null, false);
|
||||
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
||||
acceptNode: function (node) {
|
||||
if ( node.data.trim().length > 0 ) {
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
} else {
|
||||
return NodeFilter.FILTER_REJECT;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
var node;
|
||||
var result;
|
||||
while ((node = treeWalker.nextNode())) {
|
||||
result = func(node);
|
||||
if(result) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Mapping.prototype.findRanges = function(view){
|
||||
var columns = [];
|
||||
var scrollWidth = view.contents.scrollWidth();
|
||||
var count = this.layout.count(scrollWidth);
|
||||
var column = this.layout.column;
|
||||
var gap = this.layout.gap;
|
||||
var start, end;
|
||||
|
||||
for (var i = 0; i < count.pages; i++) {
|
||||
start = (column + gap) * i;
|
||||
end = (column * (i+1)) + (gap * i);
|
||||
columns.push({
|
||||
start: this.findStart(view.document.body, start, end),
|
||||
end: this.findEnd(view.document.body, start, end)
|
||||
});
|
||||
}
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
Mapping.prototype.findStart = function(root, start, end){
|
||||
var stack = [root];
|
||||
var $el;
|
||||
var found;
|
||||
var $prev = root;
|
||||
while (stack.length) {
|
||||
|
||||
$el = stack.shift();
|
||||
|
||||
found = this.walk($el, function(node){
|
||||
var left, right;
|
||||
var elPos;
|
||||
var elRange;
|
||||
|
||||
|
||||
if(node.nodeType == Node.TEXT_NODE){
|
||||
elRange = document.createRange();
|
||||
elRange.selectNodeContents(node);
|
||||
elPos = elRange.getBoundingClientRect();
|
||||
} else {
|
||||
elPos = node.getBoundingClientRect();
|
||||
}
|
||||
|
||||
left = elPos.left;
|
||||
right = elPos.right;
|
||||
|
||||
if( left >= start && left <= end ) {
|
||||
return node;
|
||||
} else if (right > start) {
|
||||
return node;
|
||||
} else {
|
||||
$prev = node;
|
||||
stack.push(node);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if(found) {
|
||||
return this.findTextStartRange(found, start, end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return last element
|
||||
return this.findTextStartRange($prev, start, end);
|
||||
};
|
||||
|
||||
Mapping.prototype.findEnd = function(root, start, end){
|
||||
var stack = [root];
|
||||
var $el;
|
||||
var $prev = root;
|
||||
var found;
|
||||
|
||||
while (stack.length) {
|
||||
|
||||
$el = stack.shift();
|
||||
|
||||
found = this.walk($el, function(node){
|
||||
|
||||
var left, right;
|
||||
var elPos;
|
||||
var elRange;
|
||||
|
||||
|
||||
if(node.nodeType == Node.TEXT_NODE){
|
||||
elRange = document.createRange();
|
||||
elRange.selectNodeContents(node);
|
||||
elPos = elRange.getBoundingClientRect();
|
||||
} else {
|
||||
elPos = node.getBoundingClientRect();
|
||||
}
|
||||
|
||||
left = elPos.left;
|
||||
right = elPos.right;
|
||||
|
||||
if(left > end && $prev) {
|
||||
return $prev;
|
||||
} else if(right > end) {
|
||||
return node;
|
||||
} else {
|
||||
$prev = node;
|
||||
stack.push(node);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
if(found){
|
||||
return this.findTextEndRange(found, start, end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// end of chapter
|
||||
return this.findTextEndRange($prev, start, end);
|
||||
};
|
||||
|
||||
|
||||
Mapping.prototype.findTextStartRange = function(node, start, end){
|
||||
var ranges = this.splitTextNodeIntoRanges(node);
|
||||
var prev;
|
||||
var range;
|
||||
var pos;
|
||||
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
range = ranges[i];
|
||||
|
||||
pos = range.getBoundingClientRect();
|
||||
|
||||
if( pos.left >= start ) {
|
||||
return range;
|
||||
}
|
||||
|
||||
prev = range;
|
||||
|
||||
}
|
||||
|
||||
return ranges[0];
|
||||
};
|
||||
|
||||
Mapping.prototype.findTextEndRange = function(node, start, end){
|
||||
var ranges = this.splitTextNodeIntoRanges(node);
|
||||
var prev;
|
||||
var range;
|
||||
var pos;
|
||||
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
range = ranges[i];
|
||||
|
||||
pos = range.getBoundingClientRect();
|
||||
|
||||
if(pos.left > end && prev) {
|
||||
return prev;
|
||||
} else if(pos.right > end) {
|
||||
return range;
|
||||
}
|
||||
|
||||
prev = range;
|
||||
|
||||
}
|
||||
|
||||
// Ends before limit
|
||||
return ranges[ranges.length-1];
|
||||
|
||||
};
|
||||
|
||||
Mapping.prototype.splitTextNodeIntoRanges = function(node, _splitter){
|
||||
var ranges = [];
|
||||
var textContent = node.textContent || "";
|
||||
var text = textContent.trim();
|
||||
var range;
|
||||
var rect;
|
||||
var list;
|
||||
var doc = node.ownerDocument;
|
||||
var splitter = _splitter || " ";
|
||||
|
||||
pos = text.indexOf(splitter);
|
||||
|
||||
if(pos === -1 || node.nodeType != Node.TEXT_NODE) {
|
||||
range = doc.createRange();
|
||||
range.selectNodeContents(node);
|
||||
return [range];
|
||||
}
|
||||
|
||||
range = doc.createRange();
|
||||
range.setStart(node, 0);
|
||||
range.setEnd(node, pos);
|
||||
ranges.push(range);
|
||||
range = false;
|
||||
|
||||
while ( pos != -1 ) {
|
||||
|
||||
pos = text.indexOf(splitter, pos + 1);
|
||||
if(pos > 0) {
|
||||
|
||||
if(range) {
|
||||
range.setEnd(node, pos);
|
||||
ranges.push(range);
|
||||
}
|
||||
|
||||
range = doc.createRange();
|
||||
range.setStart(node, pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
if(range) {
|
||||
range.setEnd(node, text.length);
|
||||
ranges.push(range);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Mapping.prototype.rangePairToCfiPair = function(cfiBase, rangePair){
|
||||
|
||||
var startRange = rangePair.start;
|
||||
var endRange = rangePair.end;
|
||||
|
||||
startRange.collapse(true);
|
||||
endRange.collapse(true);
|
||||
|
||||
// startCfi = section.cfiFromRange(startRange);
|
||||
// endCfi = section.cfiFromRange(endRange);
|
||||
startCfi = new EpubCFI(startRange, cfiBase).toString();
|
||||
endCfi = new EpubCFI(endRange, cfiBase).toString();
|
||||
|
||||
return {
|
||||
start: startCfi,
|
||||
end: endCfi
|
||||
class Mapping {
|
||||
constructor(layout) {
|
||||
this.layout = layout;
|
||||
};
|
||||
|
||||
};
|
||||
section(view) {
|
||||
var ranges = this.findRanges(view);
|
||||
var map = this.rangeListToCfiList(view.section.cfiBase, ranges);
|
||||
|
||||
Mapping.prototype.rangeListToCfiList = function(cfiBase, columns){
|
||||
var map = [];
|
||||
var rangePair, cifPair;
|
||||
return map;
|
||||
};
|
||||
|
||||
for (var i = 0; i < columns.length; i++) {
|
||||
cifPair = this.rangePairToCfiPair(cfiBase, columns[i]);
|
||||
page(contents, cfiBase, start, end) {
|
||||
var root = contents && contents.document ? contents.document.body : false;
|
||||
|
||||
map.push(cifPair);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
return this.rangePairToCfiPair(cfiBase, {
|
||||
start: this.findStart(root, start, end),
|
||||
end: this.findEnd(root, start, end)
|
||||
});
|
||||
};
|
||||
|
||||
return map;
|
||||
};
|
||||
walk(root, func) {
|
||||
//var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, null, false);
|
||||
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
|
||||
acceptNode: function (node) {
|
||||
if ( node.data.trim().length > 0 ) {
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
} else {
|
||||
return NodeFilter.FILTER_REJECT;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
var node;
|
||||
var result;
|
||||
while ((node = treeWalker.nextNode())) {
|
||||
result = func(node);
|
||||
if(result) break;
|
||||
}
|
||||
|
||||
module.exports = Mapping;
|
||||
return result;
|
||||
};
|
||||
|
||||
findRanges(view){
|
||||
var columns = [];
|
||||
var scrollWidth = view.contents.scrollWidth();
|
||||
var count = this.layout.count(scrollWidth);
|
||||
var column = this.layout.column;
|
||||
var gap = this.layout.gap;
|
||||
var start, end;
|
||||
|
||||
for (var i = 0; i < count.pages; i++) {
|
||||
start = (column + gap) * i;
|
||||
end = (column * (i+1)) + (gap * i);
|
||||
columns.push({
|
||||
start: this.findStart(view.document.body, start, end),
|
||||
end: this.findEnd(view.document.body, start, end)
|
||||
});
|
||||
}
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
findStart(root, start, end){
|
||||
var stack = [root];
|
||||
var $el;
|
||||
var found;
|
||||
var $prev = root;
|
||||
while (stack.length) {
|
||||
|
||||
$el = stack.shift();
|
||||
|
||||
found = this.walk($el, function(node){
|
||||
var left, right;
|
||||
var elPos;
|
||||
var elRange;
|
||||
|
||||
|
||||
if(node.nodeType == Node.TEXT_NODE){
|
||||
elRange = document.createRange();
|
||||
elRange.selectNodeContents(node);
|
||||
elPos = elRange.getBoundingClientRect();
|
||||
} else {
|
||||
elPos = node.getBoundingClientRect();
|
||||
}
|
||||
|
||||
left = elPos.left;
|
||||
right = elPos.right;
|
||||
|
||||
if( left >= start && left <= end ) {
|
||||
return node;
|
||||
} else if (right > start) {
|
||||
return node;
|
||||
} else {
|
||||
$prev = node;
|
||||
stack.push(node);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if(found) {
|
||||
return this.findTextStartRange(found, start, end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return last element
|
||||
return this.findTextStartRange($prev, 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, function(node){
|
||||
|
||||
var left, right;
|
||||
var elPos;
|
||||
var elRange;
|
||||
|
||||
|
||||
if(node.nodeType == Node.TEXT_NODE){
|
||||
elRange = document.createRange();
|
||||
elRange.selectNodeContents(node);
|
||||
elPos = elRange.getBoundingClientRect();
|
||||
} else {
|
||||
elPos = node.getBoundingClientRect();
|
||||
}
|
||||
|
||||
left = elPos.left;
|
||||
right = elPos.right;
|
||||
|
||||
if(left > end && $prev) {
|
||||
return $prev;
|
||||
} else if(right > end) {
|
||||
return node;
|
||||
} else {
|
||||
$prev = node;
|
||||
stack.push(node);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
if(found){
|
||||
return this.findTextEndRange(found, start, end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// end of chapter
|
||||
return this.findTextEndRange($prev, start, end);
|
||||
};
|
||||
|
||||
|
||||
findTextStartRange(node, start, end){
|
||||
var ranges = this.splitTextNodeIntoRanges(node);
|
||||
var prev;
|
||||
var range;
|
||||
var pos;
|
||||
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
range = ranges[i];
|
||||
|
||||
pos = range.getBoundingClientRect();
|
||||
|
||||
if( pos.left >= start ) {
|
||||
return range;
|
||||
}
|
||||
|
||||
prev = range;
|
||||
|
||||
}
|
||||
|
||||
return ranges[0];
|
||||
};
|
||||
|
||||
findTextEndRange(node, start, end){
|
||||
var ranges = this.splitTextNodeIntoRanges(node);
|
||||
var prev;
|
||||
var range;
|
||||
var pos;
|
||||
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
range = ranges[i];
|
||||
|
||||
pos = range.getBoundingClientRect();
|
||||
|
||||
if(pos.left > end && prev) {
|
||||
return prev;
|
||||
} else if(pos.right > end) {
|
||||
return range;
|
||||
}
|
||||
|
||||
prev = range;
|
||||
|
||||
}
|
||||
|
||||
// Ends before limit
|
||||
return ranges[ranges.length-1];
|
||||
|
||||
};
|
||||
|
||||
splitTextNodeIntoRanges(node, _splitter){
|
||||
var ranges = [];
|
||||
var textContent = node.textContent || "";
|
||||
var text = textContent.trim();
|
||||
var range;
|
||||
var rect;
|
||||
var list;
|
||||
var doc = node.ownerDocument;
|
||||
var splitter = _splitter || " ";
|
||||
|
||||
var pos = text.indexOf(splitter);
|
||||
|
||||
if(pos === -1 || node.nodeType != Node.TEXT_NODE) {
|
||||
range = doc.createRange();
|
||||
range.selectNodeContents(node);
|
||||
return [range];
|
||||
}
|
||||
|
||||
range = doc.createRange();
|
||||
range.setStart(node, 0);
|
||||
range.setEnd(node, pos);
|
||||
ranges.push(range);
|
||||
range = false;
|
||||
|
||||
while ( pos != -1 ) {
|
||||
|
||||
pos = text.indexOf(splitter, pos + 1);
|
||||
if(pos > 0) {
|
||||
|
||||
if(range) {
|
||||
range.setEnd(node, pos);
|
||||
ranges.push(range);
|
||||
}
|
||||
|
||||
range = doc.createRange();
|
||||
range.setStart(node, pos+1);
|
||||
}
|
||||
}
|
||||
|
||||
if(range) {
|
||||
range.setEnd(node, text.length);
|
||||
ranges.push(range);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
};
|
||||
|
||||
|
||||
|
||||
rangePairToCfiPair(cfiBase, rangePair){
|
||||
|
||||
var startRange = rangePair.start;
|
||||
var endRange = rangePair.end;
|
||||
|
||||
startRange.collapse(true);
|
||||
endRange.collapse(true);
|
||||
|
||||
// startCfi = section.cfiFromRange(startRange);
|
||||
// endCfi = section.cfiFromRange(endRange);
|
||||
let startCfi = new EpubCFI(startRange, cfiBase).toString();
|
||||
let endCfi = new EpubCFI(endRange, cfiBase).toString();
|
||||
|
||||
return {
|
||||
start: startCfi,
|
||||
end: endCfi
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
rangeListToCfiList(cfiBase, columns){
|
||||
var map = [];
|
||||
var rangePair, cifPair;
|
||||
|
||||
for (var i = 0; i < columns.length; i++) {
|
||||
cifPair = this.rangePairToCfiPair(cfiBase, columns[i]);
|
||||
|
||||
map.push(cifPair);
|
||||
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
}
|
||||
|
||||
export default Mapping;
|
||||
|
|
|
@ -1,200 +1,201 @@
|
|||
var core = require('./core');
|
||||
var path = require('path');
|
||||
import {qs, qsa, querySelectorByType} from './utils/core';
|
||||
|
||||
/**
|
||||
* Navigation Parser
|
||||
* @param {document} xml navigation html / xhtml / ncx
|
||||
*/
|
||||
function Navigation(xml){
|
||||
this.toc = [];
|
||||
this.tocByHref = {};
|
||||
this.tocById = {};
|
||||
class Navigation {
|
||||
constructor(xml) {
|
||||
this.toc = [];
|
||||
this.tocByHref = {};
|
||||
this.tocById = {};
|
||||
|
||||
if (xml) {
|
||||
this.parse(xml);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse out the navigation items
|
||||
* @param {document} xml navigation html / xhtml / ncx
|
||||
*/
|
||||
Navigation.prototype.parse = function(xml) {
|
||||
var html = core.qs(xml, "html");
|
||||
var ncx = core.qs(xml, "ncx");
|
||||
|
||||
if(html) {
|
||||
this.toc = this.parseNav(xml);
|
||||
} else if(ncx){
|
||||
this.toc = this.parseNcx(xml);
|
||||
}
|
||||
|
||||
this.unpack(this.toc);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unpack navigation items
|
||||
* @private
|
||||
* @param {array} toc
|
||||
*/
|
||||
Navigation.prototype.unpack = function(toc) {
|
||||
var item;
|
||||
|
||||
for (var i = 0; i < toc.length; i++) {
|
||||
item = toc[i];
|
||||
this.tocByHref[item.href] = i;
|
||||
this.tocById[item.id] = i;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an item from the navigation
|
||||
* @param {string} target
|
||||
* @return {object} navItems
|
||||
*/
|
||||
Navigation.prototype.get = function(target) {
|
||||
var index;
|
||||
|
||||
if(!target) {
|
||||
return this.toc;
|
||||
}
|
||||
|
||||
if(target.indexOf("#") === 0) {
|
||||
index = this.tocById[target.substring(1)];
|
||||
} else if(target in this.tocByHref){
|
||||
index = this.tocByHref[target];
|
||||
}
|
||||
|
||||
return this.toc[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse from a Epub > 3.0 Nav
|
||||
* @private
|
||||
* @param {document} navHtml
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
Navigation.prototype.parseNav = function(navHtml){
|
||||
var navElement = core.querySelectorByType(navHtml, "nav", "toc");
|
||||
var navItems = navElement ? core.qsa(navElement, "li") : [];
|
||||
var length = navItems.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item, parent;
|
||||
|
||||
if(!navItems || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.navItem(navItems[i]);
|
||||
toc[item.id] = item;
|
||||
if(!item.parent) {
|
||||
list.push(item);
|
||||
} else {
|
||||
parent = toc[item.parent];
|
||||
parent.subitems.push(item);
|
||||
if (xml) {
|
||||
this.parse(xml);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a navItem
|
||||
* @private
|
||||
* @param {element} item
|
||||
* @return {object} navItem
|
||||
*/
|
||||
Navigation.prototype.navItem = function(item){
|
||||
var id = item.getAttribute('id') || false,
|
||||
content = core.qs(item, "a"),
|
||||
src = content.getAttribute('href') || '',
|
||||
text = content.textContent || "",
|
||||
subitems = [],
|
||||
parentNode = item.parentNode,
|
||||
parent;
|
||||
|
||||
if(parentNode && parentNode.nodeName === "navPoint") {
|
||||
parent = parentNode.getAttribute('id');
|
||||
}
|
||||
|
||||
return {
|
||||
"id": id,
|
||||
"href": src,
|
||||
"label": text,
|
||||
"subitems" : subitems,
|
||||
"parent" : parent
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse from a Epub > 3.0 NC
|
||||
* @private
|
||||
* @param {document} navHtml
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
Navigation.prototype.parseNcx = function(tocXml){
|
||||
var navPoints = core.qsa(tocXml, "navPoint");
|
||||
var length = navPoints.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item, parent;
|
||||
/**
|
||||
* Parse out the navigation items
|
||||
* @param {document} xml navigation html / xhtml / ncx
|
||||
*/
|
||||
parse(xml) {
|
||||
var html = qs(xml, "html");
|
||||
var ncx = qs(xml, "ncx");
|
||||
|
||||
if(!navPoints || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.ncxItem(navPoints[i]);
|
||||
toc[item.id] = item;
|
||||
if(!item.parent) {
|
||||
list.push(item);
|
||||
} else {
|
||||
parent = toc[item.parent];
|
||||
parent.subitems.push(item);
|
||||
if(html) {
|
||||
this.toc = this.parseNav(xml);
|
||||
} else if(ncx){
|
||||
this.toc = this.parseNcx(xml);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a ncxItem
|
||||
* @private
|
||||
* @param {element} item
|
||||
* @return {object} ncxItem
|
||||
*/
|
||||
Navigation.prototype.ncxItem = function(item){
|
||||
var id = item.getAttribute('id') || false,
|
||||
content = core.qs(item, "content"),
|
||||
src = content.getAttribute('src'),
|
||||
navLabel = core.qs(item, "navLabel"),
|
||||
text = navLabel.textContent ? navLabel.textContent : "",
|
||||
subitems = [],
|
||||
parentNode = item.parentNode,
|
||||
parent;
|
||||
|
||||
if(parentNode && parentNode.nodeName === "navPoint") {
|
||||
parent = parentNode.getAttribute('id');
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
"id": id,
|
||||
"href": src,
|
||||
"label": text,
|
||||
"subitems" : subitems,
|
||||
"parent" : parent
|
||||
this.unpack(this.toc);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* forEach pass through
|
||||
* @param {Function} fn function to run on each item
|
||||
* @return {method} forEach loop
|
||||
*/
|
||||
Navigation.prototype.forEach = function(fn) {
|
||||
return this.toc.forEach(fn);
|
||||
};
|
||||
/**
|
||||
* Unpack navigation items
|
||||
* @private
|
||||
* @param {array} toc
|
||||
*/
|
||||
unpack(toc) {
|
||||
var item;
|
||||
|
||||
module.exports = Navigation;
|
||||
for (var i = 0; i < toc.length; i++) {
|
||||
item = toc[i];
|
||||
this.tocByHref[item.href] = i;
|
||||
this.tocById[item.id] = i;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an item from the navigation
|
||||
* @param {string} target
|
||||
* @return {object} navItems
|
||||
*/
|
||||
get(target) {
|
||||
var index;
|
||||
|
||||
if(!target) {
|
||||
return this.toc;
|
||||
}
|
||||
|
||||
if(target.indexOf("#") === 0) {
|
||||
index = this.tocById[target.substring(1)];
|
||||
} else if(target in this.tocByHref){
|
||||
index = this.tocByHref[target];
|
||||
}
|
||||
|
||||
return this.toc[index];
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse from a Epub > 3.0 Nav
|
||||
* @private
|
||||
* @param {document} navHtml
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
parseNav(navHtml){
|
||||
var navElement = querySelectorByType(navHtml, "nav", "toc");
|
||||
var navItems = navElement ? qsa(navElement, "li") : [];
|
||||
var length = navItems.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item, parent;
|
||||
|
||||
if(!navItems || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.navItem(navItems[i]);
|
||||
toc[item.id] = item;
|
||||
if(!item.parent) {
|
||||
list.push(item);
|
||||
} else {
|
||||
parent = toc[item.parent];
|
||||
parent.subitems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a navItem
|
||||
* @private
|
||||
* @param {element} item
|
||||
* @return {object} navItem
|
||||
*/
|
||||
navItem(item){
|
||||
var id = item.getAttribute('id') || false,
|
||||
content = qs(item, "a"),
|
||||
src = content.getAttribute('href') || '',
|
||||
text = content.textContent || "",
|
||||
subitems = [],
|
||||
parentNode = item.parentNode,
|
||||
parent;
|
||||
|
||||
if(parentNode && parentNode.nodeName === "navPoint") {
|
||||
parent = parentNode.getAttribute('id');
|
||||
}
|
||||
|
||||
return {
|
||||
"id": id,
|
||||
"href": src,
|
||||
"label": text,
|
||||
"subitems" : subitems,
|
||||
"parent" : parent
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse from a Epub > 3.0 NC
|
||||
* @private
|
||||
* @param {document} navHtml
|
||||
* @return {array} navigation list
|
||||
*/
|
||||
parseNcx(tocXml){
|
||||
var navPoints = qsa(tocXml, "navPoint");
|
||||
var length = navPoints.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item, parent;
|
||||
|
||||
if(!navPoints || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.ncxItem(navPoints[i]);
|
||||
toc[item.id] = item;
|
||||
if(!item.parent) {
|
||||
list.push(item);
|
||||
} else {
|
||||
parent = toc[item.parent];
|
||||
parent.subitems.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a ncxItem
|
||||
* @private
|
||||
* @param {element} item
|
||||
* @return {object} ncxItem
|
||||
*/
|
||||
ncxItem(item){
|
||||
var id = item.getAttribute('id') || false,
|
||||
content = qs(item, "content"),
|
||||
src = content.getAttribute('src'),
|
||||
navLabel = qs(item, "navLabel"),
|
||||
text = navLabel.textContent ? navLabel.textContent : "",
|
||||
subitems = [],
|
||||
parentNode = item.parentNode,
|
||||
parent;
|
||||
|
||||
if(parentNode && parentNode.nodeName === "navPoint") {
|
||||
parent = parentNode.getAttribute('id');
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
"id": id,
|
||||
"href": src,
|
||||
"label": text,
|
||||
"subitems" : subitems,
|
||||
"parent" : parent
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* forEach pass through
|
||||
* @param {Function} fn function to run on each item
|
||||
* @return {method} forEach loop
|
||||
*/
|
||||
forEach(fn) {
|
||||
return this.toc.forEach(fn);
|
||||
};
|
||||
}
|
||||
|
||||
export default Navigation;
|
||||
|
|
519
src/packaging.js
519
src/packaging.js
|
@ -1,282 +1,283 @@
|
|||
var path = require('path');
|
||||
var core = require('./core');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
import {qs, qsa, qsp} from './utils/core';
|
||||
import EpubCFI from './epubcfi';
|
||||
|
||||
/**
|
||||
* Open Packaging Format Parser
|
||||
* @class
|
||||
* @param {document} packageDocument OPF XML
|
||||
*/
|
||||
function Packaging(packageDocument) {
|
||||
if (packageDocument) {
|
||||
this.parse(packageDocument);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse OPF XML
|
||||
* @param {document} packageDocument OPF XML
|
||||
* @return {object} parsed package parts
|
||||
*/
|
||||
Packaging.prototype.parse = function(packageDocument){
|
||||
var metadataNode, manifestNode, spineNode;
|
||||
|
||||
if(!packageDocument) {
|
||||
console.error("Package File Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
metadataNode = core.qs(packageDocument, "metadata");
|
||||
if(!metadataNode) {
|
||||
console.error("No Metadata Found");
|
||||
return;
|
||||
}
|
||||
|
||||
manifestNode = core.qs(packageDocument, "manifest");
|
||||
if(!manifestNode) {
|
||||
console.error("No Manifest Found");
|
||||
return;
|
||||
}
|
||||
|
||||
spineNode = core.qs(packageDocument, "spine");
|
||||
if(!spineNode) {
|
||||
console.error("No Spine Found");
|
||||
return;
|
||||
}
|
||||
|
||||
this.manifest = this.parseManifest(manifestNode);
|
||||
this.navPath = this.findNavPath(manifestNode);
|
||||
this.ncxPath = this.findNcxPath(manifestNode, spineNode);
|
||||
this.coverPath = this.findCoverPath(packageDocument);
|
||||
|
||||
this.spineNodeIndex = Array.prototype.indexOf.call(spineNode.parentNode.childNodes, spineNode);
|
||||
|
||||
this.spine = this.parseSpine(spineNode, this.manifest);
|
||||
|
||||
this.metadata = this.parseMetadata(metadataNode);
|
||||
|
||||
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
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Metadata
|
||||
* @private
|
||||
* @param {document} xml
|
||||
* @return {object} metadata
|
||||
*/
|
||||
Packaging.prototype.parseMetadata = function(xml){
|
||||
var metadata = {};
|
||||
|
||||
metadata.title = this.getElementText(xml, 'title');
|
||||
metadata.creator = this.getElementText(xml, 'creator');
|
||||
metadata.description = this.getElementText(xml, 'description');
|
||||
|
||||
metadata.pubdate = this.getElementText(xml, 'date');
|
||||
|
||||
metadata.publisher = this.getElementText(xml, 'publisher');
|
||||
|
||||
metadata.identifier = this.getElementText(xml, "identifier");
|
||||
metadata.language = this.getElementText(xml, "language");
|
||||
metadata.rights = this.getElementText(xml, "rights");
|
||||
|
||||
metadata.modified_date = this.getPropertyText(xml, 'dcterms:modified');
|
||||
|
||||
metadata.layout = this.getPropertyText(xml, "rendition:layout");
|
||||
metadata.orientation = this.getPropertyText(xml, 'rendition:orientation');
|
||||
metadata.flow = this.getPropertyText(xml, 'rendition:flow');
|
||||
metadata.viewport = this.getPropertyText(xml, 'rendition:viewport');
|
||||
// metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction");
|
||||
|
||||
return metadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Manifest
|
||||
* @private
|
||||
* @param {document} manifestXml
|
||||
* @return {object} manifest
|
||||
*/
|
||||
Packaging.prototype.parseManifest = function(manifestXml){
|
||||
var manifest = {};
|
||||
|
||||
//-- Turn items into an array
|
||||
// var selected = manifestXml.querySelectorAll("item");
|
||||
var selected = core.qsa(manifestXml, "item");
|
||||
var items = Array.prototype.slice.call(selected);
|
||||
|
||||
//-- Create an object with the id as key
|
||||
items.forEach(function(item){
|
||||
var id = item.getAttribute('id'),
|
||||
href = item.getAttribute('href') || '',
|
||||
type = item.getAttribute('media-type') || '',
|
||||
properties = item.getAttribute('properties') || '';
|
||||
|
||||
manifest[id] = {
|
||||
'href' : href,
|
||||
// 'url' : href,
|
||||
'type' : type,
|
||||
'properties' : properties.length ? properties.split(' ') : []
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
return manifest;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Spine
|
||||
* @param {document} spineXml
|
||||
* @param {Packaging.manifest} manifest
|
||||
* @return {object} spine
|
||||
*/
|
||||
Packaging.prototype.parseSpine = function(spineXml, manifest){
|
||||
var spine = [];
|
||||
|
||||
var selected = spineXml.getElementsByTagName("itemref"),
|
||||
items = Array.prototype.slice.call(selected);
|
||||
|
||||
var epubcfi = new EpubCFI();
|
||||
|
||||
//-- Add to array to mantain ordering and cross reference with manifest
|
||||
items.forEach(function(item, index){
|
||||
var idref = item.getAttribute('idref');
|
||||
// var cfiBase = epubcfi.generateChapterComponent(spineNodeIndex, index, Id);
|
||||
var props = item.getAttribute('properties') || '';
|
||||
var propArray = props.length ? props.split(' ') : [];
|
||||
// var manifestProps = manifest[Id].properties;
|
||||
// var manifestPropArray = manifestProps.length ? manifestProps.split(' ') : [];
|
||||
|
||||
var itemref = {
|
||||
'idref' : idref,
|
||||
'linear' : item.getAttribute('linear') || '',
|
||||
'properties' : propArray,
|
||||
// 'href' : manifest[Id].href,
|
||||
// 'url' : manifest[Id].url,
|
||||
'index' : index
|
||||
// 'cfiBase' : cfiBase
|
||||
};
|
||||
spine.push(itemref);
|
||||
});
|
||||
|
||||
return spine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find TOC NAV
|
||||
* @private
|
||||
*/
|
||||
Packaging.prototype.findNavPath = function(manifestNode){
|
||||
// Find item with property 'nav'
|
||||
// Should catch nav irregardless of order
|
||||
// var node = manifestNode.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']");
|
||||
var node = core.qsp(manifestNode, "item", {"properties":"nav"});
|
||||
return node ? node.getAttribute('href') : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find TOC NCX
|
||||
* media-type="application/x-dtbncx+xml" href="toc.ncx"
|
||||
* @private
|
||||
*/
|
||||
Packaging.prototype.findNcxPath = function(manifestNode, spineNode){
|
||||
// var node = manifestNode.querySelector("item[media-type='application/x-dtbncx+xml']");
|
||||
var node = core.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
|
||||
// according to http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4.1.2,
|
||||
// "The item that describes the NCX must be referenced by the spine toc attribute."
|
||||
if (!node) {
|
||||
tocId = spineNode.getAttribute("toc");
|
||||
if(tocId) {
|
||||
// node = manifestNode.querySelector("item[id='" + tocId + "']");
|
||||
node = manifestNode.getElementById(tocId);
|
||||
class Packaging {
|
||||
constructor(packageDocument) {
|
||||
if (packageDocument) {
|
||||
this.parse(packageDocument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return node ? node.getAttribute('href') : false;
|
||||
};
|
||||
/**
|
||||
* Parse OPF XML
|
||||
* @param {document} packageDocument OPF XML
|
||||
* @return {object} parsed package parts
|
||||
*/
|
||||
parse(packageDocument){
|
||||
var metadataNode, manifestNode, spineNode;
|
||||
|
||||
/**
|
||||
* Find the Cover Path
|
||||
* <item properties="cover-image" id="ci" href="cover.svg" media-type="image/svg+xml" />
|
||||
* Fallback for Epub 2.0
|
||||
* @param {document} packageXml
|
||||
* @return {string} href
|
||||
*/
|
||||
Packaging.prototype.findCoverPath = function(packageXml){
|
||||
var pkg = core.qs(packageXml, "package");
|
||||
var epubVersion = pkg.getAttribute('version');
|
||||
if(!packageDocument) {
|
||||
console.error("Package File Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (epubVersion === '2.0') {
|
||||
var metaCover = core.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') : '';
|
||||
metadataNode = qs(packageDocument, "metadata");
|
||||
if(!metadataNode) {
|
||||
console.error("No Metadata Found");
|
||||
return;
|
||||
}
|
||||
|
||||
manifestNode = qs(packageDocument, "manifest");
|
||||
if(!manifestNode) {
|
||||
console.error("No Manifest Found");
|
||||
return;
|
||||
}
|
||||
|
||||
spineNode = qs(packageDocument, "spine");
|
||||
if(!spineNode) {
|
||||
console.error("No Spine Found");
|
||||
return;
|
||||
}
|
||||
|
||||
this.manifest = this.parseManifest(manifestNode);
|
||||
this.navPath = this.findNavPath(manifestNode);
|
||||
this.ncxPath = this.findNcxPath(manifestNode, spineNode);
|
||||
this.coverPath = this.findCoverPath(packageDocument);
|
||||
|
||||
this.spineNodeIndex = Array.prototype.indexOf.call(spineNode.parentNode.childNodes, spineNode);
|
||||
|
||||
this.spine = this.parseSpine(spineNode, this.manifest);
|
||||
|
||||
this.metadata = this.parseMetadata(metadataNode);
|
||||
|
||||
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
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Metadata
|
||||
* @private
|
||||
* @param {document} xml
|
||||
* @return {object} metadata
|
||||
*/
|
||||
parseMetadata(xml){
|
||||
var metadata = {};
|
||||
|
||||
metadata.title = this.getElementText(xml, 'title');
|
||||
metadata.creator = this.getElementText(xml, 'creator');
|
||||
metadata.description = this.getElementText(xml, 'description');
|
||||
|
||||
metadata.pubdate = this.getElementText(xml, 'date');
|
||||
|
||||
metadata.publisher = this.getElementText(xml, 'publisher');
|
||||
|
||||
metadata.identifier = this.getElementText(xml, "identifier");
|
||||
metadata.language = this.getElementText(xml, "language");
|
||||
metadata.rights = this.getElementText(xml, "rights");
|
||||
|
||||
metadata.modified_date = this.getPropertyText(xml, 'dcterms:modified');
|
||||
|
||||
metadata.layout = this.getPropertyText(xml, "rendition:layout");
|
||||
metadata.orientation = this.getPropertyText(xml, 'rendition:orientation');
|
||||
metadata.flow = this.getPropertyText(xml, 'rendition:flow');
|
||||
metadata.viewport = this.getPropertyText(xml, 'rendition:viewport');
|
||||
// metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction");
|
||||
|
||||
return metadata;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Manifest
|
||||
* @private
|
||||
* @param {document} manifestXml
|
||||
* @return {object} manifest
|
||||
*/
|
||||
parseManifest(manifestXml){
|
||||
var manifest = {};
|
||||
|
||||
//-- Turn items into an array
|
||||
// var selected = manifestXml.querySelectorAll("item");
|
||||
var selected = qsa(manifestXml, "item");
|
||||
var items = Array.prototype.slice.call(selected);
|
||||
|
||||
//-- Create an object with the id as key
|
||||
items.forEach(function(item){
|
||||
var id = item.getAttribute('id'),
|
||||
href = item.getAttribute('href') || '',
|
||||
type = item.getAttribute('media-type') || '',
|
||||
properties = item.getAttribute('properties') || '';
|
||||
|
||||
manifest[id] = {
|
||||
'href' : href,
|
||||
// 'url' : href,
|
||||
'type' : type,
|
||||
'properties' : properties.length ? properties.split(' ') : []
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
return manifest;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Spine
|
||||
* @param {document} spineXml
|
||||
* @param {Packaging.manifest} manifest
|
||||
* @return {object} spine
|
||||
*/
|
||||
parseSpine(spineXml, manifest){
|
||||
var spine = [];
|
||||
|
||||
var selected = spineXml.getElementsByTagName("itemref"),
|
||||
items = Array.prototype.slice.call(selected);
|
||||
|
||||
var epubcfi = new EpubCFI();
|
||||
|
||||
//-- Add to array to mantain ordering and cross reference with manifest
|
||||
items.forEach(function(item, index){
|
||||
var idref = item.getAttribute('idref');
|
||||
// var cfiBase = epubcfi.generateChapterComponent(spineNodeIndex, index, Id);
|
||||
var props = item.getAttribute('properties') || '';
|
||||
var propArray = props.length ? props.split(' ') : [];
|
||||
// var manifestProps = manifest[Id].properties;
|
||||
// var manifestPropArray = manifestProps.length ? manifestProps.split(' ') : [];
|
||||
|
||||
var itemref = {
|
||||
'idref' : idref,
|
||||
'linear' : item.getAttribute('linear') || '',
|
||||
'properties' : propArray,
|
||||
// 'href' : manifest[Id].href,
|
||||
// 'url' : manifest[Id].url,
|
||||
'index' : index
|
||||
// 'cfiBase' : cfiBase
|
||||
};
|
||||
spine.push(itemref);
|
||||
});
|
||||
|
||||
return spine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find TOC NAV
|
||||
* @private
|
||||
*/
|
||||
findNavPath(manifestNode){
|
||||
// Find item with property 'nav'
|
||||
// Should catch nav irregardless of order
|
||||
// var node = manifestNode.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']");
|
||||
var node = qsp(manifestNode, "item", {"properties":"nav"});
|
||||
return node ? node.getAttribute('href') : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find TOC NCX
|
||||
* media-type="application/x-dtbncx+xml" href="toc.ncx"
|
||||
* @private
|
||||
*/
|
||||
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 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
|
||||
// according to http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4.1.2,
|
||||
// "The item that describes the NCX must be referenced by the spine toc attribute."
|
||||
if (!node) {
|
||||
tocId = spineNode.getAttribute("toc");
|
||||
if(tocId) {
|
||||
// node = manifestNode.querySelector("item[id='" + tocId + "']");
|
||||
node = manifestNode.getElementById(tocId);
|
||||
}
|
||||
}
|
||||
|
||||
return node ? node.getAttribute('href') : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the Cover Path
|
||||
* <item properties="cover-image" id="ci" href="cover.svg" media-type="image/svg+xml" />
|
||||
* Fallback for Epub 2.0
|
||||
* @param {document} packageXml
|
||||
* @return {string} href
|
||||
*/
|
||||
findCoverPath(packageXml){
|
||||
var pkg = qs(packageXml, "package");
|
||||
var epubVersion = pkg.getAttribute('version');
|
||||
|
||||
if (epubVersion === '2.0') {
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
// var node = packageXml.querySelector("item[properties='cover-image']");
|
||||
var node = qsp(packageXml, 'item', {'properties':'cover-image'});
|
||||
return node ? node.getAttribute('href') : '';
|
||||
}
|
||||
}
|
||||
else {
|
||||
// var node = packageXml.querySelector("item[properties='cover-image']");
|
||||
var node = core.qsp(packageXml, 'item', {'properties':'cover-image'});
|
||||
return node ? node.getAttribute('href') : '';
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get text of a namespaced element
|
||||
* @private
|
||||
* @param {document} xml
|
||||
* @param {string} tag
|
||||
* @return {string} text
|
||||
*/
|
||||
Packaging.prototype.getElementText = function(xml, tag){
|
||||
var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag),
|
||||
el;
|
||||
/**
|
||||
* Get text of a namespaced element
|
||||
* @private
|
||||
* @param {document} xml
|
||||
* @param {string} tag
|
||||
* @return {string} text
|
||||
*/
|
||||
getElementText(xml, tag){
|
||||
var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag),
|
||||
el;
|
||||
|
||||
if(!found || found.length === 0) return '';
|
||||
if(!found || found.length === 0) return '';
|
||||
|
||||
el = found[0];
|
||||
el = found[0];
|
||||
|
||||
if(el.childNodes.length){
|
||||
return el.childNodes[0].nodeValue;
|
||||
}
|
||||
if(el.childNodes.length){
|
||||
return el.childNodes[0].nodeValue;
|
||||
}
|
||||
|
||||
return '';
|
||||
return '';
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get text by property
|
||||
* @private
|
||||
* @param {document} xml
|
||||
* @param {string} property
|
||||
* @return {string} text
|
||||
*/
|
||||
Packaging.prototype.getPropertyText = function(xml, property){
|
||||
var el = core.qsp(xml, "meta", {"property":property});
|
||||
/**
|
||||
* Get text by property
|
||||
* @private
|
||||
* @param {document} xml
|
||||
* @param {string} property
|
||||
* @return {string} text
|
||||
*/
|
||||
getPropertyText(xml, property){
|
||||
var el = qsp(xml, "meta", {"property":property});
|
||||
|
||||
if(el && el.childNodes.length){
|
||||
return el.childNodes[0].nodeValue;
|
||||
}
|
||||
if(el && el.childNodes.length){
|
||||
return el.childNodes[0].nodeValue;
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
return '';
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Packaging;
|
||||
export default Packaging;
|
||||
|
|
484
src/pagelist.js
484
src/pagelist.js
|
@ -1,249 +1,257 @@
|
|||
var EpubCFI = require('./epubcfi');
|
||||
var core = require('./core');
|
||||
import EpubCFI from './epubcfi';
|
||||
import {
|
||||
qs,
|
||||
qsa,
|
||||
querySelectorByType,
|
||||
indexOfSorted,
|
||||
locationOf
|
||||
} from './utils/core';
|
||||
|
||||
/**
|
||||
* Page List Parser
|
||||
* @param {[document]} xml
|
||||
*/
|
||||
function PageList(xml) {
|
||||
this.pages = [];
|
||||
this.locations = [];
|
||||
this.epubcfi = new EpubCFI();
|
||||
class PageList {
|
||||
constructor(xml) {
|
||||
this.pages = [];
|
||||
this.locations = [];
|
||||
this.epubcfi = new EpubCFI();
|
||||
|
||||
if (xml) {
|
||||
this.pageList = this.parse(xml);
|
||||
}
|
||||
|
||||
if(this.pageList && this.pageList.length) {
|
||||
this.process(this.pageList);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse PageList Xml
|
||||
* @param {document} xml
|
||||
*/
|
||||
PageList.prototype.parse = function(xml) {
|
||||
var html = core.qs(xml, "html");
|
||||
// var ncx = core.qs(xml, "ncx");
|
||||
|
||||
if(html) {
|
||||
this.toc = this.parseNav(xml);
|
||||
} else if(ncx){ // Not supported
|
||||
// this.toc = this.parseNcx(xml);
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a Nav PageList
|
||||
* @private
|
||||
* @param {document} navHtml
|
||||
* @return {PageList.item[]} list
|
||||
*/
|
||||
PageList.prototype.parseNav = function(navHtml){
|
||||
var navElement = core.querySelectorByType(navHtml, "nav", "page-list");
|
||||
var navItems = navElement ? core.qsa(navElement, "li") : [];
|
||||
var length = navItems.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item;
|
||||
|
||||
if(!navItems || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.item(navItems[i]);
|
||||
list.push(item);
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Page List Item
|
||||
* @private
|
||||
* @param {object} item
|
||||
* @return {object} pageListItem
|
||||
*/
|
||||
PageList.prototype.item = function(item){
|
||||
var id = item.getAttribute('id') || false,
|
||||
content = core.qs(item, "a"),
|
||||
href = content.getAttribute('href') || '',
|
||||
text = content.textContent || "",
|
||||
page = parseInt(text),
|
||||
isCfi = href.indexOf("epubcfi"),
|
||||
split,
|
||||
packageUrl,
|
||||
cfi;
|
||||
|
||||
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
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
"href" : href,
|
||||
"page" : page
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process pageList items
|
||||
* @private
|
||||
* @param {array} pageList
|
||||
*/
|
||||
PageList.prototype.process = function(pageList){
|
||||
pageList.forEach(function(item){
|
||||
this.pages.push(item.page);
|
||||
if (item.cfi) {
|
||||
this.locations.push(item.cfi);
|
||||
if (xml) {
|
||||
this.pageList = this.parse(xml);
|
||||
}
|
||||
}, this);
|
||||
this.firstPage = parseInt(this.pages[0]);
|
||||
this.lastPage = parseInt(this.pages[this.pages.length-1]);
|
||||
this.totalPages = this.lastPage - this.firstPage;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replace HREFs with CFI
|
||||
* TODO: implement getting CFI from Href
|
||||
*/
|
||||
PageList.prototype.addCFIs = function() {
|
||||
this.pageList.forEach(function(pg){
|
||||
if(!pg.cfi) {
|
||||
// epubcfi.generateCfiFromHref(pg.href, book).then(function(cfi){
|
||||
// pg.cfi = cfi;
|
||||
// pg.packageUrl = book.settings.packageUrl;
|
||||
// });
|
||||
if(this.pageList && this.pageList.length) {
|
||||
this.process(this.pageList);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse PageList Xml
|
||||
* @param {document} xml
|
||||
*/
|
||||
parse(xml) {
|
||||
var html = qs(xml, "html");
|
||||
// var ncx = qs(xml, "ncx");
|
||||
|
||||
if(html) {
|
||||
this.toc = this.parseNav(xml);
|
||||
} else if(ncx){ // Not supported
|
||||
// this.toc = this.parseNcx(xml);
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a Nav PageList
|
||||
* @private
|
||||
* @param {document} navHtml
|
||||
* @return {PageList.item[]} list
|
||||
*/
|
||||
parseNav(navHtml){
|
||||
var navElement = querySelectorByType(navHtml, "nav", "page-list");
|
||||
var navItems = navElement ? qsa(navElement, "li") : [];
|
||||
var length = navItems.length;
|
||||
var i;
|
||||
var toc = {};
|
||||
var list = [];
|
||||
var item;
|
||||
|
||||
if(!navItems || length === 0) return list;
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
item = this.item(navItems[i]);
|
||||
list.push(item);
|
||||
}
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
* Page List Item
|
||||
* @private
|
||||
* @param {object} item
|
||||
* @return {object} pageListItem
|
||||
*/
|
||||
item(item){
|
||||
var id = item.getAttribute('id') || false,
|
||||
content = qs(item, "a"),
|
||||
href = content.getAttribute('href') || '',
|
||||
text = content.textContent || "",
|
||||
page = parseInt(text),
|
||||
isCfi = href.indexOf("epubcfi"),
|
||||
split,
|
||||
packageUrl,
|
||||
cfi;
|
||||
|
||||
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
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
"href" : href,
|
||||
"page" : page
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process pageList items
|
||||
* @private
|
||||
* @param {array} pageList
|
||||
*/
|
||||
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.totalPages = this.lastPage - this.firstPage;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replace HREFs with CFI
|
||||
* TODO: implement getting CFI from Href
|
||||
*/
|
||||
addCFIs() {
|
||||
this.pageList.forEach(function(pg){
|
||||
if(!pg.cfi) {
|
||||
// epubcfi.generateCfiFromHref(pg.href, book).then(function(cfi){
|
||||
// pg.cfi = cfi;
|
||||
// pg.packageUrl = book.settings.packageUrl;
|
||||
// });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
EPUBJS.generateCfiFromHref(href, book) {
|
||||
var uri = EPUBJS.core.uri(href);
|
||||
var path = uri.path;
|
||||
var fragment = uri.fragment;
|
||||
var spinePos = book.spineIndexByURL[path];
|
||||
var loaded;
|
||||
var deferred = new RSVP.defer();
|
||||
var epubcfi = new EPUBJS.EpubCFI();
|
||||
var spineItem;
|
||||
|
||||
if(typeof spinePos !== "undefined"){
|
||||
spineItem = book.spine[spinePos];
|
||||
loaded = book.loadXml(spineItem.url);
|
||||
loaded.then(function(doc){
|
||||
var element = doc.getElementById(fragment);
|
||||
var cfi;
|
||||
cfi = epubcfi.generateCfiFromElement(element, spineItem.cfiBase);
|
||||
deferred.resolve(cfi);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get a PageList result from a EpubCFI
|
||||
* @param {string} cfi EpubCFI String
|
||||
* @return {string} page
|
||||
*/
|
||||
pageFromCfi(cfi){
|
||||
var pg = -1;
|
||||
|
||||
// Check if the pageList has not been set yet
|
||||
if(this.locations.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: check if CFI is valid?
|
||||
|
||||
// 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) {
|
||||
pg = this.pages[index];
|
||||
} else {
|
||||
// Otherwise add it to the list of locations
|
||||
// Insert it in the correct position in the locations page
|
||||
//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) {
|
||||
// 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an EpubCFI from a Page List Item
|
||||
* @param {string} pg
|
||||
* @return {string} cfi
|
||||
*/
|
||||
cfiFromPage(pg){
|
||||
var cfi = -1;
|
||||
// check that pg is an int
|
||||
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) {
|
||||
cfi = this.locations[index];
|
||||
}
|
||||
// TODO: handle pages not in the list
|
||||
return cfi;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a Page from Book percentage
|
||||
* @param {number} percent
|
||||
* @return {string} page
|
||||
*/
|
||||
pageFromPercentage(percent){
|
||||
var pg = Math.round(this.totalPages * percent);
|
||||
return pg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a value between 0 - 1 corresponding to the location of a page
|
||||
* @param {int} pg the page
|
||||
* @return {number} percentage
|
||||
*/
|
||||
percentageFromPage(pg){
|
||||
var percentage = (pg - this.firstPage) / this.totalPages;
|
||||
return Math.round(percentage * 1000) / 1000;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a value between 0 - 1 corresponding to the location of a cfi
|
||||
* @param {string} cfi EpubCFI String
|
||||
* @return {number} percentage
|
||||
*/
|
||||
percentageFromCfi(cfi){
|
||||
var pg = this.pageFromCfi(cfi);
|
||||
var percentage = this.percentageFromPage(pg);
|
||||
return percentage;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
EPUBJS.EpubCFI.prototype.generateCfiFromHref = function(href, book) {
|
||||
var uri = EPUBJS.core.uri(href);
|
||||
var path = uri.path;
|
||||
var fragment = uri.fragment;
|
||||
var spinePos = book.spineIndexByURL[path];
|
||||
var loaded;
|
||||
var deferred = new RSVP.defer();
|
||||
var epubcfi = new EPUBJS.EpubCFI();
|
||||
var spineItem;
|
||||
|
||||
if(typeof spinePos !== "undefined"){
|
||||
spineItem = book.spine[spinePos];
|
||||
loaded = book.loadXml(spineItem.url);
|
||||
loaded.then(function(doc){
|
||||
var element = doc.getElementById(fragment);
|
||||
var cfi;
|
||||
cfi = epubcfi.generateCfiFromElement(element, spineItem.cfiBase);
|
||||
deferred.resolve(cfi);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get a PageList result from a EpubCFI
|
||||
* @param {string} cfi EpubCFI String
|
||||
* @return {string} page
|
||||
*/
|
||||
PageList.prototype.pageFromCfi = function(cfi){
|
||||
var pg = -1;
|
||||
|
||||
// Check if the pageList has not been set yet
|
||||
if(this.locations.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: check if CFI is valid?
|
||||
|
||||
// check if the cfi is in the location list
|
||||
// var index = this.locations.indexOf(cfi);
|
||||
var index = core.indexOfSorted(cfi, this.locations, this.epubcfi.compare);
|
||||
if(index != -1) {
|
||||
pg = this.pages[index];
|
||||
} else {
|
||||
// Otherwise add it to the list of locations
|
||||
// Insert it in the correct position in the locations page
|
||||
//index = EPUBJS.core.insert(cfi, this.locations, this.epubcfi.compare);
|
||||
index = EPUBJS.core.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) {
|
||||
// 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an EpubCFI from a Page List Item
|
||||
* @param {string} pg
|
||||
* @return {string} cfi
|
||||
*/
|
||||
PageList.prototype.cfiFromPage = function(pg){
|
||||
var cfi = -1;
|
||||
// check that pg is an int
|
||||
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) {
|
||||
cfi = this.locations[index];
|
||||
}
|
||||
// TODO: handle pages not in the list
|
||||
return cfi;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a Page from Book percentage
|
||||
* @param {number} percent
|
||||
* @return {string} page
|
||||
*/
|
||||
PageList.prototype.pageFromPercentage = function(percent){
|
||||
var pg = Math.round(this.totalPages * percent);
|
||||
return pg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a value between 0 - 1 corresponding to the location of a page
|
||||
* @param {int} pg the page
|
||||
* @return {number} percentage
|
||||
*/
|
||||
PageList.prototype.percentageFromPage = function(pg){
|
||||
var percentage = (pg - this.firstPage) / this.totalPages;
|
||||
return Math.round(percentage * 1000) / 1000;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a value between 0 - 1 corresponding to the location of a cfi
|
||||
* @param {string} cfi EpubCFI String
|
||||
* @return {number} percentage
|
||||
*/
|
||||
PageList.prototype.percentageFromCfi = function(cfi){
|
||||
var pg = this.pageFromCfi(cfi);
|
||||
var percentage = this.percentageFromPage(pg);
|
||||
return percentage;
|
||||
};
|
||||
|
||||
module.exports = PageList;
|
||||
export default PageList;
|
||||
|
|
344
src/queue.js
344
src/queue.js
|
@ -1,198 +1,201 @@
|
|||
var core = require('./core');
|
||||
import {defer, requestAnimationFrame} from './utils/core';
|
||||
|
||||
/**
|
||||
* Queue for handling tasks one at a time
|
||||
* @class
|
||||
* @param {scope} context what this will resolve to in the tasks
|
||||
*/
|
||||
function Queue(context){
|
||||
this._q = [];
|
||||
this.context = context;
|
||||
this.tick = core.requestAnimationFrame;
|
||||
this.running = false;
|
||||
this.paused = false;
|
||||
};
|
||||
class Queue {
|
||||
constructor(context){
|
||||
this._q = [];
|
||||
this.context = context;
|
||||
this.tick = requestAnimationFrame;
|
||||
this.running = false;
|
||||
this.paused = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an item to the queue
|
||||
* @return {Promise}
|
||||
*/
|
||||
Queue.prototype.enqueue = function() {
|
||||
var deferred, promise;
|
||||
var queued;
|
||||
var task = [].shift.call(arguments);
|
||||
var args = arguments;
|
||||
/**
|
||||
* Add an item to the queue
|
||||
* @return {Promise}
|
||||
*/
|
||||
enqueue() {
|
||||
var deferred, promise;
|
||||
var queued;
|
||||
var task = [].shift.call(arguments);
|
||||
var args = arguments;
|
||||
|
||||
// Handle single args without context
|
||||
// if(args && !Array.isArray(args)) {
|
||||
// args = [args];
|
||||
// }
|
||||
if(!task) {
|
||||
return console.error("No Task Provided");
|
||||
}
|
||||
// Handle single args without context
|
||||
// if(args && !Array.isArray(args)) {
|
||||
// args = [args];
|
||||
// }
|
||||
if(!task) {
|
||||
return console.error("No Task Provided");
|
||||
}
|
||||
|
||||
if(typeof task === "function"){
|
||||
if(typeof task === "function"){
|
||||
|
||||
deferred = new core.defer();
|
||||
promise = deferred.promise;
|
||||
deferred = new defer();
|
||||
promise = deferred.promise;
|
||||
|
||||
queued = {
|
||||
"task" : task,
|
||||
"args" : args,
|
||||
//"context" : context,
|
||||
"deferred" : deferred,
|
||||
"promise" : promise
|
||||
};
|
||||
queued = {
|
||||
"task" : task,
|
||||
"args" : args,
|
||||
//"context" : context,
|
||||
"deferred" : deferred,
|
||||
"promise" : promise
|
||||
};
|
||||
|
||||
} else {
|
||||
// Task is a promise
|
||||
queued = {
|
||||
"promise" : task
|
||||
};
|
||||
} else {
|
||||
// Task is a promise
|
||||
queued = {
|
||||
"promise" : task
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this._q.push(queued);
|
||||
this._q.push(queued);
|
||||
|
||||
// Wait to start queue flush
|
||||
if (this.paused == false && !this.running) {
|
||||
// setTimeout(this.flush.bind(this), 0);
|
||||
// this.tick.call(window, this.run.bind(this));
|
||||
this.run();
|
||||
}
|
||||
// Wait to start queue flush
|
||||
if (this.paused == false && !this.running) {
|
||||
// setTimeout(this.flush.bind(this), 0);
|
||||
// this.tick.call(window, this.run.bind(this));
|
||||
this.run();
|
||||
}
|
||||
|
||||
return queued.promise;
|
||||
};
|
||||
return queued.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run one item
|
||||
* @return {Promise}
|
||||
*/
|
||||
Queue.prototype.dequeue = function(){
|
||||
var inwait, task, result;
|
||||
/**
|
||||
* Run one item
|
||||
* @return {Promise}
|
||||
*/
|
||||
dequeue(){
|
||||
var inwait, task, result;
|
||||
|
||||
if(this._q.length) {
|
||||
inwait = this._q.shift();
|
||||
task = inwait.task;
|
||||
if(task){
|
||||
// console.log(task)
|
||||
if(this._q.length) {
|
||||
inwait = this._q.shift();
|
||||
task = inwait.task;
|
||||
if(task){
|
||||
// console.log(task)
|
||||
|
||||
result = task.apply(this.context, inwait.args);
|
||||
result = task.apply(this.context, inwait.args);
|
||||
|
||||
if(result && typeof result["then"] === "function") {
|
||||
// Task is a function that returns a promise
|
||||
return result.then(function(){
|
||||
inwait.deferred.resolve.apply(this.context, arguments);
|
||||
}, function(reason) {
|
||||
inwait.deferred.reject.apply(this.context, arguments);
|
||||
}.bind(this));
|
||||
} else {
|
||||
// Task resolves immediately
|
||||
inwait.deferred.resolve.apply(this.context, result);
|
||||
if(result && typeof result["then"] === "function") {
|
||||
// Task is a function that returns a promise
|
||||
return result.then(function(){
|
||||
inwait.deferred.resolve.apply(this.context, arguments);
|
||||
}.bind(this), function(reason) {
|
||||
inwait.deferred.reject.apply(this.context, arguments);
|
||||
}.bind(this));
|
||||
} else {
|
||||
// Task resolves immediately
|
||||
inwait.deferred.resolve.apply(this.context, result);
|
||||
return inwait.promise;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if(inwait.promise) {
|
||||
// Task is a promise
|
||||
return inwait.promise;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else if(inwait.promise) {
|
||||
// Task is a promise
|
||||
} else {
|
||||
inwait = new defer();
|
||||
inwait.deferred.resolve();
|
||||
return inwait.promise;
|
||||
}
|
||||
|
||||
} else {
|
||||
inwait = new core.defer();
|
||||
inwait.deferred.resolve();
|
||||
return inwait.promise;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
// Run All Immediately
|
||||
dump(){
|
||||
while(this._q.length) {
|
||||
this.dequeue();
|
||||
}
|
||||
};
|
||||
|
||||
// Run All Immediately
|
||||
Queue.prototype.dump = function(){
|
||||
while(this._q.length) {
|
||||
this.dequeue();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Run all tasks sequentially, at convince
|
||||
* @return {Promise}
|
||||
*/
|
||||
run(){
|
||||
|
||||
/**
|
||||
* Run all tasks sequentially, at convince
|
||||
* @return {Promise}
|
||||
*/
|
||||
Queue.prototype.run = function(){
|
||||
|
||||
if(!this.running){
|
||||
this.running = true;
|
||||
this.defered = new core.defer();
|
||||
}
|
||||
|
||||
this.tick.call(window, function() {
|
||||
|
||||
if(this._q.length) {
|
||||
|
||||
this.dequeue()
|
||||
.then(function(){
|
||||
this.run();
|
||||
}.bind(this));
|
||||
|
||||
} else {
|
||||
this.defered.resolve();
|
||||
this.running = undefined;
|
||||
if(!this.running){
|
||||
this.running = true;
|
||||
this.defered = new defer();
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
this.tick.call(window, function() {
|
||||
|
||||
// Unpause
|
||||
if(this.paused == true) {
|
||||
this.paused = false;
|
||||
}
|
||||
if(this._q.length) {
|
||||
|
||||
return this.defered.promise;
|
||||
};
|
||||
this.dequeue()
|
||||
.then(function(){
|
||||
this.run();
|
||||
}.bind(this));
|
||||
|
||||
/**
|
||||
* Flush all, as quickly as possible
|
||||
* @return {Promise}
|
||||
*/
|
||||
Queue.prototype.flush = function(){
|
||||
|
||||
if(this.running){
|
||||
return this.running;
|
||||
}
|
||||
|
||||
if(this._q.length) {
|
||||
this.running = this.dequeue()
|
||||
.then(function(){
|
||||
} else {
|
||||
this.defered.resolve();
|
||||
this.running = undefined;
|
||||
return this.flush();
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
return this.running;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
// Unpause
|
||||
if(this.paused == true) {
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all items in wait
|
||||
*/
|
||||
Queue.prototype.clear = function(){
|
||||
this._q = [];
|
||||
this.running = false;
|
||||
};
|
||||
return this.defered.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of tasks in the queue
|
||||
* @return {int} tasks
|
||||
*/
|
||||
Queue.prototype.length = function(){
|
||||
return this._q.length;
|
||||
};
|
||||
/**
|
||||
* Flush all, as quickly as possible
|
||||
* @return {Promise}
|
||||
*/
|
||||
flush(){
|
||||
|
||||
if(this.running){
|
||||
return this.running;
|
||||
}
|
||||
|
||||
if(this._q.length) {
|
||||
this.running = this.dequeue()
|
||||
.then(function(){
|
||||
this.running = undefined;
|
||||
return this.flush();
|
||||
}.bind(this));
|
||||
|
||||
return this.running;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear all items in wait
|
||||
*/
|
||||
clear(){
|
||||
this._q = [];
|
||||
this.running = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of tasks in the queue
|
||||
* @return {int} tasks
|
||||
*/
|
||||
length(){
|
||||
return this._q.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pause a running queue
|
||||
*/
|
||||
pause(){
|
||||
this.paused = true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause a running queue
|
||||
*/
|
||||
Queue.prototype.pause = function(){
|
||||
this.paused = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new task from a callback
|
||||
|
@ -203,25 +206,28 @@ Queue.prototype.pause = function(){
|
|||
* @param {scope} context
|
||||
* @return {function} task
|
||||
*/
|
||||
function Task(task, args, context){
|
||||
class Task {
|
||||
constructor(task, args, context){
|
||||
|
||||
return function(){
|
||||
var toApply = arguments || [];
|
||||
return function(){
|
||||
var toApply = arguments || [];
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
var callback = function(value){
|
||||
resolve(value);
|
||||
};
|
||||
// Add the callback to the arguments list
|
||||
toApply.push(callback);
|
||||
return new Promise(function(resolve, reject) {
|
||||
var callback = function(value){
|
||||
resolve(value);
|
||||
};
|
||||
// Add the callback to the arguments list
|
||||
toApply.push(callback);
|
||||
|
||||
// Apply all arguments to the functions
|
||||
task.apply(this, toApply);
|
||||
// Apply all arguments to the functions
|
||||
task.apply(this, toApply);
|
||||
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = Queue;
|
||||
export default Queue;
|
||||
|
|
1059
src/rendition.js
1059
src/rendition.js
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,8 @@
|
|||
// var URI = require('urijs');
|
||||
var core = require('./core');
|
||||
var Url = require('./core').Url;
|
||||
// import URI from 'urijs';
|
||||
import { qs } from './utils/core';
|
||||
import Url from './utils/url';
|
||||
|
||||
function base(doc, section){
|
||||
export function replaceBase(doc, section){
|
||||
var base;
|
||||
var head;
|
||||
|
||||
|
@ -12,8 +12,8 @@ function base(doc, section){
|
|||
|
||||
// head = doc.querySelector("head");
|
||||
// base = head.querySelector("base");
|
||||
head = core.qs(doc, "head");
|
||||
base = core.qs(head, "base");
|
||||
head = qs(doc, "head");
|
||||
base = qs(head, "base");
|
||||
|
||||
if(!base) {
|
||||
base = doc.createElement("base");
|
||||
|
@ -23,7 +23,7 @@ function base(doc, section){
|
|||
base.setAttribute("href", section.url);
|
||||
}
|
||||
|
||||
function canonical(doc, section){
|
||||
export function replaceCanonical(doc, section){
|
||||
var head;
|
||||
var link;
|
||||
var url = section.url; // window.location.origin + window.location.pathname + "?loc=" + encodeURIComponent(section.url);
|
||||
|
@ -32,8 +32,8 @@ function canonical(doc, section){
|
|||
return;
|
||||
}
|
||||
|
||||
head = core.qs(doc, "head");
|
||||
link = core.qs(head, "link[rel='canonical']");
|
||||
head = qs(doc, "head");
|
||||
link = qs(head, "link[rel='canonical']");
|
||||
|
||||
if (link) {
|
||||
link.setAttribute("href", url);
|
||||
|
@ -45,10 +45,10 @@ function canonical(doc, section){
|
|||
}
|
||||
}
|
||||
|
||||
function links(view, renderer) {
|
||||
export function replaceLinks(view, renderer) {
|
||||
|
||||
var links = view.document.querySelectorAll("a[href]");
|
||||
var replaceLinks = function(link){
|
||||
var replaceLink = function(link){
|
||||
var href = link.getAttribute("href");
|
||||
|
||||
if(href.indexOf("mailto:") === 0){
|
||||
|
@ -92,13 +92,13 @@ function links(view, renderer) {
|
|||
}.bind(this);
|
||||
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
replaceLinks(links[i]);
|
||||
replaceLink(links[i]);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
function substitute(content, urls, replacements) {
|
||||
export function substitute(content, urls, replacements) {
|
||||
urls.forEach(function(url, i){
|
||||
if (url && replacements[i]) {
|
||||
content = content.replace(new RegExp(url, 'g'), replacements[i]);
|
||||
|
@ -106,9 +106,3 @@ function substitute(content, urls, replacements) {
|
|||
});
|
||||
return content;
|
||||
}
|
||||
module.exports = {
|
||||
'base': base,
|
||||
'canonical' : canonical,
|
||||
'links': links,
|
||||
'substitute': substitute
|
||||
};
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
var core = require('./core');
|
||||
var Path = require('./core').Path;
|
||||
import {defer, isXml, parse} from './utils/core';
|
||||
import Path from './utils/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 BLOB_RESPONSE = supportsURL ? "blob" : "arraybuffer";
|
||||
var uri;
|
||||
|
||||
var deferred = new core.defer();
|
||||
var deferred = new defer();
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
|
@ -49,7 +49,7 @@ function request(url, type, withCredentials, headers) {
|
|||
}
|
||||
|
||||
|
||||
if(core.isXml(type)) {
|
||||
if(isXml(type)) {
|
||||
// xhr.responseType = "document";
|
||||
xhr.overrideMimeType('text/xml'); // for OPF parsing
|
||||
}
|
||||
|
@ -106,16 +106,16 @@ function request(url, type, withCredentials, headers) {
|
|||
if(responseXML){
|
||||
r = this.responseXML;
|
||||
} else
|
||||
if(core.isXml(type)){
|
||||
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 = core.parse(this.response, "text/xml");
|
||||
r = parse(this.response, "text/xml");
|
||||
}else
|
||||
if(type == 'xhtml'){
|
||||
r = core.parse(this.response, "application/xhtml+xml");
|
||||
r = parse(this.response, "application/xhtml+xml");
|
||||
}else
|
||||
if(type == 'html' || type == 'htm'){
|
||||
r = core.parse(this.response, "text/html");
|
||||
r = parse(this.response, "text/html");
|
||||
}else
|
||||
if(type == 'json'){
|
||||
r = JSON.parse(this.response);
|
||||
|
@ -149,4 +149,4 @@ function request(url, type, withCredentials, headers) {
|
|||
return deferred.promise;
|
||||
};
|
||||
|
||||
module.exports = request;
|
||||
export default request;
|
||||
|
|
415
src/resources.js
415
src/resources.js
|
@ -1,7 +1,7 @@
|
|||
var replace = require('./replacements');
|
||||
var core = require('./core');
|
||||
var Path = require('./core').Path;
|
||||
var path = require('path');
|
||||
import {substitute} from './replacements';
|
||||
import {createBase64Url, createBlobUrl} from './utils/core';
|
||||
import Path from './utils/path';
|
||||
import path from 'path-webpack';
|
||||
|
||||
/**
|
||||
* Handle Package Resources
|
||||
|
@ -12,234 +12,235 @@ var path = require('path');
|
|||
* @param {[Archive]} options.archive
|
||||
* @param {[method]} options.resolver
|
||||
*/
|
||||
function Resources(manifest, options) {
|
||||
this.settings = {
|
||||
replacements: (options && options.replacements) || 'base64',
|
||||
archive: (options && options.archive),
|
||||
resolver: (options && options.resolver)
|
||||
};
|
||||
this.manifest = manifest;
|
||||
this.resources = Object.keys(manifest).
|
||||
map(function (key){
|
||||
return manifest[key];
|
||||
});
|
||||
class Resources {
|
||||
constructor(manifest, options) {
|
||||
this.settings = {
|
||||
replacements: (options && options.replacements) || 'base64',
|
||||
archive: (options && options.archive),
|
||||
resolver: (options && options.resolver)
|
||||
};
|
||||
this.manifest = manifest;
|
||||
this.resources = Object.keys(manifest).
|
||||
map(function (key){
|
||||
return manifest[key];
|
||||
});
|
||||
|
||||
this.replacementUrls = [];
|
||||
this.replacementUrls = [];
|
||||
|
||||
this.split();
|
||||
this.splitUrls();
|
||||
}
|
||||
|
||||
/**
|
||||
* Split resources by type
|
||||
* @private
|
||||
*/
|
||||
Resources.prototype.split = function(){
|
||||
|
||||
// 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") {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Only CSS
|
||||
this.css = this.resources.
|
||||
filter(function (item){
|
||||
if (item.type === "text/css") {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert split resources into Urls
|
||||
* @private
|
||||
*/
|
||||
Resources.prototype.splitUrls = function(){
|
||||
|
||||
// All Assets Urls
|
||||
this.urls = this.assets.
|
||||
map(function(item) {
|
||||
return item.href;
|
||||
}.bind(this));
|
||||
|
||||
// Css Urls
|
||||
this.cssUrls = this.css.map(function(item) {
|
||||
return item.href;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Create blob urls for all the assets
|
||||
* @param {Archive} archive
|
||||
* @param {resolver} resolver Url resolver
|
||||
* @return {Promise} returns replacement urls
|
||||
*/
|
||||
Resources.prototype.replacements = function(archive, resolver){
|
||||
archive = archive || this.settings.archive;
|
||||
resolver = resolver || this.settings.resolver;
|
||||
|
||||
if (this.settings.replacements === "none") {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(this.urls);
|
||||
}.bind(this));
|
||||
this.split();
|
||||
this.splitUrls();
|
||||
}
|
||||
|
||||
var replacements = this.urls.
|
||||
map(function(url) {
|
||||
var absolute = resolver(url);
|
||||
/**
|
||||
* Split resources by type
|
||||
* @private
|
||||
*/
|
||||
split(){
|
||||
|
||||
return archive.createUrl(absolute, {"base64": (this.settings.replacements === "base64")});
|
||||
}.bind(this))
|
||||
|
||||
return Promise.all(replacements)
|
||||
.then(function(replacementUrls) {
|
||||
this.replacementUrls = replacementUrls;
|
||||
return replacementUrls;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace URLs in CSS resources
|
||||
* @private
|
||||
* @param {[Archive]} archive
|
||||
* @param {[method]} resolver
|
||||
* @return {Promise}
|
||||
*/
|
||||
Resources.prototype.replaceCss = function(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) {
|
||||
// switch the url in the replacementUrls
|
||||
var indexInUrls = this.urls.indexOf(href);
|
||||
if (indexInUrls > -1) {
|
||||
this.replacementUrls[indexInUrls] = replacementUrl;
|
||||
// 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") {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Only CSS
|
||||
this.css = this.resources.
|
||||
filter(function (item){
|
||||
if (item.type === "text/css") {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert split resources into Urls
|
||||
* @private
|
||||
*/
|
||||
splitUrls(){
|
||||
|
||||
// All Assets Urls
|
||||
this.urls = this.assets.
|
||||
map(function(item) {
|
||||
return item.href;
|
||||
}.bind(this));
|
||||
|
||||
replaced.push(replacement);
|
||||
}.bind(this));
|
||||
return Promise.all(replaced);
|
||||
};
|
||||
// Css Urls
|
||||
this.cssUrls = this.css.map(function(item) {
|
||||
return item.href;
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a new CSS file with the replaced URLs
|
||||
* @private
|
||||
* @param {string} href the original css file
|
||||
* @param {[Archive]} archive
|
||||
* @param {[method]} resolver
|
||||
* @return {Promise} returns a BlobUrl to the new CSS file or a data url
|
||||
*/
|
||||
Resources.prototype.createCssFile = function(href, archive, resolver){
|
||||
var newUrl;
|
||||
var indexInUrls;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create blob urls for all the assets
|
||||
* @param {Archive} archive
|
||||
* @param {resolver} resolver Url resolver
|
||||
* @return {Promise} returns replacement urls
|
||||
*/
|
||||
replacements(archive, resolver){
|
||||
archive = archive || this.settings.archive;
|
||||
resolver = resolver || this.settings.resolver;
|
||||
|
||||
if (path.isAbsolute(href)) {
|
||||
return new Promise(function(resolve, reject){
|
||||
resolve(urls, replacementUrls);
|
||||
});
|
||||
if (this.settings.replacements === "none") {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(this.urls);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
var absolute = resolver(href);
|
||||
var replacements = this.urls.
|
||||
map(function(url) {
|
||||
var absolute = resolver(url);
|
||||
|
||||
// Get the text of the css file from the archive
|
||||
var textResponse = archive.getText(absolute);
|
||||
// Get asset links relative to css file
|
||||
var relUrls = this.urls.map(function(assetHref) {
|
||||
var resolved = resolver(assetHref);
|
||||
var relative = new Path(absolute).relative(resolved);
|
||||
return archive.createUrl(absolute, {"base64": (this.settings.replacements === "base64")});
|
||||
}.bind(this))
|
||||
|
||||
return relative;
|
||||
return Promise.all(replacements)
|
||||
.then(function(replacementUrls) {
|
||||
this.replacementUrls = replacementUrls;
|
||||
return replacementUrls;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
return textResponse.then(function (text) {
|
||||
// Replacements in the css text
|
||||
text = replace.substitute(text, relUrls, this.replacementUrls);
|
||||
/**
|
||||
* Replace URLs in CSS resources
|
||||
* @private
|
||||
* @param {[Archive]} archive
|
||||
* @param {[method]} resolver
|
||||
* @return {Promise}
|
||||
*/
|
||||
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) {
|
||||
// switch the url in the replacementUrls
|
||||
var indexInUrls = this.urls.indexOf(href);
|
||||
if (indexInUrls > -1) {
|
||||
this.replacementUrls[indexInUrls] = replacementUrl;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
// Get the new url
|
||||
if (this.settings.replacements === "base64") {
|
||||
newUrl = core.createBase64Url(text, 'text/css');
|
||||
} else {
|
||||
newUrl = core.createBlobUrl(text, 'text/css');
|
||||
replaced.push(replacement);
|
||||
}.bind(this));
|
||||
return Promise.all(replaced);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new CSS file with the replaced URLs
|
||||
* @private
|
||||
* @param {string} href the original css file
|
||||
* @param {[Archive]} archive
|
||||
* @param {[method]} resolver
|
||||
* @return {Promise} returns a BlobUrl to the new CSS file or a data url
|
||||
*/
|
||||
createCssFile(href, archive, resolver){
|
||||
var newUrl;
|
||||
var indexInUrls;
|
||||
archive = archive || this.settings.archive;
|
||||
resolver = resolver || this.settings.resolver;
|
||||
|
||||
if (path.isAbsolute(href)) {
|
||||
return new Promise(function(resolve, reject){
|
||||
resolve(urls, replacementUrls);
|
||||
});
|
||||
}
|
||||
|
||||
return newUrl;
|
||||
}.bind(this));
|
||||
var absolute = resolver(href);
|
||||
|
||||
};
|
||||
// Get the text of the css file from the archive
|
||||
var textResponse = archive.getText(absolute);
|
||||
// Get asset links relative to css file
|
||||
var relUrls = this.urls.map(function(assetHref) {
|
||||
var resolved = resolver(assetHref);
|
||||
var relative = new Path(absolute).relative(resolved);
|
||||
|
||||
/**
|
||||
* Resolve all resources URLs relative to an absolute URL
|
||||
* @param {string} absolute to be resolved to
|
||||
* @param {[resolver]} resolver
|
||||
* @return {string[]} array with relative Urls
|
||||
*/
|
||||
Resources.prototype.relativeTo = function(absolute, resolver){
|
||||
resolver = resolver || this.settings.resolver;
|
||||
return relative;
|
||||
}.bind(this));
|
||||
|
||||
// Get Urls relative to current sections
|
||||
return this.urls.
|
||||
map(function(href) {
|
||||
var resolved = resolver(href);
|
||||
var relative = new Path(absolute).relative(resolved);
|
||||
return relative;
|
||||
}.bind(this));
|
||||
};
|
||||
return textResponse.then(function (text) {
|
||||
// Replacements in the css text
|
||||
text = substitute(text, relUrls, this.replacementUrls);
|
||||
|
||||
/**
|
||||
* Get a URL for a resource
|
||||
* @param {string} path
|
||||
* @return {string} url
|
||||
*/
|
||||
Resources.prototype.get = function(path) {
|
||||
var indexInUrls = this.urls.indexOf(path);
|
||||
if (indexInUrls === -1) {
|
||||
return;
|
||||
}
|
||||
if (this.replacementUrls.length) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(this.replacementUrls[indexInUrls]);
|
||||
}.bind(this));
|
||||
} else {
|
||||
return archive.createUrl(absolute,
|
||||
{"base64": (this.settings.replacements === "base64")})
|
||||
// Get the new url
|
||||
if (this.settings.replacements === "base64") {
|
||||
newUrl = createBase64Url(text, 'text/css');
|
||||
} else {
|
||||
newUrl = createBlobUrl(text, 'text/css');
|
||||
}
|
||||
|
||||
return newUrl;
|
||||
}.bind(this));
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve all resources URLs relative to an absolute URL
|
||||
* @param {string} absolute to be resolved to
|
||||
* @param {[resolver]} resolver
|
||||
* @return {string[]} array with relative Urls
|
||||
*/
|
||||
relativeTo(absolute, resolver){
|
||||
resolver = resolver || this.settings.resolver;
|
||||
|
||||
// Get Urls relative to current sections
|
||||
return this.urls.
|
||||
map(function(href) {
|
||||
var resolved = resolver(href);
|
||||
var relative = new Path(absolute).relative(resolved);
|
||||
return relative;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a URL for a resource
|
||||
* @param {string} path
|
||||
* @return {string} url
|
||||
*/
|
||||
get(path) {
|
||||
var indexInUrls = this.urls.indexOf(path);
|
||||
if (indexInUrls === -1) {
|
||||
return;
|
||||
}
|
||||
if (this.replacementUrls.length) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolve(this.replacementUrls[indexInUrls]);
|
||||
}.bind(this));
|
||||
} else {
|
||||
return archive.createUrl(absolute,
|
||||
{"base64": (this.settings.replacements === "base64")})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute urls in content, with replacements,
|
||||
* relative to a url if provided
|
||||
* @param {string} content
|
||||
* @param {[string]} url url to resolve to
|
||||
* @return {string} content with urls substituted
|
||||
*/
|
||||
substitute(content, url) {
|
||||
var relUrls;
|
||||
if (url) {
|
||||
relUrls = this.relativeTo(url);
|
||||
} else {
|
||||
relUrls = this.urls;
|
||||
}
|
||||
return substitute(content, relUrls, this.replacementUrls);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute urls in content, with replacements,
|
||||
* relative to a url if provided
|
||||
* @param {string} content
|
||||
* @param {[string]} url url to resolve to
|
||||
* @return {string} content with urls substituted
|
||||
*/
|
||||
Resources.prototype.substitute = function(content, url) {
|
||||
var relUrls;
|
||||
if (url) {
|
||||
relUrls = this.relativeTo(url);
|
||||
} else {
|
||||
relUrls = this.urls;
|
||||
}
|
||||
return replace.substitute(content, relUrls, this.replacementUrls);
|
||||
};
|
||||
|
||||
|
||||
module.exports = Resources;
|
||||
export default Resources;
|
||||
|
|
336
src/section.js
336
src/section.js
|
@ -1,7 +1,7 @@
|
|||
var core = require('./core');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
var Hook = require('./hook');
|
||||
var Url = require('./core').Url;
|
||||
import { defer } from './utils/core';
|
||||
import EpubCFI from './epubcfi';
|
||||
import Hook from './hook';
|
||||
import Url from './utils/url';
|
||||
|
||||
/**
|
||||
* Represents a Section of the Book
|
||||
|
@ -9,178 +9,180 @@ var Url = require('./core').Url;
|
|||
* @param {object} item The spine item representing the section
|
||||
* @param {object} hooks hooks for serialize and content
|
||||
*/
|
||||
function Section(item, hooks){
|
||||
this.idref = item.idref;
|
||||
this.linear = item.linear;
|
||||
this.properties = item.properties;
|
||||
this.index = item.index;
|
||||
this.href = item.href;
|
||||
this.url = item.url;
|
||||
this.next = item.next;
|
||||
this.prev = item.prev;
|
||||
class Section {
|
||||
constructor(item, hooks){
|
||||
this.idref = item.idref;
|
||||
this.linear = item.linear;
|
||||
this.properties = item.properties;
|
||||
this.index = item.index;
|
||||
this.href = item.href;
|
||||
this.url = item.url;
|
||||
this.next = item.next;
|
||||
this.prev = item.prev;
|
||||
|
||||
this.cfiBase = item.cfiBase;
|
||||
this.cfiBase = item.cfiBase;
|
||||
|
||||
if (hooks) {
|
||||
this.hooks = hooks;
|
||||
} else {
|
||||
this.hooks = {};
|
||||
this.hooks.serialize = new Hook(this);
|
||||
this.hooks.content = new Hook(this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the section from its url
|
||||
* @param {method} _request a request method to use for loading
|
||||
* @return {document} a promise with the xml document
|
||||
*/
|
||||
Section.prototype.load = function(_request){
|
||||
var request = _request || this.request || require('./request');
|
||||
var loading = new core.defer();
|
||||
var loaded = loading.promise;
|
||||
|
||||
if(this.contents) {
|
||||
loading.resolve(this.contents);
|
||||
} else {
|
||||
request(this.url)
|
||||
.then(function(xml){
|
||||
var base;
|
||||
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(){
|
||||
loading.resolve(this.contents);
|
||||
}.bind(this))
|
||||
.catch(function(error){
|
||||
loading.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
return loaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a base tag for resolving urls in the section
|
||||
* @private
|
||||
* @param {document} _document
|
||||
*/
|
||||
Section.prototype.base = function(_document){
|
||||
var task = new core.defer();
|
||||
var base = _document.createElement("base"); // TODO: check if exists
|
||||
var head;
|
||||
|
||||
base.setAttribute("href", window.location.origin + "/" +this.url);
|
||||
|
||||
if(_document) {
|
||||
head = _document.querySelector("head");
|
||||
}
|
||||
if(head) {
|
||||
head.insertBefore(base, head.firstChild);
|
||||
task.resolve();
|
||||
} else {
|
||||
task.reject(new Error("No head to insert into"));
|
||||
}
|
||||
|
||||
|
||||
return task.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the contents of a section
|
||||
* @param {method} _request a request method to use for loading
|
||||
* @return {string} output a serialized XML Document
|
||||
*/
|
||||
Section.prototype.render = function(_request){
|
||||
var rendering = new core.defer();
|
||||
var rendered = rendering.promise;
|
||||
this.output; // TODO: better way to return this from hooks?
|
||||
|
||||
this.load(_request).
|
||||
then(function(contents){
|
||||
var serializer;
|
||||
|
||||
if (typeof XMLSerializer === "undefined") {
|
||||
XMLSerializer = require('xmldom').XMLSerializer;
|
||||
if (hooks) {
|
||||
this.hooks = hooks;
|
||||
} else {
|
||||
this.hooks = {};
|
||||
this.hooks.serialize = new Hook(this);
|
||||
this.hooks.content = new Hook(this);
|
||||
}
|
||||
serializer = new XMLSerializer();
|
||||
this.output = serializer.serializeToString(contents);
|
||||
return this.output;
|
||||
}.bind(this)).
|
||||
then(function(){
|
||||
return this.hooks.serialize.trigger(this.output, this);
|
||||
}.bind(this)).
|
||||
then(function(){
|
||||
rendering.resolve(this.output);
|
||||
}.bind(this))
|
||||
.catch(function(error){
|
||||
rendering.reject(error);
|
||||
});
|
||||
|
||||
return rendered;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a string in a section
|
||||
* TODO: need reimplementation from v0.2
|
||||
* @param {string} query [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
Section.prototype.find = function(query){
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Reconciles the current chapters layout properies with
|
||||
* the global layout properities.
|
||||
* @param {object} global The globa layout settings object, chapter properties string
|
||||
* @return {object} layoutProperties Object with layout properties
|
||||
*/
|
||||
Section.prototype.reconcileLayoutSettings = function(global){
|
||||
//-- Get the global defaults
|
||||
var settings = {
|
||||
layout : global.layout,
|
||||
spread : global.spread,
|
||||
orientation : global.orientation
|
||||
};
|
||||
|
||||
//-- Get the chapter's display type
|
||||
this.properties.forEach(function(prop){
|
||||
var rendition = prop.replace("rendition:", '');
|
||||
var split = rendition.indexOf("-");
|
||||
var property, value;
|
||||
/**
|
||||
* Load the section from its url
|
||||
* @param {method} _request a request method to use for loading
|
||||
* @return {document} a promise with the xml document
|
||||
*/
|
||||
load(_request){
|
||||
var request = _request || this.request || require('./request');
|
||||
var loading = new defer();
|
||||
var loaded = loading.promise;
|
||||
|
||||
if(split != -1){
|
||||
property = rendition.slice(0, split);
|
||||
value = rendition.slice(split+1);
|
||||
if(this.contents) {
|
||||
loading.resolve(this.contents);
|
||||
} else {
|
||||
request(this.url)
|
||||
.then(function(xml){
|
||||
var base;
|
||||
var directory = new Url(this.url).directory;
|
||||
|
||||
settings[property] = value;
|
||||
this.document = xml;
|
||||
this.contents = xml.documentElement;
|
||||
|
||||
return this.hooks.content.trigger(this.document, this);
|
||||
}.bind(this))
|
||||
.then(function(){
|
||||
loading.resolve(this.contents);
|
||||
}.bind(this))
|
||||
.catch(function(error){
|
||||
loading.reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
return settings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a CFI from a Range in the Section
|
||||
* @param {range} _range
|
||||
* @return {string} cfi an EpubCFI string
|
||||
*/
|
||||
Section.prototype.cfiFromRange = function(_range) {
|
||||
return new EpubCFI(_range, this.cfiBase).toString();
|
||||
};
|
||||
return loaded;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a CFI from an Element in the Section
|
||||
* @param {element} el
|
||||
* @return {string} cfi an EpubCFI string
|
||||
*/
|
||||
Section.prototype.cfiFromElement = function(el) {
|
||||
return new EpubCFI(el, this.cfiBase).toString();
|
||||
};
|
||||
/**
|
||||
* Adds a base tag for resolving urls in the section
|
||||
* @private
|
||||
* @param {document} _document
|
||||
*/
|
||||
base(_document){
|
||||
var task = new defer();
|
||||
var base = _document.createElement("base"); // TODO: check if exists
|
||||
var head;
|
||||
|
||||
module.exports = Section;
|
||||
base.setAttribute("href", window.location.origin + "/" +this.url);
|
||||
|
||||
if(_document) {
|
||||
head = _document.querySelector("head");
|
||||
}
|
||||
if(head) {
|
||||
head.insertBefore(base, head.firstChild);
|
||||
task.resolve();
|
||||
} else {
|
||||
task.reject(new Error("No head to insert into"));
|
||||
}
|
||||
|
||||
|
||||
return task.promise;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the contents of a section
|
||||
* @param {method} _request a request method to use for loading
|
||||
* @return {string} output a serialized XML Document
|
||||
*/
|
||||
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 serializer;
|
||||
|
||||
if (typeof XMLSerializer === "undefined") {
|
||||
XMLSerializer = require('xmldom').XMLSerializer;
|
||||
}
|
||||
serializer = new XMLSerializer();
|
||||
this.output = serializer.serializeToString(contents);
|
||||
return this.output;
|
||||
}.bind(this)).
|
||||
then(function(){
|
||||
return this.hooks.serialize.trigger(this.output, this);
|
||||
}.bind(this)).
|
||||
then(function(){
|
||||
rendering.resolve(this.output);
|
||||
}.bind(this))
|
||||
.catch(function(error){
|
||||
rendering.reject(error);
|
||||
});
|
||||
|
||||
return rendered;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find a string in a section
|
||||
* TODO: need reimplementation from v0.2
|
||||
* @param {string} query [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
find(query){
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Reconciles the current chapters layout properies with
|
||||
* the global layout properities.
|
||||
* @param {object} global The globa layout settings object, chapter properties string
|
||||
* @return {object} layoutProperties Object with layout properties
|
||||
*/
|
||||
reconcileLayoutSettings(global){
|
||||
//-- Get the global defaults
|
||||
var settings = {
|
||||
layout : global.layout,
|
||||
spread : global.spread,
|
||||
orientation : global.orientation
|
||||
};
|
||||
|
||||
//-- Get the chapter's display type
|
||||
this.properties.forEach(function(prop){
|
||||
var rendition = prop.replace("rendition:", '');
|
||||
var split = rendition.indexOf("-");
|
||||
var property, value;
|
||||
|
||||
if(split != -1){
|
||||
property = rendition.slice(0, split);
|
||||
value = rendition.slice(split+1);
|
||||
|
||||
settings[property] = value;
|
||||
}
|
||||
});
|
||||
return settings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a CFI from a Range in the Section
|
||||
* @param {range} _range
|
||||
* @return {string} cfi an EpubCFI string
|
||||
*/
|
||||
cfiFromRange(_range) {
|
||||
return new EpubCFI(_range, this.cfiBase).toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a CFI from an Element in the Section
|
||||
* @param {element} el
|
||||
* @return {string} cfi an EpubCFI string
|
||||
*/
|
||||
cfiFromElement(el) {
|
||||
return new EpubCFI(el, this.cfiBase).toString();
|
||||
};
|
||||
}
|
||||
|
||||
export default Section;
|
||||
|
|
264
src/spine.js
264
src/spine.js
|
@ -1,161 +1,163 @@
|
|||
var core = require('./core');
|
||||
var EpubCFI = require('./epubcfi');
|
||||
var Hook = require('./hook');
|
||||
var Section = require('./section');
|
||||
var replacements = require('./replacements');
|
||||
import core from './utils/core';
|
||||
import EpubCFI from './epubcfi';
|
||||
import Hook from './hook';
|
||||
import Section from './section';
|
||||
import {replaceBase, replaceCanonical} from './replacements';
|
||||
|
||||
/**
|
||||
* A collection of Spine Items
|
||||
*/
|
||||
function Spine(){
|
||||
this.spineItems = [];
|
||||
this.spineByHref = {};
|
||||
this.spineById = {};
|
||||
class Spine {
|
||||
constructor() {
|
||||
this.spineItems = [];
|
||||
this.spineByHref = {};
|
||||
this.spineById = {};
|
||||
|
||||
this.hooks = {};
|
||||
this.hooks.serialize = new Hook();
|
||||
this.hooks.content = new Hook();
|
||||
this.hooks = {};
|
||||
this.hooks.serialize = new Hook();
|
||||
this.hooks.content = new Hook();
|
||||
|
||||
// Register replacements
|
||||
this.hooks.content.register(replacements.base);
|
||||
this.hooks.content.register(replacements.canonical);
|
||||
// Register replacements
|
||||
this.hooks.content.register(replaceBase);
|
||||
this.hooks.content.register(replaceCanonical);
|
||||
|
||||
this.epubcfi = new EpubCFI();
|
||||
this.epubcfi = new EpubCFI();
|
||||
|
||||
this.loaded = false;
|
||||
};
|
||||
this.loaded = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unpack items from a opf into spine items
|
||||
* @param {Package} _package
|
||||
* @param {method} resolver URL resolver
|
||||
*/
|
||||
Spine.prototype.unpack = function(_package, resolver) {
|
||||
/**
|
||||
* Unpack items from a opf into spine items
|
||||
* @param {Package} _package
|
||||
* @param {method} resolver URL resolver
|
||||
*/
|
||||
unpack(_package, resolver) {
|
||||
|
||||
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 = _package.spine;
|
||||
this.manifest = _package.manifest;
|
||||
this.spineNodeIndex = _package.spineNodeIndex;
|
||||
this.baseUrl = _package.baseUrl || _package.basePath || '';
|
||||
this.length = this.items.length;
|
||||
|
||||
this.items.forEach(function(item, index){
|
||||
var href, url;
|
||||
var manifestItem = this.manifest[item.idref];
|
||||
var spineItem;
|
||||
this.items.forEach(function(item, index){
|
||||
var href, url;
|
||||
var manifestItem = this.manifest[item.idref];
|
||||
var spineItem;
|
||||
|
||||
item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.idref);
|
||||
item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.idref);
|
||||
|
||||
if(manifestItem) {
|
||||
item.href = manifestItem.href;
|
||||
item.url = resolver(item.href, true);
|
||||
if(manifestItem) {
|
||||
item.href = manifestItem.href;
|
||||
item.url = resolver(item.href, true);
|
||||
|
||||
if(manifestItem.properties.length){
|
||||
item.properties.push.apply(item.properties, manifestItem.properties);
|
||||
if(manifestItem.properties.length){
|
||||
item.properties.push.apply(item.properties, manifestItem.properties);
|
||||
}
|
||||
}
|
||||
|
||||
item.prev = function(){ return this.get(index-1); }.bind(this);
|
||||
item.next = function(){ return this.get(index+1); }.bind(this);
|
||||
|
||||
spineItem = new Section(item, this.hooks);
|
||||
|
||||
this.append(spineItem);
|
||||
|
||||
|
||||
}.bind(this));
|
||||
|
||||
this.loaded = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an item from the spine
|
||||
* @param {[string|int]} target
|
||||
* @return {Section} section
|
||||
* @example spine.get();
|
||||
* @example spine.get(1);
|
||||
* @example spine.get("chap1.html");
|
||||
* @example spine.get("#id1234");
|
||||
*/
|
||||
get(target) {
|
||||
var index = 0;
|
||||
|
||||
if(this.epubcfi.isCfiString(target)) {
|
||||
cfi = new EpubCFI(target);
|
||||
index = cfi.spinePos;
|
||||
} else if(target && (typeof target === "number" || isNaN(target) === false)){
|
||||
index = target;
|
||||
} else if(target && target.indexOf("#") === 0) {
|
||||
index = this.spineById[target.substring(1)];
|
||||
} else if(target) {
|
||||
// Remove fragments
|
||||
target = target.split("#")[0];
|
||||
index = this.spineByHref[target];
|
||||
}
|
||||
|
||||
item.prev = function(){ return this.get(index-1); }.bind(this);
|
||||
item.next = function(){ return this.get(index+1); }.bind(this);
|
||||
return this.spineItems[index] || null;
|
||||
};
|
||||
|
||||
spineItem = new Section(item, this.hooks);
|
||||
/**
|
||||
* Append a Section to the Spine
|
||||
* @private
|
||||
* @param {Section} section
|
||||
*/
|
||||
append(section) {
|
||||
var index = this.spineItems.length;
|
||||
section.index = index;
|
||||
|
||||
this.append(spineItem);
|
||||
this.spineItems.push(section);
|
||||
|
||||
this.spineByHref[section.href] = index;
|
||||
this.spineById[section.idref] = index;
|
||||
|
||||
}.bind(this));
|
||||
return index;
|
||||
};
|
||||
|
||||
this.loaded = true;
|
||||
};
|
||||
/**
|
||||
* Prepend a Section to the Spine
|
||||
* @private
|
||||
* @param {Section} section
|
||||
*/
|
||||
prepend(section) {
|
||||
var index = this.spineItems.unshift(section);
|
||||
this.spineByHref[section.href] = 0;
|
||||
this.spineById[section.idref] = 0;
|
||||
|
||||
/**
|
||||
* Get an item from the spine
|
||||
* @param {[string|int]} target
|
||||
* @return {Section} section
|
||||
* @example spine.get();
|
||||
* @example spine.get(1);
|
||||
* @example spine.get("chap1.html");
|
||||
* @example spine.get("#id1234");
|
||||
*/
|
||||
Spine.prototype.get = function(target) {
|
||||
var index = 0;
|
||||
// Re-index
|
||||
this.spineItems.forEach(function(item, index){
|
||||
item.index = index;
|
||||
});
|
||||
|
||||
if(this.epubcfi.isCfiString(target)) {
|
||||
cfi = new EpubCFI(target);
|
||||
index = cfi.spinePos;
|
||||
} else if(target && (typeof target === "number" || isNaN(target) === false)){
|
||||
index = target;
|
||||
} else if(target && target.indexOf("#") === 0) {
|
||||
index = this.spineById[target.substring(1)];
|
||||
} else if(target) {
|
||||
// Remove fragments
|
||||
target = target.split("#")[0];
|
||||
index = this.spineByHref[target];
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
return this.spineItems[index] || null;
|
||||
};
|
||||
// insert(section, index) {
|
||||
//
|
||||
// };
|
||||
|
||||
/**
|
||||
* Append a Section to the Spine
|
||||
* @private
|
||||
* @param {Section} section
|
||||
*/
|
||||
Spine.prototype.append = function(section) {
|
||||
var index = this.spineItems.length;
|
||||
section.index = index;
|
||||
/**
|
||||
* Remove a Section from the Spine
|
||||
* @private
|
||||
* @param {Section} section
|
||||
*/
|
||||
remove(section) {
|
||||
var index = this.spineItems.indexOf(section);
|
||||
|
||||
this.spineItems.push(section);
|
||||
if(index > -1) {
|
||||
delete this.spineByHref[section.href];
|
||||
delete this.spineById[section.idref];
|
||||
|
||||
this.spineByHref[section.href] = index;
|
||||
this.spineById[section.idref] = index;
|
||||
return this.spineItems.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
return index;
|
||||
};
|
||||
/**
|
||||
* Loop over the Sections in the Spine
|
||||
* @return {method} forEach
|
||||
*/
|
||||
each() {
|
||||
return this.spineItems.forEach.apply(this.spineItems, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend a Section to the Spine
|
||||
* @private
|
||||
* @param {Section} section
|
||||
*/
|
||||
Spine.prototype.prepend = function(section) {
|
||||
var index = this.spineItems.unshift(section);
|
||||
this.spineByHref[section.href] = 0;
|
||||
this.spineById[section.idref] = 0;
|
||||
|
||||
// Re-index
|
||||
this.spineItems.forEach(function(item, index){
|
||||
item.index = index;
|
||||
});
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Spine.prototype.insert = function(section, index) {
|
||||
//
|
||||
// };
|
||||
|
||||
/**
|
||||
* Remove a Section from the Spine
|
||||
* @private
|
||||
* @param {Section} section
|
||||
*/
|
||||
Spine.prototype.remove = function(section) {
|
||||
var index = this.spineItems.indexOf(section);
|
||||
|
||||
if(index > -1) {
|
||||
delete this.spineByHref[section.href];
|
||||
delete this.spineById[section.idref];
|
||||
|
||||
return this.spineItems.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loop over the Sections in the Spine
|
||||
* @return {method} forEach
|
||||
*/
|
||||
Spine.prototype.each = function() {
|
||||
return this.spineItems.forEach.apply(this.spineItems, arguments);
|
||||
};
|
||||
|
||||
module.exports = Spine;
|
||||
export default Spine;
|
||||
|
|
301
src/themes.js
301
src/themes.js
|
@ -1,155 +1,158 @@
|
|||
var Url = require('./core').Url;
|
||||
import Url from './utils/url';
|
||||
|
||||
function Themes(rendition) {
|
||||
this.rendition = rendition;
|
||||
this._themes = {
|
||||
"default" : {
|
||||
"rules" : [],
|
||||
"url" : "",
|
||||
"serialized" : ''
|
||||
class Themes {
|
||||
constructor(rendition) {
|
||||
this.rendition = rendition;
|
||||
this._themes = {
|
||||
"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));
|
||||
|
||||
}
|
||||
|
||||
register () {
|
||||
if (arguments.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (arguments.length === 1 && typeof(arguments[0]) === "object") {
|
||||
return this.registerThemes(arguments[0]);
|
||||
}
|
||||
if (arguments.length === 1 && typeof(arguments[0]) === "string") {
|
||||
return this.default(arguments[0]);
|
||||
}
|
||||
if (arguments.length === 2 && typeof(arguments[1]) === "string") {
|
||||
return this.registerUrl(arguments[0], arguments[1]);
|
||||
}
|
||||
if (arguments.length === 2 && typeof(arguments[1]) === "object") {
|
||||
return this.registerRules(arguments[0], arguments[1]);
|
||||
}
|
||||
};
|
||||
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));
|
||||
|
||||
default (theme) {
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
if (typeof(theme) === "string") {
|
||||
return this.registerUrl("default", theme);
|
||||
}
|
||||
if (typeof(theme) === "object") {
|
||||
return this.registerRules("default", theme);
|
||||
}
|
||||
};
|
||||
|
||||
registerThemes (themes) {
|
||||
for (var theme in themes) {
|
||||
if (themes.hasOwnProperty(theme)) {
|
||||
if (typeof(themes[theme]) === "string") {
|
||||
this.registerUrl(theme, themes[theme]);
|
||||
} else {
|
||||
this.registerRules(theme, themes[theme]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
registerUrl (name, input) {
|
||||
var url = new Url(input);
|
||||
this._themes[name] = { "url": url.toString() };
|
||||
};
|
||||
|
||||
registerRules (name, rules) {
|
||||
this._themes[name] = { "rules": rules };
|
||||
// TODO: serialize css rules
|
||||
};
|
||||
|
||||
apply (name) {
|
||||
var prev = this._current;
|
||||
var contents;
|
||||
|
||||
this._current = name;
|
||||
this.update(name);
|
||||
|
||||
contents = this.rendition.getContents();
|
||||
contents.forEach(function (content) {
|
||||
content.removeClass(prev);
|
||||
content.addClass(name);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
update (name) {
|
||||
var contents = this.rendition.getContents();
|
||||
contents.forEach(function (content) {
|
||||
this.add(name, content);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
inject (view) {
|
||||
var links = [];
|
||||
var themes = this._themes;
|
||||
var theme;
|
||||
|
||||
for (var name in themes) {
|
||||
if (themes.hasOwnProperty(name)) {
|
||||
theme = themes[name];
|
||||
if(theme.rules || (theme.url && links.indexOf(theme.url) === -1)) {
|
||||
this.add(name, view.contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this._current) {
|
||||
view.contents.addClass(this._current);
|
||||
}
|
||||
};
|
||||
|
||||
add (name, contents) {
|
||||
var theme = this._themes[name];
|
||||
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (theme.url) {
|
||||
contents.addStylesheet(theme.url);
|
||||
} else if (theme.serialized) {
|
||||
// TODO: handle serialized
|
||||
} else if (theme.rules && theme.rules.length) {
|
||||
contents.addStylesheetRules(theme.rules);
|
||||
theme.injected = true;
|
||||
}
|
||||
};
|
||||
|
||||
override (name, value) {
|
||||
var contents = this.rendition.getContents();
|
||||
|
||||
this._overrides[name] = value;
|
||||
|
||||
contents.forEach(function (content) {
|
||||
content.css(name, this._overrides[name]);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
overrides (view) {
|
||||
var contents = view.contents;
|
||||
var overrides = this._overrides;
|
||||
var rules = [];
|
||||
|
||||
for (var rule in overrides) {
|
||||
if (overrides.hasOwnProperty(rule)) {
|
||||
contents.css(rule, overrides[rule]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fontSize (size) {
|
||||
this.override("font-size", size);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Themes.prototype.register = function () {
|
||||
if (arguments.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (arguments.length === 1 && typeof(arguments[0]) === "object") {
|
||||
return this.registerThemes(arguments[0]);
|
||||
}
|
||||
if (arguments.length === 1 && typeof(arguments[0]) === "string") {
|
||||
return this.default(arguments[0]);
|
||||
}
|
||||
if (arguments.length === 2 && typeof(arguments[1]) === "string") {
|
||||
return this.registerUrl(arguments[0], arguments[1]);
|
||||
}
|
||||
if (arguments.length === 2 && typeof(arguments[1]) === "object") {
|
||||
return this.registerRules(arguments[0], arguments[1]);
|
||||
}
|
||||
};
|
||||
|
||||
Themes.prototype.default = function (theme) {
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
if (typeof(theme) === "string") {
|
||||
return this.registerUrl("default", theme);
|
||||
}
|
||||
if (typeof(theme) === "object") {
|
||||
return this.registerRules("default", theme);
|
||||
}
|
||||
};
|
||||
|
||||
Themes.prototype.registerThemes = function (themes) {
|
||||
for (var theme in themes) {
|
||||
if (themes.hasOwnProperty(theme)) {
|
||||
if (typeof(themes[theme]) === "string") {
|
||||
this.registerUrl(theme, themes[theme]);
|
||||
} else {
|
||||
this.registerRules(theme, themes[theme]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Themes.prototype.registerUrl = function (name, input) {
|
||||
var url = new Url(input);
|
||||
this._themes[name] = { "url": url.toString() };
|
||||
};
|
||||
|
||||
Themes.prototype.registerRules = function (name, rules) {
|
||||
this._themes[name] = { "rules": rules };
|
||||
// TODO: serialize css rules
|
||||
};
|
||||
|
||||
Themes.prototype.apply = function (name) {
|
||||
var prev = this._current;
|
||||
var contents;
|
||||
|
||||
this._current = name;
|
||||
this.update(name);
|
||||
|
||||
contents = this.rendition.getContents();
|
||||
contents.forEach(function (content) {
|
||||
content.removeClass(prev);
|
||||
content.addClass(name);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Themes.prototype.update = function (name) {
|
||||
var contents = this.rendition.getContents();
|
||||
contents.forEach(function (content) {
|
||||
this.add(name, content);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Themes.prototype.inject = function (view) {
|
||||
var links = [];
|
||||
var themes = this._themes;
|
||||
var theme;
|
||||
|
||||
for (var name in themes) {
|
||||
if (themes.hasOwnProperty(name)) {
|
||||
theme = themes[name];
|
||||
if(theme.rules || (theme.url && links.indexOf(theme.url) === -1)) {
|
||||
this.add(name, view.contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this._current) {
|
||||
view.contents.addClass(this._current);
|
||||
}
|
||||
};
|
||||
|
||||
Themes.prototype.add = function (name, contents) {
|
||||
var theme = this._themes[name];
|
||||
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (theme.url) {
|
||||
contents.addStylesheet(theme.url);
|
||||
} else if (theme.serialized) {
|
||||
// TODO: handle serialized
|
||||
} else if (theme.rules && theme.rules.length) {
|
||||
contents.addStylesheetRules(theme.rules);
|
||||
theme.injected = true;
|
||||
}
|
||||
};
|
||||
|
||||
Themes.prototype.override = function (name, value) {
|
||||
var contents = this.rendition.getContents();
|
||||
|
||||
this._overrides[name] = value;
|
||||
|
||||
contents.forEach(function (content) {
|
||||
content.css(name, this._overrides[name]);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Themes.prototype.overrides = function (view) {
|
||||
var contents = view.contents;
|
||||
var overrides = this._overrides;
|
||||
var rules = [];
|
||||
|
||||
for (var rule in overrides) {
|
||||
if (overrides.hasOwnProperty(rule)) {
|
||||
contents.css(rule, overrides[rule]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Themes.prototype.fontSize = function (size) {
|
||||
this.override("font-size", size);
|
||||
};
|
||||
|
||||
module.exports = Themes;
|
||||
export default Themes;
|
||||
|
|
|
@ -1,144 +1,11 @@
|
|||
var base64 = require('base64-js');
|
||||
var path = require('path');
|
||||
export const requestAnimationFrame = (typeof window != 'undefined') ? (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame) : false;
|
||||
|
||||
var requestAnimationFrame = (typeof window != 'undefined') ? (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame) : false;
|
||||
|
||||
/**
|
||||
* creates a uri object
|
||||
* @param {string} urlString a url string (relative or absolute)
|
||||
* @param {[string]} baseString optional base for the url,
|
||||
* default to window.location.href
|
||||
* @return {object} url
|
||||
*/
|
||||
function Url(urlString, baseString) {
|
||||
var absolute = (urlString.indexOf('://') > -1);
|
||||
var pathname = urlString;
|
||||
|
||||
this.Url = undefined;
|
||||
this.href = urlString;
|
||||
this.protocol = "";
|
||||
this.origin = "";
|
||||
this.fragment = "";
|
||||
this.search = "";
|
||||
this.base = baseString;
|
||||
|
||||
if (!absolute && (typeof(baseString) !== "string")) {
|
||||
this.base = window && window.location.href;
|
||||
}
|
||||
|
||||
// URL Polyfill doesn't throw an error if base is empty
|
||||
if (absolute || this.base) {
|
||||
try {
|
||||
this.Url = new URL(urlString, this.base);
|
||||
this.href = this.Url.href;
|
||||
|
||||
this.protocol = this.Url.protocol;
|
||||
this.origin = this.Url.origin;
|
||||
this.fragment = this.Url.fragment;
|
||||
this.search = this.Url.search;
|
||||
|
||||
pathname = this.Url.pathname;
|
||||
} catch (e) {
|
||||
// Skip URL parsing
|
||||
this.Url = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
this.Path = new Path(pathname);
|
||||
this.directory = this.Path.directory;
|
||||
this.filename = this.Path.filename;
|
||||
this.extension = this.Path.extension;
|
||||
|
||||
}
|
||||
|
||||
Url.prototype.path = function () {
|
||||
return this.Path;
|
||||
};
|
||||
|
||||
Url.prototype.resolve = function (what) {
|
||||
var isAbsolute = (what.indexOf('://') > -1);
|
||||
var fullpath;
|
||||
|
||||
if (isAbsolute) {
|
||||
return what;
|
||||
}
|
||||
|
||||
fullpath = path.resolve(this.directory, what);
|
||||
return this.origin + fullpath;
|
||||
};
|
||||
|
||||
Url.prototype.relative = function (what) {
|
||||
return path.relative(what, this.directory);
|
||||
};
|
||||
|
||||
Url.prototype.toString = function () {
|
||||
return this.href;
|
||||
};
|
||||
|
||||
function Path(pathString) {
|
||||
var protocol;
|
||||
var parsed;
|
||||
|
||||
protocol = pathString.indexOf('://');
|
||||
if (protocol > -1) {
|
||||
pathString = new URL(pathString).pathname;
|
||||
}
|
||||
|
||||
parsed = this.parse(pathString);
|
||||
|
||||
this.path = pathString;
|
||||
|
||||
if (this.isDirectory(pathString)) {
|
||||
this.directory = pathString;
|
||||
} else {
|
||||
this.directory = parsed.dir + "/";
|
||||
}
|
||||
|
||||
this.filename = parsed.base;
|
||||
this.extension = parsed.ext.slice(1);
|
||||
|
||||
}
|
||||
|
||||
Path.prototype.parse = function (what) {
|
||||
return path.parse(what);
|
||||
};
|
||||
|
||||
Path.prototype.isAbsolute = function (what) {
|
||||
return path.isAbsolute(what || this.path);
|
||||
};
|
||||
|
||||
Path.prototype.isDirectory = function (what) {
|
||||
return (what.charAt(what.length-1) === '/');
|
||||
};
|
||||
|
||||
Path.prototype.resolve = function (what) {
|
||||
return path.resolve(this.directory, what);
|
||||
};
|
||||
|
||||
Path.prototype.relative = function (what) {
|
||||
return path.relative(this.directory, what);
|
||||
};
|
||||
|
||||
Path.prototype.splitPath = function(filename) {
|
||||
return this.splitPathRe.exec(filename).slice(1);
|
||||
};
|
||||
|
||||
Path.prototype.toString = function () {
|
||||
return this.path;
|
||||
};
|
||||
|
||||
function assertPath(path) {
|
||||
if (typeof path !== 'string') {
|
||||
throw new TypeError('Path must be a string. Received ', path);
|
||||
}
|
||||
};
|
||||
|
||||
function isElement(obj) {
|
||||
export function isElement(obj) {
|
||||
return !!(obj && obj.nodeType == 1);
|
||||
};
|
||||
|
||||
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
||||
function uuid() {
|
||||
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;
|
||||
|
@ -149,7 +16,7 @@ function uuid() {
|
|||
};
|
||||
|
||||
// From Lodash
|
||||
function values(object) {
|
||||
export function values(object) {
|
||||
var index = -1,
|
||||
props = Object.keys(object),
|
||||
length = props.length,
|
||||
|
@ -161,70 +28,7 @@ function values(object) {
|
|||
return result;
|
||||
};
|
||||
|
||||
function resolveUrl(base, path) {
|
||||
var url = [],
|
||||
segments = [],
|
||||
baseUri = uri(base),
|
||||
pathUri = uri(path),
|
||||
baseDirectory = baseUri.directory,
|
||||
pathDirectory = pathUri.directory,
|
||||
directories = [],
|
||||
// folders = base.split("/"),
|
||||
paths;
|
||||
|
||||
// if(uri.host) {
|
||||
// return path;
|
||||
// }
|
||||
|
||||
if(baseDirectory[0] === "/") {
|
||||
baseDirectory = baseDirectory.substring(1);
|
||||
}
|
||||
|
||||
if(pathDirectory[pathDirectory.length-1] === "/") {
|
||||
baseDirectory = baseDirectory.substring(0, baseDirectory.length-1);
|
||||
}
|
||||
|
||||
if(pathDirectory[0] === "/") {
|
||||
pathDirectory = pathDirectory.substring(1);
|
||||
}
|
||||
|
||||
if(pathDirectory[pathDirectory.length-1] === "/") {
|
||||
pathDirectory = pathDirectory.substring(0, pathDirectory.length-1);
|
||||
}
|
||||
|
||||
if(baseDirectory) {
|
||||
directories = baseDirectory.split("/");
|
||||
}
|
||||
|
||||
paths = pathDirectory.split("/");
|
||||
|
||||
paths.reverse().forEach(function(part, index){
|
||||
if(part === ".."){
|
||||
directories.pop();
|
||||
} else if(part === directories[directories.length-1]) {
|
||||
directories.pop();
|
||||
segments.unshift(part);
|
||||
} else {
|
||||
segments.unshift(part);
|
||||
}
|
||||
});
|
||||
|
||||
url = [baseUri.origin];
|
||||
|
||||
if(directories.length) {
|
||||
url = url.concat(directories);
|
||||
}
|
||||
|
||||
if(segments) {
|
||||
url = url.concat(segments);
|
||||
}
|
||||
|
||||
url = url.concat(pathUri.filename);
|
||||
|
||||
return url.join("/");
|
||||
};
|
||||
|
||||
function documentHeight() {
|
||||
export function documentHeight() {
|
||||
return Math.max(
|
||||
document.documentElement.clientHeight,
|
||||
document.body.scrollHeight,
|
||||
|
@ -234,15 +38,15 @@ function documentHeight() {
|
|||
);
|
||||
};
|
||||
|
||||
function isNumber(n) {
|
||||
export function isNumber(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
};
|
||||
|
||||
function isFloat(n) {
|
||||
export function isFloat(n) {
|
||||
return isNumber(n) && (Math.floor(n) !== n);
|
||||
}
|
||||
|
||||
function prefixed(unprefixed) {
|
||||
export function prefixed(unprefixed) {
|
||||
var vendors = ["Webkit", "Moz", "O", "ms" ],
|
||||
prefixes = ['-Webkit-', '-moz-', '-o-', '-ms-'],
|
||||
upper = unprefixed[0].toUpperCase() + unprefixed.slice(1),
|
||||
|
@ -261,7 +65,7 @@ function prefixed(unprefixed) {
|
|||
return unprefixed;
|
||||
};
|
||||
|
||||
function defaults(obj) {
|
||||
export function defaults(obj) {
|
||||
for (var i = 1, length = arguments.length; i < length; i++) {
|
||||
var source = arguments[i];
|
||||
for (var prop in source) {
|
||||
|
@ -271,7 +75,7 @@ function defaults(obj) {
|
|||
return obj;
|
||||
};
|
||||
|
||||
function extend(target) {
|
||||
export function extend(target) {
|
||||
var sources = [].slice.call(arguments, 1);
|
||||
sources.forEach(function (source) {
|
||||
if(!source) return;
|
||||
|
@ -284,14 +88,14 @@ function extend(target) {
|
|||
|
||||
// Fast quicksort insert for sorted array -- based on:
|
||||
// http://stackoverflow.com/questions/1344500/efficient-way-to-insert-a-number-into-a-sorted-array-of-numbers
|
||||
function insert(item, array, compareFunction) {
|
||||
export function insert(item, array, compareFunction) {
|
||||
var location = locationOf(item, array, compareFunction);
|
||||
array.splice(location, 0, item);
|
||||
|
||||
return location;
|
||||
};
|
||||
// Returns where something would fit in
|
||||
function locationOf(item, array, compareFunction, _start, _end) {
|
||||
export function locationOf(item, array, compareFunction, _start, _end) {
|
||||
var start = _start || 0;
|
||||
var end = _end || array.length;
|
||||
var pivot = parseInt(start + (end - start) / 2);
|
||||
|
@ -322,7 +126,7 @@ function locationOf(item, array, compareFunction, _start, _end) {
|
|||
}
|
||||
};
|
||||
// Returns -1 of mpt found
|
||||
function indexOfSorted(item, array, compareFunction, _start, _end) {
|
||||
export function indexOfSorted(item, array, compareFunction, _start, _end) {
|
||||
var start = _start || 0;
|
||||
var end = _end || array.length;
|
||||
var pivot = parseInt(start + (end - start) / 2);
|
||||
|
@ -352,7 +156,7 @@ function indexOfSorted(item, array, compareFunction, _start, _end) {
|
|||
}
|
||||
};
|
||||
|
||||
function bounds(el) {
|
||||
export function bounds(el) {
|
||||
|
||||
var style = window.getComputedStyle(el);
|
||||
var widthProps = ["width", "paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"];
|
||||
|
@ -376,7 +180,7 @@ function bounds(el) {
|
|||
|
||||
};
|
||||
|
||||
function borders(el) {
|
||||
export function borders(el) {
|
||||
|
||||
var style = window.getComputedStyle(el);
|
||||
var widthProps = ["paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"];
|
||||
|
@ -400,7 +204,7 @@ function borders(el) {
|
|||
|
||||
};
|
||||
|
||||
function windowBounds() {
|
||||
export function windowBounds() {
|
||||
|
||||
var width = window.innerWidth;
|
||||
var height = window.innerHeight;
|
||||
|
@ -417,7 +221,7 @@ function windowBounds() {
|
|||
};
|
||||
|
||||
//https://stackoverflow.com/questions/13482352/xquery-looking-for-text-with-single-quote/13483496#13483496
|
||||
function cleanStringForXpath(str) {
|
||||
export function cleanStringForXpath(str) {
|
||||
var parts = str.match(/[^'"]+|['"]/g);
|
||||
parts = parts.map(function(part){
|
||||
if (part === "'") {
|
||||
|
@ -432,7 +236,7 @@ function cleanStringForXpath(str) {
|
|||
return "concat(\'\'," + parts.join(",") + ")";
|
||||
};
|
||||
|
||||
function indexOfTextNode(textNode){
|
||||
export function indexOfTextNode(textNode){
|
||||
var parent = textNode.parentNode;
|
||||
var children = parent.childNodes;
|
||||
var sib;
|
||||
|
@ -448,17 +252,17 @@ function indexOfTextNode(textNode){
|
|||
return index;
|
||||
};
|
||||
|
||||
function isXml(ext) {
|
||||
export function isXml(ext) {
|
||||
return ['xml', 'opf', 'ncx'].indexOf(ext) > -1;
|
||||
}
|
||||
|
||||
function createBlob(content, mime){
|
||||
export function createBlob(content, mime){
|
||||
var blob = new Blob([content], {type : mime });
|
||||
|
||||
return blob;
|
||||
};
|
||||
|
||||
function createBlobUrl(content, mime){
|
||||
export function createBlobUrl(content, mime){
|
||||
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||
var tempUrl;
|
||||
var blob = this.createBlob(content, mime);
|
||||
|
@ -468,7 +272,7 @@ function createBlobUrl(content, mime){
|
|||
return tempUrl;
|
||||
};
|
||||
|
||||
function createBase64Url(content, mime){
|
||||
export function createBase64Url(content, mime){
|
||||
var string;
|
||||
var data;
|
||||
var datauri;
|
||||
|
@ -485,11 +289,11 @@ function createBase64Url(content, mime){
|
|||
return datauri;
|
||||
};
|
||||
|
||||
function type(obj){
|
||||
export function type(obj){
|
||||
return Object.prototype.toString.call(obj).slice(8, -1);
|
||||
}
|
||||
|
||||
function parse(markup, mime, forceXMLDom) {
|
||||
export function parse(markup, mime, forceXMLDom) {
|
||||
var doc;
|
||||
|
||||
if (typeof DOMParser === "undefined" || forceXMLDom) {
|
||||
|
@ -502,7 +306,7 @@ function parse(markup, mime, forceXMLDom) {
|
|||
return doc;
|
||||
}
|
||||
|
||||
function qs(el, sel) {
|
||||
export function qs(el, sel) {
|
||||
var elements;
|
||||
if (!el) {
|
||||
throw new Error('No Element Provided');
|
||||
|
@ -518,7 +322,7 @@ function qs(el, sel) {
|
|||
}
|
||||
}
|
||||
|
||||
function qsa(el, sel) {
|
||||
export function qsa(el, sel) {
|
||||
|
||||
if (typeof el.querySelector != "undefined") {
|
||||
return el.querySelectorAll(sel);
|
||||
|
@ -527,7 +331,7 @@ function qsa(el, sel) {
|
|||
}
|
||||
}
|
||||
|
||||
function qsp(el, sel, props) {
|
||||
export function qsp(el, sel, props) {
|
||||
var q, filtered;
|
||||
if (typeof el.querySelector != "undefined") {
|
||||
sel += '[';
|
||||
|
@ -558,7 +362,7 @@ function qsp(el, sel, props) {
|
|||
* @param {element} root element to start with
|
||||
* @param {function} func function to run on each element
|
||||
*/
|
||||
function sprint(root, func) {
|
||||
export function sprint(root, func) {
|
||||
var doc = root.ownerDocument || root;
|
||||
if (typeof(doc.createTreeWalker) !== "undefined") {
|
||||
treeWalker(root, func, NodeFilter.SHOW_TEXT);
|
||||
|
@ -571,14 +375,15 @@ function sprint(root, func) {
|
|||
}
|
||||
}
|
||||
|
||||
function treeWalker(root, func, filter) {
|
||||
export function treeWalker(root, func, filter) {
|
||||
var treeWalker = document.createTreeWalker(root, filter, null, false);
|
||||
let node;
|
||||
while ((node = treeWalker.nextNode())) {
|
||||
func(node);
|
||||
}
|
||||
}
|
||||
|
||||
// function walk(root, func, onlyText) {
|
||||
// export function walk(root, func, onlyText) {
|
||||
// var node = root;
|
||||
//
|
||||
// if (node && !onlyText || node.nodeType === 3) { // Node.TEXT_NODE
|
||||
|
@ -597,7 +402,7 @@ function treeWalker(root, func, filter) {
|
|||
* @param callback return false for continue,true for break
|
||||
* @return boolean true: break visit;
|
||||
*/
|
||||
function walk(node,callback){
|
||||
export function walk(node,callback){
|
||||
if(callback(node)){
|
||||
return true;
|
||||
}
|
||||
|
@ -608,7 +413,7 @@ function walk(node,callback){
|
|||
}
|
||||
}
|
||||
|
||||
function blob2base64(blob, cb) {
|
||||
export function blob2base64(blob, cb) {
|
||||
var reader = new FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
reader.onloadend = function() {
|
||||
|
@ -617,7 +422,7 @@ function blob2base64(blob, cb) {
|
|||
}
|
||||
|
||||
// From: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred#backwards_forwards_compatible
|
||||
function defer() {
|
||||
export function defer() {
|
||||
/* A method to resolve the associated Promise with the value passed.
|
||||
* If the promise is already settled it does nothing.
|
||||
*
|
||||
|
@ -648,14 +453,14 @@ function defer() {
|
|||
Object.freeze(this);
|
||||
}
|
||||
|
||||
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) {
|
||||
query = this.qsa(html, element);
|
||||
query = qsa(html, element);
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
if(query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type) {
|
||||
return query[i];
|
||||
|
@ -666,53 +471,14 @@ function defer() {
|
|||
}
|
||||
}
|
||||
|
||||
function children(el) {
|
||||
var children = [];
|
||||
export function findChildren(el) {
|
||||
var result = [];
|
||||
var childNodes = el.parentNode.childNodes;
|
||||
for (var i = 0; i < childNodes.length; i++) {
|
||||
node = childNodes[i];
|
||||
let node = childNodes[i];
|
||||
if (node.nodeType === 1) {
|
||||
children.push(node);
|
||||
result.push(node);
|
||||
}
|
||||
};
|
||||
return children;
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
'isElement': isElement,
|
||||
'uuid': uuid,
|
||||
'values': values,
|
||||
'resolveUrl': resolveUrl,
|
||||
'indexOfSorted': indexOfSorted,
|
||||
'documentHeight': documentHeight,
|
||||
'isNumber': isNumber,
|
||||
'isFloat': isFloat,
|
||||
'prefixed': prefixed,
|
||||
'defaults': defaults,
|
||||
'extend': extend,
|
||||
'insert': insert,
|
||||
'locationOf': locationOf,
|
||||
'indexOfSorted': indexOfSorted,
|
||||
'requestAnimationFrame': requestAnimationFrame,
|
||||
'bounds': bounds,
|
||||
'borders': borders,
|
||||
'windowBounds': windowBounds,
|
||||
'cleanStringForXpath': cleanStringForXpath,
|
||||
'indexOfTextNode': indexOfTextNode,
|
||||
'isXml': isXml,
|
||||
'createBlob': createBlob,
|
||||
'createBlobUrl': createBlobUrl,
|
||||
'type': type,
|
||||
'parse' : parse,
|
||||
'qs' : qs,
|
||||
'qsa' : qsa,
|
||||
'qsp' : qsp,
|
||||
'blob2base64' : blob2base64,
|
||||
'createBase64Url': createBase64Url,
|
||||
'defer': defer,
|
||||
'Url': Url,
|
||||
'Path': Path,
|
||||
'querySelectorByType': querySelectorByType,
|
||||
'sprint' : sprint,
|
||||
'children' : children
|
||||
};
|
57
src/utils/path.js
Normal file
57
src/utils/path.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import path from 'path-webpack';
|
||||
|
||||
class Path {
|
||||
constructor(pathString) {
|
||||
var protocol;
|
||||
var parsed;
|
||||
|
||||
protocol = pathString.indexOf('://');
|
||||
if (protocol > -1) {
|
||||
pathString = new URL(pathString).pathname;
|
||||
}
|
||||
|
||||
parsed = this.parse(pathString);
|
||||
|
||||
this.path = pathString;
|
||||
|
||||
if (this.isDirectory(pathString)) {
|
||||
this.directory = pathString;
|
||||
} else {
|
||||
this.directory = parsed.dir + "/";
|
||||
}
|
||||
|
||||
this.filename = parsed.base;
|
||||
this.extension = parsed.ext.slice(1);
|
||||
|
||||
}
|
||||
|
||||
parse (what) {
|
||||
return path.parse(what);
|
||||
};
|
||||
|
||||
isAbsolute (what) {
|
||||
return path.isAbsolute(what || this.path);
|
||||
};
|
||||
|
||||
isDirectory (what) {
|
||||
return (what.charAt(what.length-1) === '/');
|
||||
};
|
||||
|
||||
resolve (what) {
|
||||
return path.resolve(this.directory, what);
|
||||
};
|
||||
|
||||
relative (what) {
|
||||
return path.relative(this.directory, what);
|
||||
};
|
||||
|
||||
splitPath(filename) {
|
||||
return this.splitPathRe.exec(filename).slice(1);
|
||||
};
|
||||
|
||||
toString () {
|
||||
return this.path;
|
||||
};
|
||||
}
|
||||
|
||||
export default Path
|
83
src/utils/url.js
Normal file
83
src/utils/url.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
import Path from './path'
|
||||
import path from 'path-webpack';
|
||||
|
||||
/**
|
||||
* creates a uri object
|
||||
* @param {string} urlString a url string (relative or absolute)
|
||||
* @param {[string]} baseString optional base for the url,
|
||||
* default to window.location.href
|
||||
* @return {object} url
|
||||
*/
|
||||
|
||||
class Url {
|
||||
constructor(urlString, baseString) {
|
||||
var absolute = (urlString.indexOf('://') > -1);
|
||||
var pathname = urlString;
|
||||
|
||||
this.Url = undefined;
|
||||
this.href = urlString;
|
||||
this.protocol = "";
|
||||
this.origin = "";
|
||||
this.fragment = "";
|
||||
this.search = "";
|
||||
this.base = baseString;
|
||||
|
||||
if (!absolute && (typeof(baseString) !== "string")) {
|
||||
this.base = window && 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
|
||||
this.Url = new URL(urlString, this.base);
|
||||
} else {
|
||||
this.Url = new URL(urlString);
|
||||
}
|
||||
this.href = this.Url.href;
|
||||
|
||||
this.protocol = this.Url.protocol;
|
||||
this.origin = this.Url.origin;
|
||||
this.fragment = this.Url.fragment;
|
||||
this.search = this.Url.search;
|
||||
|
||||
pathname = this.Url.pathname;
|
||||
} catch (e) {
|
||||
// Skip URL parsing
|
||||
this.Url = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
this.Path = new Path(pathname);
|
||||
this.directory = this.Path.directory;
|
||||
this.filename = this.Path.filename;
|
||||
this.extension = this.Path.extension;
|
||||
|
||||
}
|
||||
|
||||
path () {
|
||||
return this.Path;
|
||||
};
|
||||
|
||||
resolve (what) {
|
||||
var isAbsolute = (what.indexOf('://') > -1);
|
||||
var fullpath;
|
||||
|
||||
if (isAbsolute) {
|
||||
return what;
|
||||
}
|
||||
|
||||
fullpath = path.resolve(this.directory, what);
|
||||
return this.origin + fullpath;
|
||||
};
|
||||
|
||||
relative (what) {
|
||||
return path.relative(what, this.directory);
|
||||
};
|
||||
|
||||
toString () {
|
||||
return this.href;
|
||||
};
|
||||
}
|
||||
|
||||
export default Url
|
|
@ -9,7 +9,7 @@ describe('Core', function() {
|
|||
|
||||
describe('Url', function () {
|
||||
|
||||
var Url = require('../src/core').Url;
|
||||
var Url = require('../src/utils/url');
|
||||
|
||||
it("Url()", function() {
|
||||
var url = new Url("http://example.com/fred/chasen/derf.html");
|
||||
|
@ -61,7 +61,7 @@ describe('Core', function() {
|
|||
|
||||
describe('Path', function () {
|
||||
|
||||
var Path = require('../src/core').Path;
|
||||
var Path = require('../src/utils/path');
|
||||
|
||||
it("Path()", function() {
|
||||
var path = new Path("/fred/chasen/derf.html");
|
||||
|
|
|
@ -8,13 +8,13 @@ describe('EpubCFI', function() {
|
|||
var EpubCFI = require('../src/epubcfi.js');
|
||||
|
||||
it('parse a cfi on init', function() {
|
||||
var cfi = EpubCFI("epubcfi(/6/2[cover]!/6)");
|
||||
var cfi = new EpubCFI("epubcfi(/6/2[cover]!/6)");
|
||||
|
||||
assert.equal( cfi.spinePos, 0, "spinePos is parsed as the first item" );
|
||||
});
|
||||
|
||||
it('parse a cfi and ignore the base if present', function() {
|
||||
var cfi = EpubCFI("epubcfi(/6/2[cover]!/6)", "/6/6[end]");
|
||||
var cfi = new EpubCFI("epubcfi(/6/2[cover]!/6)", "/6/6[end]");
|
||||
|
||||
assert.equal( cfi.spinePos, 0, "base is ignored and spinePos is parsed as the first item" );
|
||||
});
|
||||
|
@ -56,9 +56,9 @@ describe('EpubCFI', function() {
|
|||
describe('#toString()', function() {
|
||||
it('parse a cfi and write it back', function() {
|
||||
|
||||
assert.equal(EpubCFI("epubcfi(/6/2[cover]!/6)").toString(), "epubcfi(/6/2[cover]!/6)", "output cfi string is same as input" );
|
||||
assert.equal(EpubCFI("epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)").toString(), "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)", "output cfi string is same as input" );
|
||||
assert.equal(EpubCFI("epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)").toString(), "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)", "output cfi string is same as input" );
|
||||
assert.equal(new EpubCFI("epubcfi(/6/2[cover]!/6)").toString(), "epubcfi(/6/2[cover]!/6)", "output cfi string is same as input" );
|
||||
assert.equal(new EpubCFI("epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)").toString(), "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)", "output cfi string is same as input" );
|
||||
assert.equal(new EpubCFI("epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)").toString(), "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)", "output cfi string is same as input" );
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -73,7 +73,7 @@ describe('EpubCFI', function() {
|
|||
});
|
||||
|
||||
it('determine the type of a cfi', function() {
|
||||
var ogcfi = EpubCFI("epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)");
|
||||
var ogcfi = new EpubCFI("epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)");
|
||||
var cfi = new EpubCFI();
|
||||
|
||||
assert.equal( cfi.checkType(ogcfi), 'EpubCFI' );
|
||||
|
|
|
@ -2,12 +2,10 @@ var assert = require('assert');
|
|||
|
||||
describe('Locations', function() {
|
||||
var Locations = require('../src/locations');
|
||||
var core = require('../src/core');
|
||||
var chapter = require('raw-loader!./fixtures/locations.xhtml');
|
||||
var core = require('../src/utils/core');
|
||||
|
||||
describe('#parse', function() {
|
||||
var Locations = require('../src/locations');
|
||||
var core = require('../src/core');
|
||||
var chapter = require('raw-loader!./fixtures/locations.xhtml');
|
||||
|
||||
it('parse locations from a document', function() {
|
||||
|
@ -25,7 +23,6 @@ describe('Locations', function() {
|
|||
|
||||
var locations = new Locations();
|
||||
var result = locations.parse(contents, "/6/4[chap01ref]", 100);
|
||||
console.log(result);
|
||||
assert.equal(result.length, 15);
|
||||
|
||||
});
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
var webpack = require("webpack");
|
||||
var path = require('path');
|
||||
var BabiliPlugin = require("babili-webpack-plugin");
|
||||
var PROD = (process.env.NODE_ENV === 'production')
|
||||
var LEGACY = (process.env.LEGACY)
|
||||
var hostname = "localhost";
|
||||
var port = "8080";
|
||||
var enter = LEGACY ? {
|
||||
"epub.legacy": ["babel-polyfill", "./libs/url/url.js", "./src/epub.js"]
|
||||
} : {
|
||||
"epub": "./src/epub.js",
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
epub: "./src/epub.js",
|
||||
polyfills: ["./node_modules/es6-promise/dist/es6-promise.auto.js", "./libs/url/url.js"]
|
||||
},
|
||||
devtool: 'source-map',
|
||||
entry: enter,
|
||||
devtool: PROD ? false : 'source-map',
|
||||
output: {
|
||||
path: path.resolve("./dist"),
|
||||
// path: "./dist",
|
||||
filename: "[name].js",
|
||||
filename: PROD ? "[name].min.js" : "[name].js",
|
||||
sourceMapFilename: "[name].js.map",
|
||||
library: "ePub",
|
||||
libraryTarget: "umd",
|
||||
|
@ -23,9 +27,9 @@ module.exports = {
|
|||
"jszip": "JSZip",
|
||||
"xmldom": "xmldom"
|
||||
},
|
||||
plugins: [
|
||||
// new webpack.IgnorePlugin(/punycode|IPv6/),
|
||||
],
|
||||
plugins: PROD ? [
|
||||
new BabiliPlugin()
|
||||
] : [],
|
||||
resolve: {
|
||||
alias: {
|
||||
path: "path-webpack"
|
||||
|
@ -35,5 +39,24 @@ module.exports = {
|
|||
host: hostname,
|
||||
port: port,
|
||||
inline: true
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
LEGACY ? {
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: "babel-loader",
|
||||
query: {
|
||||
presets: ['es2015'],
|
||||
plugins: [
|
||||
"add-module-exports",
|
||||
]
|
||||
}
|
||||
} : {
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: "babel-loader"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue