mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-04 15:09:16 +02:00
Documentation clean up
This commit is contained in:
parent
7ff4c8e37c
commit
77846842cd
20 changed files with 1090 additions and 8239 deletions
|
@ -1,28 +1,9 @@
|
||||||
// Manage annotations for a book?
|
|
||||||
|
|
||||||
/*
|
|
||||||
let a = rendition.annotations.highlight(cfiRange, data)
|
|
||||||
|
|
||||||
a.on("added", () => console.log("added"))
|
|
||||||
a.on("removed", () => console.log("removed"))
|
|
||||||
a.on("clicked", () => console.log("clicked"))
|
|
||||||
|
|
||||||
a.update(data)
|
|
||||||
a.remove();
|
|
||||||
a.text();
|
|
||||||
|
|
||||||
rendition.annotations.show()
|
|
||||||
rendition.annotations.hide()
|
|
||||||
|
|
||||||
rendition.annotations.highlights.show()
|
|
||||||
rendition.annotations.highlights.hide()
|
|
||||||
*/
|
|
||||||
|
|
||||||
import EventEmitter from "event-emitter";
|
import EventEmitter from "event-emitter";
|
||||||
import EpubCFI from "./epubcfi";
|
import EpubCFI from "./epubcfi";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles managing adding & removing Annotations
|
* Handles managing adding & removing Annotations
|
||||||
|
* @param {Rendition} rendition
|
||||||
* @class
|
* @class
|
||||||
*/
|
*/
|
||||||
class Annotations {
|
class Annotations {
|
||||||
|
@ -39,6 +20,14 @@ class Annotations {
|
||||||
this.rendition.hooks.unloaded.register(this.clear.bind(this));
|
this.rendition.hooks.unloaded.register(this.clear.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an annotation to store
|
||||||
|
* @param {string} type Type of annotation to add: "highlight", "underline", "mark"
|
||||||
|
* @param {EpubCFI} cfiRange EpubCFI range to attach annotation to
|
||||||
|
* @param {object} data Data to assign to annotation
|
||||||
|
* @param {function} [cb] Callback after annotation is added
|
||||||
|
* @returns {Annotation} annotation
|
||||||
|
*/
|
||||||
add (type, cfiRange, data, cb) {
|
add (type, cfiRange, data, cb) {
|
||||||
let hash = encodeURI(cfiRange);
|
let hash = encodeURI(cfiRange);
|
||||||
let cfi = new EpubCFI(cfiRange);
|
let cfi = new EpubCFI(cfiRange);
|
||||||
|
@ -70,6 +59,11 @@ class Annotations {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an annotation from store
|
||||||
|
* @param {EpubCFI} cfiRange EpubCFI range the annotation is attached to
|
||||||
|
* @param {string} type Type of annotation to add: "highlight", "underline", "mark"
|
||||||
|
*/
|
||||||
remove (cfiRange, type) {
|
remove (cfiRange, type) {
|
||||||
let hash = encodeURI(cfiRange);
|
let hash = encodeURI(cfiRange);
|
||||||
|
|
||||||
|
@ -92,30 +86,65 @@ class Annotations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an annotations by Section Index
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
_removeFromAnnotationBySectionIndex (sectionIndex, hash) {
|
_removeFromAnnotationBySectionIndex (sectionIndex, hash) {
|
||||||
this._annotationsBySectionIndex[sectionIndex] = this._annotationsAt(sectionIndex).filter(h => h !== hash);
|
this._annotationsBySectionIndex[sectionIndex] = this._annotationsAt(sectionIndex).filter(h => h !== hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get annotations by Section Index
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
_annotationsAt (index) {
|
_annotationsAt (index) {
|
||||||
return this._annotationsBySectionIndex[index];
|
return this._annotationsBySectionIndex[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a highlight to the store
|
||||||
|
* @param {EpubCFI} cfiRange EpubCFI range to attach annotation to
|
||||||
|
* @param {object} data Data to assign to annotation
|
||||||
|
* @param {function} cb Callback after annotation is added
|
||||||
|
*/
|
||||||
highlight (cfiRange, data, cb) {
|
highlight (cfiRange, data, cb) {
|
||||||
this.add("highlight", cfiRange, data, cb);
|
this.add("highlight", cfiRange, data, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a underline to the store
|
||||||
|
* @param {EpubCFI} cfiRange EpubCFI range to attach annotation to
|
||||||
|
* @param {object} data Data to assign to annotation
|
||||||
|
* @param {function} cb Callback after annotation is added
|
||||||
|
*/
|
||||||
underline (cfiRange, data, cb) {
|
underline (cfiRange, data, cb) {
|
||||||
this.add("underline", cfiRange, data, cb);
|
this.add("underline", cfiRange, data, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a mark to the store
|
||||||
|
* @param {EpubCFI} cfiRange EpubCFI range to attach annotation to
|
||||||
|
* @param {object} data Data to assign to annotation
|
||||||
|
* @param {function} cb Callback after annotation is added
|
||||||
|
*/
|
||||||
mark (cfiRange, data, cb) {
|
mark (cfiRange, data, cb) {
|
||||||
this.add("mark", cfiRange, data, cb);
|
this.add("mark", cfiRange, data, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iterate over annotations in the store
|
||||||
|
*/
|
||||||
each () {
|
each () {
|
||||||
return this._annotations.forEach.apply(this._annotations, arguments);
|
return this._annotations.forEach.apply(this._annotations, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for injecting annotation into a view
|
||||||
|
* @param {View} view
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
inject (view) {
|
inject (view) {
|
||||||
let sectionIndex = view.index;
|
let sectionIndex = view.index;
|
||||||
if (sectionIndex in this._annotationsBySectionIndex) {
|
if (sectionIndex in this._annotationsBySectionIndex) {
|
||||||
|
@ -127,6 +156,11 @@ class Annotations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for removing annotation from a view
|
||||||
|
* @param {View} view
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
clear (view) {
|
clear (view) {
|
||||||
let sectionIndex = view.index;
|
let sectionIndex = view.index;
|
||||||
if (sectionIndex in this._annotationsBySectionIndex) {
|
if (sectionIndex in this._annotationsBySectionIndex) {
|
||||||
|
@ -138,16 +172,35 @@ class Annotations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Not Implemented] Show annotations
|
||||||
|
* @TODO: needs implementation in View
|
||||||
|
*/
|
||||||
show () {
|
show () {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Not Implemented] Hide annotations
|
||||||
|
* @TODO: needs implementation in View
|
||||||
|
*/
|
||||||
hide () {
|
hide () {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation object
|
||||||
|
* @class
|
||||||
|
* @param {object} options
|
||||||
|
* @param {string} options.type Type of annotation to add: "highlight", "underline", "mark"
|
||||||
|
* @param {EpubCFI} options.cfiRange EpubCFI range to attach annotation to
|
||||||
|
* @param {object} options.data Data to assign to annotation
|
||||||
|
* @param {int} options.sectionIndex Index in the Spine of the Section annotation belongs to
|
||||||
|
* @param {function} [options.cb] Callback after annotation is added
|
||||||
|
* @returns {Annotation} annotation
|
||||||
|
*/
|
||||||
class Annotation {
|
class Annotation {
|
||||||
|
|
||||||
constructor ({
|
constructor ({
|
||||||
|
@ -165,18 +218,21 @@ class Annotation {
|
||||||
this.cb = cb;
|
this.cb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update stored data
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
update (data) {
|
update (data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to a view
|
||||||
|
* @param {View} view
|
||||||
|
*/
|
||||||
attach (view) {
|
attach (view) {
|
||||||
let {cfiRange, data, type, mark, cb} = this;
|
let {cfiRange, data, type, mark, cb} = this;
|
||||||
let result;
|
let result;
|
||||||
/*
|
|
||||||
if (mark) {
|
|
||||||
return; // already added
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (type === "highlight") {
|
if (type === "highlight") {
|
||||||
result = view.highlight(cfiRange, data, cb);
|
result = view.highlight(cfiRange, data, cb);
|
||||||
|
@ -191,6 +247,10 @@ class Annotation {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove from a view
|
||||||
|
* @param {View} view
|
||||||
|
*/
|
||||||
detach (view) {
|
detach (view) {
|
||||||
let {cfiRange, type} = this;
|
let {cfiRange, type} = this;
|
||||||
let result;
|
let result;
|
||||||
|
@ -210,8 +270,12 @@ class Annotation {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [Not Implemented] Get text of an annotation
|
||||||
|
* @TODO: needs implementation in contents
|
||||||
|
*/
|
||||||
text () {
|
text () {
|
||||||
// TODO: needs implementation in contents
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,9 @@ class Archive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request
|
* Request a url from the archive
|
||||||
* @param {string} url a url to request from the archive
|
* @param {string} url a url to request from the archive
|
||||||
* @param {[string]} type specify the type of the returned result
|
* @param {string} [type] specify the type of the returned result
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
request(url, type){
|
request(url, type){
|
||||||
|
@ -98,7 +98,7 @@ class Archive {
|
||||||
* Handle the response from request
|
* Handle the response from request
|
||||||
* @private
|
* @private
|
||||||
* @param {any} response
|
* @param {any} response
|
||||||
* @param {[string]} type
|
* @param {string} [type]
|
||||||
* @return {any} the parsed result
|
* @return {any} the parsed result
|
||||||
*/
|
*/
|
||||||
handleResponse(response, type){
|
handleResponse(response, type){
|
||||||
|
@ -128,7 +128,7 @@ class Archive {
|
||||||
/**
|
/**
|
||||||
* Get a Blob from Archive by Url
|
* Get a Blob from Archive by Url
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {[string]} mimeType
|
* @param {string} [mimeType]
|
||||||
* @return {Blob}
|
* @return {Blob}
|
||||||
*/
|
*/
|
||||||
getBlob(url, mimeType){
|
getBlob(url, mimeType){
|
||||||
|
@ -146,7 +146,7 @@ class Archive {
|
||||||
/**
|
/**
|
||||||
* Get Text from Archive by Url
|
* Get Text from Archive by Url
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {[string]} encoding
|
* @param {string} [encoding]
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getText(url, encoding){
|
getText(url, encoding){
|
||||||
|
@ -163,7 +163,7 @@ class Archive {
|
||||||
/**
|
/**
|
||||||
* Get a base64 encoded result from Archive by Url
|
* Get a base64 encoded result from Archive by Url
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {[string]} mimeType
|
* @param {string} [mimeType]
|
||||||
* @return {string} base64 encoded
|
* @return {string} base64 encoded
|
||||||
*/
|
*/
|
||||||
getBase64(url, mimeType){
|
getBase64(url, mimeType){
|
||||||
|
@ -181,7 +181,7 @@ class Archive {
|
||||||
/**
|
/**
|
||||||
* Create a Url from an unarchived item
|
* Create a Url from an unarchived item
|
||||||
* @param {string} url
|
* @param {string} url
|
||||||
* @param {[object]} options.base64 use base64 encoding or blob url
|
* @param {object} [options.base64] use base64 encoding or blob url
|
||||||
* @return {Promise} url promise with Url string
|
* @return {Promise} url promise with Url string
|
||||||
*/
|
*/
|
||||||
createUrl(url, options){
|
createUrl(url, options){
|
||||||
|
|
132
src/book.js
132
src/book.js
|
@ -1,5 +1,4 @@
|
||||||
import EventEmitter from "event-emitter";
|
import EventEmitter from "event-emitter";
|
||||||
// import path from "path";
|
|
||||||
import {extend, defer} from "./utils/core";
|
import {extend, defer} from "./utils/core";
|
||||||
import Url from "./utils/url";
|
import Url from "./utils/url";
|
||||||
import Path from "./utils/path";
|
import Path from "./utils/path";
|
||||||
|
@ -19,16 +18,27 @@ import { EVENTS } from "./utils/constants";
|
||||||
const CONTAINER_PATH = "META-INF/container.xml";
|
const CONTAINER_PATH = "META-INF/container.xml";
|
||||||
const EPUBJS_VERSION = "0.3";
|
const EPUBJS_VERSION = "0.3";
|
||||||
|
|
||||||
|
const INPUT_TYPE = {
|
||||||
|
BINARY: "binary",
|
||||||
|
BASE64: "base64",
|
||||||
|
EPUB: "epub",
|
||||||
|
OPF: "opf",
|
||||||
|
MANIFEST: "json",
|
||||||
|
DIRECTORY: "directory"
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Book
|
* An Epub representation with methods for the loading, parsing and manipulation
|
||||||
|
* of its contents.
|
||||||
* @class
|
* @class
|
||||||
* @param {string} url
|
* @param {string} [url]
|
||||||
* @param {object} options
|
* @param {object} [options]
|
||||||
* @param {method} options.requestMethod a request function to use instead of the default
|
* @param {method} [options.requestMethod] a request function to use instead of the default
|
||||||
* @param {boolean} [options.requestCredentials=undefined] send the xhr request withCredentials
|
* @param {boolean} [options.requestCredentials=undefined] send the xhr request withCredentials
|
||||||
* @param {object} [options.requestHeaders=undefined] send the xhr request headers
|
* @param {object} [options.requestHeaders=undefined] send the xhr request headers
|
||||||
* @param {string} [options.encoding=binary] optional to pass 'binary' or base64' for archived Epubs
|
* @param {string} [options.encoding=binary] optional to pass 'binary' or base64' for archived Epubs
|
||||||
* @param {string} [options.replacements=none] use base64, blobUrl, or none for replacing assets in archived Epubs
|
* @param {string} [options.replacements=none] use base64, blobUrl, or none for replacing assets in archived Epubs
|
||||||
|
* @param {method} [options.canonical] optional function to determine canonical urls for a path
|
||||||
* @returns {Book}
|
* @returns {Book}
|
||||||
* @example new Book("/path/to/book.epub", {})
|
* @example new Book("/path/to/book.epub", {})
|
||||||
* @example new Book({ replacements: "blobUrl" })
|
* @example new Book({ replacements: "blobUrl" })
|
||||||
|
@ -57,7 +67,8 @@ class Book {
|
||||||
// Promises
|
// Promises
|
||||||
this.opening = new defer();
|
this.opening = new defer();
|
||||||
/**
|
/**
|
||||||
* @property {promise} opened returns after the book is loaded
|
* @member {promise} opened returns after the book is loaded
|
||||||
|
* @memberof Book
|
||||||
*/
|
*/
|
||||||
this.opened = this.opening.promise;
|
this.opened = this.opening.promise;
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
|
@ -82,9 +93,9 @@ class Book {
|
||||||
resources: this.loading.resources.promise
|
resources: this.loading.resources.promise
|
||||||
};
|
};
|
||||||
|
|
||||||
// this.ready = RSVP.hash(this.loaded);
|
|
||||||
/**
|
/**
|
||||||
* @property {promise} ready returns after the book is loaded and parsed
|
* @member {promise} ready returns after the book is loaded and parsed
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.ready = Promise.all([
|
this.ready = Promise.all([
|
||||||
|
@ -102,70 +113,93 @@ class Book {
|
||||||
// this._q = queue(this);
|
// this._q = queue(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {method} request
|
* @member {method} request
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.request = this.settings.requestMethod || request;
|
this.request = this.settings.requestMethod || request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Spine} spine
|
* @member {Spine} spine
|
||||||
|
* @memberof Book
|
||||||
*/
|
*/
|
||||||
this.spine = new Spine();
|
this.spine = new Spine();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Locations} locations
|
* @member {Locations} locations
|
||||||
|
* @memberof Book
|
||||||
*/
|
*/
|
||||||
this.locations = new Locations(this.spine, this.load.bind(this));
|
this.locations = new Locations(this.spine, this.load.bind(this));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Navigation} navigation
|
* @member {Navigation} navigation
|
||||||
|
* @memberof Book
|
||||||
*/
|
*/
|
||||||
this.navigation = undefined;
|
this.navigation = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {PageList} pagelist
|
* @member {PageList} pagelist
|
||||||
|
* @memberof Book
|
||||||
*/
|
*/
|
||||||
this.pageList = undefined;
|
this.pageList = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Url} url
|
* @member {Url} url
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.url = undefined;
|
this.url = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Path} path
|
* @member {Path} path
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.path = undefined;
|
this.path = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {boolean} archived
|
* @member {boolean} archived
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.archived = false;
|
this.archived = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Archive} archive
|
* @member {Archive} archive
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.archive = undefined;
|
this.archive = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Resources} resources
|
* @member {Resources} resources
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.resources = undefined;
|
this.resources = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property {Rendition} rendition
|
* @member {Rendition} rendition
|
||||||
|
* @memberof Book
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.rendition = undefined;
|
this.rendition = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @member {Container} container
|
||||||
|
* @memberof Book
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
this.container = undefined;
|
this.container = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @member {Packaging} packaging
|
||||||
|
* @memberof Book
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
this.packaging = undefined;
|
this.packaging = undefined;
|
||||||
this.toc = undefined;
|
|
||||||
|
// this.toc = undefined;
|
||||||
|
|
||||||
if(url) {
|
if(url) {
|
||||||
this.open(url).catch((error) => {
|
this.open(url).catch((error) => {
|
||||||
|
@ -177,8 +211,8 @@ class Book {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a epub or url
|
* Open a epub or url
|
||||||
* @param {string} input URL, Path or ArrayBuffer
|
* @param {string | ArrayBuffer} input Url, Path or ArrayBuffer
|
||||||
* @param {string} [what] to force opening
|
* @param {string} [what="binary", "base64", "epub", "opf", "json", "directory"] force opening as a certain type
|
||||||
* @returns {Promise} of when the book has been loaded
|
* @returns {Promise} of when the book has been loaded
|
||||||
* @example book.open("/path/to/book.epub")
|
* @example book.open("/path/to/book.epub")
|
||||||
*/
|
*/
|
||||||
|
@ -186,23 +220,23 @@ class Book {
|
||||||
var opening;
|
var opening;
|
||||||
var type = what || this.determineType(input);
|
var type = what || this.determineType(input);
|
||||||
|
|
||||||
if (type === "binary") {
|
if (type === INPUT_TYPE.BINARY) {
|
||||||
this.archived = true;
|
this.archived = true;
|
||||||
this.url = new Url("/", "");
|
this.url = new Url("/", "");
|
||||||
opening = this.openEpub(input);
|
opening = this.openEpub(input);
|
||||||
} else if (type === "base64") {
|
} else if (type === INPUT_TYPE.BASE64) {
|
||||||
this.archived = true;
|
this.archived = true;
|
||||||
this.url = new Url("/", "");
|
this.url = new Url("/", "");
|
||||||
opening = this.openEpub(input, type);
|
opening = this.openEpub(input, type);
|
||||||
} else if (type === "epub") {
|
} else if (type === INPUT_TYPE.EPUB) {
|
||||||
this.archived = true;
|
this.archived = true;
|
||||||
this.url = new Url("/", "");
|
this.url = new Url("/", "");
|
||||||
opening = this.request(input, "binary")
|
opening = this.request(input, "binary")
|
||||||
.then(this.openEpub.bind(this));
|
.then(this.openEpub.bind(this));
|
||||||
} else if(type == "opf") {
|
} else if(type == INPUT_TYPE.OPF) {
|
||||||
this.url = new Url(input);
|
this.url = new Url(input);
|
||||||
opening = this.openPackaging(this.url.Path.toString());
|
opening = this.openPackaging(this.url.Path.toString());
|
||||||
} else if(type == "json") {
|
} else if(type == INPUT_TYPE.MANIFEST) {
|
||||||
this.url = new Url(input);
|
this.url = new Url(input);
|
||||||
opening = this.openManifest(this.url.Path.toString());
|
opening = this.openManifest(this.url.Path.toString());
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,7 +252,7 @@ class Book {
|
||||||
* Open an archived epub
|
* Open an archived epub
|
||||||
* @private
|
* @private
|
||||||
* @param {binary} data
|
* @param {binary} data
|
||||||
* @param {[string]} encoding
|
* @param {string} [encoding]
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
openEpub(data, encoding) {
|
openEpub(data, encoding) {
|
||||||
|
@ -296,7 +330,7 @@ class Book {
|
||||||
/**
|
/**
|
||||||
* Resolve a path to it's absolute position in the Book
|
* Resolve a path to it's absolute position in the Book
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @param {[boolean]} absolute force resolving the full URL
|
* @param {boolean} [absolute] force resolving the full URL
|
||||||
* @return {string} the resolved path string
|
* @return {string} the resolved path string
|
||||||
*/
|
*/
|
||||||
resolve(path, absolute) {
|
resolve(path, absolute) {
|
||||||
|
@ -354,11 +388,11 @@ class Book {
|
||||||
var extension;
|
var extension;
|
||||||
|
|
||||||
if (this.settings.encoding === "base64") {
|
if (this.settings.encoding === "base64") {
|
||||||
return "base64";
|
return INPUT_TYPE.BASE64;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof(input) != "string") {
|
if(typeof(input) != "string") {
|
||||||
return "binary";
|
return INPUT_TYPE.BINARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
url = new Url(input);
|
url = new Url(input);
|
||||||
|
@ -366,19 +400,19 @@ class Book {
|
||||||
extension = path.extension;
|
extension = path.extension;
|
||||||
|
|
||||||
if (!extension) {
|
if (!extension) {
|
||||||
return "directory";
|
return INPUT_TYPE.DIRECTORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(extension === "epub"){
|
if(extension === "epub"){
|
||||||
return "epub";
|
return INPUT_TYPE.EPUB;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(extension === "opf"){
|
if(extension === "opf"){
|
||||||
return "opf";
|
return INPUT_TYPE.OPF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(extension === "json"){
|
if(extension === "json"){
|
||||||
return "json";
|
return INPUT_TYPE.MANIFEST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +435,7 @@ class Book {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.loadNavigation(this.package).then(() => {
|
this.loadNavigation(this.package).then(() => {
|
||||||
this.toc = this.navigation.toc;
|
// this.toc = this.navigation.toc;
|
||||||
this.loading.navigation.resolve(this.navigation);
|
this.loading.navigation.resolve(this.navigation);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -472,24 +506,22 @@ class Book {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alias for book.spine.get
|
* Gets a Section of the Book from the Spine
|
||||||
|
* Alias for `book.spine.get`
|
||||||
* @param {string} target
|
* @param {string} target
|
||||||
|
* @return {Section}
|
||||||
*/
|
*/
|
||||||
section(target) {
|
section(target) {
|
||||||
return this.spine.get(target);
|
return this.spine.get(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sugar to render a book
|
* Sugar to render a book to an element
|
||||||
* @param {element} element element to add the views to
|
* @param {element | string} element element or string to add a rendition to
|
||||||
* @param {[object]} options
|
* @param {object} [options]
|
||||||
* @return {Rendition}
|
* @return {Rendition}
|
||||||
*/
|
*/
|
||||||
renderTo(element, options) {
|
renderTo(element, options) {
|
||||||
// var renderMethod = (options && options.method) ?
|
|
||||||
// options.method :
|
|
||||||
// "single";
|
|
||||||
|
|
||||||
this.rendition = new Rendition(this, options);
|
this.rendition = new Rendition(this, options);
|
||||||
this.rendition.attachTo(element);
|
this.rendition.attachTo(element);
|
||||||
|
|
||||||
|
@ -516,7 +548,7 @@ class Book {
|
||||||
* Unarchive a zipped epub
|
* Unarchive a zipped epub
|
||||||
* @private
|
* @private
|
||||||
* @param {binary} input epub data
|
* @param {binary} input epub data
|
||||||
* @param {[string]} encoding
|
* @param {string} [encoding]
|
||||||
* @return {Archive}
|
* @return {Archive}
|
||||||
*/
|
*/
|
||||||
unarchive(input, encoding) {
|
unarchive(input, encoding) {
|
||||||
|
@ -539,13 +571,11 @@ class Book {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return retrieved;
|
return retrieved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* load replacement urls
|
* Load replacement urls
|
||||||
* @private
|
* @private
|
||||||
* @return {Promise} completed loading urls
|
* @return {Promise} completed loading urls
|
||||||
*/
|
*/
|
||||||
|
@ -582,7 +612,7 @@ class Book {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the Book Key using the identifer in the manifest or other string provided
|
* Generates the Book Key using the identifer in the manifest or other string provided
|
||||||
* @param {[string]} identifier to use instead of metadata identifier
|
* @param {string} [identifier] to use instead of metadata identifier
|
||||||
* @return {string} key
|
* @return {string} key
|
||||||
*/
|
*/
|
||||||
key(identifier) {
|
key(identifier) {
|
||||||
|
@ -590,6 +620,9 @@ class Book {
|
||||||
return `epubjs:${EPUBJS_VERSION}:${ident}`;
|
return `epubjs:${EPUBJS_VERSION}:${ident}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy the Book and all associated objects
|
||||||
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
this.opened = undefined;
|
this.opened = undefined;
|
||||||
this.loading = undefined;
|
this.loading = undefined;
|
||||||
|
@ -621,7 +654,6 @@ class Book {
|
||||||
this.url = undefined;
|
this.url = undefined;
|
||||||
this.path = undefined;
|
this.path = undefined;
|
||||||
this.archived = false;
|
this.archived = false;
|
||||||
this.toc = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {qs} from "./utils/core";
|
||||||
/**
|
/**
|
||||||
* Handles Parsing and Accessing an Epub Container
|
* Handles Parsing and Accessing an Epub Container
|
||||||
* @class
|
* @class
|
||||||
* @param {[document]} containerDocument xml document
|
* @param {document} [containerDocument] xml document
|
||||||
*/
|
*/
|
||||||
class Container {
|
class Container {
|
||||||
constructor(containerDocument) {
|
constructor(containerDocument) {
|
||||||
|
|
268
src/contents.js
268
src/contents.js
|
@ -11,6 +11,14 @@ const isWebkit = !isChrome && /AppleWebKit/.test(navigator.userAgent);
|
||||||
const ELEMENT_NODE = 1;
|
const ELEMENT_NODE = 1;
|
||||||
const TEXT_NODE = 3;
|
const TEXT_NODE = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles DOM manipulation, queries and events for View contents
|
||||||
|
* @class
|
||||||
|
* @param {document} doc Document
|
||||||
|
* @param {element} content Parent Element (typically Body)
|
||||||
|
* @param {string} cfiBase Section component of CFIs
|
||||||
|
* @param {number} sectionIndex Index in Spine of Conntent's Section
|
||||||
|
*/
|
||||||
class Contents {
|
class Contents {
|
||||||
constructor(doc, content, cfiBase, sectionIndex) {
|
constructor(doc, content, cfiBase, sectionIndex) {
|
||||||
// Blank Cfi for Parsing
|
// Blank Cfi for Parsing
|
||||||
|
@ -34,10 +42,18 @@ class Contents {
|
||||||
this.listeners();
|
this.listeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get DOM events that are listened for and passed along
|
||||||
|
*/
|
||||||
static get listenedEvents() {
|
static get listenedEvents() {
|
||||||
return DOM_EVENTS;
|
return DOM_EVENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or Set width
|
||||||
|
* @param {number} [w]
|
||||||
|
* @returns {number} width
|
||||||
|
*/
|
||||||
width(w) {
|
width(w) {
|
||||||
// var frame = this.documentElement;
|
// var frame = this.documentElement;
|
||||||
var frame = this.content;
|
var frame = this.content;
|
||||||
|
@ -56,6 +72,11 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or Set height
|
||||||
|
* @param {number} [h]
|
||||||
|
* @returns {number} height
|
||||||
|
*/
|
||||||
height(h) {
|
height(h) {
|
||||||
// var frame = this.documentElement;
|
// var frame = this.documentElement;
|
||||||
var frame = this.content;
|
var frame = this.content;
|
||||||
|
@ -73,6 +94,11 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or Set width of the contents
|
||||||
|
* @param {number} [w]
|
||||||
|
* @returns {number} width
|
||||||
|
*/
|
||||||
contentWidth(w) {
|
contentWidth(w) {
|
||||||
|
|
||||||
var content = this.content || this.document.body;
|
var content = this.content || this.document.body;
|
||||||
|
@ -90,6 +116,11 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or Set height of the contents
|
||||||
|
* @param {number} [h]
|
||||||
|
* @returns {number} height
|
||||||
|
*/
|
||||||
contentHeight(h) {
|
contentHeight(h) {
|
||||||
|
|
||||||
var content = this.content || this.document.body;
|
var content = this.content || this.document.body;
|
||||||
|
@ -106,6 +137,10 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the width of the text using Range
|
||||||
|
* @returns {number} width
|
||||||
|
*/
|
||||||
textWidth() {
|
textWidth() {
|
||||||
let width;
|
let width;
|
||||||
let range = this.document.createRange();
|
let range = this.document.createRange();
|
||||||
|
@ -125,6 +160,10 @@ class Contents {
|
||||||
return Math.round(width);
|
return Math.round(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the height of the text using Range
|
||||||
|
* @returns {number} height
|
||||||
|
*/
|
||||||
textHeight() {
|
textHeight() {
|
||||||
let height;
|
let height;
|
||||||
let range = this.document.createRange();
|
let range = this.document.createRange();
|
||||||
|
@ -142,18 +181,30 @@ class Contents {
|
||||||
return Math.round(height);
|
return Math.round(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get documentElement scrollWidth
|
||||||
|
* @returns {number} width
|
||||||
|
*/
|
||||||
scrollWidth() {
|
scrollWidth() {
|
||||||
var width = this.documentElement.scrollWidth;
|
var width = this.documentElement.scrollWidth;
|
||||||
|
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get documentElement scrollHeight
|
||||||
|
* @returns {number} height
|
||||||
|
*/
|
||||||
scrollHeight() {
|
scrollHeight() {
|
||||||
var height = this.documentElement.scrollHeight;
|
var height = this.documentElement.scrollHeight;
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set overflow css style of the contents
|
||||||
|
* @param {string} [overflow]
|
||||||
|
*/
|
||||||
overflow(overflow) {
|
overflow(overflow) {
|
||||||
|
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
|
@ -163,6 +214,10 @@ class Contents {
|
||||||
return this.window.getComputedStyle(this.documentElement)["overflow"];
|
return this.window.getComputedStyle(this.documentElement)["overflow"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set overflowX css style of the documentElement
|
||||||
|
* @param {string} [overflow]
|
||||||
|
*/
|
||||||
overflowX(overflow) {
|
overflowX(overflow) {
|
||||||
|
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
|
@ -172,6 +227,10 @@ class Contents {
|
||||||
return this.window.getComputedStyle(this.documentElement)["overflowX"];
|
return this.window.getComputedStyle(this.documentElement)["overflowX"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set overflowY css style of the documentElement
|
||||||
|
* @param {string} [overflow]
|
||||||
|
*/
|
||||||
overflowY(overflow) {
|
overflowY(overflow) {
|
||||||
|
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
|
@ -181,6 +240,12 @@ class Contents {
|
||||||
return this.window.getComputedStyle(this.documentElement)["overflowY"];
|
return this.window.getComputedStyle(this.documentElement)["overflowY"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Css styles on the contents element (typically Body)
|
||||||
|
* @param {string} property
|
||||||
|
* @param {string} value
|
||||||
|
* @param {boolean} [priority] set as "important"
|
||||||
|
*/
|
||||||
css(property, value, priority) {
|
css(property, value, priority) {
|
||||||
var content = this.content || this.document.body;
|
var content = this.content || this.document.body;
|
||||||
|
|
||||||
|
@ -191,6 +256,16 @@ class Contents {
|
||||||
return this.window.getComputedStyle(content)[property];
|
return this.window.getComputedStyle(content)[property];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or Set the viewport element
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {string} [options.width]
|
||||||
|
* @param {string} [options.height]
|
||||||
|
* @param {string} [options.scale]
|
||||||
|
* @param {string} [options.minimum]
|
||||||
|
* @param {string} [options.maximum]
|
||||||
|
* @param {string} [options.scalable]
|
||||||
|
*/
|
||||||
viewport(options) {
|
viewport(options) {
|
||||||
var _width, _height, _scale, _minimum, _maximum, _scalable;
|
var _width, _height, _scale, _minimum, _maximum, _scalable;
|
||||||
// var width, height, scale, minimum, maximum, scalable;
|
// var width, height, scale, minimum, maximum, scalable;
|
||||||
|
@ -288,32 +363,18 @@ class Contents {
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
// layout(layoutFunc) {
|
* Event emitter for when the contents has expanded
|
||||||
//
|
* @private
|
||||||
// this.iframe.style.display = "inline-block";
|
*/
|
||||||
//
|
|
||||||
// // Reset Body Styles
|
|
||||||
// this.content.style.margin = "0";
|
|
||||||
// //this.document.body.style.display = "inline-block";
|
|
||||||
// //this.document.documentElement.style.width = "auto";
|
|
||||||
//
|
|
||||||
// if(layoutFunc){
|
|
||||||
// layoutFunc(this);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.onLayout(this);
|
|
||||||
//
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// onLayout(view) {
|
|
||||||
// // stub
|
|
||||||
// };
|
|
||||||
|
|
||||||
expand() {
|
expand() {
|
||||||
this.emit(EVENTS.CONTENTS.EXPAND);
|
this.emit(EVENTS.CONTENTS.EXPAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add DOM listeners
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
listeners() {
|
listeners() {
|
||||||
|
|
||||||
this.imageLoadListeners();
|
this.imageLoadListeners();
|
||||||
|
@ -335,6 +396,10 @@ class Contents {
|
||||||
this.linksHandler();
|
this.linksHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove DOM listeners
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
removeListeners() {
|
removeListeners() {
|
||||||
|
|
||||||
this.removeEventListeners();
|
this.removeEventListeners();
|
||||||
|
@ -344,6 +409,11 @@ class Contents {
|
||||||
clearTimeout(this.expanding);
|
clearTimeout(this.expanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if size of contents has changed and
|
||||||
|
* emit 'resize' event if it has.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
resizeCheck() {
|
resizeCheck() {
|
||||||
let width = this.textWidth();
|
let width = this.textWidth();
|
||||||
let height = this.textHeight();
|
let height = this.textHeight();
|
||||||
|
@ -360,6 +430,10 @@ class Contents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Poll for resize detection
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
resizeListeners() {
|
resizeListeners() {
|
||||||
var width, height;
|
var width, height;
|
||||||
// Test size again
|
// Test size again
|
||||||
|
@ -370,6 +444,10 @@ class Contents {
|
||||||
this.expanding = setTimeout(this.resizeListeners.bind(this), 350);
|
this.expanding = setTimeout(this.resizeListeners.bind(this), 350);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use css transitions to detect resize
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
transitionListeners() {
|
transitionListeners() {
|
||||||
let body = this.content;
|
let body = this.content;
|
||||||
|
|
||||||
|
@ -381,13 +459,16 @@ class Contents {
|
||||||
this.document.addEventListener('transitionend', this.resizeCheck.bind(this));
|
this.document.addEventListener('transitionend', this.resizeCheck.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
//https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js
|
/**
|
||||||
|
* Listen for media query changes and emit 'expand' event
|
||||||
|
* Adapted from: https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
mediaQueryListeners() {
|
mediaQueryListeners() {
|
||||||
var sheets = this.document.styleSheets;
|
var sheets = this.document.styleSheets;
|
||||||
var mediaChangeHandler = function(m){
|
var mediaChangeHandler = function(m){
|
||||||
if(m.matches && !this._expanding) {
|
if(m.matches && !this._expanding) {
|
||||||
setTimeout(this.expand.bind(this), 1);
|
setTimeout(this.expand.bind(this), 1);
|
||||||
// this.expand();
|
|
||||||
}
|
}
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
|
@ -411,6 +492,10 @@ class Contents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use MutationObserver to listen for changes in the DOM and check for resize
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
resizeObservers() {
|
resizeObservers() {
|
||||||
// create an observer instance
|
// create an observer instance
|
||||||
this.observer = new MutationObserver((mutations) => {
|
this.observer = new MutationObserver((mutations) => {
|
||||||
|
@ -437,22 +522,36 @@ class Contents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for font load and check for resize when loaded
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
fontLoadListeners(target) {
|
fontLoadListeners(target) {
|
||||||
if (!this.document || !this.document.fonts) {
|
if (!this.document || !this.document.fonts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.document.fonts.ready.then(function () {
|
this.document.fonts.ready.then(function () {
|
||||||
this.expand();
|
this.resizeCheck();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the documentElement
|
||||||
|
* @returns {element} documentElement
|
||||||
|
*/
|
||||||
root() {
|
root() {
|
||||||
if(!this.document) return null;
|
if(!this.document) return null;
|
||||||
return this.document.documentElement;
|
return this.document.documentElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the location offset of a EpubCFI or an #id
|
||||||
|
* @param {string | EpubCFI} target
|
||||||
|
* @param {string} [ignoreClass] for the cfi
|
||||||
|
* @returns { {left: Number, top: Number }
|
||||||
|
*/
|
||||||
locationOf(target, ignoreClass) {
|
locationOf(target, ignoreClass) {
|
||||||
var position;
|
var position;
|
||||||
var targetPos = {"left": 0, "top": 0};
|
var targetPos = {"left": 0, "top": 0};
|
||||||
|
@ -517,6 +616,10 @@ class Contents {
|
||||||
return targetPos;
|
return targetPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a stylesheet link to the document head
|
||||||
|
* @param {string} src url
|
||||||
|
*/
|
||||||
addStylesheet(src) {
|
addStylesheet(src) {
|
||||||
return new Promise(function(resolve, reject){
|
return new Promise(function(resolve, reject){
|
||||||
var $stylesheet;
|
var $stylesheet;
|
||||||
|
@ -553,8 +656,12 @@ class Contents {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule
|
/**
|
||||||
// Object: https://github.com/desirable-objects/json-to-css
|
* Append stylesheet rules to a generate stylesheet
|
||||||
|
* Array: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule
|
||||||
|
* Object: https://github.com/desirable-objects/json-to-css
|
||||||
|
* @param {array | object} rules
|
||||||
|
*/
|
||||||
addStylesheetRules(rules) {
|
addStylesheetRules(rules) {
|
||||||
var styleEl;
|
var styleEl;
|
||||||
var styleSheet;
|
var styleSheet;
|
||||||
|
@ -615,6 +722,11 @@ class Contents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a script tag to the document head
|
||||||
|
* @param {string} src url
|
||||||
|
* @returns {Promise} loaded
|
||||||
|
*/
|
||||||
addScript(src) {
|
addScript(src) {
|
||||||
|
|
||||||
return new Promise(function(resolve, reject){
|
return new Promise(function(resolve, reject){
|
||||||
|
@ -644,6 +756,10 @@ class Contents {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a class to the contents container
|
||||||
|
* @param {string} className
|
||||||
|
*/
|
||||||
addClass(className) {
|
addClass(className) {
|
||||||
var content;
|
var content;
|
||||||
|
|
||||||
|
@ -657,6 +773,10 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a class from the contents container
|
||||||
|
* @param {string} removeClass
|
||||||
|
*/
|
||||||
removeClass(className) {
|
removeClass(className) {
|
||||||
var content;
|
var content;
|
||||||
|
|
||||||
|
@ -670,6 +790,10 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add DOM event listeners
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
addEventListeners(){
|
addEventListeners(){
|
||||||
if(!this.document) {
|
if(!this.document) {
|
||||||
return;
|
return;
|
||||||
|
@ -681,6 +805,10 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove DOM event listeners
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
removeEventListeners(){
|
removeEventListeners(){
|
||||||
if(!this.document) {
|
if(!this.document) {
|
||||||
return;
|
return;
|
||||||
|
@ -691,11 +819,18 @@ class Contents {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass browser events
|
/**
|
||||||
|
* Emit passed browser events
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
triggerEvent(e){
|
triggerEvent(e){
|
||||||
this.emit(e.type, e);
|
this.emit(e.type, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add listener for text selection
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
addSelectionListeners(){
|
addSelectionListeners(){
|
||||||
if(!this.document) {
|
if(!this.document) {
|
||||||
return;
|
return;
|
||||||
|
@ -703,6 +838,10 @@ class Contents {
|
||||||
this.document.addEventListener("selectionchange", this.onSelectionChange.bind(this), false);
|
this.document.addEventListener("selectionchange", this.onSelectionChange.bind(this), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove listener for text selection
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
removeSelectionListeners(){
|
removeSelectionListeners(){
|
||||||
if(!this.document) {
|
if(!this.document) {
|
||||||
return;
|
return;
|
||||||
|
@ -710,6 +849,10 @@ class Contents {
|
||||||
this.document.removeEventListener("selectionchange", this.onSelectionChange, false);
|
this.document.removeEventListener("selectionchange", this.onSelectionChange, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle getting text on selection
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
onSelectionChange(e){
|
onSelectionChange(e){
|
||||||
if (this.selectionEndTimeout) {
|
if (this.selectionEndTimeout) {
|
||||||
clearTimeout(this.selectionEndTimeout);
|
clearTimeout(this.selectionEndTimeout);
|
||||||
|
@ -720,6 +863,10 @@ class Contents {
|
||||||
}.bind(this), 250);
|
}.bind(this), 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit event on text selection
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
triggerSelectedEvent(selection){
|
triggerSelectedEvent(selection){
|
||||||
var range, cfirange;
|
var range, cfirange;
|
||||||
|
|
||||||
|
@ -734,24 +881,48 @@ class Contents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Dom Range from EpubCFI
|
||||||
|
* @param {EpubCFI} _cfi
|
||||||
|
* @param {string} [ignoreClass]
|
||||||
|
* @returns {Range} range
|
||||||
|
*/
|
||||||
range(_cfi, ignoreClass){
|
range(_cfi, ignoreClass){
|
||||||
var cfi = new EpubCFI(_cfi);
|
var cfi = new EpubCFI(_cfi);
|
||||||
return cfi.toRange(this.document, ignoreClass);
|
return cfi.toRange(this.document, ignoreClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an EpubCFI from a Dom Range
|
||||||
|
* @param {Range} range
|
||||||
|
* @param {string} [ignoreClass]
|
||||||
|
* @returns {EpubCFI} cfi
|
||||||
|
*/
|
||||||
cfiFromRange(range, ignoreClass){
|
cfiFromRange(range, ignoreClass){
|
||||||
return new EpubCFI(range, this.cfiBase, ignoreClass).toString();
|
return new EpubCFI(range, this.cfiBase, ignoreClass).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an EpubCFI from a Dom node
|
||||||
|
* @param {node} node
|
||||||
|
* @param {string} [ignoreClass]
|
||||||
|
* @returns {EpubCFI} cfi
|
||||||
|
*/
|
||||||
cfiFromNode(node, ignoreClass){
|
cfiFromNode(node, ignoreClass){
|
||||||
return new EpubCFI(node, this.cfiBase, ignoreClass).toString();
|
return new EpubCFI(node, this.cfiBase, ignoreClass).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: find where this is used - remove?
|
||||||
map(layout){
|
map(layout){
|
||||||
var map = new Mapping(layout);
|
var map = new Mapping(layout);
|
||||||
return map.section();
|
return map.section();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size the contents to a given width and height
|
||||||
|
* @param {number} [width]
|
||||||
|
* @param {number} [height]
|
||||||
|
*/
|
||||||
size(width, height){
|
size(width, height){
|
||||||
var viewport = { scale: 1.0, scalable: "no" };
|
var viewport = { scale: 1.0, scalable: "no" };
|
||||||
|
|
||||||
|
@ -775,6 +946,13 @@ class Contents {
|
||||||
this.viewport(viewport);
|
this.viewport(viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply columns to the contents for pagination
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @param {number} columnWidth
|
||||||
|
* @param {number} gap
|
||||||
|
*/
|
||||||
columns(width, height, columnWidth, gap){
|
columns(width, height, columnWidth, gap){
|
||||||
var COLUMN_AXIS = prefixed("column-axis");
|
var COLUMN_AXIS = prefixed("column-axis");
|
||||||
var COLUMN_GAP = prefixed("column-gap");
|
var COLUMN_GAP = prefixed("column-gap");
|
||||||
|
@ -810,6 +988,12 @@ class Contents {
|
||||||
this.css(COLUMN_WIDTH, columnWidth+"px");
|
this.css(COLUMN_WIDTH, columnWidth+"px");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale contents from center
|
||||||
|
* @param {number} scale
|
||||||
|
* @param {number} offsetX
|
||||||
|
* @param {number} offsetY
|
||||||
|
*/
|
||||||
scaler(scale, offsetX, offsetY){
|
scaler(scale, offsetX, offsetY){
|
||||||
var scaleStr = "scale(" + scale + ")";
|
var scaleStr = "scale(" + scale + ")";
|
||||||
var translateStr = "";
|
var translateStr = "";
|
||||||
|
@ -823,6 +1007,11 @@ class Contents {
|
||||||
this.css("transform", scaleStr + translateStr);
|
this.css("transform", scaleStr + translateStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fit contents into a fixed width and height
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
*/
|
||||||
fit(width, height){
|
fit(width, height){
|
||||||
var viewport = this.viewport();
|
var viewport = this.viewport();
|
||||||
var widthScale = width / parseInt(viewport.width);
|
var widthScale = width / parseInt(viewport.width);
|
||||||
|
@ -843,6 +1032,10 @@ class Contents {
|
||||||
this.css("background-color", "transparent");
|
this.css("background-color", "transparent");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the direction of the text
|
||||||
|
* @param {string} [dir="ltr"] "rtl" | "ltr"
|
||||||
|
*/
|
||||||
direction(dir) {
|
direction(dir) {
|
||||||
if (this.documentElement) {
|
if (this.documentElement) {
|
||||||
this.documentElement.style["direction"] = dir;
|
this.documentElement.style["direction"] = dir;
|
||||||
|
@ -855,12 +1048,20 @@ class Contents {
|
||||||
return mapping.page(this, cfiBase, start, end);
|
return mapping.page(this, cfiBase, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit event when link in content is clicked
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
linksHandler() {
|
linksHandler() {
|
||||||
replaceLinks(this.content, (href) => {
|
replaceLinks(this.content, (href) => {
|
||||||
this.emit(EVENTS.CONTENTS.LINK_CLICKED, href);
|
this.emit(EVENTS.CONTENTS.LINK_CLICKED, href);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the writingMode of the text
|
||||||
|
* @param {string} [mode="horizontal-tb"] "horizontal-tb" | "vertical-rl" | "vertical-lr"
|
||||||
|
*/
|
||||||
writingMode(mode) {
|
writingMode(mode) {
|
||||||
let WRITING_MODE = prefixed("writing-mode");
|
let WRITING_MODE = prefixed("writing-mode");
|
||||||
|
|
||||||
|
@ -871,6 +1072,11 @@ class Contents {
|
||||||
return this.window.getComputedStyle(this.documentElement)[WRITING_MODE] || '';
|
return this.window.getComputedStyle(this.documentElement)[WRITING_MODE] || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the layoutStyle of the content
|
||||||
|
* @param {string} [style="paginated"] "scrolling" | "paginated"
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
layoutStyle(style) {
|
layoutStyle(style) {
|
||||||
|
|
||||||
if (style) {
|
if (style) {
|
||||||
|
@ -881,6 +1087,12 @@ class Contents {
|
||||||
return this._layoutStyle || "paginated";
|
return this._layoutStyle || "paginated";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the epubReadingSystem object to the navigator
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} version
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
epubReadingSystem(name, version) {
|
epubReadingSystem(name, version) {
|
||||||
navigator.epubReadingSystem = {
|
navigator.epubReadingSystem = {
|
||||||
name: name,
|
name: name,
|
||||||
|
|
14
src/epub.js
14
src/epub.js
|
@ -1,10 +1,14 @@
|
||||||
import Book from "./book";
|
import Book from "./book";
|
||||||
import EpubCFI from "./epubcfi";
|
|
||||||
import Rendition from "./rendition";
|
import Rendition from "./rendition";
|
||||||
|
import EpubCFI from "./epubcfi";
|
||||||
import Contents from "./contents";
|
import Contents from "./contents";
|
||||||
import * as core from "./utils/core";
|
import * as core from "./utils/core";
|
||||||
import '../libs/url/url-polyfill'
|
import '../libs/url/url-polyfill'
|
||||||
|
|
||||||
|
import IframeView from "./managers/views/iframe";
|
||||||
|
import DefaultViewManager from "./managers/default";
|
||||||
|
import ContinuousViewManager from "./managers/continuous";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Book
|
* Creates a new Book
|
||||||
* @param {string|ArrayBuffer} url URL, Path or ArrayBuffer
|
* @param {string|ArrayBuffer} url URL, Path or ArrayBuffer
|
||||||
|
@ -30,7 +34,7 @@ ePub.utils = core;
|
||||||
ePub.ViewManagers = {};
|
ePub.ViewManagers = {};
|
||||||
ePub.Views = {};
|
ePub.Views = {};
|
||||||
/**
|
/**
|
||||||
* register plugins
|
* Register Managers and Views
|
||||||
*/
|
*/
|
||||||
ePub.register = {
|
ePub.register = {
|
||||||
/**
|
/**
|
||||||
|
@ -48,10 +52,10 @@ ePub.register = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default Views
|
// Default Views
|
||||||
ePub.register.view("iframe", require("./managers/views/iframe"));
|
ePub.register.view("iframe", IframeView);
|
||||||
|
|
||||||
// Default View Managers
|
// Default View Managers
|
||||||
ePub.register.manager("default", require("./managers/default"));
|
ePub.register.manager("default", DefaultViewManager);
|
||||||
ePub.register.manager("continuous", require("./managers/continuous"));
|
ePub.register.manager("continuous", ContinuousViewManager);
|
||||||
|
|
||||||
export default ePub;
|
export default ePub;
|
||||||
|
|
|
@ -1,24 +1,27 @@
|
||||||
import {extend, type, findChildren, RangeObject, isNumber} from "./utils/core";
|
import {extend, type, findChildren, RangeObject, isNumber} from "./utils/core";
|
||||||
|
|
||||||
/**
|
|
||||||
EPUB CFI spec: http://www.idpf.org/epub/linking/cfi/epub-cfi.html
|
|
||||||
|
|
||||||
Implements:
|
|
||||||
- Character Offset: epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)
|
|
||||||
- Simple Ranges : epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)
|
|
||||||
|
|
||||||
Does Not Implement:
|
|
||||||
- Temporal Offset (~)
|
|
||||||
- Spatial Offset (@)
|
|
||||||
- Temporal-Spatial Offset (~ + @)
|
|
||||||
- Text Location Assertion ([)
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ELEMENT_NODE = 1;
|
const ELEMENT_NODE = 1;
|
||||||
const TEXT_NODE = 3;
|
const TEXT_NODE = 3;
|
||||||
// const COMMENT_NODE = 8;
|
const COMMENT_NODE = 8;
|
||||||
const DOCUMENT_NODE = 9;
|
const DOCUMENT_NODE = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsing and creation of EpubCFIs: http://www.idpf.org/epub/linking/cfi/epub-cfi.html
|
||||||
|
|
||||||
|
* Implements:
|
||||||
|
* - Character Offset: epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)
|
||||||
|
* - Simple Ranges : epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)
|
||||||
|
|
||||||
|
* Does Not Implement:
|
||||||
|
* - Temporal Offset (~)
|
||||||
|
* - Spatial Offset (@)
|
||||||
|
* - Temporal-Spatial Offset (~ + @)
|
||||||
|
* - Text Location Assertion ([)
|
||||||
|
* @class
|
||||||
|
@param {string | Range | Node } [cfiFrom]
|
||||||
|
@param {string | object} [base]
|
||||||
|
@param {string} [ignoreClass] class to ignore when parsing DOM
|
||||||
|
*/
|
||||||
class EpubCFI {
|
class EpubCFI {
|
||||||
constructor(cfiFrom, base, ignoreClass){
|
constructor(cfiFrom, base, ignoreClass){
|
||||||
var type;
|
var type;
|
||||||
|
@ -65,6 +68,10 @@ class EpubCFI {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the type of constructor input
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
checkType(cfi) {
|
checkType(cfi) {
|
||||||
|
|
||||||
if (this.isCfiString(cfi)) {
|
if (this.isCfiString(cfi)) {
|
||||||
|
@ -81,6 +88,11 @@ class EpubCFI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a cfi string to a CFI object representation
|
||||||
|
* @param {string} cfiStr
|
||||||
|
* @returns {object} cfi
|
||||||
|
*/
|
||||||
parse(cfiStr) {
|
parse(cfiStr) {
|
||||||
var cfi = {
|
var cfi = {
|
||||||
spinePos: -1,
|
spinePos: -1,
|
||||||
|
@ -289,6 +301,10 @@ class EpubCFI {
|
||||||
return segmentString;
|
return segmentString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert CFI to a epubcfi(...) string
|
||||||
|
* @returns {string} epubcfi
|
||||||
|
*/
|
||||||
toString() {
|
toString() {
|
||||||
var cfiString = "epubcfi(";
|
var cfiString = "epubcfi(";
|
||||||
|
|
||||||
|
@ -313,6 +329,11 @@ class EpubCFI {
|
||||||
return cfiString;
|
return cfiString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare which of two CFIs is earlier in the text
|
||||||
|
* @returns {number} First is earlier = 1, Second is earlier = -1, They are equal = 0
|
||||||
|
*/
|
||||||
compare(cfiOne, cfiTwo) {
|
compare(cfiOne, cfiTwo) {
|
||||||
var stepsA, stepsB;
|
var stepsA, stepsB;
|
||||||
var terminalA, terminalB;
|
var terminalA, terminalB;
|
||||||
|
@ -477,6 +498,13 @@ class EpubCFI {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a CFI object from a Range
|
||||||
|
* @param {Range} range
|
||||||
|
* @param {string | object} base
|
||||||
|
* @param {string} [ignoreClass]
|
||||||
|
* @returns {object} cfi
|
||||||
|
*/
|
||||||
fromRange(range, base, ignoreClass) {
|
fromRange(range, base, ignoreClass) {
|
||||||
var cfi = {
|
var cfi = {
|
||||||
range: false,
|
range: false,
|
||||||
|
@ -564,6 +592,13 @@ class EpubCFI {
|
||||||
return cfi;
|
return cfi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a CFI object from a Node
|
||||||
|
* @param {Node} anchor
|
||||||
|
* @param {string | object} base
|
||||||
|
* @param {string} [ignoreClass]
|
||||||
|
* @returns {object} cfi
|
||||||
|
*/
|
||||||
fromNode(anchor, base, ignoreClass) {
|
fromNode(anchor, base, ignoreClass) {
|
||||||
var cfi = {
|
var cfi = {
|
||||||
range: false,
|
range: false,
|
||||||
|
@ -886,6 +921,12 @@ class EpubCFI {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a DOM range representing a CFI
|
||||||
|
* @param {document} _doc document referenced in the base
|
||||||
|
* @param {string} [ignoreClass]
|
||||||
|
* @return {Range}
|
||||||
|
*/
|
||||||
toRange(_doc, ignoreClass) {
|
toRange(_doc, ignoreClass) {
|
||||||
var doc = _doc || document;
|
var doc = _doc || document;
|
||||||
var range;
|
var range;
|
||||||
|
@ -953,7 +994,11 @@ class EpubCFI {
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is a cfi string, should be wrapped with "epubcfi()"
|
/**
|
||||||
|
* Check if a string is wrapped with "epubcfi()"
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
isCfiString(str) {
|
isCfiString(str) {
|
||||||
if(typeof str === "string" &&
|
if(typeof str === "string" &&
|
||||||
str.indexOf("epubcfi(") === 0 &&
|
str.indexOf("epubcfi(") === 0 &&
|
||||||
|
@ -978,6 +1023,10 @@ class EpubCFI {
|
||||||
return cfi;
|
return cfi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collapse a CFI Range to a single CFI Position
|
||||||
|
* @param {boolean} [toStart=false]
|
||||||
|
*/
|
||||||
collapse(toStart) {
|
collapse(toStart) {
|
||||||
if (!this.range) {
|
if (!this.range) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* Figures out the CSS to apply for a layout
|
* Figures out the CSS values to apply for a layout
|
||||||
* @class
|
* @class
|
||||||
* @param {object} settings
|
* @param {object} settings
|
||||||
* @param {[string=reflowable]} settings.layout
|
* @param {string} [settings.layout='reflowable']
|
||||||
* @param {[string]} settings.spread
|
* @param {string} [settings.spread]
|
||||||
* @param {[int=800]} settings.minSpreadWidth
|
* @param {int} [settings.minSpreadWidth=800]
|
||||||
* @param {[boolean=false]} settings.evenSpreads
|
* @param {boolean} [settings.evenSpreads=false]
|
||||||
*/
|
*/
|
||||||
class Layout {
|
class Layout {
|
||||||
constructor(settings) {
|
constructor(settings) {
|
||||||
|
@ -166,7 +166,7 @@ class Layout {
|
||||||
/**
|
/**
|
||||||
* Apply Css to a Document
|
* Apply Css to a Document
|
||||||
* @param {Contents} contents
|
* @param {Contents} contents
|
||||||
* @return {[Promise]}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
format(contents){
|
format(contents){
|
||||||
var formating;
|
var formating;
|
||||||
|
@ -184,12 +184,12 @@ class Layout {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count number of pages
|
* Count number of pages
|
||||||
* @param {number} totalWidth
|
* @param {number} totalLength
|
||||||
* @return {number} spreads
|
* @param {number} pageLength
|
||||||
* @return {number} pages
|
* @return {{spreads: Number, pages: Number}}
|
||||||
*/
|
*/
|
||||||
count(totalLength, pageLength) {
|
count(totalLength, pageLength) {
|
||||||
// var totalWidth = contents.scrollWidth();
|
|
||||||
let spreads, pages;
|
let spreads, pages;
|
||||||
|
|
||||||
if (this.name === "pre-paginated") {
|
if (this.name === "pre-paginated") {
|
||||||
|
|
|
@ -171,6 +171,11 @@ class Locations {
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a location from an EpubCFI
|
||||||
|
* @param {EpubCFI} cfi
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
locationFromCfi(cfi){
|
locationFromCfi(cfi){
|
||||||
let loc;
|
let loc;
|
||||||
if (EpubCFI.prototype.isCfiString(cfi)) {
|
if (EpubCFI.prototype.isCfiString(cfi)) {
|
||||||
|
@ -190,6 +195,11 @@ class Locations {
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a percentage position in locations from an EpubCFI
|
||||||
|
* @param {EpubCFI} cfi
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
percentageFromCfi(cfi) {
|
percentageFromCfi(cfi) {
|
||||||
if(this._locations.length === 0) {
|
if(this._locations.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -200,6 +210,11 @@ class Locations {
|
||||||
return this.percentageFromLocation(loc);
|
return this.percentageFromLocation(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a percentage position from a location index
|
||||||
|
* @param {number} location
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
percentageFromLocation(loc) {
|
percentageFromLocation(loc) {
|
||||||
if (!loc || !this.total) {
|
if (!loc || !this.total) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -208,6 +223,11 @@ class Locations {
|
||||||
return (loc / this.total);
|
return (loc / this.total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an EpubCFI from location index
|
||||||
|
* @param {number} loc
|
||||||
|
* @return {EpubCFI} cfi
|
||||||
|
*/
|
||||||
cfiFromLocation(loc){
|
cfiFromLocation(loc){
|
||||||
var cfi = -1;
|
var cfi = -1;
|
||||||
// check that pg is an int
|
// check that pg is an int
|
||||||
|
@ -222,6 +242,11 @@ class Locations {
|
||||||
return cfi;
|
return cfi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an EpubCFI from location percentage
|
||||||
|
* @param {number} percentage
|
||||||
|
* @return {EpubCFI} cfi
|
||||||
|
*/
|
||||||
cfiFromPercentage(percentage){
|
cfiFromPercentage(percentage){
|
||||||
let loc;
|
let loc;
|
||||||
if (percentage > 1) {
|
if (percentage > 1) {
|
||||||
|
@ -239,6 +264,10 @@ class Locations {
|
||||||
return this.cfiFromLocation(loc);
|
return this.cfiFromLocation(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load locations from JSON
|
||||||
|
* @param {json} locations
|
||||||
|
*/
|
||||||
load(locations){
|
load(locations){
|
||||||
if (typeof locations === "string") {
|
if (typeof locations === "string") {
|
||||||
this._locations = JSON.parse(locations);
|
this._locations = JSON.parse(locations);
|
||||||
|
@ -249,11 +278,15 @@ class Locations {
|
||||||
return this._locations;
|
return this._locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
save(json){
|
/**
|
||||||
|
* Save locations to JSON
|
||||||
|
* @return {json}
|
||||||
|
*/
|
||||||
|
save(){
|
||||||
return JSON.stringify(this._locations);
|
return JSON.stringify(this._locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrent(json){
|
getCurrent(){
|
||||||
return this._current;
|
return this._current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,14 +317,23 @@ class Locations {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current location
|
||||||
|
*/
|
||||||
get currentLocation() {
|
get currentLocation() {
|
||||||
return this._current;
|
return this._current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current location
|
||||||
|
*/
|
||||||
set currentLocation(curr) {
|
set currentLocation(curr) {
|
||||||
this.setCurrent(curr);
|
this.setCurrent(curr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locations length
|
||||||
|
*/
|
||||||
length () {
|
length () {
|
||||||
return this._locations.length;
|
return this._locations.length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import EpubCFI from "./epubcfi";
|
import EpubCFI from "./epubcfi";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map text locations to CFI ranges
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
class Mapping {
|
class Mapping {
|
||||||
constructor(layout, direction, axis, dev) {
|
constructor(layout, direction, axis, dev) {
|
||||||
this.layout = layout;
|
this.layout = layout;
|
||||||
|
@ -8,6 +12,9 @@ class Mapping {
|
||||||
this._dev = dev;
|
this._dev = dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find CFI pairs for entire section at once
|
||||||
|
*/
|
||||||
section(view) {
|
section(view) {
|
||||||
var ranges = this.findRanges(view);
|
var ranges = this.findRanges(view);
|
||||||
var map = this.rangeListToCfiList(view.section.cfiBase, ranges);
|
var map = this.rangeListToCfiList(view.section.cfiBase, ranges);
|
||||||
|
@ -15,6 +22,9 @@ class Mapping {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find CFI pairs for a page
|
||||||
|
*/
|
||||||
page(contents, cfiBase, start, end) {
|
page(contents, cfiBase, start, end) {
|
||||||
var root = contents && contents.document ? contents.document.body : false;
|
var root = contents && contents.document ? contents.document.body : false;
|
||||||
var result;
|
var result;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page List Parser
|
* Page List Parser
|
||||||
* @param {[document]} xml
|
* @param {document} [xml]
|
||||||
*/
|
*/
|
||||||
class PageList {
|
class PageList {
|
||||||
constructor(xml) {
|
constructor(xml) {
|
||||||
|
|
254
src/rendition.js
254
src/rendition.js
|
@ -11,19 +11,22 @@ import Annotations from "./annotations";
|
||||||
import { EVENTS } from "./utils/constants";
|
import { EVENTS } from "./utils/constants";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [Rendition description]
|
* Displays an Epub as a series of Views for each Section.
|
||||||
|
* Requires Manager and View class to handle specifics of rendering
|
||||||
|
* the section contetn.
|
||||||
* @class
|
* @class
|
||||||
* @param {Book} book
|
* @param {Book} book
|
||||||
* @param {object} options
|
* @param {object} [options]
|
||||||
* @param {int} options.width
|
* @param {number} [options.width]
|
||||||
* @param {int} options.height
|
* @param {number} [options.height]
|
||||||
* @param {string} options.ignoreClass
|
* @param {string} [options.ignoreClass] class for the cfi parser to ignore
|
||||||
* @param {string} options.manager
|
* @param {string | function | object} [options.manager='default']
|
||||||
* @param {string} options.view
|
* @param {string | function} [options.view='iframe']
|
||||||
* @param {string} options.layout
|
* @param {string} [options.layout] layout to force
|
||||||
* @param {string} options.spread
|
* @param {string} [options.spread] force spread value
|
||||||
* @param {int} options.minSpreadWidth overridden by spread: none (never) / both (always)
|
* @param {number} [options.minSpreadWidth] overridden by spread: none (never) / both (always)
|
||||||
* @param {string} options.stylesheet url of stylesheet to be injected
|
* @param {string} [options.stylesheet] url of stylesheet to be injected
|
||||||
|
* @param {string} [options.script] url of script to be injected
|
||||||
*/
|
*/
|
||||||
class Rendition {
|
class Rendition {
|
||||||
constructor(book, options) {
|
constructor(book, options) {
|
||||||
|
@ -50,19 +53,15 @@ class Rendition {
|
||||||
|
|
||||||
this.book = book;
|
this.book = book;
|
||||||
|
|
||||||
// this.views = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds Hook methods to the Rendition prototype
|
* Adds Hook methods to the Rendition prototype
|
||||||
* @property {Hook} hooks
|
* @member {object} hooks
|
||||||
|
* @property {Hook} hooks.content
|
||||||
|
* @memberof Rendition
|
||||||
*/
|
*/
|
||||||
this.hooks = {};
|
this.hooks = {};
|
||||||
this.hooks.display = new Hook(this);
|
this.hooks.display = new Hook(this);
|
||||||
this.hooks.serialize = new Hook(this);
|
this.hooks.serialize = new Hook(this);
|
||||||
/**
|
|
||||||
* @property {method} hooks.content
|
|
||||||
* @type {Hook}
|
|
||||||
*/
|
|
||||||
this.hooks.content = new Hook(this);
|
this.hooks.content = new Hook(this);
|
||||||
this.hooks.unloaded = new Hook(this);
|
this.hooks.unloaded = new Hook(this);
|
||||||
this.hooks.layout = new Hook(this);
|
this.hooks.layout = new Hook(this);
|
||||||
|
@ -83,20 +82,60 @@ class Rendition {
|
||||||
this.book.spine.hooks.content.register(this.injectScript.bind(this));
|
this.book.spine.hooks.content.register(this.injectScript.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.hooks.display.register(this.afterDisplay.bind(this));
|
/**
|
||||||
|
* @member {Themes} themes
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.themes = new Themes(this);
|
this.themes = new Themes(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @member {Annotations} annotations
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.annotations = new Annotations(this);
|
this.annotations = new Annotations(this);
|
||||||
|
|
||||||
this.epubcfi = new EpubCFI();
|
this.epubcfi = new EpubCFI();
|
||||||
|
|
||||||
this.q = new Queue(this);
|
this.q = new Queue(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Rendered Location Range
|
||||||
|
* @typedef location
|
||||||
|
* @type {Object}
|
||||||
|
* @property {object} start
|
||||||
|
* @property {string} start.index
|
||||||
|
* @property {string} start.href
|
||||||
|
* @property {object} start.displayed
|
||||||
|
* @property {EpubCFI} start.cfi
|
||||||
|
* @property {number} start.location
|
||||||
|
* @property {number} start.percentage
|
||||||
|
* @property {number} start.displayed.page
|
||||||
|
* @property {number} start.displayed.total
|
||||||
|
* @property {object} end
|
||||||
|
* @property {string} end.index
|
||||||
|
* @property {string} end.href
|
||||||
|
* @property {object} end.displayed
|
||||||
|
* @property {EpubCFI} end.cfi
|
||||||
|
* @property {number} end.location
|
||||||
|
* @property {number} end.percentage
|
||||||
|
* @property {number} end.displayed.page
|
||||||
|
* @property {number} end.displayed.total
|
||||||
|
* @property {boolean} atStart
|
||||||
|
* @property {boolean} atEnd
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
|
this.location = undefined;
|
||||||
|
|
||||||
|
// Hold queue until book is opened
|
||||||
this.q.enqueue(this.book.opened);
|
this.q.enqueue(this.book.opened);
|
||||||
|
|
||||||
// Block the queue until rendering is started
|
|
||||||
this.starting = new defer();
|
this.starting = new defer();
|
||||||
|
/**
|
||||||
|
* @member {promise} started returns after the rendition has started
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.started = this.starting.promise;
|
this.started = this.starting.promise;
|
||||||
|
// Block the queue until rendering is started
|
||||||
this.q.enqueue(this.start);
|
this.q.enqueue(this.start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,20 +148,19 @@ class Rendition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Require the manager from passed string, or as a function
|
* Require the manager from passed string, or as a class function
|
||||||
* @param {string|function} manager [description]
|
* @param {string|object} manager [description]
|
||||||
* @return {method}
|
* @return {method}
|
||||||
*/
|
*/
|
||||||
requireManager(manager) {
|
requireManager(manager) {
|
||||||
var viewManager;
|
var viewManager;
|
||||||
|
|
||||||
// If manager is a string, try to load from register managers,
|
// If manager is a string, try to load from global registered managers
|
||||||
// or require included managers directly
|
if (typeof manager === "string" && typeof ePub != "undefined") {
|
||||||
if (typeof manager === "string") {
|
// Use global
|
||||||
// Use global or require
|
viewManager = ePub.ViewManagers[manager];
|
||||||
viewManager = typeof ePub != "undefined" ? ePub.ViewManagers[manager] : undefined; //require("./managers/"+manager);
|
|
||||||
} else {
|
} else {
|
||||||
// otherwise, assume we were passed a function
|
// otherwise, assume we were passed a class function
|
||||||
viewManager = manager;
|
viewManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,17 +168,19 @@ class Rendition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Require the view from passed string, or as a function
|
* Require the view from passed string, or as a class function
|
||||||
* @param {string|function} view
|
* @param {string|object} view
|
||||||
* @return {view}
|
* @return {view}
|
||||||
*/
|
*/
|
||||||
requireView(view) {
|
requireView(view) {
|
||||||
var View;
|
var View;
|
||||||
|
|
||||||
if (typeof view == "string") {
|
// If view is a string, try to load from global registered views,
|
||||||
View = typeof ePub != "undefined" ? ePub.Views[view] : undefined; //require("./views/"+view);
|
if (typeof view == "string" && typeof ePub != "undefined") {
|
||||||
|
// Use global
|
||||||
|
View = ePub.Views[view];
|
||||||
} else {
|
} else {
|
||||||
// otherwise, assume we were passed a function
|
// otherwise, assume we were passed a class function
|
||||||
View = view;
|
View = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +227,11 @@ class Rendition {
|
||||||
// Listen for scroll changes
|
// Listen for scroll changes
|
||||||
this.manager.on(EVENTS.MANAGERS.SCROLLED, this.reportLocation.bind(this));
|
this.manager.on(EVENTS.MANAGERS.SCROLLED, this.reportLocation.bind(this));
|
||||||
|
|
||||||
// Trigger that rendering has started
|
/**
|
||||||
|
* Emit that rendering has started
|
||||||
|
* @event started
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.STARTED);
|
this.emit(EVENTS.RENDITION.STARTED);
|
||||||
|
|
||||||
// Start processing queue
|
// Start processing queue
|
||||||
|
@ -210,7 +254,11 @@ class Rendition {
|
||||||
"height" : this.settings.height
|
"height" : this.settings.height
|
||||||
});
|
});
|
||||||
|
|
||||||
// Trigger Attached
|
/**
|
||||||
|
* Emit that rendering has attached to an element
|
||||||
|
* @event attached
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.ATTACHED);
|
this.emit(EVENTS.RENDITION.ATTACHED);
|
||||||
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
@ -270,9 +318,21 @@ class Rendition {
|
||||||
displaying.resolve(section);
|
displaying.resolve(section);
|
||||||
this.displaying = undefined;
|
this.displaying = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit that a section has been displayed
|
||||||
|
* @event displayed
|
||||||
|
* @param {Section} section
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.DISPLAYED, section);
|
this.emit(EVENTS.RENDITION.DISPLAYED, section);
|
||||||
this.reportLocation();
|
this.reportLocation();
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
|
/**
|
||||||
|
* Emit that has been an error displaying
|
||||||
|
* @event displayError
|
||||||
|
* @param {Section} section
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.DISPLAY_ERROR, err);
|
this.emit(EVENTS.RENDITION.DISPLAY_ERROR, err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -325,7 +385,7 @@ class Rendition {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report what has been displayed
|
* Report what section has been displayed
|
||||||
* @private
|
* @private
|
||||||
* @param {*} view
|
* @param {*} view
|
||||||
*/
|
*/
|
||||||
|
@ -337,6 +397,13 @@ class Rendition {
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (view.contents) {
|
if (view.contents) {
|
||||||
this.hooks.content.trigger(view.contents, this).then(() => {
|
this.hooks.content.trigger(view.contents, this).then(() => {
|
||||||
|
/**
|
||||||
|
* Emit that a section has been rendered
|
||||||
|
* @event rendered
|
||||||
|
* @param {Section} section
|
||||||
|
* @param {View} view
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.RENDERED, view.section, view);
|
this.emit(EVENTS.RENDITION.RENDERED, view.section, view);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -344,7 +411,6 @@ class Rendition {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// this.reportLocation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -354,6 +420,13 @@ class Rendition {
|
||||||
*/
|
*/
|
||||||
afterRemoved(view){
|
afterRemoved(view){
|
||||||
this.hooks.unloaded.trigger(view, this).then(() => {
|
this.hooks.unloaded.trigger(view, this).then(() => {
|
||||||
|
/**
|
||||||
|
* Emit that a section has been removed
|
||||||
|
* @event removed
|
||||||
|
* @param {Section} section
|
||||||
|
* @param {View} view
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.REMOVED, view.section, view);
|
this.emit(EVENTS.RENDITION.REMOVED, view.section, view);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -364,6 +437,13 @@ class Rendition {
|
||||||
*/
|
*/
|
||||||
onResized(size){
|
onResized(size){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit that the rendition has been resized
|
||||||
|
* @event resized
|
||||||
|
* @param {number} width
|
||||||
|
* @param {height} height
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.RESIZED, {
|
this.emit(EVENTS.RENDITION.RESIZED, {
|
||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height
|
height: size.height
|
||||||
|
@ -380,11 +460,12 @@ class Rendition {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
onOrientationChange(orientation){
|
onOrientationChange(orientation){
|
||||||
// Handled in resize event
|
/**
|
||||||
// if (this.location) {
|
* Emit that the rendition has been rotated
|
||||||
// this.display(this.location.start.cfi);
|
* @event orientationchange
|
||||||
// }
|
* @param {string} orientation
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.ORIENTATION_CHANGE, orientation);
|
this.emit(EVENTS.RENDITION.ORIENTATION_CHANGE, orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,14 +531,6 @@ class Rendition {
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyLayoutProperties(){
|
|
||||||
// var settings = this.determineLayoutProperties(this.book.package.metadata);
|
|
||||||
//
|
|
||||||
// this.flow(settings.flow);
|
|
||||||
//
|
|
||||||
// this.layout(settings);
|
|
||||||
// };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust the flow of the rendition to paginated or scrolled
|
* Adjust the flow of the rendition to paginated or scrolled
|
||||||
* (scrolled-continuous vs scrolled-doc are handled by different view managers)
|
* (scrolled-continuous vs scrolled-doc are handled by different view managers)
|
||||||
|
@ -548,7 +621,8 @@ class Rendition {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report the current location
|
* Report the current location
|
||||||
* @private
|
* @fires relocated
|
||||||
|
* @fires locationChanged
|
||||||
*/
|
*/
|
||||||
reportLocation(){
|
reportLocation(){
|
||||||
return this.q.enqueue(function reportedLocation(){
|
return this.q.enqueue(function reportedLocation(){
|
||||||
|
@ -583,6 +657,17 @@ class Rendition {
|
||||||
|
|
||||||
this.location = located;
|
this.location = located;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event locationChanged
|
||||||
|
* @deprecated
|
||||||
|
* @type {object}
|
||||||
|
* @property {number} index
|
||||||
|
* @property {string} href
|
||||||
|
* @property {EpubCFI} start
|
||||||
|
* @property {EpubCFI} end
|
||||||
|
* @property {number} percentage
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.LOCATION_CHANGED, {
|
this.emit(EVENTS.RENDITION.LOCATION_CHANGED, {
|
||||||
index: this.location.start.index,
|
index: this.location.start.index,
|
||||||
href: this.location.start.href,
|
href: this.location.start.href,
|
||||||
|
@ -591,6 +676,11 @@ class Rendition {
|
||||||
percentage: this.location.start.percentage
|
percentage: this.location.start.percentage
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @event relocated
|
||||||
|
* @type {displayedLocation}
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.RELOCATED, this.location);
|
this.emit(EVENTS.RENDITION.RELOCATED, this.location);
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
@ -598,8 +688,8 @@ class Rendition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Current Location CFI
|
* Get the Current Location object
|
||||||
* @return {EpubCFI} location (may be a promise)
|
* @return {displayedLocation | promise} location (may be a promise)
|
||||||
*/
|
*/
|
||||||
currentLocation(){
|
currentLocation(){
|
||||||
var location = this.manager.currentLocation();
|
var location = this.manager.currentLocation();
|
||||||
|
@ -614,6 +704,12 @@ class Rendition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Rendition#locationRange from location
|
||||||
|
* passed by the Manager
|
||||||
|
* @returns {displayedLocation}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
located(location){
|
located(location){
|
||||||
if (!location.length) {
|
if (!location.length) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -740,6 +836,13 @@ class Rendition {
|
||||||
* @param {EpubCFI} cfirange
|
* @param {EpubCFI} cfirange
|
||||||
*/
|
*/
|
||||||
triggerSelectedEvent(cfirange, contents){
|
triggerSelectedEvent(cfirange, contents){
|
||||||
|
/**
|
||||||
|
* Emit that a text selection has occured
|
||||||
|
* @event selected
|
||||||
|
* @param {EpubCFI} cfirange
|
||||||
|
* @param {Contents} contents
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.SELECTED, cfirange, contents);
|
this.emit(EVENTS.RENDITION.SELECTED, cfirange, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,6 +852,14 @@ class Rendition {
|
||||||
* @param {EpubCFI} cfirange
|
* @param {EpubCFI} cfirange
|
||||||
*/
|
*/
|
||||||
triggerMarkEvent(cfiRange, data, contents){
|
triggerMarkEvent(cfiRange, data, contents){
|
||||||
|
/**
|
||||||
|
* Emit that a mark was clicked
|
||||||
|
* @event markClicked
|
||||||
|
* @param {EpubCFI} cfirange
|
||||||
|
* @param {object} data
|
||||||
|
* @param {Contents} contents
|
||||||
|
* @memberof Rendition
|
||||||
|
*/
|
||||||
this.emit(EVENTS.RENDITION.MARK_CLICKED, cfiRange, data, contents);
|
this.emit(EVENTS.RENDITION.MARK_CLICKED, cfiRange, data, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -772,7 +883,8 @@ class Rendition {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to adjust images to fit in columns
|
* Hook to adjust images to fit in columns
|
||||||
* @param {View} view
|
* @param {Contents} contents
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
adjustImages(contents) {
|
adjustImages(contents) {
|
||||||
|
|
||||||
|
@ -799,15 +911,28 @@ class Rendition {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Contents object of each rendered view
|
||||||
|
* @returns {Contents[]}
|
||||||
|
*/
|
||||||
getContents () {
|
getContents () {
|
||||||
return this.manager ? this.manager.getContents() : [];
|
return this.manager ? this.manager.getContents() : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the views member from the manager
|
||||||
|
* @returns {Views}
|
||||||
|
*/
|
||||||
views () {
|
views () {
|
||||||
let views = this.manager ? this.manager.views : undefined;
|
let views = this.manager ? this.manager.views : undefined;
|
||||||
return views || [];
|
return views || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to handle link clicks in rendered content
|
||||||
|
* @param {Contents} contents
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
handleLinks(contents) {
|
handleLinks(contents) {
|
||||||
if (contents) {
|
if (contents) {
|
||||||
contents.on(EVENTS.CONTENTS.LINK_CLICKED, (href) => {
|
contents.on(EVENTS.CONTENTS.LINK_CLICKED, (href) => {
|
||||||
|
@ -817,6 +942,13 @@ class Rendition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to handle injecting stylesheet before
|
||||||
|
* a Section is serialized
|
||||||
|
* @param {document} doc
|
||||||
|
* @param {Section} section
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
injectStylesheet(doc, section) {
|
injectStylesheet(doc, section) {
|
||||||
let style = doc.createElement("link");
|
let style = doc.createElement("link");
|
||||||
style.setAttribute("type", "text/css");
|
style.setAttribute("type", "text/css");
|
||||||
|
@ -825,6 +957,13 @@ class Rendition {
|
||||||
doc.getElementsByTagName("head")[0].appendChild(style);
|
doc.getElementsByTagName("head")[0].appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to handle injecting scripts before
|
||||||
|
* a Section is serialized
|
||||||
|
* @param {document} doc
|
||||||
|
* @param {Section} section
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
injectScript(doc, section) {
|
injectScript(doc, section) {
|
||||||
let script = doc.createElement("script");
|
let script = doc.createElement("script");
|
||||||
script.setAttribute("type", "text/javascript");
|
script.setAttribute("type", "text/javascript");
|
||||||
|
@ -833,6 +972,13 @@ class Rendition {
|
||||||
doc.getElementsByTagName("head")[0].appendChild(script);
|
doc.getElementsByTagName("head")[0].appendChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to handle the document identifier before
|
||||||
|
* a Section is serialized
|
||||||
|
* @param {document} doc
|
||||||
|
* @param {Section} section
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
injectIdentifier(doc, section) {
|
injectIdentifier(doc, section) {
|
||||||
let ident = this.book.package.metadata.identifier;
|
let ident = this.book.package.metadata.identifier;
|
||||||
let meta = doc.createElement("meta");
|
let meta = doc.createElement("meta");
|
||||||
|
|
|
@ -150,8 +150,8 @@ class Resources {
|
||||||
/**
|
/**
|
||||||
* Replace URLs in CSS resources
|
* Replace URLs in CSS resources
|
||||||
* @private
|
* @private
|
||||||
* @param {[Archive]} archive
|
* @param {Archive} [archive]
|
||||||
* @param {[method]} resolver
|
* @param {method} [resolver]
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
replaceCss(archive, resolver){
|
replaceCss(archive, resolver){
|
||||||
|
@ -277,7 +277,7 @@ class Resources {
|
||||||
* Substitute urls in content, with replacements,
|
* Substitute urls in content, with replacements,
|
||||||
* relative to a url if provided
|
* relative to a url if provided
|
||||||
* @param {string} content
|
* @param {string} content
|
||||||
* @param {[string]} url url to resolve to
|
* @param {string} [url] url to resolve to
|
||||||
* @return {string} content with urls substituted
|
* @return {string} content with urls substituted
|
||||||
*/
|
*/
|
||||||
substitute(content, url) {
|
substitute(content, url) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { replaceBase } from "./utils/replacements";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Section of the Book
|
* Represents a Section of the Book
|
||||||
|
*
|
||||||
* In most books this is equivelent to a Chapter
|
* In most books this is equivelent to a Chapter
|
||||||
* @param {object} item The spine item representing the section
|
* @param {object} item The spine item representing the section
|
||||||
* @param {object} hooks hooks for serialize and content
|
* @param {object} hooks hooks for serialize and content
|
||||||
|
|
|
@ -112,7 +112,7 @@ class Spine {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an item from the spine
|
* Get an item from the spine
|
||||||
* @param {[string|int]} target
|
* @param {string|int} [target]
|
||||||
* @return {Section} section
|
* @return {Section} section
|
||||||
* @example spine.get();
|
* @example spine.get();
|
||||||
* @example spine.get(1);
|
* @example spine.get(1);
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import Url from "./utils/url";
|
import Url from "./utils/url";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Themes to apply to displayed content
|
||||||
|
* @class
|
||||||
|
* @param {Rendition} rendition
|
||||||
|
*/
|
||||||
class Themes {
|
class Themes {
|
||||||
constructor(rendition) {
|
constructor(rendition) {
|
||||||
this.rendition = rendition;
|
this.rendition = rendition;
|
||||||
|
@ -18,6 +23,13 @@ class Themes {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add themes to be used by a rendition
|
||||||
|
* @param {object | string}
|
||||||
|
* @example themes.register("light", "http://example.com/light.css")
|
||||||
|
* @example themes.register("light", { "body": { "color": "purple"}})
|
||||||
|
* @example themes.register({ "light" : {...}, "dark" : {...}})
|
||||||
|
*/
|
||||||
register () {
|
register () {
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -36,6 +48,12 @@ class Themes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a default theme to be used by a rendition
|
||||||
|
* @param {object | string} theme
|
||||||
|
* @example themes.register("http://example.com/default.css")
|
||||||
|
* @example themes.register({ "body": { "color": "purple"}})
|
||||||
|
*/
|
||||||
default (theme) {
|
default (theme) {
|
||||||
if (!theme) {
|
if (!theme) {
|
||||||
return;
|
return;
|
||||||
|
@ -154,10 +172,18 @@ class Themes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the font size of a rendition
|
||||||
|
* @param {number} size
|
||||||
|
*/
|
||||||
fontSize (size) {
|
fontSize (size) {
|
||||||
this.override("font-size", size);
|
this.override("font-size", size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust the font-family of a rendition
|
||||||
|
* @param {string} f
|
||||||
|
*/
|
||||||
font (f) {
|
font (f) {
|
||||||
this.override("font-family", f);
|
this.override("font-family", f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
/**
|
||||||
|
* Core Utilities and Helpers
|
||||||
|
* @module Core
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendor prefixed requestAnimationFrame
|
||||||
|
* @returns {function} requestAnimationFrame
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export const requestAnimationFrame = (typeof window != "undefined") ? (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame) : false;
|
export const requestAnimationFrame = (typeof window != "undefined") ? (window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame) : false;
|
||||||
const ELEMENT_NODE = 1;
|
const ELEMENT_NODE = 1;
|
||||||
const TEXT_NODE = 3;
|
const TEXT_NODE = 3;
|
||||||
|
@ -5,11 +15,12 @@ const COMMENT_NODE = 8;
|
||||||
const DOCUMENT_NODE = 9;
|
const DOCUMENT_NODE = 9;
|
||||||
const _URL = typeof URL != "undefined" ? URL : (typeof window != "undefined" ? (window.URL || window.webkitURL || window.mozURL) : undefined);
|
const _URL = typeof URL != "undefined" ? URL : (typeof window != "undefined" ? (window.URL || window.webkitURL || window.mozURL) : undefined);
|
||||||
|
|
||||||
export function isElement(obj) {
|
/**
|
||||||
return !!(obj && obj.nodeType == 1);
|
* Generates a UUID
|
||||||
}
|
* based on: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
||||||
|
* @returns {string} uuid
|
||||||
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function uuid() {
|
export function uuid() {
|
||||||
var d = new Date().getTime();
|
var d = new Date().getTime();
|
||||||
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
||||||
|
@ -20,6 +31,11 @@ export function uuid() {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the height of a document
|
||||||
|
* @returns {number} height
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function documentHeight() {
|
export function documentHeight() {
|
||||||
return Math.max(
|
return Math.max(
|
||||||
document.documentElement.clientHeight,
|
document.documentElement.clientHeight,
|
||||||
|
@ -30,15 +46,37 @@ export function documentHeight() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a node is an element
|
||||||
|
* @returns {boolean}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
|
export function isElement(obj) {
|
||||||
|
return !!(obj && obj.nodeType == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function isNumber(n) {
|
export function isNumber(n) {
|
||||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function isFloat(n) {
|
export function isFloat(n) {
|
||||||
let f = parseFloat(n);
|
let f = parseFloat(n);
|
||||||
return f === n && isNumber(n) && (Math.floor(f) !== n);
|
return f === n && isNumber(n) && (Math.floor(f) !== n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a prefixed css property
|
||||||
|
* @returns {string}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function prefixed(unprefixed) {
|
export function prefixed(unprefixed) {
|
||||||
var vendors = ["Webkit", "webkit", "Moz", "O", "ms" ];
|
var vendors = ["Webkit", "webkit", "Moz", "O", "ms" ];
|
||||||
var prefixes = ["-webkit-", "-webkit-", "-moz-", "-o-", "-ms-"];
|
var prefixes = ["-webkit-", "-webkit-", "-moz-", "-o-", "-ms-"];
|
||||||
|
@ -58,6 +96,12 @@ export function prefixed(unprefixed) {
|
||||||
return unprefixed;
|
return unprefixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply defaults to an object
|
||||||
|
* @param {object} obj
|
||||||
|
* @returns {object}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function defaults(obj) {
|
export function defaults(obj) {
|
||||||
for (var i = 1, length = arguments.length; i < length; i++) {
|
for (var i = 1, length = arguments.length; i < length; i++) {
|
||||||
var source = arguments[i];
|
var source = arguments[i];
|
||||||
|
@ -68,6 +112,12 @@ export function defaults(obj) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend undefined properties of an object
|
||||||
|
* @param {object} target
|
||||||
|
* @returns {object}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function extend(target) {
|
export function extend(target) {
|
||||||
var sources = [].slice.call(arguments, 1);
|
var sources = [].slice.call(arguments, 1);
|
||||||
sources.forEach(function (source) {
|
sources.forEach(function (source) {
|
||||||
|
@ -79,8 +129,15 @@ export function extend(target) {
|
||||||
return target;
|
return 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
|
* 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
|
||||||
|
* @param {any} item
|
||||||
|
* @param {array} array
|
||||||
|
* @param {function} [compareFunction]
|
||||||
|
* @returns {number} location (in array)
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function insert(item, array, compareFunction) {
|
export function insert(item, array, compareFunction) {
|
||||||
var location = locationOf(item, array, compareFunction);
|
var location = locationOf(item, array, compareFunction);
|
||||||
array.splice(location, 0, item);
|
array.splice(location, 0, item);
|
||||||
|
@ -88,7 +145,16 @@ export function insert(item, array, compareFunction) {
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns where something would fit in
|
/**
|
||||||
|
* Finds where something would fit into a sorted array
|
||||||
|
* @param {any} item
|
||||||
|
* @param {array} array
|
||||||
|
* @param {function} [compareFunction]
|
||||||
|
* @param {function} [_start]
|
||||||
|
* @param {function} [_end]
|
||||||
|
* @returns {number} location (in array)
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function locationOf(item, array, compareFunction, _start, _end) {
|
export function locationOf(item, array, compareFunction, _start, _end) {
|
||||||
var start = _start || 0;
|
var start = _start || 0;
|
||||||
var end = _end || array.length;
|
var end = _end || array.length;
|
||||||
|
@ -119,7 +185,17 @@ export function locationOf(item, array, compareFunction, _start, _end) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns -1 of mpt found
|
/**
|
||||||
|
* Finds index of something in a sorted array
|
||||||
|
* Returns -1 if not found
|
||||||
|
* @param {any} item
|
||||||
|
* @param {array} array
|
||||||
|
* @param {function} [compareFunction]
|
||||||
|
* @param {function} [_start]
|
||||||
|
* @param {function} [_end]
|
||||||
|
* @returns {number} index (in array) or -1
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function indexOfSorted(item, array, compareFunction, _start, _end) {
|
export function indexOfSorted(item, array, compareFunction, _start, _end) {
|
||||||
var start = _start || 0;
|
var start = _start || 0;
|
||||||
var end = _end || array.length;
|
var end = _end || array.length;
|
||||||
|
@ -149,7 +225,13 @@ export function indexOfSorted(item, array, compareFunction, _start, _end) {
|
||||||
return indexOfSorted(item, array, compareFunction, start, pivot);
|
return indexOfSorted(item, array, compareFunction, start, pivot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Find the bounds of an element
|
||||||
|
* taking padding and margin into account
|
||||||
|
* @param {element} el
|
||||||
|
* @returns {{ width: Number, height: Number}}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function bounds(el) {
|
export function bounds(el) {
|
||||||
|
|
||||||
var style = window.getComputedStyle(el);
|
var style = window.getComputedStyle(el);
|
||||||
|
@ -174,6 +256,13 @@ export function bounds(el) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the bounds of an element
|
||||||
|
* taking padding, margin and borders into account
|
||||||
|
* @param {element} el
|
||||||
|
* @returns {{ width: Number, height: Number}}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function borders(el) {
|
export function borders(el) {
|
||||||
|
|
||||||
var style = window.getComputedStyle(el);
|
var style = window.getComputedStyle(el);
|
||||||
|
@ -198,6 +287,11 @@ export function borders(el) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the equivelent of getBoundingClientRect of a browser window
|
||||||
|
* @returns {{ width: Number, height: Number, top: Number, left: Number, right: Number, bottom: Number }}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function windowBounds() {
|
export function windowBounds() {
|
||||||
|
|
||||||
var width = window.innerWidth;
|
var width = window.innerWidth;
|
||||||
|
@ -214,22 +308,11 @@ export function windowBounds() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-- https://stackoverflow.com/questions/13482352/xquery-looking-for-text-with-single-quote/13483496#13483496
|
/**
|
||||||
export function cleanStringForXpath(str) {
|
* Gets the index of a node in its parent
|
||||||
var parts = str.match(/[^'"]+|['"]/g);
|
* @private
|
||||||
parts = parts.map(function(part){
|
* @memberof Core
|
||||||
if (part === "'") {
|
*/
|
||||||
return "\"\'\""; // output "'"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part === "\"") {
|
|
||||||
return "\'\"\'"; // output '"'
|
|
||||||
}
|
|
||||||
return `\'${part}\'`;
|
|
||||||
});
|
|
||||||
return `concat(\'\',${ parts.join(",") })`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function indexOfNode(node, typeId) {
|
export function indexOfNode(node, typeId) {
|
||||||
var parent = node.parentNode;
|
var parent = node.parentNode;
|
||||||
var children = parent.childNodes;
|
var children = parent.childNodes;
|
||||||
|
@ -246,22 +329,54 @@ export function indexOfNode(node, typeId) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the index of a text node in its parent
|
||||||
|
* @param {node} textNode
|
||||||
|
* @returns {number} index
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function indexOfTextNode(textNode) {
|
export function indexOfTextNode(textNode) {
|
||||||
return indexOfNode(textNode, TEXT_NODE);
|
return indexOfNode(textNode, TEXT_NODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the index of an element node in its parent
|
||||||
|
* @param {element} elementNode
|
||||||
|
* @returns {number} index
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function indexOfElementNode(elementNode) {
|
export function indexOfElementNode(elementNode) {
|
||||||
return indexOfNode(elementNode, ELEMENT_NODE);
|
return indexOfNode(elementNode, ELEMENT_NODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if extension is xml
|
||||||
|
* @param {string} ext
|
||||||
|
* @returns {boolean}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function isXml(ext) {
|
export function isXml(ext) {
|
||||||
return ["xml", "opf", "ncx"].indexOf(ext) > -1;
|
return ["xml", "opf", "ncx"].indexOf(ext) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new blob
|
||||||
|
* @param {any} content
|
||||||
|
* @param {string} mime
|
||||||
|
* @returns {Blob}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function createBlob(content, mime){
|
export function createBlob(content, mime){
|
||||||
return new Blob([content], {type : mime });
|
return new Blob([content], {type : mime });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new blob url
|
||||||
|
* @param {any} content
|
||||||
|
* @param {string} mime
|
||||||
|
* @returns {string} url
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function createBlobUrl(content, mime){
|
export function createBlobUrl(content, mime){
|
||||||
var tempUrl;
|
var tempUrl;
|
||||||
var blob = createBlob(content, mime);
|
var blob = createBlob(content, mime);
|
||||||
|
@ -271,11 +386,22 @@ export function createBlobUrl(content, mime){
|
||||||
return tempUrl;
|
return tempUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a blob url
|
||||||
|
* @param {string} url
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function revokeBlobUrl(url){
|
export function revokeBlobUrl(url){
|
||||||
return _URL.revokeObjectURL(url);
|
return _URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new base64 encoded url
|
||||||
|
* @param {any} content
|
||||||
|
* @param {string} mime
|
||||||
|
* @returns {string} url
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function createBase64Url(content, mime){
|
export function createBase64Url(content, mime){
|
||||||
var data;
|
var data;
|
||||||
var datauri;
|
var datauri;
|
||||||
|
@ -292,10 +418,24 @@ export function createBase64Url(content, mime){
|
||||||
return datauri;
|
return datauri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get type of an object
|
||||||
|
* @param {object} obj
|
||||||
|
* @returns {string} type
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function type(obj){
|
export function type(obj){
|
||||||
return Object.prototype.toString.call(obj).slice(8, -1);
|
return Object.prototype.toString.call(obj).slice(8, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse xml (or html) markup
|
||||||
|
* @param {string} markup
|
||||||
|
* @param {string} mime
|
||||||
|
* @param {boolean} forceXMLDom force using xmlDom to parse instead of native parser
|
||||||
|
* @returns {document} document
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function parse(markup, mime, forceXMLDom) {
|
export function parse(markup, mime, forceXMLDom) {
|
||||||
var doc;
|
var doc;
|
||||||
var Parser;
|
var Parser;
|
||||||
|
@ -317,6 +457,13 @@ export function parse(markup, mime, forceXMLDom) {
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* querySelector polyfill
|
||||||
|
* @param {element} el
|
||||||
|
* @param {string} sel selector string
|
||||||
|
* @returns {element} element
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function qs(el, sel) {
|
export function qs(el, sel) {
|
||||||
var elements;
|
var elements;
|
||||||
if (!el) {
|
if (!el) {
|
||||||
|
@ -333,6 +480,13 @@ export function qs(el, sel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* querySelectorAll polyfill
|
||||||
|
* @param {element} el
|
||||||
|
* @param {string} sel selector string
|
||||||
|
* @returns {element[]} elements
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function qsa(el, sel) {
|
export function qsa(el, sel) {
|
||||||
|
|
||||||
if (typeof el.querySelector != "undefined") {
|
if (typeof el.querySelector != "undefined") {
|
||||||
|
@ -342,6 +496,14 @@ export function qsa(el, sel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* querySelector by property
|
||||||
|
* @param {element} el
|
||||||
|
* @param {string} sel selector string
|
||||||
|
* @param {props[]} props
|
||||||
|
* @returns {element[]} elements
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function qsp(el, sel, props) {
|
export function qsp(el, sel, props) {
|
||||||
var q, filtered;
|
var q, filtered;
|
||||||
if (typeof el.querySelector != "undefined") {
|
if (typeof el.querySelector != "undefined") {
|
||||||
|
@ -370,6 +532,7 @@ export function qsp(el, sel, props) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sprint through all text nodes in a document
|
* Sprint through all text nodes in a document
|
||||||
|
* @memberof Core
|
||||||
* @param {element} root element to start with
|
* @param {element} root element to start with
|
||||||
* @param {function} func function to run on each element
|
* @param {function} func function to run on each element
|
||||||
*/
|
*/
|
||||||
|
@ -394,24 +557,10 @@ export function treeWalker(root, func, filter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export function walk(root, func, onlyText) {
|
|
||||||
// var node = root;
|
|
||||||
//
|
|
||||||
// if (node && !onlyText || node.nodeType === 3) { // Node.TEXT_NODE
|
|
||||||
// func(node);
|
|
||||||
// }
|
|
||||||
// console.log(root);
|
|
||||||
//
|
|
||||||
// node = node.firstChild;
|
|
||||||
// while(node) {
|
|
||||||
// walk(node, func, onlyText);
|
|
||||||
// node = node.nextSibling;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param callback return false for continue,true for break
|
* @memberof Core
|
||||||
* @return boolean true: break visit;
|
* @param {node} node
|
||||||
|
* @param {callback} return false for continue,true for break inside callback
|
||||||
*/
|
*/
|
||||||
export function walk(node,callback){
|
export function walk(node,callback){
|
||||||
if(callback(node)){
|
if(callback(node)){
|
||||||
|
@ -429,6 +578,12 @@ export function walk(node,callback){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a blob to a base64 encoded string
|
||||||
|
* @param {Blog} blob
|
||||||
|
* @returns {string}
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function blob2base64(blob) {
|
export function blob2base64(blob) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
@ -439,7 +594,12 @@ export function blob2base64(blob) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// From: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred#backwards_forwards_compatible
|
|
||||||
|
/**
|
||||||
|
* Creates a new pending promise and provides methods to resolve or reject it.
|
||||||
|
* From: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred#backwards_forwards_compatible
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function defer() {
|
export function defer() {
|
||||||
/* A method to resolve the associated Promise with the value passed.
|
/* A method to resolve the associated Promise with the value passed.
|
||||||
* If the promise is already settled it does nothing.
|
* If the promise is already settled it does nothing.
|
||||||
|
@ -471,6 +631,14 @@ export function defer() {
|
||||||
Object.freeze(this);
|
Object.freeze(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* querySelector with filter by epub type
|
||||||
|
* @param {element} html
|
||||||
|
* @param {string} element element type to find
|
||||||
|
* @param {string} type epub type to find
|
||||||
|
* @returns {element[]} elements
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function querySelectorByType(html, element, type){
|
export function querySelectorByType(html, element, type){
|
||||||
var query;
|
var query;
|
||||||
if (typeof html.querySelector != "undefined") {
|
if (typeof html.querySelector != "undefined") {
|
||||||
|
@ -490,6 +658,12 @@ export function querySelectorByType(html, element, type){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find direct decendents of an element
|
||||||
|
* @param {element} el
|
||||||
|
* @returns {element[]} children
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function findChildren(el) {
|
export function findChildren(el) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var childNodes = el.childNodes;
|
var childNodes = el.childNodes;
|
||||||
|
@ -502,6 +676,12 @@ export function findChildren(el) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all parents (ancestors) of an element
|
||||||
|
* @param {element} node
|
||||||
|
* @returns {element[]} parents
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function parents(node) {
|
export function parents(node) {
|
||||||
var nodes = [node];
|
var nodes = [node];
|
||||||
for (; node; node = node.parentNode) {
|
for (; node; node = node.parentNode) {
|
||||||
|
@ -510,6 +690,14 @@ export function parents(node) {
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all direct decendents of a specific type
|
||||||
|
* @param {element} el
|
||||||
|
* @param {string} nodeName
|
||||||
|
* @param {boolean} [single]
|
||||||
|
* @returns {element[]} children
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function filterChildren(el, nodeName, single) {
|
export function filterChildren(el, nodeName, single) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var childNodes = el.childNodes;
|
var childNodes = el.childNodes;
|
||||||
|
@ -528,6 +716,13 @@ export function filterChildren(el, nodeName, single) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter all parents (ancestors) with tag name
|
||||||
|
* @param {element} node
|
||||||
|
* @param {string} tagname
|
||||||
|
* @returns {element[]} parents
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export function getParentByTagName(node, tagname) {
|
export function getParentByTagName(node, tagname) {
|
||||||
let parent;
|
let parent;
|
||||||
if (node === null || tagname === '') return;
|
if (node === null || tagname === '') return;
|
||||||
|
@ -540,6 +735,11 @@ export function getParentByTagName(node, tagname) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight Polyfill for DOM Range
|
||||||
|
* @class
|
||||||
|
* @memberof Core
|
||||||
|
*/
|
||||||
export class RangeObject {
|
export class RangeObject {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.collapsed = false;
|
this.collapsed = false;
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import path from "path-webpack";
|
import path from "path-webpack";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Path object for parsing and manipulation of a path strings
|
||||||
|
*
|
||||||
|
* Uses a polyfill for Nodejs path: https://nodejs.org/api/path.html
|
||||||
|
* @param {string} pathString a url string (relative or absolute)
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
class Path {
|
class Path {
|
||||||
constructor(pathString) {
|
constructor(pathString) {
|
||||||
var protocol;
|
var protocol;
|
||||||
|
@ -25,22 +32,50 @@ class Path {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the path: https://nodejs.org/api/path.html#path_path_parse_path
|
||||||
|
* @param {string} what
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
parse (what) {
|
parse (what) {
|
||||||
return path.parse(what);
|
return path.parse(what);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} what
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
isAbsolute (what) {
|
isAbsolute (what) {
|
||||||
return path.isAbsolute(what || this.path);
|
return path.isAbsolute(what || this.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if path ends with a directory
|
||||||
|
* @param {string} what
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
isDirectory (what) {
|
isDirectory (what) {
|
||||||
return (what.charAt(what.length-1) === "/");
|
return (what.charAt(what.length-1) === "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a path against the directory of the Path
|
||||||
|
*
|
||||||
|
* https://nodejs.org/api/path.html#path_path_resolve_paths
|
||||||
|
* @param {string} what
|
||||||
|
* @returns {string} resolved
|
||||||
|
*/
|
||||||
resolve (what) {
|
resolve (what) {
|
||||||
return path.resolve(this.directory, what);
|
return path.resolve(this.directory, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a path relative to the directory of the Path
|
||||||
|
*
|
||||||
|
* https://nodejs.org/api/path.html#path_path_relative_from_to
|
||||||
|
* @param {string} what
|
||||||
|
* @returns {string} relative
|
||||||
|
*/
|
||||||
relative (what) {
|
relative (what) {
|
||||||
return path.relative(this.directory, what);
|
return path.relative(this.directory, what);
|
||||||
}
|
}
|
||||||
|
@ -49,6 +84,10 @@ class Path {
|
||||||
return this.splitPathRe.exec(filename).slice(1);
|
return this.splitPathRe.exec(filename).slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path string
|
||||||
|
* @returns {string} path
|
||||||
|
*/
|
||||||
toString () {
|
toString () {
|
||||||
return this.path;
|
return this.path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,11 @@ import Path from "./path";
|
||||||
import path from "path-webpack";
|
import path from "path-webpack";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a uri object
|
* creates a Url object for parsing and manipulation of a url string
|
||||||
* @param {string} urlString a url string (relative or absolute)
|
* @param {string} urlString a url string (relative or absolute)
|
||||||
* @param {[string]} baseString optional base for the url,
|
* @param {string} [baseString] optional base for the url,
|
||||||
* default to window.location.href
|
* default to window.location.href
|
||||||
* @return {object} url
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Url {
|
class Url {
|
||||||
constructor(urlString, baseString) {
|
constructor(urlString, baseString) {
|
||||||
var absolute = (urlString.indexOf("://") > -1);
|
var absolute = (urlString.indexOf("://") > -1);
|
||||||
|
@ -66,10 +64,17 @@ class Url {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Path}
|
||||||
|
*/
|
||||||
path () {
|
path () {
|
||||||
return this.Path;
|
return this.Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a relative path to a absolute url
|
||||||
|
* @returns {string} url
|
||||||
|
*/
|
||||||
resolve (what) {
|
resolve (what) {
|
||||||
var isAbsolute = (what.indexOf("://") > -1);
|
var isAbsolute = (what.indexOf("://") > -1);
|
||||||
var fullpath;
|
var fullpath;
|
||||||
|
@ -82,10 +87,17 @@ class Url {
|
||||||
return this.origin + fullpath;
|
return this.origin + fullpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a path relative to the url
|
||||||
|
* @returns {string} path
|
||||||
|
*/
|
||||||
relative (what) {
|
relative (what) {
|
||||||
return path.relative(what, this.directory);
|
return path.relative(what, this.directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
toString () {
|
toString () {
|
||||||
return this.href;
|
return this.href;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue