epubjs
Version:
Parse and Render Epubs
297 lines (249 loc) • 7.15 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _core = require("./utils/core");
var _queue = require("./utils/queue");
var _queue2 = _interopRequireDefault(_queue);
var _epubcfi = require("./epubcfi");
var _epubcfi2 = _interopRequireDefault(_epubcfi);
var _eventEmitter = require("event-emitter");
var _eventEmitter2 = _interopRequireDefault(_eventEmitter);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Find Locations for a Book
* @param {Spine} spine
* @param {request} request
*/
var Locations = function () {
function Locations(spine, request) {
_classCallCheck(this, Locations);
this.spine = spine;
this.request = request;
this.q = new _queue2.default(this);
this.epubcfi = new _epubcfi2.default();
this._locations = [];
this.total = 0;
this.break = 150;
this._current = 0;
}
/**
* Load all of sections in the book to generate locations
* @param {int} chars how many chars to split on
* @return {object} locations
*/
_createClass(Locations, [{
key: "generate",
value: function generate(chars) {
if (chars) {
this.break = chars;
}
this.q.pause();
this.spine.each(function (section) {
this.q.enqueue(this.process.bind(this), section);
}.bind(this));
return this.q.run().then(function () {
this.total = this._locations.length - 1;
if (this._currentCfi) {
this.currentLocation = this._currentCfi;
}
return this._locations;
// console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end));
}.bind(this));
}
}, {
key: "createRange",
value: function createRange() {
return {
startContainer: undefined,
startOffset: undefined,
endContainer: undefined,
endOffset: undefined
};
}
}, {
key: "process",
value: function process(section) {
return section.load(this.request).then(function (contents) {
var locations = this.parse(contents, section.cfiBase);
this._locations = this._locations.concat(locations);
}.bind(this));
}
}, {
key: "parse",
value: function parse(contents, cfiBase, chars) {
var locations = [];
var range;
var doc = contents.ownerDocument;
var body = (0, _core.qs)(doc, "body");
var counter = 0;
var prev;
var _break = chars || this.break;
var parser = function parser(node) {
var len = node.length;
var dist;
var pos = 0;
if (node.textContent.trim().length === 0) {
return false; // continue
}
// Start range
if (counter == 0) {
range = this.createRange();
range.startContainer = node;
range.startOffset = 0;
}
dist = _break - counter;
// Node is smaller than a break,
// skip over it
if (dist > len) {
counter += len;
pos = len;
}
while (pos < len) {
// counter = this.break;
pos += dist;
// Gone over
if (pos >= len) {
// Continue counter for next node
counter = len - (pos - _break);
// At End
} else {
// End the previous range
range.endContainer = node;
range.endOffset = pos;
// cfi = section.cfiFromRange(range);
var cfi = new _epubcfi2.default(range, cfiBase).toString();
locations.push(cfi);
counter = 0;
// Start new range
pos += 1;
range = this.createRange();
range.startContainer = node;
range.startOffset = pos;
dist = _break;
}
}
prev = node;
};
(0, _core.sprint)(body, parser.bind(this));
// Close remaining
if (range && range.startContainer && prev) {
range.endContainer = prev;
range.endOffset = prev.length;
// cfi = section.cfiFromRange(range);
var cfi = new _epubcfi2.default(range, cfiBase).toString();
locations.push(cfi);
counter = 0;
}
return locations;
}
}, {
key: "locationFromCfi",
value: function locationFromCfi(cfi) {
// Check if the location has not been set yet
if (this._locations.length === 0) {
return -1;
}
return (0, _core.locationOf)(cfi.start, this._locations, this.epubcfi.compare);
}
}, {
key: "percentageFromCfi",
value: function percentageFromCfi(cfi) {
if (this._locations.length === 0) {
return null;
}
// Find closest cfi
var loc = this.locationFromCfi(cfi);
// Get percentage in total
return this.percentageFromLocation(loc);
}
}, {
key: "percentageFromLocation",
value: function percentageFromLocation(loc) {
if (!loc || !this.total) {
return 0;
}
return loc / this.total;
}
}, {
key: "cfiFromLocation",
value: function cfiFromLocation(loc) {
var cfi = -1;
// check that pg is an int
if (typeof loc != "number") {
loc = parseInt(loc);
}
if (loc >= 0 && loc < this._locations.length) {
cfi = this._locations[loc];
}
return cfi;
}
}, {
key: "cfiFromPercentage",
value: function cfiFromPercentage(value) {
var percentage = value > 1 ? value / 100 : value; // Normalize value to 0-1
var loc = Math.ceil(this.total * percentage);
return this.cfiFromLocation(loc);
}
}, {
key: "load",
value: function load(locations) {
this._locations = JSON.parse(locations);
this.total = this._locations.length - 1;
return this._locations;
}
}, {
key: "save",
value: function save(json) {
return JSON.stringify(this._locations);
}
}, {
key: "getCurrent",
value: function getCurrent(json) {
return this._current;
}
}, {
key: "setCurrent",
value: function setCurrent(curr) {
var loc;
if (typeof curr == "string") {
this._currentCfi = curr;
} else if (typeof curr == "number") {
this._current = curr;
} else {
return;
}
if (this._locations.length === 0) {
return;
}
if (typeof curr == "string") {
loc = this.locationFromCfi(curr);
this._current = loc;
} else {
loc = curr;
}
this.emit("changed", {
percentage: this.percentageFromLocation(loc)
});
}
}, {
key: "length",
value: function length() {
return this._locations.length;
}
}, {
key: "currentLocation",
get: function get() {
return this._current;
},
set: function set(curr) {
this.setCurrent(curr);
}
}]);
return Locations;
}();
(0, _eventEmitter2.default)(Locations.prototype);
exports.default = Locations;
module.exports = exports["default"];