UNPKG

epubjs

Version:
1,826 lines (1,558 loc) 607 kB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("JSZip")); else if(typeof define === 'function' && define.amd) define(["JSZip"], factory); else if(typeof exports === 'object') exports["ePub"] = factory(require("JSZip")); else root["ePub"] = factory(root["JSZip"]); })(window, function(__WEBPACK_EXTERNAL_MODULE__29__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = "/dist/"; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 30); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "requestAnimationFrame", function() { return requestAnimationFrame; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "uuid", function() { return uuid; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "documentHeight", function() { return documentHeight; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isElement", function() { return isElement; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isNumber", function() { return isNumber; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isFloat", function() { return isFloat; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "prefixed", function() { return prefixed; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaults", function() { return defaults; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "extend", function() { return extend; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "insert", function() { return insert; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "locationOf", function() { return locationOf; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfSorted", function() { return indexOfSorted; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bounds", function() { return bounds; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "borders", function() { return borders; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "nodeBounds", function() { return nodeBounds; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowBounds", function() { return windowBounds; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfNode", function() { return indexOfNode; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfTextNode", function() { return indexOfTextNode; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfElementNode", function() { return indexOfElementNode; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isXml", function() { return isXml; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createBlob", function() { return createBlob; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createBlobUrl", function() { return createBlobUrl; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "revokeBlobUrl", function() { return revokeBlobUrl; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createBase64Url", function() { return createBase64Url; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "type", function() { return type; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return parse; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "qs", function() { return qs; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "qsa", function() { return qsa; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "qsp", function() { return qsp; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sprint", function() { return sprint; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "treeWalker", function() { return treeWalker; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "walk", function() { return walk; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "blob2base64", function() { return blob2base64; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defer", function() { return defer; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "querySelectorByType", function() { return querySelectorByType; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findChildren", function() { return findChildren; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parents", function() { return parents; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "filterChildren", function() { return filterChildren; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getParentByTagName", function() { return getParentByTagName; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RangeObject", function() { return RangeObject; }); /* harmony import */ var _xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(15); /* harmony import */ var _xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0__); /** * Core Utilities and Helpers * @module Core */ /** * Vendor prefixed requestAnimationFrame * @returns {function} requestAnimationFrame * @memberof Core */ const requestAnimationFrame = typeof window != "undefined" ? window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame : false; const ELEMENT_NODE = 1; const TEXT_NODE = 3; const COMMENT_NODE = 8; const DOCUMENT_NODE = 9; const _URL = typeof URL != "undefined" ? URL : typeof window != "undefined" ? window.URL || window.webkitURL || window.mozURL : undefined; /** * Generates a UUID * based on: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript * @returns {string} uuid * @memberof Core */ function uuid() { var d = new Date().getTime(); var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { var r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c == "x" ? r : r & 0x7 | 0x8).toString(16); }); return uuid; } /** * Gets the height of a document * @returns {number} height * @memberof Core */ function documentHeight() { return Math.max(document.documentElement.clientHeight, document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight); } /** * Checks if a node is an element * @param {object} obj * @returns {boolean} * @memberof Core */ function isElement(obj) { return !!(obj && obj.nodeType == 1); } /** * @param {any} n * @returns {boolean} * @memberof Core */ function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } /** * @param {any} n * @returns {boolean} * @memberof Core */ function isFloat(n) { let f = parseFloat(n); if (isNumber(n) === false) { return false; } if (typeof n === "string" && n.indexOf(".") > -1) { return true; } return Math.floor(f) !== f; } /** * Get a prefixed css property * @param {string} unprefixed * @returns {string} * @memberof Core */ function prefixed(unprefixed) { var vendors = ["Webkit", "webkit", "Moz", "O", "ms"]; var prefixes = ["-webkit-", "-webkit-", "-moz-", "-o-", "-ms-"]; var lower = unprefixed.toLowerCase(); var length = vendors.length; if (typeof document === "undefined" || typeof document.body.style[lower] != "undefined") { return unprefixed; } for (var i = 0; i < length; i++) { if (typeof document.body.style[prefixes[i] + lower] != "undefined") { return prefixes[i] + lower; } } return unprefixed; } /** * Apply defaults to an object * @param {object} obj * @returns {object} * @memberof Core */ function defaults(obj) { for (var i = 1, length = arguments.length; i < length; i++) { var source = arguments[i]; for (var prop in source) { if (obj[prop] === void 0) obj[prop] = source[prop]; } } return obj; } /** * Extend properties of an object * @param {object} target * @returns {object} * @memberof Core */ function extend(target) { var sources = [].slice.call(arguments, 1); sources.forEach(function (source) { if (!source) return; Object.getOwnPropertyNames(source).forEach(function (propName) { Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)); }); }); 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 * @param {any} item * @param {array} array * @param {function} [compareFunction] * @returns {number} location (in array) * @memberof Core */ function insert(item, array, compareFunction) { var location = locationOf(item, array, compareFunction); array.splice(location, 0, item); return location; } /** * 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 */ function locationOf(item, array, compareFunction, _start, _end) { var start = _start || 0; var end = _end || array.length; var pivot = parseInt(start + (end - start) / 2); var compared; if (!compareFunction) { compareFunction = function (a, b) { if (a > b) return 1; if (a < b) return -1; if (a == b) return 0; }; } if (end - start <= 0) { return pivot; } compared = compareFunction(array[pivot], item); if (end - start === 1) { return compared >= 0 ? pivot : pivot + 1; } if (compared === 0) { return pivot; } if (compared === -1) { return locationOf(item, array, compareFunction, pivot, end); } else { return locationOf(item, array, compareFunction, start, pivot); } } /** * 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 */ function indexOfSorted(item, array, compareFunction, _start, _end) { var start = _start || 0; var end = _end || array.length; var pivot = parseInt(start + (end - start) / 2); var compared; if (!compareFunction) { compareFunction = function (a, b) { if (a > b) return 1; if (a < b) return -1; if (a == b) return 0; }; } if (end - start <= 0) { return -1; // Not found } compared = compareFunction(array[pivot], item); if (end - start === 1) { return compared === 0 ? pivot : -1; } if (compared === 0) { return pivot; // Found } if (compared === -1) { return indexOfSorted(item, array, compareFunction, pivot, end); } else { 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 */ function bounds(el) { var style = window.getComputedStyle(el); var widthProps = ["width", "paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"]; var heightProps = ["height", "paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"]; var width = 0; var height = 0; widthProps.forEach(function (prop) { width += parseFloat(style[prop]) || 0; }); heightProps.forEach(function (prop) { height += parseFloat(style[prop]) || 0; }); return { height: height, width: width }; } /** * Find the bounds of an element * taking padding, margin and borders into account * @param {element} el * @returns {{ width: Number, height: Number}} * @memberof Core */ function borders(el) { var style = window.getComputedStyle(el); var widthProps = ["paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"]; var heightProps = ["paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"]; var width = 0; var height = 0; widthProps.forEach(function (prop) { width += parseFloat(style[prop]) || 0; }); heightProps.forEach(function (prop) { height += parseFloat(style[prop]) || 0; }); return { height: height, width: width }; } /** * Find the bounds of any node * allows for getting bounds of text nodes by wrapping them in a range * @param {node} node * @returns {BoundingClientRect} * @memberof Core */ function nodeBounds(node) { let elPos; let doc = node.ownerDocument; if (node.nodeType == Node.TEXT_NODE) { let elRange = doc.createRange(); elRange.selectNodeContents(node); elPos = elRange.getBoundingClientRect(); } else { elPos = node.getBoundingClientRect(); } return elPos; } /** * Find the equivalent of getBoundingClientRect of a browser window * @returns {{ width: Number, height: Number, top: Number, left: Number, right: Number, bottom: Number }} * @memberof Core */ function windowBounds() { var width = window.innerWidth; var height = window.innerHeight; return { top: 0, left: 0, right: width, bottom: height, width: width, height: height }; } /** * Gets the index of a node in its parent * @param {Node} node * @param {string} typeId * @return {number} index * @memberof Core */ function indexOfNode(node, typeId) { var parent = node.parentNode; var children = parent.childNodes; var sib; var index = -1; for (var i = 0; i < children.length; i++) { sib = children[i]; if (sib.nodeType === typeId) { index++; } if (sib == node) break; } return index; } /** * Gets the index of a text node in its parent * @param {node} textNode * @returns {number} index * @memberof Core */ function indexOfTextNode(textNode) { return indexOfNode(textNode, TEXT_NODE); } /** * Gets the index of an element node in its parent * @param {element} elementNode * @returns {number} index * @memberof Core */ function indexOfElementNode(elementNode) { return indexOfNode(elementNode, ELEMENT_NODE); } /** * Check if extension is xml * @param {string} ext * @returns {boolean} * @memberof Core */ function isXml(ext) { return ["xml", "opf", "ncx"].indexOf(ext) > -1; } /** * Create a new blob * @param {any} content * @param {string} mime * @returns {Blob} * @memberof Core */ function createBlob(content, mime) { return new Blob([content], { type: mime }); } /** * Create a new blob url * @param {any} content * @param {string} mime * @returns {string} url * @memberof Core */ function createBlobUrl(content, mime) { var tempUrl; var blob = createBlob(content, mime); tempUrl = _URL.createObjectURL(blob); return tempUrl; } /** * Remove a blob url * @param {string} url * @memberof Core */ function revokeBlobUrl(url) { return _URL.revokeObjectURL(url); } /** * Create a new base64 encoded url * @param {any} content * @param {string} mime * @returns {string} url * @memberof Core */ function createBase64Url(content, mime) { var data; var datauri; if (typeof content !== "string") { // Only handles strings return; } data = btoa(content); datauri = "data:" + mime + ";base64," + data; return datauri; } /** * Get type of an object * @param {object} obj * @returns {string} type * @memberof Core */ function type(obj) { 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 */ function parse(markup, mime, forceXMLDom) { var doc; var Parser; if (typeof DOMParser === "undefined" || forceXMLDom) { Parser = _xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0__["DOMParser"]; } else { Parser = DOMParser; } // Remove byte order mark before parsing // https://www.w3.org/International/questions/qa-byte-order-mark if (markup.charCodeAt(0) === 0xFEFF) { markup = markup.slice(1); } doc = new Parser().parseFromString(markup, mime); return doc; } /** * querySelector polyfill * @param {element} el * @param {string} sel selector string * @returns {element} element * @memberof Core */ function qs(el, sel) { var elements; if (!el) { throw new Error("No Element Provided"); } if (typeof el.querySelector != "undefined") { return el.querySelector(sel); } else { elements = el.getElementsByTagName(sel); if (elements.length) { return elements[0]; } } } /** * querySelectorAll polyfill * @param {element} el * @param {string} sel selector string * @returns {element[]} elements * @memberof Core */ function qsa(el, sel) { if (typeof el.querySelector != "undefined") { return el.querySelectorAll(sel); } else { return el.getElementsByTagName(sel); } } /** * querySelector by property * @param {element} el * @param {string} sel selector string * @param {object[]} props * @returns {element[]} elements * @memberof Core */ function qsp(el, sel, props) { var q, filtered; if (typeof el.querySelector != "undefined") { sel += "["; for (var prop in props) { sel += prop + "~='" + props[prop] + "'"; } sel += "]"; return el.querySelector(sel); } else { q = el.getElementsByTagName(sel); filtered = Array.prototype.slice.call(q, 0).filter(function (el) { for (var prop in props) { if (el.getAttribute(prop) === props[prop]) { return true; } } return false; }); if (filtered) { return filtered[0]; } } } /** * Sprint through all text nodes in a document * @memberof Core * @param {element} root element to start with * @param {function} func function to run on each element */ function sprint(root, func) { var doc = root.ownerDocument || root; if (typeof doc.createTreeWalker !== "undefined") { treeWalker(root, func, NodeFilter.SHOW_TEXT); } else { walk(root, function (node) { if (node && node.nodeType === 3) { // Node.TEXT_NODE func(node); } }, true); } } /** * Create a treeWalker * @memberof Core * @param {element} root element to start with * @param {function} func function to run on each element * @param {function | object} filter function or object to filter with */ function treeWalker(root, func, filter) { var treeWalker = document.createTreeWalker(root, filter, null, false); let node; while (node = treeWalker.nextNode()) { func(node); } } /** * @memberof Core * @param {node} node * @param {callback} return false for continue,true for break inside callback */ function walk(node, callback) { if (callback(node)) { return true; } node = node.firstChild; if (node) { do { let walked = walk(node, callback); if (walked) { return true; } node = node.nextSibling; } while (node); } } /** * Convert a blob to a base64 encoded string * @param {Blog} blob * @returns {string} * @memberof Core */ function blob2base64(blob) { return new Promise(function (resolve, reject) { var reader = new FileReader(); reader.readAsDataURL(blob); reader.onloadend = function () { resolve(reader.result); }; }); } /** * Creates a new pending promise and provides methods to resolve or reject it. * From: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred#backwards_forwards_compatible * @memberof Core */ function defer() { /* A method to resolve the associated Promise with the value passed. * If the promise is already settled it does nothing. * * @param {anything} value : This value is used to resolve the promise * If the value is a Promise then the associated promise assumes the state * of Promise passed as value. */ this.resolve = null; /* A method to reject the associated Promise with the value passed. * If the promise is already settled it does nothing. * * @param {anything} reason: The reason for the rejection of the Promise. * Generally its an Error object. If however a Promise is passed, then the Promise * itself will be the reason for rejection no matter the state of the Promise. */ this.reject = null; this.id = uuid(); /* A newly created Pomise object. * Initially in pending state. */ this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); 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 */ function querySelectorByType(html, element, type) { var query; if (typeof html.querySelector != "undefined") { query = html.querySelector(`${element}[*|type="${type}"]`); } // Handle IE not supporting namespaced epub:type in querySelector if (!query || query.length === 0) { query = qsa(html, element); for (var i = 0; i < query.length; i++) { if (query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type || query[i].getAttribute("epub:type") === type) { return query[i]; } } } else { return query; } } /** * Find direct descendents of an element * @param {element} el * @returns {element[]} children * @memberof Core */ function findChildren(el) { var result = []; var childNodes = el.childNodes; for (var i = 0; i < childNodes.length; i++) { let node = childNodes[i]; if (node.nodeType === 1) { result.push(node); } } return result; } /** * Find all parents (ancestors) of an element * @param {element} node * @returns {element[]} parents * @memberof Core */ function parents(node) { var nodes = [node]; for (; node; node = node.parentNode) { nodes.unshift(node); } return nodes; } /** * Find all direct descendents of a specific type * @param {element} el * @param {string} nodeName * @param {boolean} [single] * @returns {element[]} children * @memberof Core */ function filterChildren(el, nodeName, single) { var result = []; var childNodes = el.childNodes; for (var i = 0; i < childNodes.length; i++) { let node = childNodes[i]; if (node.nodeType === 1 && node.nodeName.toLowerCase() === nodeName) { if (single) { return node; } else { result.push(node); } } } if (!single) { return result; } } /** * Filter all parents (ancestors) with tag name * @param {element} node * @param {string} tagname * @returns {element[]} parents * @memberof Core */ function getParentByTagName(node, tagname) { let parent; if (node === null || tagname === '') return; parent = node.parentNode; while (parent.nodeType === 1) { if (parent.tagName.toLowerCase() === tagname) { return parent; } parent = parent.parentNode; } } /** * Lightweight Polyfill for DOM Range * @class * @memberof Core */ class RangeObject { constructor() { this.collapsed = false; this.commonAncestorContainer = undefined; this.endContainer = undefined; this.endOffset = undefined; this.startContainer = undefined; this.startOffset = undefined; } setStart(startNode, startOffset) { this.startContainer = startNode; this.startOffset = startOffset; if (!this.endContainer) { this.collapse(true); } else { this.commonAncestorContainer = this._commonAncestorContainer(); } this._checkCollapsed(); } setEnd(endNode, endOffset) { this.endContainer = endNode; this.endOffset = endOffset; if (!this.startContainer) { this.collapse(false); } else { this.collapsed = false; this.commonAncestorContainer = this._commonAncestorContainer(); } this._checkCollapsed(); } collapse(toStart) { this.collapsed = true; if (toStart) { this.endContainer = this.startContainer; this.endOffset = this.startOffset; this.commonAncestorContainer = this.startContainer.parentNode; } else { this.startContainer = this.endContainer; this.startOffset = this.endOffset; this.commonAncestorContainer = this.endOffset.parentNode; } } selectNode(referenceNode) { let parent = referenceNode.parentNode; let index = Array.prototype.indexOf.call(parent.childNodes, referenceNode); this.setStart(parent, index); this.setEnd(parent, index + 1); } selectNodeContents(referenceNode) { let end = referenceNode.childNodes[referenceNode.childNodes - 1]; let endIndex = referenceNode.nodeType === 3 ? referenceNode.textContent.length : parent.childNodes.length; this.setStart(referenceNode, 0); this.setEnd(referenceNode, endIndex); } _commonAncestorContainer(startContainer, endContainer) { var startParents = parents(startContainer || this.startContainer); var endParents = parents(endContainer || this.endContainer); if (startParents[0] != endParents[0]) return undefined; for (var i = 0; i < startParents.length; i++) { if (startParents[i] != endParents[i]) { return startParents[i - 1]; } } } _checkCollapsed() { if (this.startContainer === this.endContainer && this.startOffset === this.endOffset) { this.collapsed = true; } else { this.collapsed = false; } } toString() {// TODO: implement walking between start and end to find text } } /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return EPUBJS_VERSION; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return DOM_EVENTS; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return EVENTS; }); const EPUBJS_VERSION = "0.3"; // Dom events to listen for const DOM_EVENTS = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "mousemove", "click", "touchend", "touchstart", "touchmove"]; const EVENTS = { BOOK: { OPEN_FAILED: "openFailed" }, CONTENTS: { EXPAND: "expand", RESIZE: "resize", SELECTED: "selected", SELECTED_RANGE: "selectedRange", LINK_CLICKED: "linkClicked" }, LOCATIONS: { CHANGED: "changed" }, MANAGERS: { RESIZE: "resize", RESIZED: "resized", ORIENTATION_CHANGE: "orientationchange", ADDED: "added", SCROLL: "scroll", SCROLLED: "scrolled", REMOVED: "removed" }, VIEWS: { AXIS: "axis", WRITING_MODE: "writingMode", LOAD_ERROR: "loaderror", RENDERED: "rendered", RESIZED: "resized", DISPLAYED: "displayed", SHOWN: "shown", HIDDEN: "hidden", MARK_CLICKED: "markClicked" }, RENDITION: { STARTED: "started", ATTACHED: "attached", DISPLAYED: "displayed", DISPLAY_ERROR: "displayerror", RENDERED: "rendered", REMOVED: "removed", RESIZED: "resized", ORIENTATION_CHANGE: "orientationchange", LOCATION_CHANGED: "locationChanged", RELOCATED: "relocated", MARK_CLICKED: "markClicked", SELECTED: "selected", LAYOUT: "layout" }, LAYOUT: { UPDATED: "updated" }, ANNOTATION: { ATTACH: "attach", DETACH: "detach" } }; /***/ }), /* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony import */ var _utils_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0); const ELEMENT_NODE = 1; const TEXT_NODE = 3; const COMMENT_NODE = 8; 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 { constructor(cfiFrom, base, ignoreClass) { var type; this.str = ""; this.base = {}; this.spinePos = 0; // For compatibility this.range = false; // true || false; this.path = {}; this.start = null; this.end = null; // Allow instantiation without the "new" keyword if (!(this instanceof EpubCFI)) { return new EpubCFI(cfiFrom, base, ignoreClass); } if (typeof base === "string") { this.base = this.parseComponent(base); } else if (typeof base === "object" && base.steps) { this.base = base; } type = this.checkType(cfiFrom); if (type === "string") { this.str = cfiFrom; return Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["extend"])(this, this.parse(cfiFrom)); } else if (type === "range") { return Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["extend"])(this, this.fromRange(cfiFrom, this.base, ignoreClass)); } else if (type === "node") { return Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["extend"])(this, this.fromNode(cfiFrom, this.base, ignoreClass)); } else if (type === "EpubCFI" && cfiFrom.path) { return cfiFrom; } else if (!cfiFrom) { return this; } else { throw new TypeError("not a valid argument for EpubCFI"); } } /** * Check the type of constructor input * @private */ checkType(cfi) { if (this.isCfiString(cfi)) { return "string"; // Is a range object } else if (cfi && typeof cfi === "object" && (Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["type"])(cfi) === "Range" || typeof cfi.startContainer != "undefined")) { return "range"; } else if (cfi && typeof cfi === "object" && typeof cfi.nodeType != "undefined") { // || typeof cfi === "function" return "node"; } else if (cfi && typeof cfi === "object" && cfi instanceof EpubCFI) { return "EpubCFI"; } else { return false; } } /** * Parse a cfi string to a CFI object representation * @param {string} cfiStr * @returns {object} cfi */ parse(cfiStr) { var cfi = { spinePos: -1, range: false, base: {}, path: {}, start: null, end: null }; var baseComponent, pathComponent, range; if (typeof cfiStr !== "string") { return { spinePos: -1 }; } if (cfiStr.indexOf("epubcfi(") === 0 && cfiStr[cfiStr.length - 1] === ")") { // Remove initial epubcfi( and ending ) cfiStr = cfiStr.slice(8, cfiStr.length - 1); } baseComponent = this.getChapterComponent(cfiStr); // Make sure this is a valid cfi or return if (!baseComponent) { return { spinePos: -1 }; } cfi.base = this.parseComponent(baseComponent); pathComponent = this.getPathComponent(cfiStr); cfi.path = this.parseComponent(pathComponent); range = this.getRange(cfiStr); if (range) { cfi.range = true; cfi.start = this.parseComponent(range[0]); cfi.end = this.parseComponent(range[1]); } // Get spine node position // cfi.spineSegment = cfi.base.steps[1]; // Chapter segment is always the second step cfi.spinePos = cfi.base.steps[1].index; return cfi; } parseComponent(componentStr) { var component = { steps: [], terminal: { offset: null, assertion: null } }; var parts = componentStr.split(":"); var steps = parts[0].split("/"); var terminal; if (parts.length > 1) { terminal = parts[1]; component.terminal = this.parseTerminal(terminal); } if (steps[0] === "") { steps.shift(); // Ignore the first slash } component.steps = steps.map(function (step) { return this.parseStep(step); }.bind(this)); return component; } parseStep(stepStr) { var type, num, index, has_brackets, id; has_brackets = stepStr.match(/\[(.*)\]/); if (has_brackets && has_brackets[1]) { id = has_brackets[1]; } //-- Check if step is a text node or element num = parseInt(stepStr); if (isNaN(num)) { return; } if (num % 2 === 0) { // Even = is an element type = "element"; index = num / 2 - 1; } else { type = "text"; index = (num - 1) / 2; } return { "type": type, "index": index, "id": id || null }; } parseTerminal(termialStr) { var characterOffset, textLocationAssertion; var assertion = termialStr.match(/\[(.*)\]/); if (assertion && assertion[1]) { characterOffset = parseInt(termialStr.split("[")[0]); textLocationAssertion = assertion[1]; } else { characterOffset = parseInt(termialStr); } if (!Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["isNumber"])(characterOffset)) { characterOffset = null; } return { "offset": characterOffset, "assertion": textLocationAssertion }; } getChapterComponent(cfiStr) { var indirection = cfiStr.split("!"); return indirection[0]; } getPathComponent(cfiStr) { var indirection = cfiStr.split("!"); if (indirection[1]) { let ranges = indirection[1].split(","); return ranges[0]; } } getRange(cfiStr) { var ranges = cfiStr.split(","); if (ranges.length === 3) { return [ranges[1], ranges[2]]; } return false; } getCharecterOffsetComponent(cfiStr) { var splitStr = cfiStr.split(":"); return splitStr[1] || ""; } joinSteps(steps) { if (!steps) { return ""; } return steps.map(function (part) { var segment = ""; if (part.type === "element") { segment += (part.index + 1) * 2; } if (part.type === "text") { segment += 1 + 2 * part.index; // TODO: double check that this is odd } if (part.id) { segment += "[" + part.id + "]"; } return segment; }).join("/"); } segmentString(segment) { var segmentString = "/"; segmentString += this.joinSteps(segment.steps); if (segment.terminal && segment.terminal.offset != null) { segmentString += ":" + segment.terminal.offset; } if (segment.terminal && segment.terminal.assertion != null) { segmentString += "[" + segment.terminal.assertion + "]"; } return segmentString; } /** * Convert CFI to a epubcfi(...) string * @returns {string} epubcfi */ toString() { var cfiString = "epubcfi("; cfiString += this.segmentString(this.base); cfiString += "!"; cfiString += this.segmentString(this.path); // Add Range, if present if (this.range && this.start) { cfiString += ","; cfiString += this.segmentString(this.start); } if (this.range && this.end) { cfiString += ","; cfiString += this.segmentString(this.end); } 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) { var stepsA, stepsB; var terminalA, terminalB; var rangeAStartSteps, rangeAEndSteps; var rangeBEndSteps, rangeBEndSteps; var rangeAStartTerminal, rangeAEndTerminal; var rangeBStartTerminal, rangeBEndTerminal; if (typeof cfiOne === "string") { cfiOne = new EpubCFI(cfiOne); } if (typeof cfiTwo === "string") { cfiTwo = new EpubCFI(cfiTwo); } // Compare Spine Positions if (cfiOne.spinePos > cfiTwo.spinePos) { return 1; } if (cfiOne.spinePos < cfiTwo.spinePos) { return -1; } if (cfiOne.range) { stepsA = cfiOne.path.steps.concat(cfiOne.start.steps); terminalA = cfiOne.start.terminal; } else { stepsA = cfiOne.path.steps; terminalA = cfiOne.path.terminal; } if (cfiTwo.range) { stepsB = cfiTwo.path.steps.concat(cfiTwo.start.steps); terminalB = cfiTwo.start.terminal; } else { stepsB = cfiTwo.path.steps; terminalB = cfiTwo.path.terminal; } // Compare Each Step in the First item for (var i = 0; i < stepsA.length; i++) { if (!stepsA[i]) { return -1; } if (!stepsB[i]) { return 1; } if (stepsA[i].index > stepsB[i].index) { return 1; } if (stepsA[i].index < stepsB[i].index) { return -1; } // Otherwise continue checking } // All steps in First equal to Second and First is Less Specific if (stepsA.length < stepsB.length) { return -1; } // Compare the character offset of the text node if (terminalA.offset > terminalB.offset) { return 1; } if (terminalA.offset < terminalB.offset) { return -1; } // CFI's are equal return 0; } step(node) { var nodeType = node.nodeType === TEXT_NODE ? "text" : "element"; return { "id": node.id, "tagName": node.tagName, "type": nodeType, "index": this.position(node) }; } filteredStep(node, ignoreClass) { var filteredNode = this.filter(node, ignoreClass); var nodeType; // Node filtered, so ignore if (!filteredNode) { return; } // Otherwise add the filter node in nodeType = filteredNode.nodeType === TEXT_NODE ? "text" : "element"; return { "id": filteredNode.id, "tagName": filteredNode.tagName, "type": nodeType, "index": this.filteredPosition(filteredNode, ignoreClass) }; } pathTo(node, offset, ignoreClass) { var segment = { steps: [], terminal: { offset: null, assertion: null } }; var currentNode = node; var step; while (currentNode && currentNode.parentNode && currentNode.parentNode.nodeType != DOCUMENT_NODE) { if (ignoreClass) { step = this.filteredStep(currentNode, ignoreClass); } else { step = this.step(currentNode); } if (step) { segment.steps.unshift(step); } currentNode = currentNode.parentNode; } if (offset != null && offset >= 0) { segment.terminal.offset = offset; // Make sure we are getting to a textNode if there is an offset if (segment.steps[segment.steps.length - 1].type != "text") { segment.steps.push({ "type": "text", "index": 0 }); } } return segment; } equalStep(stepA, stepB) { if (!stepA || !stepB) { return false; } if (stepA.index === stepB.index && stepA.id === stepB.id && stepA.type === stepB.type) { return true; } 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) { var cfi = { range: false, base: {}, path: {}, start: null, end: null }; var start = range.startContainer; var end = range.endContainer; var startOffset = range.startOffset; var endOffset = range.endOffset; var needsIgnoring = false; if (ignoreClass) { // Tell pathTo if / what to ignore needsIgnoring = start.ownerDocument.querySelector("." + ignoreClass) != null; } if (typeof base === "string") { cfi.base = this.parseComponent(base); cfi.spinePos = cfi.base.steps[1].index; } else if (typeof base === "object") { cfi.base = base; } if (range.collapsed) { if (needsIgnoring) { startOffset = this.patchOffset(start, startOffset, ignoreClass); } cfi.path = this.pathTo(start, startOffset, ignoreClass); } else { cfi.range = true; if (needsIgnoring) { startOffset = this.patchOffset(start, startOffset, ignoreClass); } cfi.start = this.pathTo(start, startOffset, ignoreClass); if (needsIgnoring) { endOffset = this.patchOffset(end, endOffset, ignoreClass); } cfi.end = this.pathTo(end, endOffset, ignoreClass); // Create a new empty path cfi.path = { steps: [], terminal: null }; // Push steps that are shared between start and end to the common path var len = cfi.start.steps.length; var i; for (i = 0; i < len; i++) { if (this.equalStep(cfi.start.steps[i], cfi.end.steps[i])) { if (i === len - 1) { // Last step is equal, check terminals if (cfi.start.terminal === cfi.end.terminal) { // CFI's are equal cfi.path.steps.push(cfi.start.steps[i]); // Not a range cfi.range = false; } } else { cfi.path.steps.push(cfi.start.steps[i]); } } else { break; } } cfi.start.steps = cfi.start.steps.slice(cfi.path.steps.length); cfi.end.steps = cfi.end.steps.slice(cfi.path.steps.length); // TODO: Add Sanity check to make sure that the end if greater than the start } 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) { var cfi = { range: false, base: {}, path: {}, start: null, end: null }; if (typeof base === "string") { cfi.base = this.parseComponent(base); cfi.spinePos = cfi.base.steps[1].index; } else if (typeof base === "object") { cfi.base = base; } cfi.path = this.pathTo(anchor, null, ignoreClass); return cfi; } filter(anchor, ignoreClass) { var needsIgnoring; var sibling; // to join with var parent, previousSibling, nextSibling; var isText = false; if (anchor.nodeType === TEXT_NODE) { isText = true; parent = anchor.parentNode; needsIgnoring = anchor.parentNode.classList.contains(ignoreClass); } else { isText = false; needsIgnoring = anchor.classList.contains(ignoreClass); } if (needsIgnoring && isText) { previousSibling = parent.previousSibling; nextSibling = parent.nextSibling; // If the sibling is a text node, join the nodes if (previousSibling && previousSibling.nodeType === TEXT_NODE) { sibling = previousSibling; } else if (nextSibling && nextSibling.nodeType === TEXT_NODE) { sibling = nextSibling; } if (sibling) { return sibling; } else { // Parent will be ignored on next step return anchor; } } else if (needsIgnoring && !isText) { // Otherwise just skip the element node return false; } else { // No need to filter return anchor; } } patchOffset(anchor, offset, ignoreClass) { if (anchor.nodeType != TEXT_NODE) { throw new Error("Anchor must be a text node"); } var curr = anchor; var totalOffset = offset; // If the parent is a ignored node, get offset from it's start if (anchor.parentNode.classList.contains(ignoreClass)) { curr = anchor.parentNode; } while (curr.previousSibling) { if (curr.previousSibling.nodeType === ELEMENT_NODE) { // Originally a text node, so join if (curr.previousSibling.classList.contains(ignoreClass)) { totalOffset += curr.previousSibling.textContent.length; } else { break; // Normal node, dont join } } else { // If the previous sibling is a text node, join the nodes totalOffset += curr.previousSibling.textContent.length; } curr = curr.previousSibling; } return totalOffset; } normalizedMap(children, nodeType, ignoreClass) { var output = {}; var prevIndex = -1; var i, len = children.length; var currNodeType; var prevNodeType; for (i = 0; i < len; i++) { currNodeType = children[i].nodeType; // Check if needs ignoring if (currNodeType === ELEMENT_NODE && children[i].classList.contains(ignoreClass)) { currNodeType = TEXT_NODE; } if (i > 0 && currNodeType === TEXT_NODE && prevNodeType === TEXT_NODE) { // join text nodes output[i] = prevIndex; } else if (nodeType === currNodeType) { prevIndex = prevIndex + 1; output[i] = prevIndex; } prevNodeType = currNodeType; } return output; } position(anchor) { var children, index; if (anchor.nodeType === ELEMENT_NODE) { children = anchor.parentNode.children; if (!children) { children = Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["findChildren"])(anchor.parentNode); } index = Array.prototype.indexOf.call(children, anchor); } else { children = this.textNodes(anchor.parentNode); index = children.indexOf(anchor); } return index; } filteredPosition(anchor, ignoreClass) { var children, index, map; if (anchor.nodeType === ELEMENT_NODE) { childre