File indexing completed on 2024-12-22 03:53:24
0001 (function webpackUniversalModuleDefinition(root, factory) { 0002 if(typeof exports === 'object' && typeof module === 'object') 0003 module.exports = factory(require("JSZip")); 0004 else if(typeof define === 'function' && define.amd) 0005 define(["JSZip"], factory); 0006 else if(typeof exports === 'object') 0007 exports["ePub"] = factory(require("JSZip")); 0008 else 0009 root["ePub"] = factory(root["JSZip"]); 0010 })(window, function(__WEBPACK_EXTERNAL_MODULE__29__) { 0011 return /******/ (function(modules) { // webpackBootstrap 0012 /******/ // The module cache 0013 /******/ var installedModules = {}; 0014 /******/ 0015 /******/ // The require function 0016 /******/ function __webpack_require__(moduleId) { 0017 /******/ 0018 /******/ // Check if module is in cache 0019 /******/ if(installedModules[moduleId]) { 0020 /******/ return installedModules[moduleId].exports; 0021 /******/ } 0022 /******/ // Create a new module (and put it into the cache) 0023 /******/ var module = installedModules[moduleId] = { 0024 /******/ i: moduleId, 0025 /******/ l: false, 0026 /******/ exports: {} 0027 /******/ }; 0028 /******/ 0029 /******/ // Execute the module function 0030 /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 0031 /******/ 0032 /******/ // Flag the module as loaded 0033 /******/ module.l = true; 0034 /******/ 0035 /******/ // Return the exports of the module 0036 /******/ return module.exports; 0037 /******/ } 0038 /******/ 0039 /******/ 0040 /******/ // expose the modules object (__webpack_modules__) 0041 /******/ __webpack_require__.m = modules; 0042 /******/ 0043 /******/ // expose the module cache 0044 /******/ __webpack_require__.c = installedModules; 0045 /******/ 0046 /******/ // define getter function for harmony exports 0047 /******/ __webpack_require__.d = function(exports, name, getter) { 0048 /******/ if(!__webpack_require__.o(exports, name)) { 0049 /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 0050 /******/ } 0051 /******/ }; 0052 /******/ 0053 /******/ // define __esModule on exports 0054 /******/ __webpack_require__.r = function(exports) { 0055 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 0056 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 0057 /******/ } 0058 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 0059 /******/ }; 0060 /******/ 0061 /******/ // create a fake namespace object 0062 /******/ // mode & 1: value is a module id, require it 0063 /******/ // mode & 2: merge all properties of value into the ns 0064 /******/ // mode & 4: return value when already ns object 0065 /******/ // mode & 8|1: behave like require 0066 /******/ __webpack_require__.t = function(value, mode) { 0067 /******/ if(mode & 1) value = __webpack_require__(value); 0068 /******/ if(mode & 8) return value; 0069 /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 0070 /******/ var ns = Object.create(null); 0071 /******/ __webpack_require__.r(ns); 0072 /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 0073 /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 0074 /******/ return ns; 0075 /******/ }; 0076 /******/ 0077 /******/ // getDefaultExport function for compatibility with non-harmony modules 0078 /******/ __webpack_require__.n = function(module) { 0079 /******/ var getter = module && module.__esModule ? 0080 /******/ function getDefault() { return module['default']; } : 0081 /******/ function getModuleExports() { return module; }; 0082 /******/ __webpack_require__.d(getter, 'a', getter); 0083 /******/ return getter; 0084 /******/ }; 0085 /******/ 0086 /******/ // Object.prototype.hasOwnProperty.call 0087 /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 0088 /******/ 0089 /******/ // __webpack_public_path__ 0090 /******/ __webpack_require__.p = "/dist/"; 0091 /******/ 0092 /******/ 0093 /******/ // Load entry module and return exports 0094 /******/ return __webpack_require__(__webpack_require__.s = 30); 0095 /******/ }) 0096 /************************************************************************/ 0097 /******/ ([ 0098 /* 0 */ 0099 /***/ (function(module, __webpack_exports__, __webpack_require__) { 0100 0101 "use strict"; 0102 __webpack_require__.r(__webpack_exports__); 0103 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "requestAnimationFrame", function() { return requestAnimationFrame; }); 0104 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "uuid", function() { return uuid; }); 0105 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "documentHeight", function() { return documentHeight; }); 0106 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isElement", function() { return isElement; }); 0107 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isNumber", function() { return isNumber; }); 0108 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isFloat", function() { return isFloat; }); 0109 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "prefixed", function() { return prefixed; }); 0110 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaults", function() { return defaults; }); 0111 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "extend", function() { return extend; }); 0112 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "insert", function() { return insert; }); 0113 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "locationOf", function() { return locationOf; }); 0114 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfSorted", function() { return indexOfSorted; }); 0115 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bounds", function() { return bounds; }); 0116 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "borders", function() { return borders; }); 0117 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "nodeBounds", function() { return nodeBounds; }); 0118 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowBounds", function() { return windowBounds; }); 0119 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfNode", function() { return indexOfNode; }); 0120 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfTextNode", function() { return indexOfTextNode; }); 0121 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "indexOfElementNode", function() { return indexOfElementNode; }); 0122 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isXml", function() { return isXml; }); 0123 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createBlob", function() { return createBlob; }); 0124 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createBlobUrl", function() { return createBlobUrl; }); 0125 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "revokeBlobUrl", function() { return revokeBlobUrl; }); 0126 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createBase64Url", function() { return createBase64Url; }); 0127 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "type", function() { return type; }); 0128 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return parse; }); 0129 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "qs", function() { return qs; }); 0130 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "qsa", function() { return qsa; }); 0131 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "qsp", function() { return qsp; }); 0132 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sprint", function() { return sprint; }); 0133 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "treeWalker", function() { return treeWalker; }); 0134 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "walk", function() { return walk; }); 0135 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "blob2base64", function() { return blob2base64; }); 0136 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defer", function() { return defer; }); 0137 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "querySelectorByType", function() { return querySelectorByType; }); 0138 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findChildren", function() { return findChildren; }); 0139 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parents", function() { return parents; }); 0140 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "filterChildren", function() { return filterChildren; }); 0141 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getParentByTagName", function() { return getParentByTagName; }); 0142 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RangeObject", function() { return RangeObject; }); 0143 /* harmony import */ var _xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(15); 0144 /* harmony import */ var _xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0__); 0145 /** 0146 * Core Utilities and Helpers 0147 * @module Core 0148 */ 0149 0150 /** 0151 * Vendor prefixed requestAnimationFrame 0152 * @returns {function} requestAnimationFrame 0153 * @memberof Core 0154 */ 0155 0156 const requestAnimationFrame = typeof window != "undefined" ? window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame : false; 0157 const ELEMENT_NODE = 1; 0158 const TEXT_NODE = 3; 0159 const COMMENT_NODE = 8; 0160 const DOCUMENT_NODE = 9; 0161 0162 const _URL = typeof URL != "undefined" ? URL : typeof window != "undefined" ? window.URL || window.webkitURL || window.mozURL : undefined; 0163 /** 0164 * Generates a UUID 0165 * based on: http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript 0166 * @returns {string} uuid 0167 * @memberof Core 0168 */ 0169 0170 0171 function uuid() { 0172 var d = new Date().getTime(); 0173 var uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { 0174 var r = (d + Math.random() * 16) % 16 | 0; 0175 d = Math.floor(d / 16); 0176 return (c == "x" ? r : r & 0x7 | 0x8).toString(16); 0177 }); 0178 return uuid; 0179 } 0180 /** 0181 * Gets the height of a document 0182 * @returns {number} height 0183 * @memberof Core 0184 */ 0185 0186 function documentHeight() { 0187 return Math.max(document.documentElement.clientHeight, document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight); 0188 } 0189 /** 0190 * Checks if a node is an element 0191 * @param {object} obj 0192 * @returns {boolean} 0193 * @memberof Core 0194 */ 0195 0196 function isElement(obj) { 0197 return !!(obj && obj.nodeType == 1); 0198 } 0199 /** 0200 * @param {any} n 0201 * @returns {boolean} 0202 * @memberof Core 0203 */ 0204 0205 function isNumber(n) { 0206 return !isNaN(parseFloat(n)) && isFinite(n); 0207 } 0208 /** 0209 * @param {any} n 0210 * @returns {boolean} 0211 * @memberof Core 0212 */ 0213 0214 function isFloat(n) { 0215 let f = parseFloat(n); 0216 0217 if (isNumber(n) === false) { 0218 return false; 0219 } 0220 0221 if (typeof n === "string" && n.indexOf(".") > -1) { 0222 return true; 0223 } 0224 0225 return Math.floor(f) !== f; 0226 } 0227 /** 0228 * Get a prefixed css property 0229 * @param {string} unprefixed 0230 * @returns {string} 0231 * @memberof Core 0232 */ 0233 0234 function prefixed(unprefixed) { 0235 var vendors = ["Webkit", "webkit", "Moz", "O", "ms"]; 0236 var prefixes = ["-webkit-", "-webkit-", "-moz-", "-o-", "-ms-"]; 0237 var lower = unprefixed.toLowerCase(); 0238 var length = vendors.length; 0239 0240 if (typeof document === "undefined" || typeof document.body.style[lower] != "undefined") { 0241 return unprefixed; 0242 } 0243 0244 for (var i = 0; i < length; i++) { 0245 if (typeof document.body.style[prefixes[i] + lower] != "undefined") { 0246 return prefixes[i] + lower; 0247 } 0248 } 0249 0250 return unprefixed; 0251 } 0252 /** 0253 * Apply defaults to an object 0254 * @param {object} obj 0255 * @returns {object} 0256 * @memberof Core 0257 */ 0258 0259 function defaults(obj) { 0260 for (var i = 1, length = arguments.length; i < length; i++) { 0261 var source = arguments[i]; 0262 0263 for (var prop in source) { 0264 if (obj[prop] === void 0) obj[prop] = source[prop]; 0265 } 0266 } 0267 0268 return obj; 0269 } 0270 /** 0271 * Extend properties of an object 0272 * @param {object} target 0273 * @returns {object} 0274 * @memberof Core 0275 */ 0276 0277 function extend(target) { 0278 var sources = [].slice.call(arguments, 1); 0279 sources.forEach(function (source) { 0280 if (!source) return; 0281 Object.getOwnPropertyNames(source).forEach(function (propName) { 0282 Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName)); 0283 }); 0284 }); 0285 return target; 0286 } 0287 /** 0288 * Fast quicksort insert for sorted array -- based on: 0289 * http://stackoverflow.com/questions/1344500/efficient-way-to-insert-a-number-into-a-sorted-array-of-numbers 0290 * @param {any} item 0291 * @param {array} array 0292 * @param {function} [compareFunction] 0293 * @returns {number} location (in array) 0294 * @memberof Core 0295 */ 0296 0297 function insert(item, array, compareFunction) { 0298 var location = locationOf(item, array, compareFunction); 0299 array.splice(location, 0, item); 0300 return location; 0301 } 0302 /** 0303 * Finds where something would fit into a sorted array 0304 * @param {any} item 0305 * @param {array} array 0306 * @param {function} [compareFunction] 0307 * @param {function} [_start] 0308 * @param {function} [_end] 0309 * @returns {number} location (in array) 0310 * @memberof Core 0311 */ 0312 0313 function locationOf(item, array, compareFunction, _start, _end) { 0314 var start = _start || 0; 0315 var end = _end || array.length; 0316 var pivot = parseInt(start + (end - start) / 2); 0317 var compared; 0318 0319 if (!compareFunction) { 0320 compareFunction = function (a, b) { 0321 if (a > b) return 1; 0322 if (a < b) return -1; 0323 if (a == b) return 0; 0324 }; 0325 } 0326 0327 if (end - start <= 0) { 0328 return pivot; 0329 } 0330 0331 compared = compareFunction(array[pivot], item); 0332 0333 if (end - start === 1) { 0334 return compared >= 0 ? pivot : pivot + 1; 0335 } 0336 0337 if (compared === 0) { 0338 return pivot; 0339 } 0340 0341 if (compared === -1) { 0342 return locationOf(item, array, compareFunction, pivot, end); 0343 } else { 0344 return locationOf(item, array, compareFunction, start, pivot); 0345 } 0346 } 0347 /** 0348 * Finds index of something in a sorted array 0349 * Returns -1 if not found 0350 * @param {any} item 0351 * @param {array} array 0352 * @param {function} [compareFunction] 0353 * @param {function} [_start] 0354 * @param {function} [_end] 0355 * @returns {number} index (in array) or -1 0356 * @memberof Core 0357 */ 0358 0359 function indexOfSorted(item, array, compareFunction, _start, _end) { 0360 var start = _start || 0; 0361 var end = _end || array.length; 0362 var pivot = parseInt(start + (end - start) / 2); 0363 var compared; 0364 0365 if (!compareFunction) { 0366 compareFunction = function (a, b) { 0367 if (a > b) return 1; 0368 if (a < b) return -1; 0369 if (a == b) return 0; 0370 }; 0371 } 0372 0373 if (end - start <= 0) { 0374 return -1; // Not found 0375 } 0376 0377 compared = compareFunction(array[pivot], item); 0378 0379 if (end - start === 1) { 0380 return compared === 0 ? pivot : -1; 0381 } 0382 0383 if (compared === 0) { 0384 return pivot; // Found 0385 } 0386 0387 if (compared === -1) { 0388 return indexOfSorted(item, array, compareFunction, pivot, end); 0389 } else { 0390 return indexOfSorted(item, array, compareFunction, start, pivot); 0391 } 0392 } 0393 /** 0394 * Find the bounds of an element 0395 * taking padding and margin into account 0396 * @param {element} el 0397 * @returns {{ width: Number, height: Number}} 0398 * @memberof Core 0399 */ 0400 0401 function bounds(el) { 0402 var style = window.getComputedStyle(el); 0403 var widthProps = ["width", "paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"]; 0404 var heightProps = ["height", "paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"]; 0405 var width = 0; 0406 var height = 0; 0407 widthProps.forEach(function (prop) { 0408 width += parseFloat(style[prop]) || 0; 0409 }); 0410 heightProps.forEach(function (prop) { 0411 height += parseFloat(style[prop]) || 0; 0412 }); 0413 return { 0414 height: height, 0415 width: width 0416 }; 0417 } 0418 /** 0419 * Find the bounds of an element 0420 * taking padding, margin and borders into account 0421 * @param {element} el 0422 * @returns {{ width: Number, height: Number}} 0423 * @memberof Core 0424 */ 0425 0426 function borders(el) { 0427 var style = window.getComputedStyle(el); 0428 var widthProps = ["paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"]; 0429 var heightProps = ["paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"]; 0430 var width = 0; 0431 var height = 0; 0432 widthProps.forEach(function (prop) { 0433 width += parseFloat(style[prop]) || 0; 0434 }); 0435 heightProps.forEach(function (prop) { 0436 height += parseFloat(style[prop]) || 0; 0437 }); 0438 return { 0439 height: height, 0440 width: width 0441 }; 0442 } 0443 /** 0444 * Find the bounds of any node 0445 * allows for getting bounds of text nodes by wrapping them in a range 0446 * @param {node} node 0447 * @returns {BoundingClientRect} 0448 * @memberof Core 0449 */ 0450 0451 function nodeBounds(node) { 0452 let elPos; 0453 let doc = node.ownerDocument; 0454 0455 if (node.nodeType == Node.TEXT_NODE) { 0456 let elRange = doc.createRange(); 0457 elRange.selectNodeContents(node); 0458 elPos = elRange.getBoundingClientRect(); 0459 } else { 0460 elPos = node.getBoundingClientRect(); 0461 } 0462 0463 return elPos; 0464 } 0465 /** 0466 * Find the equivalent of getBoundingClientRect of a browser window 0467 * @returns {{ width: Number, height: Number, top: Number, left: Number, right: Number, bottom: Number }} 0468 * @memberof Core 0469 */ 0470 0471 function windowBounds() { 0472 var width = window.innerWidth; 0473 var height = window.innerHeight; 0474 return { 0475 top: 0, 0476 left: 0, 0477 right: width, 0478 bottom: height, 0479 width: width, 0480 height: height 0481 }; 0482 } 0483 /** 0484 * Gets the index of a node in its parent 0485 * @param {Node} node 0486 * @param {string} typeId 0487 * @return {number} index 0488 * @memberof Core 0489 */ 0490 0491 function indexOfNode(node, typeId) { 0492 var parent = node.parentNode; 0493 var children = parent.childNodes; 0494 var sib; 0495 var index = -1; 0496 0497 for (var i = 0; i < children.length; i++) { 0498 sib = children[i]; 0499 0500 if (sib.nodeType === typeId) { 0501 index++; 0502 } 0503 0504 if (sib == node) break; 0505 } 0506 0507 return index; 0508 } 0509 /** 0510 * Gets the index of a text node in its parent 0511 * @param {node} textNode 0512 * @returns {number} index 0513 * @memberof Core 0514 */ 0515 0516 function indexOfTextNode(textNode) { 0517 return indexOfNode(textNode, TEXT_NODE); 0518 } 0519 /** 0520 * Gets the index of an element node in its parent 0521 * @param {element} elementNode 0522 * @returns {number} index 0523 * @memberof Core 0524 */ 0525 0526 function indexOfElementNode(elementNode) { 0527 return indexOfNode(elementNode, ELEMENT_NODE); 0528 } 0529 /** 0530 * Check if extension is xml 0531 * @param {string} ext 0532 * @returns {boolean} 0533 * @memberof Core 0534 */ 0535 0536 function isXml(ext) { 0537 return ["xml", "opf", "ncx"].indexOf(ext) > -1; 0538 } 0539 /** 0540 * Create a new blob 0541 * @param {any} content 0542 * @param {string} mime 0543 * @returns {Blob} 0544 * @memberof Core 0545 */ 0546 0547 function createBlob(content, mime) { 0548 return new Blob([content], { 0549 type: mime 0550 }); 0551 } 0552 /** 0553 * Create a new blob url 0554 * @param {any} content 0555 * @param {string} mime 0556 * @returns {string} url 0557 * @memberof Core 0558 */ 0559 0560 function createBlobUrl(content, mime) { 0561 var tempUrl; 0562 var blob = createBlob(content, mime); 0563 tempUrl = _URL.createObjectURL(blob); 0564 return tempUrl; 0565 } 0566 /** 0567 * Remove a blob url 0568 * @param {string} url 0569 * @memberof Core 0570 */ 0571 0572 function revokeBlobUrl(url) { 0573 return _URL.revokeObjectURL(url); 0574 } 0575 /** 0576 * Create a new base64 encoded url 0577 * @param {any} content 0578 * @param {string} mime 0579 * @returns {string} url 0580 * @memberof Core 0581 */ 0582 0583 function createBase64Url(content, mime) { 0584 var data; 0585 var datauri; 0586 0587 if (typeof content !== "string") { 0588 // Only handles strings 0589 return; 0590 } 0591 0592 data = btoa(content); 0593 datauri = "data:" + mime + ";base64," + data; 0594 return datauri; 0595 } 0596 /** 0597 * Get type of an object 0598 * @param {object} obj 0599 * @returns {string} type 0600 * @memberof Core 0601 */ 0602 0603 function type(obj) { 0604 return Object.prototype.toString.call(obj).slice(8, -1); 0605 } 0606 /** 0607 * Parse xml (or html) markup 0608 * @param {string} markup 0609 * @param {string} mime 0610 * @param {boolean} forceXMLDom force using xmlDom to parse instead of native parser 0611 * @returns {document} document 0612 * @memberof Core 0613 */ 0614 0615 function parse(markup, mime, forceXMLDom) { 0616 var doc; 0617 var Parser; 0618 0619 if (typeof DOMParser === "undefined" || forceXMLDom) { 0620 Parser = _xmldom_xmldom__WEBPACK_IMPORTED_MODULE_0__["DOMParser"]; 0621 } else { 0622 Parser = DOMParser; 0623 } // Remove byte order mark before parsing 0624 // https://www.w3.org/International/questions/qa-byte-order-mark 0625 0626 0627 if (markup.charCodeAt(0) === 0xFEFF) { 0628 markup = markup.slice(1); 0629 } 0630 0631 doc = new Parser().parseFromString(markup, mime); 0632 return doc; 0633 } 0634 /** 0635 * querySelector polyfill 0636 * @param {element} el 0637 * @param {string} sel selector string 0638 * @returns {element} element 0639 * @memberof Core 0640 */ 0641 0642 function qs(el, sel) { 0643 var elements; 0644 0645 if (!el) { 0646 throw new Error("No Element Provided"); 0647 } 0648 0649 if (typeof el.querySelector != "undefined") { 0650 return el.querySelector(sel); 0651 } else { 0652 elements = el.getElementsByTagName(sel); 0653 0654 if (elements.length) { 0655 return elements[0]; 0656 } 0657 } 0658 } 0659 /** 0660 * querySelectorAll polyfill 0661 * @param {element} el 0662 * @param {string} sel selector string 0663 * @returns {element[]} elements 0664 * @memberof Core 0665 */ 0666 0667 function qsa(el, sel) { 0668 if (typeof el.querySelector != "undefined") { 0669 return el.querySelectorAll(sel); 0670 } else { 0671 return el.getElementsByTagName(sel); 0672 } 0673 } 0674 /** 0675 * querySelector by property 0676 * @param {element} el 0677 * @param {string} sel selector string 0678 * @param {object[]} props 0679 * @returns {element[]} elements 0680 * @memberof Core 0681 */ 0682 0683 function qsp(el, sel, props) { 0684 var q, filtered; 0685 0686 if (typeof el.querySelector != "undefined") { 0687 sel += "["; 0688 0689 for (var prop in props) { 0690 sel += prop + "~='" + props[prop] + "'"; 0691 } 0692 0693 sel += "]"; 0694 return el.querySelector(sel); 0695 } else { 0696 q = el.getElementsByTagName(sel); 0697 filtered = Array.prototype.slice.call(q, 0).filter(function (el) { 0698 for (var prop in props) { 0699 if (el.getAttribute(prop) === props[prop]) { 0700 return true; 0701 } 0702 } 0703 0704 return false; 0705 }); 0706 0707 if (filtered) { 0708 return filtered[0]; 0709 } 0710 } 0711 } 0712 /** 0713 * Sprint through all text nodes in a document 0714 * @memberof Core 0715 * @param {element} root element to start with 0716 * @param {function} func function to run on each element 0717 */ 0718 0719 function sprint(root, func) { 0720 var doc = root.ownerDocument || root; 0721 0722 if (typeof doc.createTreeWalker !== "undefined") { 0723 treeWalker(root, func, NodeFilter.SHOW_TEXT); 0724 } else { 0725 walk(root, function (node) { 0726 if (node && node.nodeType === 3) { 0727 // Node.TEXT_NODE 0728 func(node); 0729 } 0730 }, true); 0731 } 0732 } 0733 /** 0734 * Create a treeWalker 0735 * @memberof Core 0736 * @param {element} root element to start with 0737 * @param {function} func function to run on each element 0738 * @param {function | object} filter function or object to filter with 0739 */ 0740 0741 function treeWalker(root, func, filter) { 0742 var treeWalker = document.createTreeWalker(root, filter, null, false); 0743 let node; 0744 0745 while (node = treeWalker.nextNode()) { 0746 func(node); 0747 } 0748 } 0749 /** 0750 * @memberof Core 0751 * @param {node} node 0752 * @param {callback} return false for continue,true for break inside callback 0753 */ 0754 0755 function walk(node, callback) { 0756 if (callback(node)) { 0757 return true; 0758 } 0759 0760 node = node.firstChild; 0761 0762 if (node) { 0763 do { 0764 let walked = walk(node, callback); 0765 0766 if (walked) { 0767 return true; 0768 } 0769 0770 node = node.nextSibling; 0771 } while (node); 0772 } 0773 } 0774 /** 0775 * Convert a blob to a base64 encoded string 0776 * @param {Blog} blob 0777 * @returns {string} 0778 * @memberof Core 0779 */ 0780 0781 function blob2base64(blob) { 0782 return new Promise(function (resolve, reject) { 0783 var reader = new FileReader(); 0784 reader.readAsDataURL(blob); 0785 0786 reader.onloadend = function () { 0787 resolve(reader.result); 0788 }; 0789 }); 0790 } 0791 /** 0792 * Creates a new pending promise and provides methods to resolve or reject it. 0793 * From: https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred#backwards_forwards_compatible 0794 * @memberof Core 0795 */ 0796 0797 function defer() { 0798 /* A method to resolve the associated Promise with the value passed. 0799 * If the promise is already settled it does nothing. 0800 * 0801 * @param {anything} value : This value is used to resolve the promise 0802 * If the value is a Promise then the associated promise assumes the state 0803 * of Promise passed as value. 0804 */ 0805 this.resolve = null; 0806 /* A method to reject the associated Promise with the value passed. 0807 * If the promise is already settled it does nothing. 0808 * 0809 * @param {anything} reason: The reason for the rejection of the Promise. 0810 * Generally its an Error object. If however a Promise is passed, then the Promise 0811 * itself will be the reason for rejection no matter the state of the Promise. 0812 */ 0813 0814 this.reject = null; 0815 this.id = uuid(); 0816 /* A newly created Pomise object. 0817 * Initially in pending state. 0818 */ 0819 0820 this.promise = new Promise((resolve, reject) => { 0821 this.resolve = resolve; 0822 this.reject = reject; 0823 }); 0824 Object.freeze(this); 0825 } 0826 /** 0827 * querySelector with filter by epub type 0828 * @param {element} html 0829 * @param {string} element element type to find 0830 * @param {string} type epub type to find 0831 * @returns {element[]} elements 0832 * @memberof Core 0833 */ 0834 0835 function querySelectorByType(html, element, type) { 0836 var query; 0837 0838 if (typeof html.querySelector != "undefined") { 0839 query = html.querySelector(`${element}[*|type="${type}"]`); 0840 } // Handle IE not supporting namespaced epub:type in querySelector 0841 0842 0843 if (!query || query.length === 0) { 0844 query = qsa(html, element); 0845 0846 for (var i = 0; i < query.length; i++) { 0847 if (query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type || query[i].getAttribute("epub:type") === type) { 0848 return query[i]; 0849 } 0850 } 0851 } else { 0852 return query; 0853 } 0854 } 0855 /** 0856 * Find direct descendents of an element 0857 * @param {element} el 0858 * @returns {element[]} children 0859 * @memberof Core 0860 */ 0861 0862 function findChildren(el) { 0863 var result = []; 0864 var childNodes = el.childNodes; 0865 0866 for (var i = 0; i < childNodes.length; i++) { 0867 let node = childNodes[i]; 0868 0869 if (node.nodeType === 1) { 0870 result.push(node); 0871 } 0872 } 0873 0874 return result; 0875 } 0876 /** 0877 * Find all parents (ancestors) of an element 0878 * @param {element} node 0879 * @returns {element[]} parents 0880 * @memberof Core 0881 */ 0882 0883 function parents(node) { 0884 var nodes = [node]; 0885 0886 for (; node; node = node.parentNode) { 0887 nodes.unshift(node); 0888 } 0889 0890 return nodes; 0891 } 0892 /** 0893 * Find all direct descendents of a specific type 0894 * @param {element} el 0895 * @param {string} nodeName 0896 * @param {boolean} [single] 0897 * @returns {element[]} children 0898 * @memberof Core 0899 */ 0900 0901 function filterChildren(el, nodeName, single) { 0902 var result = []; 0903 var childNodes = el.childNodes; 0904 0905 for (var i = 0; i < childNodes.length; i++) { 0906 let node = childNodes[i]; 0907 0908 if (node.nodeType === 1 && node.nodeName.toLowerCase() === nodeName) { 0909 if (single) { 0910 return node; 0911 } else { 0912 result.push(node); 0913 } 0914 } 0915 } 0916 0917 if (!single) { 0918 return result; 0919 } 0920 } 0921 /** 0922 * Filter all parents (ancestors) with tag name 0923 * @param {element} node 0924 * @param {string} tagname 0925 * @returns {element[]} parents 0926 * @memberof Core 0927 */ 0928 0929 function getParentByTagName(node, tagname) { 0930 let parent; 0931 if (node === null || tagname === '') return; 0932 parent = node.parentNode; 0933 0934 while (parent.nodeType === 1) { 0935 if (parent.tagName.toLowerCase() === tagname) { 0936 return parent; 0937 } 0938 0939 parent = parent.parentNode; 0940 } 0941 } 0942 /** 0943 * Lightweight Polyfill for DOM Range 0944 * @class 0945 * @memberof Core 0946 */ 0947 0948 class RangeObject { 0949 constructor() { 0950 this.collapsed = false; 0951 this.commonAncestorContainer = undefined; 0952 this.endContainer = undefined; 0953 this.endOffset = undefined; 0954 this.startContainer = undefined; 0955 this.startOffset = undefined; 0956 } 0957 0958 setStart(startNode, startOffset) { 0959 this.startContainer = startNode; 0960 this.startOffset = startOffset; 0961 0962 if (!this.endContainer) { 0963 this.collapse(true); 0964 } else { 0965 this.commonAncestorContainer = this._commonAncestorContainer(); 0966 } 0967 0968 this._checkCollapsed(); 0969 } 0970 0971 setEnd(endNode, endOffset) { 0972 this.endContainer = endNode; 0973 this.endOffset = endOffset; 0974 0975 if (!this.startContainer) { 0976 this.collapse(false); 0977 } else { 0978 this.collapsed = false; 0979 this.commonAncestorContainer = this._commonAncestorContainer(); 0980 } 0981 0982 this._checkCollapsed(); 0983 } 0984 0985 collapse(toStart) { 0986 this.collapsed = true; 0987 0988 if (toStart) { 0989 this.endContainer = this.startContainer; 0990 this.endOffset = this.startOffset; 0991 this.commonAncestorContainer = this.startContainer.parentNode; 0992 } else { 0993 this.startContainer = this.endContainer; 0994 this.startOffset = this.endOffset; 0995 this.commonAncestorContainer = this.endOffset.parentNode; 0996 } 0997 } 0998 0999 selectNode(referenceNode) { 1000 let parent = referenceNode.parentNode; 1001 let index = Array.prototype.indexOf.call(parent.childNodes, referenceNode); 1002 this.setStart(parent, index); 1003 this.setEnd(parent, index + 1); 1004 } 1005 1006 selectNodeContents(referenceNode) { 1007 let end = referenceNode.childNodes[referenceNode.childNodes - 1]; 1008 let endIndex = referenceNode.nodeType === 3 ? referenceNode.textContent.length : parent.childNodes.length; 1009 this.setStart(referenceNode, 0); 1010 this.setEnd(referenceNode, endIndex); 1011 } 1012 1013 _commonAncestorContainer(startContainer, endContainer) { 1014 var startParents = parents(startContainer || this.startContainer); 1015 var endParents = parents(endContainer || this.endContainer); 1016 if (startParents[0] != endParents[0]) return undefined; 1017 1018 for (var i = 0; i < startParents.length; i++) { 1019 if (startParents[i] != endParents[i]) { 1020 return startParents[i - 1]; 1021 } 1022 } 1023 } 1024 1025 _checkCollapsed() { 1026 if (this.startContainer === this.endContainer && this.startOffset === this.endOffset) { 1027 this.collapsed = true; 1028 } else { 1029 this.collapsed = false; 1030 } 1031 } 1032 1033 toString() {// TODO: implement walking between start and end to find text 1034 } 1035 1036 } 1037 1038 /***/ }), 1039 /* 1 */ 1040 /***/ (function(module, __webpack_exports__, __webpack_require__) { 1041 1042 "use strict"; 1043 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return EPUBJS_VERSION; }); 1044 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return DOM_EVENTS; }); 1045 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return EVENTS; }); 1046 const EPUBJS_VERSION = "0.3"; // Dom events to listen for 1047 1048 const DOM_EVENTS = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "mousemove", "click", "touchend", "touchstart", "touchmove"]; 1049 const EVENTS = { 1050 BOOK: { 1051 OPEN_FAILED: "openFailed" 1052 }, 1053 CONTENTS: { 1054 EXPAND: "expand", 1055 RESIZE: "resize", 1056 SELECTED: "selected", 1057 SELECTED_RANGE: "selectedRange", 1058 LINK_CLICKED: "linkClicked" 1059 }, 1060 LOCATIONS: { 1061 CHANGED: "changed" 1062 }, 1063 MANAGERS: { 1064 RESIZE: "resize", 1065 RESIZED: "resized", 1066 ORIENTATION_CHANGE: "orientationchange", 1067 ADDED: "added", 1068 SCROLL: "scroll", 1069 SCROLLED: "scrolled", 1070 REMOVED: "removed" 1071 }, 1072 VIEWS: { 1073 AXIS: "axis", 1074 WRITING_MODE: "writingMode", 1075 LOAD_ERROR: "loaderror", 1076 RENDERED: "rendered", 1077 RESIZED: "resized", 1078 DISPLAYED: "displayed", 1079 SHOWN: "shown", 1080 HIDDEN: "hidden", 1081 MARK_CLICKED: "markClicked" 1082 }, 1083 RENDITION: { 1084 STARTED: "started", 1085 ATTACHED: "attached", 1086 DISPLAYED: "displayed", 1087 DISPLAY_ERROR: "displayerror", 1088 RENDERED: "rendered", 1089 REMOVED: "removed", 1090 RESIZED: "resized", 1091 ORIENTATION_CHANGE: "orientationchange", 1092 LOCATION_CHANGED: "locationChanged", 1093 RELOCATED: "relocated", 1094 MARK_CLICKED: "markClicked", 1095 SELECTED: "selected", 1096 LAYOUT: "layout" 1097 }, 1098 LAYOUT: { 1099 UPDATED: "updated" 1100 }, 1101 ANNOTATION: { 1102 ATTACH: "attach", 1103 DETACH: "detach" 1104 } 1105 }; 1106 1107 /***/ }), 1108 /* 2 */ 1109 /***/ (function(module, __webpack_exports__, __webpack_require__) { 1110 1111 "use strict"; 1112 /* harmony import */ var _utils_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0); 1113 1114 const ELEMENT_NODE = 1; 1115 const TEXT_NODE = 3; 1116 const COMMENT_NODE = 8; 1117 const DOCUMENT_NODE = 9; 1118 /** 1119 * Parsing and creation of EpubCFIs: http://www.idpf.org/epub/linking/cfi/epub-cfi.html 1120 1121 * Implements: 1122 * - Character Offset: epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3) 1123 * - Simple Ranges : epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4) 1124 1125 * Does Not Implement: 1126 * - Temporal Offset (~) 1127 * - Spatial Offset (@) 1128 * - Temporal-Spatial Offset (~ + @) 1129 * - Text Location Assertion ([) 1130 * @class 1131 @param {string | Range | Node } [cfiFrom] 1132 @param {string | object} [base] 1133 @param {string} [ignoreClass] class to ignore when parsing DOM 1134 */ 1135 1136 class EpubCFI { 1137 constructor(cfiFrom, base, ignoreClass) { 1138 var type; 1139 this.str = ""; 1140 this.base = {}; 1141 this.spinePos = 0; // For compatibility 1142 1143 this.range = false; // true || false; 1144 1145 this.path = {}; 1146 this.start = null; 1147 this.end = null; // Allow instantiation without the "new" keyword 1148 1149 if (!(this instanceof EpubCFI)) { 1150 return new EpubCFI(cfiFrom, base, ignoreClass); 1151 } 1152 1153 if (typeof base === "string") { 1154 this.base = this.parseComponent(base); 1155 } else if (typeof base === "object" && base.steps) { 1156 this.base = base; 1157 } 1158 1159 type = this.checkType(cfiFrom); 1160 1161 if (type === "string") { 1162 this.str = cfiFrom; 1163 return Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["extend"])(this, this.parse(cfiFrom)); 1164 } else if (type === "range") { 1165 return Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["extend"])(this, this.fromRange(cfiFrom, this.base, ignoreClass)); 1166 } else if (type === "node") { 1167 return Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["extend"])(this, this.fromNode(cfiFrom, this.base, ignoreClass)); 1168 } else if (type === "EpubCFI" && cfiFrom.path) { 1169 return cfiFrom; 1170 } else if (!cfiFrom) { 1171 return this; 1172 } else { 1173 throw new TypeError("not a valid argument for EpubCFI"); 1174 } 1175 } 1176 /** 1177 * Check the type of constructor input 1178 * @private 1179 */ 1180 1181 1182 checkType(cfi) { 1183 if (this.isCfiString(cfi)) { 1184 return "string"; // Is a range object 1185 } else if (cfi && typeof cfi === "object" && (Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["type"])(cfi) === "Range" || typeof cfi.startContainer != "undefined")) { 1186 return "range"; 1187 } else if (cfi && typeof cfi === "object" && typeof cfi.nodeType != "undefined") { 1188 // || typeof cfi === "function" 1189 return "node"; 1190 } else if (cfi && typeof cfi === "object" && cfi instanceof EpubCFI) { 1191 return "EpubCFI"; 1192 } else { 1193 return false; 1194 } 1195 } 1196 /** 1197 * Parse a cfi string to a CFI object representation 1198 * @param {string} cfiStr 1199 * @returns {object} cfi 1200 */ 1201 1202 1203 parse(cfiStr) { 1204 var cfi = { 1205 spinePos: -1, 1206 range: false, 1207 base: {}, 1208 path: {}, 1209 start: null, 1210 end: null 1211 }; 1212 var baseComponent, pathComponent, range; 1213 1214 if (typeof cfiStr !== "string") { 1215 return { 1216 spinePos: -1 1217 }; 1218 } 1219 1220 if (cfiStr.indexOf("epubcfi(") === 0 && cfiStr[cfiStr.length - 1] === ")") { 1221 // Remove initial epubcfi( and ending ) 1222 cfiStr = cfiStr.slice(8, cfiStr.length - 1); 1223 } 1224 1225 baseComponent = this.getChapterComponent(cfiStr); // Make sure this is a valid cfi or return 1226 1227 if (!baseComponent) { 1228 return { 1229 spinePos: -1 1230 }; 1231 } 1232 1233 cfi.base = this.parseComponent(baseComponent); 1234 pathComponent = this.getPathComponent(cfiStr); 1235 cfi.path = this.parseComponent(pathComponent); 1236 range = this.getRange(cfiStr); 1237 1238 if (range) { 1239 cfi.range = true; 1240 cfi.start = this.parseComponent(range[0]); 1241 cfi.end = this.parseComponent(range[1]); 1242 } // Get spine node position 1243 // cfi.spineSegment = cfi.base.steps[1]; 1244 // Chapter segment is always the second step 1245 1246 1247 cfi.spinePos = cfi.base.steps[1].index; 1248 return cfi; 1249 } 1250 1251 parseComponent(componentStr) { 1252 var component = { 1253 steps: [], 1254 terminal: { 1255 offset: null, 1256 assertion: null 1257 } 1258 }; 1259 var parts = componentStr.split(":"); 1260 var steps = parts[0].split("/"); 1261 var terminal; 1262 1263 if (parts.length > 1) { 1264 terminal = parts[1]; 1265 component.terminal = this.parseTerminal(terminal); 1266 } 1267 1268 if (steps[0] === "") { 1269 steps.shift(); // Ignore the first slash 1270 } 1271 1272 component.steps = steps.map(function (step) { 1273 return this.parseStep(step); 1274 }.bind(this)); 1275 return component; 1276 } 1277 1278 parseStep(stepStr) { 1279 var type, num, index, has_brackets, id; 1280 has_brackets = stepStr.match(/\[(.*)\]/); 1281 1282 if (has_brackets && has_brackets[1]) { 1283 id = has_brackets[1]; 1284 } //-- Check if step is a text node or element 1285 1286 1287 num = parseInt(stepStr); 1288 1289 if (isNaN(num)) { 1290 return; 1291 } 1292 1293 if (num % 2 === 0) { 1294 // Even = is an element 1295 type = "element"; 1296 index = num / 2 - 1; 1297 } else { 1298 type = "text"; 1299 index = (num - 1) / 2; 1300 } 1301 1302 return { 1303 "type": type, 1304 "index": index, 1305 "id": id || null 1306 }; 1307 } 1308 1309 parseTerminal(termialStr) { 1310 var characterOffset, textLocationAssertion; 1311 var assertion = termialStr.match(/\[(.*)\]/); 1312 1313 if (assertion && assertion[1]) { 1314 characterOffset = parseInt(termialStr.split("[")[0]); 1315 textLocationAssertion = assertion[1]; 1316 } else { 1317 characterOffset = parseInt(termialStr); 1318 } 1319 1320 if (!Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["isNumber"])(characterOffset)) { 1321 characterOffset = null; 1322 } 1323 1324 return { 1325 "offset": characterOffset, 1326 "assertion": textLocationAssertion 1327 }; 1328 } 1329 1330 getChapterComponent(cfiStr) { 1331 var indirection = cfiStr.split("!"); 1332 return indirection[0]; 1333 } 1334 1335 getPathComponent(cfiStr) { 1336 var indirection = cfiStr.split("!"); 1337 1338 if (indirection[1]) { 1339 let ranges = indirection[1].split(","); 1340 return ranges[0]; 1341 } 1342 } 1343 1344 getRange(cfiStr) { 1345 var ranges = cfiStr.split(","); 1346 1347 if (ranges.length === 3) { 1348 return [ranges[1], ranges[2]]; 1349 } 1350 1351 return false; 1352 } 1353 1354 getCharecterOffsetComponent(cfiStr) { 1355 var splitStr = cfiStr.split(":"); 1356 return splitStr[1] || ""; 1357 } 1358 1359 joinSteps(steps) { 1360 if (!steps) { 1361 return ""; 1362 } 1363 1364 return steps.map(function (part) { 1365 var segment = ""; 1366 1367 if (part.type === "element") { 1368 segment += (part.index + 1) * 2; 1369 } 1370 1371 if (part.type === "text") { 1372 segment += 1 + 2 * part.index; // TODO: double check that this is odd 1373 } 1374 1375 if (part.id) { 1376 segment += "[" + part.id + "]"; 1377 } 1378 1379 return segment; 1380 }).join("/"); 1381 } 1382 1383 segmentString(segment) { 1384 var segmentString = "/"; 1385 segmentString += this.joinSteps(segment.steps); 1386 1387 if (segment.terminal && segment.terminal.offset != null) { 1388 segmentString += ":" + segment.terminal.offset; 1389 } 1390 1391 if (segment.terminal && segment.terminal.assertion != null) { 1392 segmentString += "[" + segment.terminal.assertion + "]"; 1393 } 1394 1395 return segmentString; 1396 } 1397 /** 1398 * Convert CFI to a epubcfi(...) string 1399 * @returns {string} epubcfi 1400 */ 1401 1402 1403 toString() { 1404 var cfiString = "epubcfi("; 1405 cfiString += this.segmentString(this.base); 1406 cfiString += "!"; 1407 cfiString += this.segmentString(this.path); // Add Range, if present 1408 1409 if (this.range && this.start) { 1410 cfiString += ","; 1411 cfiString += this.segmentString(this.start); 1412 } 1413 1414 if (this.range && this.end) { 1415 cfiString += ","; 1416 cfiString += this.segmentString(this.end); 1417 } 1418 1419 cfiString += ")"; 1420 return cfiString; 1421 } 1422 /** 1423 * Compare which of two CFIs is earlier in the text 1424 * @returns {number} First is earlier = -1, Second is earlier = 1, They are equal = 0 1425 */ 1426 1427 1428 compare(cfiOne, cfiTwo) { 1429 var stepsA, stepsB; 1430 var terminalA, terminalB; 1431 var rangeAStartSteps, rangeAEndSteps; 1432 var rangeBEndSteps, rangeBEndSteps; 1433 var rangeAStartTerminal, rangeAEndTerminal; 1434 var rangeBStartTerminal, rangeBEndTerminal; 1435 1436 if (typeof cfiOne === "string") { 1437 cfiOne = new EpubCFI(cfiOne); 1438 } 1439 1440 if (typeof cfiTwo === "string") { 1441 cfiTwo = new EpubCFI(cfiTwo); 1442 } // Compare Spine Positions 1443 1444 1445 if (cfiOne.spinePos > cfiTwo.spinePos) { 1446 return 1; 1447 } 1448 1449 if (cfiOne.spinePos < cfiTwo.spinePos) { 1450 return -1; 1451 } 1452 1453 if (cfiOne.range) { 1454 stepsA = cfiOne.path.steps.concat(cfiOne.start.steps); 1455 terminalA = cfiOne.start.terminal; 1456 } else { 1457 stepsA = cfiOne.path.steps; 1458 terminalA = cfiOne.path.terminal; 1459 } 1460 1461 if (cfiTwo.range) { 1462 stepsB = cfiTwo.path.steps.concat(cfiTwo.start.steps); 1463 terminalB = cfiTwo.start.terminal; 1464 } else { 1465 stepsB = cfiTwo.path.steps; 1466 terminalB = cfiTwo.path.terminal; 1467 } // Compare Each Step in the First item 1468 1469 1470 for (var i = 0; i < stepsA.length; i++) { 1471 if (!stepsA[i]) { 1472 return -1; 1473 } 1474 1475 if (!stepsB[i]) { 1476 return 1; 1477 } 1478 1479 if (stepsA[i].index > stepsB[i].index) { 1480 return 1; 1481 } 1482 1483 if (stepsA[i].index < stepsB[i].index) { 1484 return -1; 1485 } // Otherwise continue checking 1486 1487 } // All steps in First equal to Second and First is Less Specific 1488 1489 1490 if (stepsA.length < stepsB.length) { 1491 return -1; 1492 } // Compare the character offset of the text node 1493 1494 1495 if (terminalA.offset > terminalB.offset) { 1496 return 1; 1497 } 1498 1499 if (terminalA.offset < terminalB.offset) { 1500 return -1; 1501 } // CFI's are equal 1502 1503 1504 return 0; 1505 } 1506 1507 step(node) { 1508 var nodeType = node.nodeType === TEXT_NODE ? "text" : "element"; 1509 return { 1510 "id": node.id, 1511 "tagName": node.tagName, 1512 "type": nodeType, 1513 "index": this.position(node) 1514 }; 1515 } 1516 1517 filteredStep(node, ignoreClass) { 1518 var filteredNode = this.filter(node, ignoreClass); 1519 var nodeType; // Node filtered, so ignore 1520 1521 if (!filteredNode) { 1522 return; 1523 } // Otherwise add the filter node in 1524 1525 1526 nodeType = filteredNode.nodeType === TEXT_NODE ? "text" : "element"; 1527 return { 1528 "id": filteredNode.id, 1529 "tagName": filteredNode.tagName, 1530 "type": nodeType, 1531 "index": this.filteredPosition(filteredNode, ignoreClass) 1532 }; 1533 } 1534 1535 pathTo(node, offset, ignoreClass) { 1536 var segment = { 1537 steps: [], 1538 terminal: { 1539 offset: null, 1540 assertion: null 1541 } 1542 }; 1543 var currentNode = node; 1544 var step; 1545 1546 while (currentNode && currentNode.parentNode && currentNode.parentNode.nodeType != DOCUMENT_NODE) { 1547 if (ignoreClass) { 1548 step = this.filteredStep(currentNode, ignoreClass); 1549 } else { 1550 step = this.step(currentNode); 1551 } 1552 1553 if (step) { 1554 segment.steps.unshift(step); 1555 } 1556 1557 currentNode = currentNode.parentNode; 1558 } 1559 1560 if (offset != null && offset >= 0) { 1561 segment.terminal.offset = offset; // Make sure we are getting to a textNode if there is an offset 1562 1563 if (segment.steps[segment.steps.length - 1].type != "text") { 1564 segment.steps.push({ 1565 "type": "text", 1566 "index": 0 1567 }); 1568 } 1569 } 1570 1571 return segment; 1572 } 1573 1574 equalStep(stepA, stepB) { 1575 if (!stepA || !stepB) { 1576 return false; 1577 } 1578 1579 if (stepA.index === stepB.index && stepA.id === stepB.id && stepA.type === stepB.type) { 1580 return true; 1581 } 1582 1583 return false; 1584 } 1585 /** 1586 * Create a CFI object from a Range 1587 * @param {Range} range 1588 * @param {string | object} base 1589 * @param {string} [ignoreClass] 1590 * @returns {object} cfi 1591 */ 1592 1593 1594 fromRange(range, base, ignoreClass) { 1595 var cfi = { 1596 range: false, 1597 base: {}, 1598 path: {}, 1599 start: null, 1600 end: null 1601 }; 1602 var start = range.startContainer; 1603 var end = range.endContainer; 1604 var startOffset = range.startOffset; 1605 var endOffset = range.endOffset; 1606 var needsIgnoring = false; 1607 1608 if (ignoreClass) { 1609 // Tell pathTo if / what to ignore 1610 needsIgnoring = start.ownerDocument.querySelector("." + ignoreClass) != null; 1611 } 1612 1613 if (typeof base === "string") { 1614 cfi.base = this.parseComponent(base); 1615 cfi.spinePos = cfi.base.steps[1].index; 1616 } else if (typeof base === "object") { 1617 cfi.base = base; 1618 } 1619 1620 if (range.collapsed) { 1621 if (needsIgnoring) { 1622 startOffset = this.patchOffset(start, startOffset, ignoreClass); 1623 } 1624 1625 cfi.path = this.pathTo(start, startOffset, ignoreClass); 1626 } else { 1627 cfi.range = true; 1628 1629 if (needsIgnoring) { 1630 startOffset = this.patchOffset(start, startOffset, ignoreClass); 1631 } 1632 1633 cfi.start = this.pathTo(start, startOffset, ignoreClass); 1634 1635 if (needsIgnoring) { 1636 endOffset = this.patchOffset(end, endOffset, ignoreClass); 1637 } 1638 1639 cfi.end = this.pathTo(end, endOffset, ignoreClass); // Create a new empty path 1640 1641 cfi.path = { 1642 steps: [], 1643 terminal: null 1644 }; // Push steps that are shared between start and end to the common path 1645 1646 var len = cfi.start.steps.length; 1647 var i; 1648 1649 for (i = 0; i < len; i++) { 1650 if (this.equalStep(cfi.start.steps[i], cfi.end.steps[i])) { 1651 if (i === len - 1) { 1652 // Last step is equal, check terminals 1653 if (cfi.start.terminal === cfi.end.terminal) { 1654 // CFI's are equal 1655 cfi.path.steps.push(cfi.start.steps[i]); // Not a range 1656 1657 cfi.range = false; 1658 } 1659 } else { 1660 cfi.path.steps.push(cfi.start.steps[i]); 1661 } 1662 } else { 1663 break; 1664 } 1665 } 1666 1667 cfi.start.steps = cfi.start.steps.slice(cfi.path.steps.length); 1668 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 1669 } 1670 1671 return cfi; 1672 } 1673 /** 1674 * Create a CFI object from a Node 1675 * @param {Node} anchor 1676 * @param {string | object} base 1677 * @param {string} [ignoreClass] 1678 * @returns {object} cfi 1679 */ 1680 1681 1682 fromNode(anchor, base, ignoreClass) { 1683 var cfi = { 1684 range: false, 1685 base: {}, 1686 path: {}, 1687 start: null, 1688 end: null 1689 }; 1690 1691 if (typeof base === "string") { 1692 cfi.base = this.parseComponent(base); 1693 cfi.spinePos = cfi.base.steps[1].index; 1694 } else if (typeof base === "object") { 1695 cfi.base = base; 1696 } 1697 1698 cfi.path = this.pathTo(anchor, null, ignoreClass); 1699 return cfi; 1700 } 1701 1702 filter(anchor, ignoreClass) { 1703 var needsIgnoring; 1704 var sibling; // to join with 1705 1706 var parent, previousSibling, nextSibling; 1707 var isText = false; 1708 1709 if (anchor.nodeType === TEXT_NODE) { 1710 isText = true; 1711 parent = anchor.parentNode; 1712 needsIgnoring = anchor.parentNode.classList.contains(ignoreClass); 1713 } else { 1714 isText = false; 1715 needsIgnoring = anchor.classList.contains(ignoreClass); 1716 } 1717 1718 if (needsIgnoring && isText) { 1719 previousSibling = parent.previousSibling; 1720 nextSibling = parent.nextSibling; // If the sibling is a text node, join the nodes 1721 1722 if (previousSibling && previousSibling.nodeType === TEXT_NODE) { 1723 sibling = previousSibling; 1724 } else if (nextSibling && nextSibling.nodeType === TEXT_NODE) { 1725 sibling = nextSibling; 1726 } 1727 1728 if (sibling) { 1729 return sibling; 1730 } else { 1731 // Parent will be ignored on next step 1732 return anchor; 1733 } 1734 } else if (needsIgnoring && !isText) { 1735 // Otherwise just skip the element node 1736 return false; 1737 } else { 1738 // No need to filter 1739 return anchor; 1740 } 1741 } 1742 1743 patchOffset(anchor, offset, ignoreClass) { 1744 if (anchor.nodeType != TEXT_NODE) { 1745 throw new Error("Anchor must be a text node"); 1746 } 1747 1748 var curr = anchor; 1749 var totalOffset = offset; // If the parent is a ignored node, get offset from it's start 1750 1751 if (anchor.parentNode.classList.contains(ignoreClass)) { 1752 curr = anchor.parentNode; 1753 } 1754 1755 while (curr.previousSibling) { 1756 if (curr.previousSibling.nodeType === ELEMENT_NODE) { 1757 // Originally a text node, so join 1758 if (curr.previousSibling.classList.contains(ignoreClass)) { 1759 totalOffset += curr.previousSibling.textContent.length; 1760 } else { 1761 break; // Normal node, dont join 1762 } 1763 } else { 1764 // If the previous sibling is a text node, join the nodes 1765 totalOffset += curr.previousSibling.textContent.length; 1766 } 1767 1768 curr = curr.previousSibling; 1769 } 1770 1771 return totalOffset; 1772 } 1773 1774 normalizedMap(children, nodeType, ignoreClass) { 1775 var output = {}; 1776 var prevIndex = -1; 1777 var i, 1778 len = children.length; 1779 var currNodeType; 1780 var prevNodeType; 1781 1782 for (i = 0; i < len; i++) { 1783 currNodeType = children[i].nodeType; // Check if needs ignoring 1784 1785 if (currNodeType === ELEMENT_NODE && children[i].classList.contains(ignoreClass)) { 1786 currNodeType = TEXT_NODE; 1787 } 1788 1789 if (i > 0 && currNodeType === TEXT_NODE && prevNodeType === TEXT_NODE) { 1790 // join text nodes 1791 output[i] = prevIndex; 1792 } else if (nodeType === currNodeType) { 1793 prevIndex = prevIndex + 1; 1794 output[i] = prevIndex; 1795 } 1796 1797 prevNodeType = currNodeType; 1798 } 1799 1800 return output; 1801 } 1802 1803 position(anchor) { 1804 var children, index; 1805 1806 if (anchor.nodeType === ELEMENT_NODE) { 1807 children = anchor.parentNode.children; 1808 1809 if (!children) { 1810 children = Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["findChildren"])(anchor.parentNode); 1811 } 1812 1813 index = Array.prototype.indexOf.call(children, anchor); 1814 } else { 1815 children = this.textNodes(anchor.parentNode); 1816 index = children.indexOf(anchor); 1817 } 1818 1819 return index; 1820 } 1821 1822 filteredPosition(anchor, ignoreClass) { 1823 var children, index, map; 1824 1825 if (anchor.nodeType === ELEMENT_NODE) { 1826 children = anchor.parentNode.children; 1827 map = this.normalizedMap(children, ELEMENT_NODE, ignoreClass); 1828 } else { 1829 children = anchor.parentNode.childNodes; // Inside an ignored node 1830 1831 if (anchor.parentNode.classList.contains(ignoreClass)) { 1832 anchor = anchor.parentNode; 1833 children = anchor.parentNode.childNodes; 1834 } 1835 1836 map = this.normalizedMap(children, TEXT_NODE, ignoreClass); 1837 } 1838 1839 index = Array.prototype.indexOf.call(children, anchor); 1840 return map[index]; 1841 } 1842 1843 stepsToXpath(steps) { 1844 var xpath = [".", "*"]; 1845 steps.forEach(function (step) { 1846 var position = step.index + 1; 1847 1848 if (step.id) { 1849 xpath.push("*[position()=" + position + " and @id='" + step.id + "']"); 1850 } else if (step.type === "text") { 1851 xpath.push("text()[" + position + "]"); 1852 } else { 1853 xpath.push("*[" + position + "]"); 1854 } 1855 }); 1856 return xpath.join("/"); 1857 } 1858 /* 1859 To get the last step if needed: 1860 // Get the terminal step 1861 lastStep = steps[steps.length-1]; 1862 // Get the query string 1863 query = this.stepsToQuery(steps); 1864 // Find the containing element 1865 startContainerParent = doc.querySelector(query); 1866 // Find the text node within that element 1867 if(startContainerParent && lastStep.type == "text") { 1868 container = startContainerParent.childNodes[lastStep.index]; 1869 } 1870 */ 1871 1872 1873 stepsToQuerySelector(steps) { 1874 var query = ["html"]; 1875 steps.forEach(function (step) { 1876 var position = step.index + 1; 1877 1878 if (step.id) { 1879 query.push("#" + step.id); 1880 } else if (step.type === "text") {// unsupported in querySelector 1881 // query.push("text()[" + position + "]"); 1882 } else { 1883 query.push("*:nth-child(" + position + ")"); 1884 } 1885 }); 1886 return query.join(">"); 1887 } 1888 1889 textNodes(container, ignoreClass) { 1890 return Array.prototype.slice.call(container.childNodes).filter(function (node) { 1891 if (node.nodeType === TEXT_NODE) { 1892 return true; 1893 } else if (ignoreClass && node.classList.contains(ignoreClass)) { 1894 return true; 1895 } 1896 1897 return false; 1898 }); 1899 } 1900 1901 walkToNode(steps, _doc, ignoreClass) { 1902 var doc = _doc || document; 1903 var container = doc.documentElement; 1904 var children; 1905 var step; 1906 var len = steps.length; 1907 var i; 1908 1909 for (i = 0; i < len; i++) { 1910 step = steps[i]; 1911 1912 if (step.type === "element") { 1913 //better to get a container using id as some times step.index may not be correct 1914 //For ex.https://github.com/futurepress/epub.js/issues/561 1915 if (step.id) { 1916 container = doc.getElementById(step.id); 1917 } else { 1918 children = container.children || Object(_utils_core__WEBPACK_IMPORTED_MODULE_0__["findChildren"])(container); 1919 container = children[step.index]; 1920 } 1921 } else if (step.type === "text") { 1922 container = this.textNodes(container, ignoreClass)[step.index]; 1923 } 1924 1925 if (!container) { 1926 //Break the for loop as due to incorrect index we can get error if 1927 //container is undefined so that other functionailties works fine 1928 //like navigation 1929 break; 1930 } 1931 } 1932 1933 return container; 1934 } 1935 1936 findNode(steps, _doc, ignoreClass) { 1937 var doc = _doc || document; 1938 var container; 1939 var xpath; 1940 1941 if (!ignoreClass && typeof doc.evaluate != "undefined") { 1942 xpath = this.stepsToXpath(steps); 1943 container = doc.evaluate(xpath, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 1944 } else if (ignoreClass) { 1945 container = this.walkToNode(steps, doc, ignoreClass); 1946 } else { 1947 container = this.walkToNode(steps, doc); 1948 } 1949 1950 return container; 1951 } 1952 1953 fixMiss(steps, offset, _doc, ignoreClass) { 1954 var container = this.findNode(steps.slice(0, -1), _doc, ignoreClass); 1955 var children = container.childNodes; 1956 var map = this.normalizedMap(children, TEXT_NODE, ignoreClass); 1957 var child; 1958 var len; 1959 var lastStepIndex = steps[steps.length - 1].index; 1960 1961 for (let childIndex in map) { 1962 if (!map.hasOwnProperty(childIndex)) return; 1963 1964 if (map[childIndex] === lastStepIndex) { 1965 child = children[childIndex]; 1966 len = child.textContent.length; 1967 1968 if (offset > len) { 1969 offset = offset - len; 1970 } else { 1971 if (child.nodeType === ELEMENT_NODE) { 1972 container = child.childNodes[0]; 1973 } else { 1974 container = child; 1975 } 1976 1977 break; 1978 } 1979 } 1980 } 1981 1982 return { 1983 container: container, 1984 offset: offset 1985 }; 1986 } 1987 /** 1988 * Creates a DOM range representing a CFI 1989 * @param {document} _doc document referenced in the base 1990 * @param {string} [ignoreClass] 1991 * @return {Range} 1992 */ 1993 1994 1995 toRange(_doc, ignoreClass) { 1996 var doc = _doc || document; 1997 var range; 1998 var start, end, startContainer, endContainer; 1999 var cfi = this; 2000 var startSteps, endSteps; 2001 var needsIgnoring = ignoreClass ? doc.querySelector("." + ignoreClass) != null : false; 2002 var missed; 2003 2004 if (typeof doc.createRange !== "undefined") { 2005 range = doc.createRange(); 2006 } else { 2007 range = new _utils_core__WEBPACK_IMPORTED_MODULE_0__["RangeObject"](); 2008 } 2009 2010 if (cfi.range) { 2011 start = cfi.start; 2012 startSteps = cfi.path.steps.concat(start.steps); 2013 startContainer = this.findNode(startSteps, doc, needsIgnoring ? ignoreClass : null); 2014 end = cfi.end; 2015 endSteps = cfi.path.steps.concat(end.steps); 2016 endContainer = this.findNode(endSteps, doc, needsIgnoring ? ignoreClass : null); 2017 } else { 2018 start = cfi.path; 2019 startSteps = cfi.path.steps; 2020 startContainer = this.findNode(cfi.path.steps, doc, needsIgnoring ? ignoreClass : null); 2021 } 2022 2023 if (startContainer) { 2024 try { 2025 if (start.terminal.offset != null) { 2026 range.setStart(startContainer, start.terminal.offset); 2027 } else { 2028 range.setStart(startContainer, 0); 2029 } 2030 } catch (e) { 2031 missed = this.fixMiss(startSteps, start.terminal.offset, doc, needsIgnoring ? ignoreClass : null); 2032 range.setStart(missed.container, missed.offset); 2033 } 2034 } else { 2035 console.log("No startContainer found for", this.toString()); // No start found 2036 2037 return null; 2038 } 2039 2040 if (endContainer) { 2041 try { 2042 if (end.terminal.offset != null) { 2043 range.setEnd(endContainer, end.terminal.offset); 2044 } else { 2045 range.setEnd(endContainer, 0); 2046 } 2047 } catch (e) { 2048 missed = this.fixMiss(endSteps, cfi.end.terminal.offset, doc, needsIgnoring ? ignoreClass : null); 2049 range.setEnd(missed.container, missed.offset); 2050 } 2051 } // doc.defaultView.getSelection().addRange(range); 2052 2053 2054 return range; 2055 } 2056 /** 2057 * Check if a string is wrapped with "epubcfi()" 2058 * @param {string} str 2059 * @returns {boolean} 2060 */ 2061 2062 2063 isCfiString(str) { 2064 if (typeof str === "string" && str.indexOf("epubcfi(") === 0 && str[str.length - 1] === ")") { 2065 return true; 2066 } 2067 2068 return false; 2069 } 2070 2071 generateChapterComponent(_spineNodeIndex, _pos, id) { 2072 var pos = parseInt(_pos), 2073 spineNodeIndex = (_spineNodeIndex + 1) * 2, 2074 cfi = "/" + spineNodeIndex + "/"; 2075 cfi += (pos + 1) * 2; 2076 2077 if (id) { 2078 cfi += "[" + id + "]"; 2079 } 2080 2081 return cfi; 2082 } 2083 /** 2084 * Collapse a CFI Range to a single CFI Position 2085 * @param {boolean} [toStart=false] 2086 */ 2087 2088 2089 collapse(toStart) { 2090 if (!this.range) { 2091 return; 2092 } 2093 2094 this.range = false; 2095 2096 if (toStart) { 2097 this.path.steps = this.path.steps.concat(this.start.steps); 2098 this.path.terminal = this.start.terminal; 2099 } else { 2100 this.path.steps = this.path.steps.concat(this.end.steps); 2101 this.path.terminal = this.end.terminal; 2102 } 2103 } 2104 2105 } 2106 2107 /* harmony default export */ __webpack_exports__["a"] = (EpubCFI); 2108 2109 /***/ }), 2110 /* 3 */ 2111 /***/ (function(module, exports, __webpack_require__) { 2112 2113 "use strict"; 2114 2115 2116 var d = __webpack_require__(31) 2117 , callable = __webpack_require__(45) 2118 2119 , apply = Function.prototype.apply, call = Function.prototype.call 2120 , create = Object.create, defineProperty = Object.defineProperty 2121 , defineProperties = Object.defineProperties 2122 , hasOwnProperty = Object.prototype.hasOwnProperty 2123 , descriptor = { configurable: true, enumerable: false, writable: true } 2124 2125 , on, once, off, emit, methods, descriptors, base; 2126 2127 on = function (type, listener) { 2128 var data; 2129 2130 callable(listener); 2131 2132 if (!hasOwnProperty.call(this, '__ee__')) { 2133 data = descriptor.value = create(null); 2134 defineProperty(this, '__ee__', descriptor); 2135 descriptor.value = null; 2136 } else { 2137 data = this.__ee__; 2138 } 2139 if (!data[type]) data[type] = listener; 2140 else if (typeof data[type] === 'object') data[type].push(listener); 2141 else data[type] = [data[type], listener]; 2142 2143 return this; 2144 }; 2145 2146 once = function (type, listener) { 2147 var once, self; 2148 2149 callable(listener); 2150 self = this; 2151 on.call(this, type, once = function () { 2152 off.call(self, type, once); 2153 apply.call(listener, this, arguments); 2154 }); 2155 2156 once.__eeOnceListener__ = listener; 2157 return this; 2158 }; 2159 2160 off = function (type, listener) { 2161 var data, listeners, candidate, i; 2162 2163 callable(listener); 2164 2165 if (!hasOwnProperty.call(this, '__ee__')) return this; 2166 data = this.__ee__; 2167 if (!data[type]) return this; 2168 listeners = data[type]; 2169 2170 if (typeof listeners === 'object') { 2171 for (i = 0; (candidate = listeners[i]); ++i) { 2172 if ((candidate === listener) || 2173 (candidate.__eeOnceListener__ === listener)) { 2174 if (listeners.length === 2) data[type] = listeners[i ? 0 : 1]; 2175 else listeners.splice(i, 1); 2176 } 2177 } 2178 } else { 2179 if ((listeners === listener) || 2180 (listeners.__eeOnceListener__ === listener)) { 2181 delete data[type]; 2182 } 2183 } 2184 2185 return this; 2186 }; 2187 2188 emit = function (type) { 2189 var i, l, listener, listeners, args; 2190 2191 if (!hasOwnProperty.call(this, '__ee__')) return; 2192 listeners = this.__ee__[type]; 2193 if (!listeners) return; 2194 2195 if (typeof listeners === 'object') { 2196 l = arguments.length; 2197 args = new Array(l - 1); 2198 for (i = 1; i < l; ++i) args[i - 1] = arguments[i]; 2199 2200 listeners = listeners.slice(); 2201 for (i = 0; (listener = listeners[i]); ++i) { 2202 apply.call(listener, this, args); 2203 } 2204 } else { 2205 switch (arguments.length) { 2206 case 1: 2207 call.call(listeners, this); 2208 break; 2209 case 2: 2210 call.call(listeners, this, arguments[1]); 2211 break; 2212 case 3: 2213 call.call(listeners, this, arguments[1], arguments[2]); 2214 break; 2215 default: 2216 l = arguments.length; 2217 args = new Array(l - 1); 2218 for (i = 1; i < l; ++i) { 2219 args[i - 1] = arguments[i]; 2220 } 2221 apply.call(listeners, this, args); 2222 } 2223 } 2224 }; 2225 2226 methods = { 2227 on: on, 2228 once: once, 2229 off: off, 2230 emit: emit 2231 }; 2232 2233 descriptors = { 2234 on: d(on), 2235 once: d(once), 2236 off: d(off), 2237 emit: d(emit) 2238 }; 2239 2240 base = defineProperties({}, descriptors); 2241 2242 module.exports = exports = function (o) { 2243 return (o == null) ? create(base) : defineProperties(Object(o), descriptors); 2244 }; 2245 exports.methods = methods; 2246 2247 2248 /***/ }), 2249 /* 4 */ 2250 /***/ (function(module, __webpack_exports__, __webpack_require__) { 2251 2252 "use strict"; 2253 /* harmony import */ var path_webpack__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7); 2254 /* harmony import */ var path_webpack__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path_webpack__WEBPACK_IMPORTED_MODULE_0__); 2255 2256 /** 2257 * Creates a Path object for parsing and manipulation of a path strings 2258 * 2259 * Uses a polyfill for Nodejs path: https://nodejs.org/api/path.html 2260 * @param {string} pathString a url string (relative or absolute) 2261 * @class 2262 */ 2263 2264 class Path { 2265 constructor(pathString) { 2266 var protocol; 2267 var parsed; 2268 protocol = pathString.indexOf("://"); 2269 2270 if (protocol > -1) { 2271 pathString = new URL(pathString).pathname; 2272 } 2273 2274 parsed = this.parse(pathString); 2275 this.path = pathString; 2276 2277 if (this.isDirectory(pathString)) { 2278 this.directory = pathString; 2279 } else { 2280 this.directory = parsed.dir + "/"; 2281 } 2282 2283 this.filename = parsed.base; 2284 this.extension = parsed.ext.slice(1); 2285 } 2286 /** 2287 * Parse the path: https://nodejs.org/api/path.html#path_path_parse_path 2288 * @param {string} what 2289 * @returns {object} 2290 */ 2291 2292 2293 parse(what) { 2294 return path_webpack__WEBPACK_IMPORTED_MODULE_0___default.a.parse(what); 2295 } 2296 /** 2297 * @param {string} what 2298 * @returns {boolean} 2299 */ 2300 2301 2302 isAbsolute(what) { 2303 return path_webpack__WEBPACK_IMPORTED_MODULE_0___default.a.isAbsolute(what || this.path); 2304 } 2305 /** 2306 * Check if path ends with a directory 2307 * @param {string} what 2308 * @returns {boolean} 2309 */ 2310 2311 2312 isDirectory(what) { 2313 return what.charAt(what.length - 1) === "/"; 2314 } 2315 /** 2316 * Resolve a path against the directory of the Path 2317 * 2318 * https://nodejs.org/api/path.html#path_path_resolve_paths 2319 * @param {string} what 2320 * @returns {string} resolved 2321 */ 2322 2323 2324 resolve(what) { 2325 return path_webpack__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(this.directory, what); 2326 } 2327 /** 2328 * Resolve a path relative to the directory of the Path 2329 * 2330 * https://nodejs.org/api/path.html#path_path_relative_from_to 2331 * @param {string} what 2332 * @returns {string} relative 2333 */ 2334 2335 2336 relative(what) { 2337 var isAbsolute = what && what.indexOf("://") > -1; 2338 2339 if (isAbsolute) { 2340 return what; 2341 } 2342 2343 return path_webpack__WEBPACK_IMPORTED_MODULE_0___default.a.relative(this.directory, what); 2344 } 2345 2346 splitPath(filename) { 2347 return this.splitPathRe.exec(filename).slice(1); 2348 } 2349 /** 2350 * Return the path string 2351 * @returns {string} path 2352 */ 2353 2354 2355 toString() { 2356 return this.path; 2357 } 2358 2359 } 2360 2361 /* harmony default export */ __webpack_exports__["a"] = (Path); 2362 2363 /***/ }), 2364 /* 5 */ 2365 /***/ (function(module, __webpack_exports__, __webpack_require__) { 2366 2367 "use strict"; 2368 /* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); 2369 /* harmony import */ var path_webpack__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7); 2370 /* harmony import */ var path_webpack__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path_webpack__WEBPACK_IMPORTED_MODULE_1__); 2371 2372 2373 /** 2374 * creates a Url object for parsing and manipulation of a url string 2375 * @param {string} urlString a url string (relative or absolute) 2376 * @param {string} [baseString] optional base for the url, 2377 * default to window.location.href 2378 */ 2379 2380 class Url { 2381 constructor(urlString, baseString) { 2382 var absolute = urlString.indexOf("://") > -1; 2383 var pathname = urlString; 2384 var basePath; 2385 this.Url = undefined; 2386 this.href = urlString; 2387 this.protocol = ""; 2388 this.origin = ""; 2389 this.hash = ""; 2390 this.hash = ""; 2391 this.search = ""; 2392 this.base = baseString; 2393 2394 if (!absolute && baseString !== false && typeof baseString !== "string" && window && window.location) { 2395 this.base = window.location.href; 2396 } // URL Polyfill doesn't throw an error if base is empty 2397 2398 2399 if (absolute || this.base) { 2400 try { 2401 if (this.base) { 2402 // Safari doesn't like an undefined base 2403 this.Url = new URL(urlString, this.base); 2404 } else { 2405 this.Url = new URL(urlString); 2406 } 2407 2408 this.href = this.Url.href; 2409 this.protocol = this.Url.protocol; 2410 this.origin = this.Url.origin; 2411 this.hash = this.Url.hash; 2412 this.search = this.Url.search; 2413 pathname = this.Url.pathname + (this.Url.search ? this.Url.search : ''); 2414 } catch (e) { 2415 // Skip URL parsing 2416 this.Url = undefined; // resolve the pathname from the base 2417 2418 if (this.base) { 2419 basePath = new _path__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](this.base); 2420 pathname = basePath.resolve(pathname); 2421 } 2422 } 2423 } 2424 2425 this.Path = new _path__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](pathname); 2426 this.directory = this.Path.directory; 2427 this.filename = this.Path.filename; 2428 this.extension = this.Path.extension; 2429 } 2430 /** 2431 * @returns {Path} 2432 */ 2433 2434 2435 path() { 2436 return this.Path; 2437 } 2438 /** 2439 * Resolves a relative path to a absolute url 2440 * @param {string} what 2441 * @returns {string} url 2442 */ 2443 2444 2445 resolve(what) { 2446 var isAbsolute = what.indexOf("://") > -1; 2447 var fullpath; 2448 2449 if (isAbsolute) { 2450 return what; 2451 } 2452 2453 fullpath = path_webpack__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.directory, what); 2454 return this.origin + fullpath; 2455 } 2456 /** 2457 * Resolve a path relative to the url 2458 * @param {string} what 2459 * @returns {string} path 2460 */ 2461 2462 2463 relative(what) { 2464 return path_webpack__WEBPACK_IMPORTED_MODULE_1___default.a.relative(what, this.directory); 2465 } 2466 /** 2467 * @returns {string} 2468 */ 2469 2470 2471 toString() { 2472 return this.href; 2473 } 2474 2475 } 2476 2477 /* harmony default export */ __webpack_exports__["a"] = (Url); 2478 2479 /***/ }), 2480 /* 6 */ 2481 /***/ (function(module, __webpack_exports__, __webpack_require__) { 2482 2483 "use strict"; 2484 /** 2485 * Hooks allow for injecting functions that must all complete in order before finishing 2486 * They will execute in parallel but all must finish before continuing 2487 * Functions may return a promise if they are async. 2488 * @param {any} context scope of this 2489 * @example this.content = new EPUBJS.Hook(this); 2490 */ 2491 class Hook { 2492 constructor(context) { 2493 this.context = context || this; 2494 this.hooks = []; 2495 } 2496 /** 2497 * Adds a function to be run before a hook completes 2498 * @example this.content.register(function(){...}); 2499 */ 2500 2501 2502 register() { 2503 for (var i = 0; i < arguments.length; ++i) { 2504 if (typeof arguments[i] === "function") { 2505 this.hooks.push(arguments[i]); 2506 } else { 2507 // unpack array 2508 for (var j = 0; j < arguments[i].length; ++j) { 2509 this.hooks.push(arguments[i][j]); 2510 } 2511 } 2512 } 2513 } 2514 /** 2515 * Removes a function 2516 * @example this.content.deregister(function(){...}); 2517 */ 2518 2519 2520 deregister(func) { 2521 let hook; 2522 2523 for (let i = 0; i < this.hooks.length; i++) { 2524 hook = this.hooks[i]; 2525 2526 if (hook === func) { 2527 this.hooks.splice(i, 1); 2528 break; 2529 } 2530 } 2531 } 2532 /** 2533 * Triggers a hook to run all functions 2534 * @example this.content.trigger(args).then(function(){...}); 2535 */ 2536 2537 2538 trigger() { 2539 var args = arguments; 2540 var context = this.context; 2541 var promises = []; 2542 this.hooks.forEach(function (task) { 2543 try { 2544 var executing = task.apply(context, args); 2545 } catch (err) { 2546 console.log(err); 2547 } 2548 2549 if (executing && typeof executing["then"] === "function") { 2550 // Task is a function that returns a promise 2551 promises.push(executing); 2552 } // Otherwise Task resolves immediately, continue 2553 2554 }); 2555 return Promise.all(promises); 2556 } // Adds a function to be run before a hook completes 2557 2558 2559 list() { 2560 return this.hooks; 2561 } 2562 2563 clear() { 2564 return this.hooks = []; 2565 } 2566 2567 } 2568 2569 /* harmony default export */ __webpack_exports__["a"] = (Hook); 2570 2571 /***/ }), 2572 /* 7 */ 2573 /***/ (function(module, exports, __webpack_require__) { 2574 2575 "use strict"; 2576 2577 2578 if (!process) { 2579 var process = { 2580 "cwd" : function () { return '/' } 2581 }; 2582 } 2583 2584 function assertPath(path) { 2585 if (typeof path !== 'string') { 2586 throw new TypeError('Path must be a string. Received ' + path); 2587 } 2588 } 2589 2590 // Resolves . and .. elements in a path with directory names 2591 function normalizeStringPosix(path, allowAboveRoot) { 2592 var res = ''; 2593 var lastSlash = -1; 2594 var dots = 0; 2595 var code; 2596 for (var i = 0; i <= path.length; ++i) { 2597 if (i < path.length) 2598 code = path.charCodeAt(i); 2599 else if (code === 47/*/*/) 2600 break; 2601 else 2602 code = 47/*/*/; 2603 if (code === 47/*/*/) { 2604 if (lastSlash === i - 1 || dots === 1) { 2605 // NOOP 2606 } else if (lastSlash !== i - 1 && dots === 2) { 2607 if (res.length < 2 || 2608 res.charCodeAt(res.length - 1) !== 46/*.*/ || 2609 res.charCodeAt(res.length - 2) !== 46/*.*/) { 2610 if (res.length > 2) { 2611 var start = res.length - 1; 2612 var j = start; 2613 for (; j >= 0; --j) { 2614 if (res.charCodeAt(j) === 47/*/*/) 2615 break; 2616 } 2617 if (j !== start) { 2618 if (j === -1) 2619 res = ''; 2620 else 2621 res = res.slice(0, j); 2622 lastSlash = i; 2623 dots = 0; 2624 continue; 2625 } 2626 } else if (res.length === 2 || res.length === 1) { 2627 res = ''; 2628 lastSlash = i; 2629 dots = 0; 2630 continue; 2631 } 2632 } 2633 if (allowAboveRoot) { 2634 if (res.length > 0) 2635 res += '/..'; 2636 else 2637 res = '..'; 2638 } 2639 } else { 2640 if (res.length > 0) 2641 res += '/' + path.slice(lastSlash + 1, i); 2642 else 2643 res = path.slice(lastSlash + 1, i); 2644 } 2645 lastSlash = i; 2646 dots = 0; 2647 } else if (code === 46/*.*/ && dots !== -1) { 2648 ++dots; 2649 } else { 2650 dots = -1; 2651 } 2652 } 2653 return res; 2654 } 2655 2656 function _format(sep, pathObject) { 2657 var dir = pathObject.dir || pathObject.root; 2658 var base = pathObject.base || 2659 ((pathObject.name || '') + (pathObject.ext || '')); 2660 if (!dir) { 2661 return base; 2662 } 2663 if (dir === pathObject.root) { 2664 return dir + base; 2665 } 2666 return dir + sep + base; 2667 } 2668 2669 var posix = { 2670 // path.resolve([from ...], to) 2671 resolve: function resolve() { 2672 var resolvedPath = ''; 2673 var resolvedAbsolute = false; 2674 var cwd; 2675 2676 for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { 2677 var path; 2678 if (i >= 0) 2679 path = arguments[i]; 2680 else { 2681 if (cwd === undefined) 2682 cwd = process.cwd(); 2683 path = cwd; 2684 } 2685 2686 assertPath(path); 2687 2688 // Skip empty entries 2689 if (path.length === 0) { 2690 continue; 2691 } 2692 2693 resolvedPath = path + '/' + resolvedPath; 2694 resolvedAbsolute = path.charCodeAt(0) === 47/*/*/; 2695 } 2696 2697 // At this point the path should be resolved to a full absolute path, but 2698 // handle relative paths to be safe (might happen when process.cwd() fails) 2699 2700 // Normalize the path 2701 resolvedPath = normalizeStringPosix(resolvedPath, !resolvedAbsolute); 2702 2703 if (resolvedAbsolute) { 2704 if (resolvedPath.length > 0) 2705 return '/' + resolvedPath; 2706 else 2707 return '/'; 2708 } else if (resolvedPath.length > 0) { 2709 return resolvedPath; 2710 } else { 2711 return '.'; 2712 } 2713 }, 2714 2715 2716 normalize: function normalize(path) { 2717 assertPath(path); 2718 2719 if (path.length === 0) 2720 return '.'; 2721 2722 var isAbsolute = path.charCodeAt(0) === 47/*/*/; 2723 var trailingSeparator = path.charCodeAt(path.length - 1) === 47/*/*/; 2724 2725 // Normalize the path 2726 path = normalizeStringPosix(path, !isAbsolute); 2727 2728 if (path.length === 0 && !isAbsolute) 2729 path = '.'; 2730 if (path.length > 0 && trailingSeparator) 2731 path += '/'; 2732 2733 if (isAbsolute) 2734 return '/' + path; 2735 return path; 2736 }, 2737 2738 2739 isAbsolute: function isAbsolute(path) { 2740 assertPath(path); 2741 return path.length > 0 && path.charCodeAt(0) === 47/*/*/; 2742 }, 2743 2744 2745 join: function join() { 2746 if (arguments.length === 0) 2747 return '.'; 2748 var joined; 2749 for (var i = 0; i < arguments.length; ++i) { 2750 var arg = arguments[i]; 2751 assertPath(arg); 2752 if (arg.length > 0) { 2753 if (joined === undefined) 2754 joined = arg; 2755 else 2756 joined += '/' + arg; 2757 } 2758 } 2759 if (joined === undefined) 2760 return '.'; 2761 return posix.normalize(joined); 2762 }, 2763 2764 2765 relative: function relative(from, to) { 2766 assertPath(from); 2767 assertPath(to); 2768 2769 if (from === to) 2770 return ''; 2771 2772 from = posix.resolve(from); 2773 to = posix.resolve(to); 2774 2775 if (from === to) 2776 return ''; 2777 2778 // Trim any leading backslashes 2779 var fromStart = 1; 2780 for (; fromStart < from.length; ++fromStart) { 2781 if (from.charCodeAt(fromStart) !== 47/*/*/) 2782 break; 2783 } 2784 var fromEnd = from.length; 2785 var fromLen = (fromEnd - fromStart); 2786 2787 // Trim any leading backslashes 2788 var toStart = 1; 2789 for (; toStart < to.length; ++toStart) { 2790 if (to.charCodeAt(toStart) !== 47/*/*/) 2791 break; 2792 } 2793 var toEnd = to.length; 2794 var toLen = (toEnd - toStart); 2795 2796 // Compare paths to find the longest common path from root 2797 var length = (fromLen < toLen ? fromLen : toLen); 2798 var lastCommonSep = -1; 2799 var i = 0; 2800 for (; i <= length; ++i) { 2801 if (i === length) { 2802 if (toLen > length) { 2803 if (to.charCodeAt(toStart + i) === 47/*/*/) { 2804 // We get here if `from` is the exact base path for `to`. 2805 // For example: from='/foo/bar'; to='/foo/bar/baz' 2806 return to.slice(toStart + i + 1); 2807 } else if (i === 0) { 2808 // We get here if `from` is the root 2809 // For example: from='/'; to='/foo' 2810 return to.slice(toStart + i); 2811 } 2812 } else if (fromLen > length) { 2813 if (from.charCodeAt(fromStart + i) === 47/*/*/) { 2814 // We get here if `to` is the exact base path for `from`. 2815 // For example: from='/foo/bar/baz'; to='/foo/bar' 2816 lastCommonSep = i; 2817 } else if (i === 0) { 2818 // We get here if `to` is the root. 2819 // For example: from='/foo'; to='/' 2820 lastCommonSep = 0; 2821 } 2822 } 2823 break; 2824 } 2825 var fromCode = from.charCodeAt(fromStart + i); 2826 var toCode = to.charCodeAt(toStart + i); 2827 if (fromCode !== toCode) 2828 break; 2829 else if (fromCode === 47/*/*/) 2830 lastCommonSep = i; 2831 } 2832 2833 var out = ''; 2834 // Generate the relative path based on the path difference between `to` 2835 // and `from` 2836 for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { 2837 if (i === fromEnd || from.charCodeAt(i) === 47/*/*/) { 2838 if (out.length === 0) 2839 out += '..'; 2840 else 2841 out += '/..'; 2842 } 2843 } 2844 2845 // Lastly, append the rest of the destination (`to`) path that comes after 2846 // the common path parts 2847 if (out.length > 0) 2848 return out + to.slice(toStart + lastCommonSep); 2849 else { 2850 toStart += lastCommonSep; 2851 if (to.charCodeAt(toStart) === 47/*/*/) 2852 ++toStart; 2853 return to.slice(toStart); 2854 } 2855 }, 2856 2857 2858 _makeLong: function _makeLong(path) { 2859 return path; 2860 }, 2861 2862 2863 dirname: function dirname(path) { 2864 assertPath(path); 2865 if (path.length === 0) 2866 return '.'; 2867 var code = path.charCodeAt(0); 2868 var hasRoot = (code === 47/*/*/); 2869 var end = -1; 2870 var matchedSlash = true; 2871 for (var i = path.length - 1; i >= 1; --i) { 2872 code = path.charCodeAt(i); 2873 if (code === 47/*/*/) { 2874 if (!matchedSlash) { 2875 end = i; 2876 break; 2877 } 2878 } else { 2879 // We saw the first non-path separator 2880 matchedSlash = false; 2881 } 2882 } 2883 2884 if (end === -1) 2885 return hasRoot ? '/' : '.'; 2886 if (hasRoot && end === 1) 2887 return '//'; 2888 return path.slice(0, end); 2889 }, 2890 2891 2892 basename: function basename(path, ext) { 2893 if (ext !== undefined && typeof ext !== 'string') 2894 throw new TypeError('"ext" argument must be a string'); 2895 assertPath(path); 2896 2897 var start = 0; 2898 var end = -1; 2899 var matchedSlash = true; 2900 var i; 2901 2902 if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { 2903 if (ext.length === path.length && ext === path) 2904 return ''; 2905 var extIdx = ext.length - 1; 2906 var firstNonSlashEnd = -1; 2907 for (i = path.length - 1; i >= 0; --i) { 2908 var code = path.charCodeAt(i); 2909 if (code === 47/*/*/) { 2910 // If we reached a path separator that was not part of a set of path 2911 // separators at the end of the string, stop now 2912 if (!matchedSlash) { 2913 start = i + 1; 2914 break; 2915 } 2916 } else { 2917 if (firstNonSlashEnd === -1) { 2918 // We saw the first non-path separator, remember this index in case 2919 // we need it if the extension ends up not matching 2920 matchedSlash = false; 2921 firstNonSlashEnd = i + 1; 2922 } 2923 if (extIdx >= 0) { 2924 // Try to match the explicit extension 2925 if (code === ext.charCodeAt(extIdx)) { 2926 if (--extIdx === -1) { 2927 // We matched the extension, so mark this as the end of our path 2928 // component 2929 end = i; 2930 } 2931 } else { 2932 // Extension does not match, so our result is the entire path 2933 // component 2934 extIdx = -1; 2935 end = firstNonSlashEnd; 2936 } 2937 } 2938 } 2939 } 2940 2941 if (start === end) 2942 end = firstNonSlashEnd; 2943 else if (end === -1) 2944 end = path.length; 2945 return path.slice(start, end); 2946 } else { 2947 for (i = path.length - 1; i >= 0; --i) { 2948 if (path.charCodeAt(i) === 47/*/*/) { 2949 // If we reached a path separator that was not part of a set of path 2950 // separators at the end of the string, stop now 2951 if (!matchedSlash) { 2952 start = i + 1; 2953 break; 2954 } 2955 } else if (end === -1) { 2956 // We saw the first non-path separator, mark this as the end of our 2957 // path component 2958 matchedSlash = false; 2959 end = i + 1; 2960 } 2961 } 2962 2963 if (end === -1) 2964 return ''; 2965 return path.slice(start, end); 2966 } 2967 }, 2968 2969 2970 extname: function extname(path) { 2971 assertPath(path); 2972 var startDot = -1; 2973 var startPart = 0; 2974 var end = -1; 2975 var matchedSlash = true; 2976 // Track the state of characters (if any) we see before our first dot and 2977 // after any path separator we find 2978 var preDotState = 0; 2979 for (var i = path.length - 1; i >= 0; --i) { 2980 var code = path.charCodeAt(i); 2981 if (code === 47/*/*/) { 2982 // If we reached a path separator that was not part of a set of path 2983 // separators at the end of the string, stop now 2984 if (!matchedSlash) { 2985 startPart = i + 1; 2986 break; 2987 } 2988 continue; 2989 } 2990 if (end === -1) { 2991 // We saw the first non-path separator, mark this as the end of our 2992 // extension 2993 matchedSlash = false; 2994 end = i + 1; 2995 } 2996 if (code === 46/*.*/) { 2997 // If this is our first dot, mark it as the start of our extension 2998 if (startDot === -1) 2999 startDot = i; 3000 else if (preDotState !== 1) 3001 preDotState = 1; 3002 } else if (startDot !== -1) { 3003 // We saw a non-dot and non-path separator before our dot, so we should 3004 // have a good chance at having a non-empty extension 3005 preDotState = -1; 3006 } 3007 } 3008 3009 if (startDot === -1 || 3010 end === -1 || 3011 // We saw a non-dot character immediately before the dot 3012 preDotState === 0 || 3013 // The (right-most) trimmed path component is exactly '..' 3014 (preDotState === 1 && 3015 startDot === end - 1 && 3016 startDot === startPart + 1)) { 3017 return ''; 3018 } 3019 return path.slice(startDot, end); 3020 }, 3021 3022 3023 format: function format(pathObject) { 3024 if (pathObject === null || typeof pathObject !== 'object') { 3025 throw new TypeError( 3026 'Parameter "pathObject" must be an object, not ' + typeof(pathObject) 3027 ); 3028 } 3029 return _format('/', pathObject); 3030 }, 3031 3032 3033 parse: function parse(path) { 3034 assertPath(path); 3035 3036 var ret = { root: '', dir: '', base: '', ext: '', name: '' }; 3037 if (path.length === 0) 3038 return ret; 3039 var code = path.charCodeAt(0); 3040 var isAbsolute = (code === 47/*/*/); 3041 var start; 3042 if (isAbsolute) { 3043 ret.root = '/'; 3044 start = 1; 3045 } else { 3046 start = 0; 3047 } 3048 var startDot = -1; 3049 var startPart = 0; 3050 var end = -1; 3051 var matchedSlash = true; 3052 var i = path.length - 1; 3053 3054 // Track the state of characters (if any) we see before our first dot and 3055 // after any path separator we find 3056 var preDotState = 0; 3057 3058 // Get non-dir info 3059 for (; i >= start; --i) { 3060 code = path.charCodeAt(i); 3061 if (code === 47/*/*/) { 3062 // If we reached a path separator that was not part of a set of path 3063 // separators at the end of the string, stop now 3064 if (!matchedSlash) { 3065 startPart = i + 1; 3066 break; 3067 } 3068 continue; 3069 } 3070 if (end === -1) { 3071 // We saw the first non-path separator, mark this as the end of our 3072 // extension 3073 matchedSlash = false; 3074 end = i + 1; 3075 } 3076 if (code === 46/*.*/) { 3077 // If this is our first dot, mark it as the start of our extension 3078 if (startDot === -1) 3079 startDot = i; 3080 else if (preDotState !== 1) 3081 preDotState = 1; 3082 } else if (startDot !== -1) { 3083 // We saw a non-dot and non-path separator before our dot, so we should 3084 // have a good chance at having a non-empty extension 3085 preDotState = -1; 3086 } 3087 } 3088 3089 if (startDot === -1 || 3090 end === -1 || 3091 // We saw a non-dot character immediately before the dot 3092 preDotState === 0 || 3093 // The (right-most) trimmed path component is exactly '..' 3094 (preDotState === 1 && 3095 startDot === end - 1 && 3096 startDot === startPart + 1)) { 3097 if (end !== -1) { 3098 if (startPart === 0 && isAbsolute) 3099 ret.base = ret.name = path.slice(1, end); 3100 else 3101 ret.base = ret.name = path.slice(startPart, end); 3102 } 3103 } else { 3104 if (startPart === 0 && isAbsolute) { 3105 ret.name = path.slice(1, startDot); 3106 ret.base = path.slice(1, end); 3107 } else { 3108 ret.name = path.slice(startPart, startDot); 3109 ret.base = path.slice(startPart, end); 3110 } 3111 ret.ext = path.slice(startDot, end); 3112 } 3113 3114 if (startPart > 0) 3115 ret.dir = path.slice(0, startPart - 1); 3116 else if (isAbsolute) 3117 ret.dir = '/'; 3118 3119 return ret; 3120 }, 3121 3122 3123 sep: '/', 3124 delimiter: ':', 3125 posix: null 3126 }; 3127 3128 3129 module.exports = posix; 3130 3131 3132 /***/ }), 3133 /* 8 */ 3134 /***/ (function(module, __webpack_exports__, __webpack_require__) { 3135 3136 "use strict"; 3137 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return replaceBase; }); 3138 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return replaceCanonical; }); 3139 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "d", function() { return replaceMeta; }); 3140 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return replaceLinks; }); 3141 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "e", function() { return substitute; }); 3142 /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0); 3143 /* harmony import */ var _url__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); 3144 /* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); 3145 3146 3147 3148 function replaceBase(doc, section) { 3149 var base; 3150 var head; 3151 var url = section.url; 3152 var absolute = url.indexOf("://") > -1; 3153 3154 if (!doc) { 3155 return; 3156 } 3157 3158 head = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(doc, "head"); 3159 base = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(head, "base"); 3160 3161 if (!base) { 3162 base = doc.createElement("base"); 3163 head.insertBefore(base, head.firstChild); 3164 } // Fix for Safari crashing if the url doesn't have an origin 3165 3166 3167 if (!absolute && window && window.location) { 3168 url = window.location.origin + url; 3169 } 3170 3171 base.setAttribute("href", url); 3172 } 3173 function replaceCanonical(doc, section) { 3174 var head; 3175 var link; 3176 var url = section.canonical; 3177 3178 if (!doc) { 3179 return; 3180 } 3181 3182 head = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(doc, "head"); 3183 link = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(head, "link[rel='canonical']"); 3184 3185 if (link) { 3186 link.setAttribute("href", url); 3187 } else { 3188 link = doc.createElement("link"); 3189 link.setAttribute("rel", "canonical"); 3190 link.setAttribute("href", url); 3191 head.appendChild(link); 3192 } 3193 } 3194 function replaceMeta(doc, section) { 3195 var head; 3196 var meta; 3197 var id = section.idref; 3198 3199 if (!doc) { 3200 return; 3201 } 3202 3203 head = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(doc, "head"); 3204 meta = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(head, "link[property='dc.identifier']"); 3205 3206 if (meta) { 3207 meta.setAttribute("content", id); 3208 } else { 3209 meta = doc.createElement("meta"); 3210 meta.setAttribute("name", "dc.identifier"); 3211 meta.setAttribute("content", id); 3212 head.appendChild(meta); 3213 } 3214 } // TODO: move me to Contents 3215 3216 function replaceLinks(contents, fn) { 3217 var links = contents.querySelectorAll("a[href]"); 3218 3219 if (!links.length) { 3220 return; 3221 } 3222 3223 var base = Object(_core__WEBPACK_IMPORTED_MODULE_0__["qs"])(contents.ownerDocument, "base"); 3224 var location = base ? base.getAttribute("href") : undefined; 3225 3226 var replaceLink = function (link) { 3227 var href = link.getAttribute("href"); 3228 3229 if (href.indexOf("mailto:") === 0) { 3230 return; 3231 } 3232 3233 var absolute = href.indexOf("://") > -1; 3234 3235 if (absolute) { 3236 link.setAttribute("target", "_blank"); 3237 } else { 3238 var linkUrl; 3239 3240 try { 3241 linkUrl = new _url__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"](href, location); 3242 } catch (error) {// NOOP 3243 } 3244 3245 link.onclick = function () { 3246 if (linkUrl && linkUrl.hash) { 3247 fn(linkUrl.Path.path + linkUrl.hash); 3248 } else if (linkUrl) { 3249 fn(linkUrl.Path.path); 3250 } else { 3251 fn(href); 3252 } 3253 3254 return false; 3255 }; 3256 } 3257 }.bind(this); 3258 3259 for (var i = 0; i < links.length; i++) { 3260 replaceLink(links[i]); 3261 } 3262 } 3263 function substitute(content, urls, replacements) { 3264 urls.forEach(function (url, i) { 3265 if (url && replacements[i]) { 3266 // Account for special characters in the file name. 3267 // See https://stackoverflow.com/a/6318729. 3268 url = url.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 3269 content = content.replace(new RegExp(url, "g"), replacements[i]); 3270 } 3271 }); 3272 return content; 3273 } 3274 3275 /***/ }), 3276 /* 9 */ 3277 /***/ (function(module, __webpack_exports__, __webpack_require__) { 3278 3279 "use strict"; 3280 /* unused harmony export Task */ 3281 /* harmony import */ var _core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0); 3282 3283 /** 3284 * Queue for handling tasks one at a time 3285 * @class 3286 * @param {scope} context what this will resolve to in the tasks 3287 */ 3288 3289 class Queue { 3290 constructor(context) { 3291 this._q = []; 3292 this.context = context; 3293 this.tick = _core__WEBPACK_IMPORTED_MODULE_0__["requestAnimationFrame"]; 3294 this.running = false; 3295 this.paused = false; 3296 } 3297 /** 3298 * Add an item to the queue 3299 * @return {Promise} 3300 */ 3301 3302 3303 enqueue() { 3304 var deferred, promise; 3305 var queued; 3306 var task = [].shift.call(arguments); 3307 var args = arguments; // Handle single args without context 3308 // if(args && !Array.isArray(args)) { 3309 // args = [args]; 3310 // } 3311 3312 if (!task) { 3313 throw new Error("No Task Provided"); 3314 } 3315 3316 if (typeof task === "function") { 3317 deferred = new _core__WEBPACK_IMPORTED_MODULE_0__["defer"](); 3318 promise = deferred.promise; 3319 queued = { 3320 "task": task, 3321 "args": args, 3322 //"context" : context, 3323 "deferred": deferred, 3324 "promise": promise 3325 }; 3326 } else { 3327 // Task is a promise 3328 queued = { 3329 "promise": task 3330 }; 3331 } 3332 3333 this._q.push(queued); // Wait to start queue flush 3334 3335 3336 if (this.paused == false && !this.running) { 3337 // setTimeout(this.flush.bind(this), 0); 3338 // this.tick.call(window, this.run.bind(this)); 3339 this.run(); 3340 } 3341 3342 return queued.promise; 3343 } 3344 /** 3345 * Run one item 3346 * @return {Promise} 3347 */ 3348 3349 3350 dequeue() { 3351 var inwait, task, result; 3352 3353 if (this._q.length && !this.paused) { 3354 inwait = this._q.shift(); 3355 task = inwait.task; 3356 3357 if (task) { 3358 // console.log(task) 3359 result = task.apply(this.context, inwait.args); 3360 3361 if (result && typeof result["then"] === "function") { 3362 // Task is a function that returns a promise 3363 return result.then(function () { 3364 inwait.deferred.resolve.apply(this.context, arguments); 3365 }.bind(this), function () { 3366 inwait.deferred.reject.apply(this.context, arguments); 3367 }.bind(this)); 3368 } else { 3369 // Task resolves immediately 3370 inwait.deferred.resolve.apply(this.context, result); 3371 return inwait.promise; 3372 } 3373 } else if (inwait.promise) { 3374 // Task is a promise 3375 return inwait.promise; 3376 } 3377 } else { 3378 inwait = new _core__WEBPACK_IMPORTED_MODULE_0__["defer"](); 3379 inwait.deferred.resolve(); 3380 return inwait.promise; 3381 } 3382 } // Run All Immediately 3383 3384 3385 dump() { 3386 while (this._q.length) { 3387 this.dequeue(); 3388 } 3389 } 3390 /** 3391 * Run all tasks sequentially, at convince 3392 * @return {Promise} 3393 */ 3394 3395 3396 run() { 3397 if (!this.running) { 3398 this.running = true; 3399 this.defered = new _core__WEBPACK_IMPORTED_MODULE_0__["defer"](); 3400 } 3401 3402 this.tick.call(window, () => { 3403 if (this._q.length) { 3404 this.dequeue().then(function () { 3405 this.run(); 3406 }.bind(this)); 3407 } else { 3408 this.defered.resolve(); 3409 this.running = undefined; 3410 } 3411 }); // Unpause 3412 3413 if (this.paused == true) { 3414 this.paused = false; 3415 } 3416 3417 return this.defered.promise; 3418 } 3419 /** 3420 * Flush all, as quickly as possible 3421 * @return {Promise} 3422 */ 3423 3424 3425 flush() { 3426 if (this.running) { 3427 return this.running; 3428 } 3429 3430 if (this._q.length) { 3431 this.running = this.dequeue().then(function () { 3432 this.running = undefined; 3433 return this.flush(); 3434 }.bind(this)); 3435 return this.running; 3436 } 3437 } 3438 /** 3439 * Clear all items in wait 3440 */ 3441 3442 3443 clear() { 3444 this._q = []; 3445 } 3446 /** 3447 * Get the number of tasks in the queue 3448 * @return {number} tasks 3449 */ 3450 3451 3452 length() { 3453 return this._q.length; 3454 } 3455 /** 3456 * Pause a running queue 3457 */ 3458 3459 3460 pause() { 3461 this.paused = true; 3462 } 3463 /** 3464 * End the queue 3465 */ 3466 3467 3468 stop() { 3469 this._q = []; 3470 this.running = false; 3471 this.paused = true; 3472 } 3473 3474 } 3475 /** 3476 * Create a new task from a callback 3477 * @class 3478 * @private 3479 * @param {function} task 3480 * @param {array} args 3481 * @param {scope} context 3482 * @return {function} task 3483 */ 3484 3485 3486 class Task { 3487 constructor(task, args, context) { 3488 return function () { 3489 var toApply = arguments || []; 3490 return new Promise((resolve, reject) => { 3491 var callback = function (value, err) { 3492 if (!value && err) { 3493 reject(err); 3494 } else { 3495 resolve(value); 3496 } 3497 }; // Add the callback to the arguments list 3498 3499 3500 toApply.push(callback); // Apply all arguments to the functions 3501 3502 task.apply(context || this, toApply); 3503 }); 3504 }; 3505 } 3506 3507 } 3508 3509 /* harmony default export */ __webpack_exports__["a"] = (Queue); 3510 3511 3512 /***/ }), 3513 /* 10 */ 3514 /***/ (function(module, __webpack_exports__, __webpack_require__) { 3515 3516 "use strict"; 3517 3518 // EXTERNAL MODULE: ./node_modules/event-emitter/index.js 3519 var event_emitter = __webpack_require__(3); 3520 var event_emitter_default = /*#__PURE__*/__webpack_require__.n(event_emitter); 3521 3522 // EXTERNAL MODULE: ./src/utils/core.js 3523 var core = __webpack_require__(0); 3524 3525 // CONCATENATED MODULE: ./src/utils/scrolltype.js 3526 // Detect RTL scroll type 3527 // Based on https://github.com/othree/jquery.rtl-scroll-type/blob/master/src/jquery.rtl-scroll.js 3528 function scrollType() { 3529 var type = "reverse"; 3530 var definer = createDefiner(); 3531 document.body.appendChild(definer); 3532 3533 if (definer.scrollLeft > 0) { 3534 type = "default"; 3535 } else { 3536 if (typeof Element !== 'undefined' && Element.prototype.scrollIntoView) { 3537 definer.children[0].children[1].scrollIntoView(); 3538 3539 if (definer.scrollLeft < 0) { 3540 type = "negative"; 3541 } 3542 } else { 3543 definer.scrollLeft = 1; 3544 3545 if (definer.scrollLeft === 0) { 3546 type = "negative"; 3547 } 3548 } 3549 } 3550 3551 document.body.removeChild(definer); 3552 return type; 3553 } 3554 function createDefiner() { 3555 var definer = document.createElement('div'); 3556 definer.dir = "rtl"; 3557 definer.style.position = "fixed"; 3558 definer.style.width = "1px"; 3559 definer.style.height = "1px"; 3560 definer.style.top = "0px"; 3561 definer.style.left = "0px"; 3562 definer.style.overflow = "hidden"; 3563 var innerDiv = document.createElement('div'); 3564 innerDiv.style.width = "2px"; 3565 var spanA = document.createElement('span'); 3566 spanA.style.width = "1px"; 3567 spanA.style.display = "inline-block"; 3568 var spanB = document.createElement('span'); 3569 spanB.style.width = "1px"; 3570 spanB.style.display = "inline-block"; 3571 innerDiv.appendChild(spanA); 3572 innerDiv.appendChild(spanB); 3573 definer.appendChild(innerDiv); 3574 return definer; 3575 } 3576 // EXTERNAL MODULE: ./src/mapping.js 3577 var mapping = __webpack_require__(11); 3578 3579 // EXTERNAL MODULE: ./src/utils/queue.js 3580 var queue = __webpack_require__(9); 3581 3582 // EXTERNAL MODULE: ./node_modules/lodash/throttle.js 3583 var throttle = __webpack_require__(28); 3584 var throttle_default = /*#__PURE__*/__webpack_require__.n(throttle); 3585 3586 // CONCATENATED MODULE: ./src/managers/helpers/stage.js 3587 3588 3589 3590 class stage_Stage { 3591 constructor(_options) { 3592 this.settings = _options || {}; 3593 this.id = "epubjs-container-" + Object(core["uuid"])(); 3594 this.container = this.create(this.settings); 3595 3596 if (this.settings.hidden) { 3597 this.wrapper = this.wrap(this.container); 3598 } 3599 } 3600 /* 3601 * Creates an element to render to. 3602 * Resizes to passed width and height or to the elements size 3603 */ 3604 3605 3606 create(options) { 3607 let height = options.height; // !== false ? options.height : "100%"; 3608 3609 let width = options.width; // !== false ? options.width : "100%"; 3610 3611 let overflow = options.overflow || false; 3612 let axis = options.axis || "vertical"; 3613 let direction = options.direction; 3614 Object(core["extend"])(this.settings, options); 3615 3616 if (options.height && Object(core["isNumber"])(options.height)) { 3617 height = options.height + "px"; 3618 } 3619 3620 if (options.width && Object(core["isNumber"])(options.width)) { 3621 width = options.width + "px"; 3622 } // Create new container element 3623 3624 3625 let container = document.createElement("div"); 3626 container.id = this.id; 3627 container.classList.add("epub-container"); // Style Element 3628 // container.style.fontSize = "0"; 3629 3630 container.style.wordSpacing = "0"; 3631 container.style.lineHeight = "0"; 3632 container.style.verticalAlign = "top"; 3633 container.style.position = "relative"; 3634 3635 if (axis === "horizontal") { 3636 // container.style.whiteSpace = "nowrap"; 3637 container.style.display = "flex"; 3638 container.style.flexDirection = "row"; 3639 container.style.flexWrap = "nowrap"; 3640 } 3641 3642 if (width) { 3643 container.style.width = width; 3644 } 3645 3646 if (height) { 3647 container.style.height = height; 3648 } 3649 3650 if (overflow) { 3651 if (overflow === "scroll" && axis === "vertical") { 3652 container.style["overflow-y"] = overflow; 3653 container.style["overflow-x"] = "hidden"; 3654 } else if (overflow === "scroll" && axis === "horizontal") { 3655 container.style["overflow-y"] = "hidden"; 3656 container.style["overflow-x"] = overflow; 3657 } else { 3658 container.style["overflow"] = overflow; 3659 } 3660 } 3661 3662 if (direction) { 3663 container.dir = direction; 3664 container.style["direction"] = direction; 3665 } 3666 3667 if (direction && this.settings.fullsize) { 3668 document.body.style["direction"] = direction; 3669 } 3670 3671 return container; 3672 } 3673 3674 wrap(container) { 3675 var wrapper = document.createElement("div"); 3676 wrapper.style.visibility = "hidden"; 3677 wrapper.style.overflow = "hidden"; 3678 wrapper.style.width = "0"; 3679 wrapper.style.height = "0"; 3680 wrapper.appendChild(container); 3681 return wrapper; 3682 } 3683 3684 getElement(_element) { 3685 var element; 3686 3687 if (Object(core["isElement"])(_element)) { 3688 element = _element; 3689 } else if (typeof _element === "string") { 3690 element = document.getElementById(_element); 3691 } 3692 3693 if (!element) { 3694 throw new Error("Not an Element"); 3695 } 3696 3697 return element; 3698 } 3699 3700 attachTo(what) { 3701 var element = this.getElement(what); 3702 var base; 3703 3704 if (!element) { 3705 return; 3706 } 3707 3708 if (this.settings.hidden) { 3709 base = this.wrapper; 3710 } else { 3711 base = this.container; 3712 } 3713 3714 element.appendChild(base); 3715 this.element = element; 3716 return element; 3717 } 3718 3719 getContainer() { 3720 return this.container; 3721 } 3722 3723 onResize(func) { 3724 // Only listen to window for resize event if width and height are not fixed. 3725 // This applies if it is set to a percent or auto. 3726 if (!Object(core["isNumber"])(this.settings.width) || !Object(core["isNumber"])(this.settings.height)) { 3727 this.resizeFunc = throttle_default()(func, 50); 3728 window.addEventListener("resize", this.resizeFunc, false); 3729 } 3730 } 3731 3732 onOrientationChange(func) { 3733 this.orientationChangeFunc = func; 3734 window.addEventListener("orientationchange", this.orientationChangeFunc, false); 3735 } 3736 3737 size(width, height) { 3738 var bounds; 3739 3740 let _width = width || this.settings.width; 3741 3742 let _height = height || this.settings.height; // If width or height are set to false, inherit them from containing element 3743 3744 3745 if (width === null) { 3746 bounds = this.element.getBoundingClientRect(); 3747 3748 if (bounds.width) { 3749 width = Math.floor(bounds.width); 3750 this.container.style.width = width + "px"; 3751 } 3752 } else { 3753 if (Object(core["isNumber"])(width)) { 3754 this.container.style.width = width + "px"; 3755 } else { 3756 this.container.style.width = width; 3757 } 3758 } 3759 3760 if (height === null) { 3761 bounds = bounds || this.element.getBoundingClientRect(); 3762 3763 if (bounds.height) { 3764 height = bounds.height; 3765 this.container.style.height = height + "px"; 3766 } 3767 } else { 3768 if (Object(core["isNumber"])(height)) { 3769 this.container.style.height = height + "px"; 3770 } else { 3771 this.container.style.height = height; 3772 } 3773 } 3774 3775 if (!Object(core["isNumber"])(width)) { 3776 width = this.container.clientWidth; 3777 } 3778 3779 if (!Object(core["isNumber"])(height)) { 3780 height = this.container.clientHeight; 3781 } 3782 3783 this.containerStyles = window.getComputedStyle(this.container); 3784 this.containerPadding = { 3785 left: parseFloat(this.containerStyles["padding-left"]) || 0, 3786 right: parseFloat(this.containerStyles["padding-right"]) || 0, 3787 top: parseFloat(this.containerStyles["padding-top"]) || 0, 3788 bottom: parseFloat(this.containerStyles["padding-bottom"]) || 0 3789 }; // Bounds not set, get them from window 3790 3791 let _windowBounds = Object(core["windowBounds"])(); 3792 3793 let bodyStyles = window.getComputedStyle(document.body); 3794 let bodyPadding = { 3795 left: parseFloat(bodyStyles["padding-left"]) || 0, 3796 right: parseFloat(bodyStyles["padding-right"]) || 0, 3797 top: parseFloat(bodyStyles["padding-top"]) || 0, 3798 bottom: parseFloat(bodyStyles["padding-bottom"]) || 0 3799 }; 3800 3801 //if (!_width) { 3802 // width = _windowBounds.width - bodyPadding.left - bodyPadding.right; 3803 //} 3804 3805 if (this.settings.fullsize && !_height || !_height) { 3806 height = _windowBounds.height - bodyPadding.top - bodyPadding.bottom; 3807 } 3808 3809 return { 3810 width: width - this.containerPadding.left - this.containerPadding.right, 3811 height: height - this.containerPadding.top - this.containerPadding.bottom 3812 }; 3813 } 3814 3815 bounds() { 3816 let box; 3817 3818 if (this.container.style.overflow !== "visible") { 3819 box = this.container && this.container.getBoundingClientRect(); 3820 } 3821 3822 if (!box || !box.width || !box.height) { 3823 return Object(core["windowBounds"])(); 3824 } else { 3825 return box; 3826 } 3827 } 3828 3829 getSheet() { 3830 var style = document.createElement("style"); // WebKit hack --> https://davidwalsh.name/add-rules-stylesheets 3831 3832 style.appendChild(document.createTextNode("")); 3833 document.head.appendChild(style); 3834 return style.sheet; 3835 } 3836 3837 addStyleRules(selector, rulesArray) { 3838 var scope = "#" + this.id + " "; 3839 var rules = ""; 3840 3841 if (!this.sheet) { 3842 this.sheet = this.getSheet(); 3843 } 3844 3845 rulesArray.forEach(function (set) { 3846 for (var prop in set) { 3847 if (set.hasOwnProperty(prop)) { 3848 rules += prop + ":" + set[prop] + ";"; 3849 } 3850 } 3851 }); 3852 this.sheet.insertRule(scope + selector + " {" + rules + "}", 0); 3853 } 3854 3855 axis(axis) { 3856 if (axis === "horizontal") { 3857 this.container.style.display = "flex"; 3858 this.container.style.flexDirection = "row"; 3859 this.container.style.flexWrap = "nowrap"; 3860 } else { 3861 this.container.style.display = "block"; 3862 } 3863 3864 this.settings.axis = axis; 3865 } // orientation(orientation) { 3866 // if (orientation === "landscape") { 3867 // 3868 // } else { 3869 // 3870 // } 3871 // 3872 // this.orientation = orientation; 3873 // } 3874 3875 3876 direction(dir) { 3877 if (this.container) { 3878 this.container.dir = dir; 3879 this.container.style["direction"] = dir; 3880 } 3881 3882 if (this.settings.fullsize) { 3883 document.body.style["direction"] = dir; 3884 } 3885 3886 this.settings.dir = dir; 3887 } 3888 3889 overflow(overflow) { 3890 if (this.container) { 3891 if (overflow === "scroll" && this.settings.axis === "vertical") { 3892 this.container.style["overflow-y"] = overflow; 3893 this.container.style["overflow-x"] = "hidden"; 3894 } else if (overflow === "scroll" && this.settings.axis === "horizontal") { 3895 this.container.style["overflow-y"] = "hidden"; 3896 this.container.style["overflow-x"] = overflow; 3897 } else { 3898 this.container.style["overflow"] = overflow; 3899 } 3900 } 3901 3902 this.settings.overflow = overflow; 3903 } 3904 3905 destroy() { 3906 var base; 3907 3908 if (this.element) { 3909 if (this.settings.hidden) { 3910 base = this.wrapper; 3911 } else { 3912 base = this.container; 3913 } 3914 3915 if (this.element.contains(this.container)) { 3916 this.element.removeChild(this.container); 3917 } 3918 3919 window.removeEventListener("resize", this.resizeFunc); 3920 window.removeEventListener("orientationChange", this.orientationChangeFunc); 3921 } 3922 } 3923 3924 } 3925 3926 /* harmony default export */ var stage = (stage_Stage); 3927 // CONCATENATED MODULE: ./src/managers/helpers/views.js 3928 class Views { 3929 constructor(container) { 3930 this.container = container; 3931 this._views = []; 3932 this.length = 0; 3933 this.hidden = false; 3934 } 3935 3936 all() { 3937 return this._views; 3938 } 3939 3940 first() { 3941 return this._views[0]; 3942 } 3943 3944 last() { 3945 return this._views[this._views.length - 1]; 3946 } 3947 3948 indexOf(view) { 3949 return this._views.indexOf(view); 3950 } 3951 3952 slice() { 3953 return this._views.slice.apply(this._views, arguments); 3954 } 3955 3956 get(i) { 3957 return this._views[i]; 3958 } 3959 3960 append(view) { 3961 this._views.push(view); 3962 3963 if (this.container) { 3964 this.container.appendChild(view.element); 3965 } 3966 3967 this.length++; 3968 return view; 3969 } 3970 3971 prepend(view) { 3972 this._views.unshift(view); 3973 3974 if (this.container) { 3975 this.container.insertBefore(view.element, this.container.firstChild); 3976 } 3977 3978 this.length++; 3979 return view; 3980 } 3981 3982 insert(view, index) { 3983 this._views.splice(index, 0, view); 3984 3985 if (this.container) { 3986 if (index < this.container.children.length) { 3987 this.container.insertBefore(view.element, this.container.children[index]); 3988 } else { 3989 this.container.appendChild(view.element); 3990 } 3991 } 3992 3993 this.length++; 3994 return view; 3995 } 3996 3997 remove(view) { 3998 var index = this._views.indexOf(view); 3999 4000 if (index > -1) { 4001 this._views.splice(index, 1); 4002 } 4003 4004 this.destroy(view); 4005 this.length--; 4006 } 4007 4008 destroy(view) { 4009 if (view.displayed) { 4010 view.destroy(); 4011 } 4012 4013 if (this.container) { 4014 this.container.removeChild(view.element); 4015 } 4016 4017 view = null; 4018 } // Iterators 4019 4020 4021 forEach() { 4022 return this._views.forEach.apply(this._views, arguments); 4023 } 4024 4025 clear() { 4026 // Remove all views 4027 var view; 4028 var len = this.length; 4029 if (!this.length) return; 4030 4031 for (var i = 0; i < len; i++) { 4032 view = this._views[i]; 4033 this.destroy(view); 4034 } 4035 4036 this._views = []; 4037 this.length = 0; 4038 } 4039 4040 find(section) { 4041 var view; 4042 var len = this.length; 4043 4044 for (var i = 0; i < len; i++) { 4045 view = this._views[i]; 4046 4047 if (view.displayed && view.section.index == section.index) { 4048 return view; 4049 } 4050 } 4051 } 4052 4053 displayed() { 4054 var displayed = []; 4055 var view; 4056 var len = this.length; 4057 4058 for (var i = 0; i < len; i++) { 4059 view = this._views[i]; 4060 4061 if (view.displayed) { 4062 displayed.push(view); 4063 } 4064 } 4065 4066 return displayed; 4067 } 4068 4069 show() { 4070 var view; 4071 var len = this.length; 4072 4073 for (var i = 0; i < len; i++) { 4074 view = this._views[i]; 4075 4076 if (view.displayed) { 4077 view.show(); 4078 } 4079 } 4080 4081 this.hidden = false; 4082 } 4083 4084 hide() { 4085 var view; 4086 var len = this.length; 4087 4088 for (var i = 0; i < len; i++) { 4089 view = this._views[i]; 4090 4091 if (view.displayed) { 4092 view.hide(); 4093 } 4094 } 4095 4096 this.hidden = true; 4097 } 4098 4099 } 4100 4101 /* harmony default export */ var views = (Views); 4102 // EXTERNAL MODULE: ./src/utils/constants.js 4103 var constants = __webpack_require__(1); 4104 4105 // CONCATENATED MODULE: ./src/managers/default/index.js 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 class default_DefaultViewManager { 4116 constructor(options) { 4117 this.name = "default"; 4118 this.optsSettings = options.settings; 4119 this.View = options.view; 4120 this.request = options.request; 4121 this.renditionQueue = options.queue; 4122 this.q = new queue["a" /* default */](this); 4123 this.settings = Object(core["extend"])(this.settings || {}, { 4124 infinite: true, 4125 hidden: false, 4126 width: undefined, 4127 height: undefined, 4128 axis: undefined, 4129 writingMode: undefined, 4130 flow: "scrolled", 4131 ignoreClass: "", 4132 fullsize: undefined, 4133 allowScriptedContent: false, 4134 allowPopups: false 4135 }); 4136 Object(core["extend"])(this.settings, options.settings || {}); 4137 this.viewSettings = { 4138 ignoreClass: this.settings.ignoreClass, 4139 axis: this.settings.axis, 4140 flow: this.settings.flow, 4141 layout: this.layout, 4142 method: this.settings.method, 4143 // srcdoc, blobUrl, write 4144 width: 0, 4145 height: 0, 4146 forceEvenPages: true, 4147 allowScriptedContent: this.settings.allowScriptedContent, 4148 allowPopups: this.settings.allowPopups 4149 }; 4150 this.rendered = false; 4151 } 4152 4153 render(element, size) { 4154 let tag = element.tagName; 4155 4156 if (typeof this.settings.fullsize === "undefined" && tag && (tag.toLowerCase() == "body" || tag.toLowerCase() == "html")) { 4157 this.settings.fullsize = true; 4158 } 4159 4160 if (this.settings.fullsize) { 4161 this.settings.overflow = "visible"; 4162 this.overflow = this.settings.overflow; 4163 } 4164 4165 this.settings.size = size; 4166 this.settings.rtlScrollType = scrollType(); // Save the stage 4167 4168 this.stage = new stage({ 4169 width: size.width, 4170 height: size.height, 4171 overflow: this.overflow, 4172 hidden: this.settings.hidden, 4173 axis: this.settings.axis, 4174 fullsize: this.settings.fullsize, 4175 direction: this.settings.direction 4176 }); 4177 this.stage.attachTo(element); // Get this stage container div 4178 4179 this.container = this.stage.getContainer(); // Views array methods 4180 4181 this.views = new views(this.container); // Calculate Stage Size 4182 4183 this._bounds = this.bounds(); 4184 this._stageSize = this.stage.size(); // Set the dimensions for views 4185 4186 this.viewSettings.width = this._stageSize.width; 4187 this.viewSettings.height = this._stageSize.height; // Function to handle a resize event. 4188 // Will only attach if width and height are both fixed. 4189 4190 this.stage.onResize(this.onResized.bind(this)); 4191 this.stage.onOrientationChange(this.onOrientationChange.bind(this)); // Add Event Listeners 4192 4193 this.addEventListeners(); // Add Layout method 4194 // this.applyLayoutMethod(); 4195 4196 if (this.layout) { 4197 this.updateLayout(); 4198 } 4199 4200 this.rendered = true; 4201 } 4202 4203 addEventListeners() { 4204 var scroller; 4205 window.addEventListener("unload", function (e) { 4206 this.destroy(); 4207 }.bind(this)); 4208 4209 if (!this.settings.fullsize) { 4210 scroller = this.container; 4211 } else { 4212 scroller = window; 4213 } 4214 4215 this._onScroll = this.onScroll.bind(this); 4216 scroller.addEventListener("scroll", this._onScroll); 4217 } 4218 4219 removeEventListeners() { 4220 var scroller; 4221 4222 if (!this.settings.fullsize) { 4223 scroller = this.container; 4224 } else { 4225 scroller = window; 4226 } 4227 4228 scroller.removeEventListener("scroll", this._onScroll); 4229 this._onScroll = undefined; 4230 } 4231 4232 destroy() { 4233 clearTimeout(this.orientationTimeout); 4234 clearTimeout(this.resizeTimeout); 4235 clearTimeout(this.afterScrolled); 4236 this.clear(); 4237 this.removeEventListeners(); 4238 this.stage.destroy(); 4239 this.rendered = false; 4240 /* 4241 clearTimeout(this.trimTimeout); 4242 if(this.settings.hidden) { 4243 this.element.removeChild(this.wrapper); 4244 } else { 4245 this.element.removeChild(this.container); 4246 } 4247 */ 4248 } 4249 4250 onOrientationChange(e) { 4251 let { 4252 orientation 4253 } = window; 4254 4255 if (this.optsSettings.resizeOnOrientationChange) { 4256 this.resize(); 4257 } // Per ampproject: 4258 // In IOS 10.3, the measured size of an element is incorrect if the 4259 // element size depends on window size directly and the measurement 4260 // happens in window.resize event. Adding a timeout for correct 4261 // measurement. See https://github.com/ampproject/amphtml/issues/8479 4262 4263 4264 clearTimeout(this.orientationTimeout); 4265 this.orientationTimeout = setTimeout(function () { 4266 this.orientationTimeout = undefined; 4267 4268 if (this.optsSettings.resizeOnOrientationChange) { 4269 this.resize(); 4270 } 4271 4272 this.emit(constants["c" /* EVENTS */].MANAGERS.ORIENTATION_CHANGE, orientation); 4273 }.bind(this), 500); 4274 } 4275 4276 onResized(e) { 4277 this.resize(); 4278 } 4279 4280 resize(width, height, epubcfi) { 4281 let stageSize = this.stage.size(width, height); // For Safari, wait for orientation to catch up 4282 // if the window is a square 4283 4284 this.winBounds = Object(core["windowBounds"])(); 4285 4286 if (this.orientationTimeout && this.winBounds.width === this.winBounds.height) { 4287 // reset the stage size for next resize 4288 this._stageSize = undefined; 4289 return; 4290 } 4291 4292 if (this._stageSize && this._stageSize.width === stageSize.width && this._stageSize.height === stageSize.height) { 4293 // Size is the same, no need to resize 4294 return; 4295 } 4296 4297 this._stageSize = stageSize; 4298 this._bounds = this.bounds(); // Clear current views 4299 4300 this.clear(); // Update for new views 4301 4302 this.viewSettings.width = this._stageSize.width; 4303 this.viewSettings.height = this._stageSize.height; 4304 this.updateLayout(); 4305 this.emit(constants["c" /* EVENTS */].MANAGERS.RESIZED, { 4306 width: this._stageSize.width, 4307 height: this._stageSize.height 4308 }, epubcfi); 4309 } 4310 4311 createView(section, forceRight) { 4312 return new this.View(section, Object(core["extend"])(this.viewSettings, { 4313 forceRight 4314 })); 4315 } 4316 4317 handleNextPrePaginated(forceRight, section, action) { 4318 let next; 4319 4320 if (this.layout.name === "pre-paginated" && this.layout.divisor > 1) { 4321 if (forceRight || section.index === 0) { 4322 // First page (cover) should stand alone for pre-paginated books 4323 return; 4324 } 4325 4326 next = section.next(); 4327 4328 if (next && !next.properties.includes("page-spread-left")) { 4329 return action.call(this, next); 4330 } 4331 } 4332 } 4333 4334 display(section, target) { 4335 var displaying = new core["defer"](); 4336 var displayed = displaying.promise; // Check if moving to target is needed 4337 4338 if (target === section.href || Object(core["isNumber"])(target)) { 4339 target = undefined; 4340 } // Check to make sure the section we want isn't already shown 4341 4342 4343 var visible = this.views.find(section); // View is already shown, just move to correct location in view 4344 4345 if (visible && section && this.layout.name !== "pre-paginated") { 4346 let offset = visible.offset(); 4347 4348 if (this.settings.direction === "ltr") { 4349 this.scrollTo(offset.left, offset.top, true); 4350 } else { 4351 let width = visible.width(); 4352 this.scrollTo(offset.left + width, offset.top, true); 4353 } 4354 4355 if (target) { 4356 let offset = visible.locationOf(target); 4357 let width = visible.width(); 4358 this.moveTo(offset, width); 4359 } 4360 4361 displaying.resolve(); 4362 return displayed; 4363 } // Hide all current views 4364 4365 4366 this.clear(); 4367 let forceRight = false; 4368 4369 if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && section.properties.includes("page-spread-right")) { 4370 forceRight = true; 4371 } 4372 4373 this.add(section, forceRight).then(function (view) { 4374 // Move to correct place within the section, if needed 4375 if (target) { 4376 let offset = view.locationOf(target); 4377 let width = view.width(); 4378 this.moveTo(offset, width); 4379 } 4380 }.bind(this), err => { 4381 displaying.reject(err); 4382 }).then(function () { 4383 return this.handleNextPrePaginated(forceRight, section, this.add); 4384 }.bind(this)).then(function () { 4385 this.views.show(); 4386 displaying.resolve(); 4387 }.bind(this)); // .then(function(){ 4388 // return this.hooks.display.trigger(view); 4389 // }.bind(this)) 4390 // .then(function(){ 4391 // this.views.show(); 4392 // }.bind(this)); 4393 4394 return displayed; 4395 } 4396 4397 afterDisplayed(view) { 4398 this.emit(constants["c" /* EVENTS */].MANAGERS.ADDED, view); 4399 } 4400 4401 afterResized(view) { 4402 this.emit(constants["c" /* EVENTS */].MANAGERS.RESIZE, view.section); 4403 } 4404 4405 moveTo(offset, width) { 4406 var distX = 0, 4407 distY = 0; 4408 4409 if (!this.isPaginated) { 4410 distY = offset.top; 4411 } else { 4412 distX = Math.floor(offset.left / this.layout.delta) * this.layout.delta; 4413 4414 if (distX + this.layout.delta > this.container.scrollWidth) { 4415 distX = this.container.scrollWidth - this.layout.delta; 4416 } 4417 4418 distY = Math.floor(offset.top / this.layout.delta) * this.layout.delta; 4419 4420 if (distY + this.layout.delta > this.container.scrollHeight) { 4421 distY = this.container.scrollHeight - this.layout.delta; 4422 } 4423 } 4424 4425 if (this.settings.direction === 'rtl') { 4426 /*** 4427 the `floor` function above (L343) is on positive values, so we should add one `layout.delta` 4428 to distX or use `Math.ceil` function, or multiply offset.left by -1 4429 before `Math.floor` 4430 */ 4431 distX = distX + this.layout.delta; 4432 distX = distX - width; 4433 } 4434 4435 this.scrollTo(distX, distY, true); 4436 } 4437 4438 add(section, forceRight) { 4439 var view = this.createView(section, forceRight); 4440 this.views.append(view); // view.on(EVENTS.VIEWS.SHOWN, this.afterDisplayed.bind(this)); 4441 4442 view.onDisplayed = this.afterDisplayed.bind(this); 4443 view.onResize = this.afterResized.bind(this); 4444 view.on(constants["c" /* EVENTS */].VIEWS.AXIS, axis => { 4445 this.updateAxis(axis); 4446 }); 4447 view.on(constants["c" /* EVENTS */].VIEWS.WRITING_MODE, mode => { 4448 this.updateWritingMode(mode); 4449 }); 4450 return view.display(this.request); 4451 } 4452 4453 append(section, forceRight) { 4454 var view = this.createView(section, forceRight); 4455 this.views.append(view); 4456 view.onDisplayed = this.afterDisplayed.bind(this); 4457 view.onResize = this.afterResized.bind(this); 4458 view.on(constants["c" /* EVENTS */].VIEWS.AXIS, axis => { 4459 this.updateAxis(axis); 4460 }); 4461 view.on(constants["c" /* EVENTS */].VIEWS.WRITING_MODE, mode => { 4462 this.updateWritingMode(mode); 4463 }); 4464 return view.display(this.request); 4465 } 4466 4467 prepend(section, forceRight) { 4468 var view = this.createView(section, forceRight); 4469 view.on(constants["c" /* EVENTS */].VIEWS.RESIZED, bounds => { 4470 this.counter(bounds); 4471 }); 4472 this.views.prepend(view); 4473 view.onDisplayed = this.afterDisplayed.bind(this); 4474 view.onResize = this.afterResized.bind(this); 4475 view.on(constants["c" /* EVENTS */].VIEWS.AXIS, axis => { 4476 this.updateAxis(axis); 4477 }); 4478 view.on(constants["c" /* EVENTS */].VIEWS.WRITING_MODE, mode => { 4479 this.updateWritingMode(mode); 4480 }); 4481 return view.display(this.request); 4482 } 4483 4484 counter(bounds) { 4485 if (this.settings.axis === "vertical") { 4486 this.scrollBy(0, bounds.heightDelta, true); 4487 } else { 4488 this.scrollBy(bounds.widthDelta, 0, true); 4489 } 4490 } // resizeView(view) { 4491 // 4492 // if(this.settings.globalLayoutProperties.layout === "pre-paginated") { 4493 // view.lock("both", this.bounds.width, this.bounds.height); 4494 // } else { 4495 // view.lock("width", this.bounds.width, this.bounds.height); 4496 // } 4497 // 4498 // }; 4499 4500 4501 next() { 4502 var next; 4503 var left; 4504 let dir = this.settings.direction; 4505 if (!this.views.length) return; 4506 4507 if (this.isPaginated && this.settings.axis === "horizontal" && (!dir || dir === "ltr")) { 4508 this.scrollLeft = this.container.scrollLeft; 4509 left = this.container.scrollLeft + this.container.offsetWidth + this.layout.delta; 4510 4511 if (left <= this.container.scrollWidth) { 4512 this.scrollBy(this.layout.delta, 0, true); 4513 } else { 4514 next = this.views.last().section.next(); 4515 } 4516 } else if (this.isPaginated && this.settings.axis === "horizontal" && dir === "rtl") { 4517 this.scrollLeft = this.container.scrollLeft; 4518 4519 if (this.settings.rtlScrollType === "default") { 4520 left = this.container.scrollLeft; 4521 4522 if (left > 0) { 4523 this.scrollBy(this.layout.delta, 0, true); 4524 } else { 4525 next = this.views.last().section.next(); 4526 } 4527 } else { 4528 left = this.container.scrollLeft + this.layout.delta * -1; 4529 4530 if (left > this.container.scrollWidth * -1) { 4531 this.scrollBy(this.layout.delta, 0, true); 4532 } else { 4533 next = this.views.last().section.next(); 4534 } 4535 } 4536 } else if (this.isPaginated && this.settings.axis === "vertical") { 4537 this.scrollTop = this.container.scrollTop; 4538 let top = this.container.scrollTop + this.container.offsetHeight; 4539 4540 if (top < this.container.scrollHeight) { 4541 this.scrollBy(0, this.layout.height, true); 4542 } else { 4543 next = this.views.last().section.next(); 4544 } 4545 } else { 4546 next = this.views.last().section.next(); 4547 } 4548 4549 if (next) { 4550 this.clear(); // The new section may have a different writing-mode from the old section. Thus, we need to update layout. 4551 4552 this.updateLayout(); 4553 let forceRight = false; 4554 4555 if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && next.properties.includes("page-spread-right")) { 4556 forceRight = true; 4557 } 4558 4559 return this.append(next, forceRight).then(function () { 4560 return this.handleNextPrePaginated(forceRight, next, this.append); 4561 }.bind(this), err => { 4562 return err; 4563 }).then(function () { 4564 // Reset position to start for scrolled-doc vertical-rl in default mode 4565 if (!this.isPaginated && this.settings.axis === "horizontal" && this.settings.direction === "rtl" && this.settings.rtlScrollType === "default") { 4566 this.scrollTo(this.container.scrollWidth, 0, true); 4567 } 4568 4569 this.views.show(); 4570 }.bind(this)); 4571 } 4572 } 4573 4574 prev() { 4575 var prev; 4576 var left; 4577 let dir = this.settings.direction; 4578 if (!this.views.length) return; 4579 4580 if (this.isPaginated && this.settings.axis === "horizontal" && (!dir || dir === "ltr")) { 4581 this.scrollLeft = this.container.scrollLeft; 4582 left = this.container.scrollLeft; 4583 4584 if (left > 0) { 4585 this.scrollBy(-this.layout.delta, 0, true); 4586 } else { 4587 prev = this.views.first().section.prev(); 4588 } 4589 } else if (this.isPaginated && this.settings.axis === "horizontal" && dir === "rtl") { 4590 this.scrollLeft = this.container.scrollLeft; 4591 4592 if (this.settings.rtlScrollType === "default") { 4593 left = this.container.scrollLeft + this.container.offsetWidth; 4594 4595 if (left < this.container.scrollWidth) { 4596 this.scrollBy(-this.layout.delta, 0, true); 4597 } else { 4598 prev = this.views.first().section.prev(); 4599 } 4600 } else { 4601 left = this.container.scrollLeft; 4602 4603 if (left < 0) { 4604 this.scrollBy(-this.layout.delta, 0, true); 4605 } else { 4606 prev = this.views.first().section.prev(); 4607 } 4608 } 4609 } else if (this.isPaginated && this.settings.axis === "vertical") { 4610 this.scrollTop = this.container.scrollTop; 4611 let top = this.container.scrollTop; 4612 4613 if (top > 0) { 4614 this.scrollBy(0, -this.layout.height, true); 4615 } else { 4616 prev = this.views.first().section.prev(); 4617 } 4618 } else { 4619 prev = this.views.first().section.prev(); 4620 } 4621 4622 if (prev) { 4623 this.clear(); // The new section may have a different writing-mode from the old section. Thus, we need to update layout. 4624 4625 this.updateLayout(); 4626 let forceRight = false; 4627 4628 if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && typeof prev.prev() !== "object") { 4629 forceRight = true; 4630 } 4631 4632 return this.prepend(prev, forceRight).then(function () { 4633 var left; 4634 4635 if (this.layout.name === "pre-paginated" && this.layout.divisor > 1) { 4636 left = prev.prev(); 4637 4638 if (left) { 4639 return this.prepend(left); 4640 } 4641 } 4642 }.bind(this), err => { 4643 return err; 4644 }).then(function () { 4645 if (this.isPaginated && this.settings.axis === "horizontal") { 4646 if (this.settings.direction === "rtl") { 4647 if (this.settings.rtlScrollType === "default") { 4648 this.scrollTo(0, 0, true); 4649 } else { 4650 this.scrollTo(this.container.scrollWidth * -1 + this.layout.delta, 0, true); 4651 } 4652 } else { 4653 this.scrollTo(this.container.scrollWidth - this.layout.delta, 0, true); 4654 } 4655 } 4656 4657 this.views.show(); 4658 }.bind(this)); 4659 } 4660 } 4661 4662 current() { 4663 var visible = this.visible(); 4664 4665 if (visible.length) { 4666 // Current is the last visible view 4667 return visible[visible.length - 1]; 4668 } 4669 4670 return null; 4671 } 4672 4673 clear() { 4674 // this.q.clear(); 4675 if (this.views) { 4676 this.views.hide(); 4677 this.scrollTo(0, 0, true); 4678 this.views.clear(); 4679 } 4680 } 4681 4682 currentLocation() { 4683 this.updateLayout(); 4684 4685 if (this.isPaginated && this.settings.axis === "horizontal") { 4686 this.location = this.paginatedLocation(); 4687 } else { 4688 this.location = this.scrolledLocation(); 4689 } 4690 4691 return this.location; 4692 } 4693 4694 scrolledLocation() { 4695 let visible = this.visible(); 4696 let container = this.container.getBoundingClientRect(); 4697 let pageHeight = container.height < window.innerHeight ? container.height : window.innerHeight; 4698 let pageWidth = container.width < window.innerWidth ? container.width : window.innerWidth; 4699 let vertical = this.settings.axis === "vertical"; 4700 let rtl = this.settings.direction === "rtl"; 4701 let offset = 0; 4702 let used = 0; 4703 4704 if (this.settings.fullsize) { 4705 offset = vertical ? window.scrollY : window.scrollX; 4706 } 4707 4708 let sections = visible.map(view => { 4709 let { 4710 index, 4711 href 4712 } = view.section; 4713 let position = view.position(); 4714 let width = view.width(); 4715 let height = view.height(); 4716 let startPos; 4717 let endPos; 4718 let stopPos; 4719 let totalPages; 4720 4721 if (vertical) { 4722 startPos = offset + container.top - position.top + used; 4723 endPos = startPos + pageHeight - used; 4724 totalPages = this.layout.count(height, pageHeight).pages; 4725 stopPos = pageHeight; 4726 } else { 4727 startPos = offset + container.left - position.left + used; 4728 endPos = startPos + pageWidth - used; 4729 totalPages = this.layout.count(width, pageWidth).pages; 4730 stopPos = pageWidth; 4731 } 4732 4733 let currPage = Math.ceil(startPos / stopPos); 4734 let pages = []; 4735 let endPage = Math.ceil(endPos / stopPos); // Reverse page counts for horizontal rtl 4736 4737 if (this.settings.direction === "rtl" && !vertical) { 4738 let tempStartPage = currPage; 4739 currPage = totalPages - endPage; 4740 endPage = totalPages - tempStartPage; 4741 } 4742 4743 pages = []; 4744 4745 for (var i = currPage; i <= endPage; i++) { 4746 let pg = i + 1; 4747 pages.push(pg); 4748 } 4749 4750 let mapping = this.mapping.page(view.contents, view.section.cfiBase, startPos, endPos); 4751 return { 4752 index, 4753 href, 4754 pages, 4755 totalPages, 4756 mapping 4757 }; 4758 }); 4759 return sections; 4760 } 4761 4762 paginatedLocation() { 4763 let visible = this.visible(); 4764 let container = this.container.getBoundingClientRect(); 4765 let left = 0; 4766 let used = 0; 4767 4768 if (this.settings.fullsize) { 4769 left = window.scrollX; 4770 } 4771 4772 let sections = visible.map(view => { 4773 let { 4774 index, 4775 href 4776 } = view.section; 4777 let offset; 4778 let position = view.position(); 4779 let width = view.width(); // Find mapping 4780 4781 let start; 4782 let end; 4783 let pageWidth; 4784 4785 if (this.settings.direction === "rtl") { 4786 offset = container.right - left; 4787 pageWidth = Math.min(Math.abs(offset - position.left), this.layout.width) - used; 4788 end = position.width - (position.right - offset) - used; 4789 start = end - pageWidth; 4790 } else { 4791 offset = container.left + left; 4792 pageWidth = Math.min(position.right - offset, this.layout.width) - used; 4793 start = offset - position.left + used; 4794 end = start + pageWidth; 4795 } 4796 4797 used += pageWidth; 4798 let mapping = this.mapping.page(view.contents, view.section.cfiBase, start, end); 4799 let totalPages = this.layout.count(width).pages; 4800 let startPage = Math.floor(start / this.layout.pageWidth); 4801 let pages = []; 4802 let endPage = Math.floor(end / this.layout.pageWidth); // start page should not be negative 4803 4804 if (startPage < 0) { 4805 startPage = 0; 4806 endPage = endPage + 1; 4807 } // Reverse page counts for rtl 4808 4809 4810 if (this.settings.direction === "rtl") { 4811 let tempStartPage = startPage; 4812 startPage = totalPages - endPage; 4813 endPage = totalPages - tempStartPage; 4814 } 4815 4816 for (var i = startPage + 1; i <= endPage; i++) { 4817 let pg = i; 4818 pages.push(pg); 4819 } 4820 4821 return { 4822 index, 4823 href, 4824 pages, 4825 totalPages, 4826 mapping 4827 }; 4828 }); 4829 return sections; 4830 } 4831 4832 isVisible(view, offsetPrev, offsetNext, _container) { 4833 var position = view.position(); 4834 4835 var container = _container || this.bounds(); 4836 4837 if (this.settings.axis === "horizontal" && position.right > container.left - offsetPrev && position.left < container.right + offsetNext) { 4838 return true; 4839 } else if (this.settings.axis === "vertical" && position.bottom > container.top - offsetPrev && position.top < container.bottom + offsetNext) { 4840 return true; 4841 } 4842 4843 return false; 4844 } 4845 4846 visible() { 4847 var container = this.bounds(); 4848 var views = this.views.displayed(); 4849 var viewsLength = views.length; 4850 var visible = []; 4851 var isVisible; 4852 var view; 4853 4854 for (var i = 0; i < viewsLength; i++) { 4855 view = views[i]; 4856 isVisible = this.isVisible(view, 0, 0, container); 4857 4858 if (isVisible === true) { 4859 visible.push(view); 4860 } 4861 } 4862 4863 return visible; 4864 } 4865 4866 scrollBy(x, y, silent) { 4867 let dir = this.settings.direction === "rtl" ? -1 : 1; 4868 4869 if (silent) { 4870 this.ignore = true; 4871 } 4872 4873 if (!this.settings.fullsize) { 4874 if (x) this.container.scrollLeft += x * dir; 4875 if (y) this.container.scrollTop += y; 4876 } else { 4877 window.scrollBy(x * dir, y * dir); 4878 } 4879 4880 this.scrolled = true; 4881 } 4882 4883 scrollTo(x, y, silent) { 4884 if (silent) { 4885 this.ignore = true; 4886 } 4887 4888 if (!this.settings.fullsize) { 4889 this.container.scrollLeft = x; 4890 this.container.scrollTop = y; 4891 } else { 4892 window.scrollTo(x, y); 4893 } 4894 4895 this.scrolled = true; 4896 } 4897 4898 onScroll() { 4899 let scrollTop; 4900 let scrollLeft; 4901 4902 if (!this.settings.fullsize) { 4903 scrollTop = this.container.scrollTop; 4904 scrollLeft = this.container.scrollLeft; 4905 } else { 4906 scrollTop = window.scrollY; 4907 scrollLeft = window.scrollX; 4908 } 4909 4910 this.scrollTop = scrollTop; 4911 this.scrollLeft = scrollLeft; 4912 4913 if (!this.ignore) { 4914 this.emit(constants["c" /* EVENTS */].MANAGERS.SCROLL, { 4915 top: scrollTop, 4916 left: scrollLeft 4917 }); 4918 clearTimeout(this.afterScrolled); 4919 this.afterScrolled = setTimeout(function () { 4920 this.emit(constants["c" /* EVENTS */].MANAGERS.SCROLLED, { 4921 top: this.scrollTop, 4922 left: this.scrollLeft 4923 }); 4924 }.bind(this), 20); 4925 } else { 4926 this.ignore = false; 4927 } 4928 } 4929 4930 bounds() { 4931 var bounds; 4932 bounds = this.stage.bounds(); 4933 return bounds; 4934 } 4935 4936 applyLayout(layout) { 4937 this.layout = layout; 4938 this.updateLayout(); 4939 4940 if (this.views && this.views.length > 0 && this.layout.name === "pre-paginated") { 4941 this.display(this.views.first().section); 4942 } // this.manager.layout(this.layout.format); 4943 4944 } 4945 4946 updateLayout() { 4947 if (!this.stage) { 4948 return; 4949 } 4950 4951 this._stageSize = this.stage.size(); 4952 4953 if (!this.isPaginated) { 4954 this.layout.calculate(this._stageSize.width, this._stageSize.height); 4955 } else { 4956 this.layout.calculate(this._stageSize.width, this._stageSize.height, this.settings.gap); // Set the look ahead offset for what is visible 4957 4958 this.settings.offset = this.layout.delta / this.layout.divisor; // this.stage.addStyleRules("iframe", [{"margin-right" : this.layout.gap + "px"}]); 4959 } // Set the dimensions for views 4960 4961 4962 this.viewSettings.width = this.layout.width; 4963 this.viewSettings.height = this.layout.height; 4964 this.setLayout(this.layout); 4965 } 4966 4967 setLayout(layout) { 4968 this.viewSettings.layout = layout; 4969 this.mapping = new mapping["a" /* default */](layout.props, this.settings.direction, this.settings.axis); 4970 4971 if (this.views) { 4972 this.views.forEach(function (view) { 4973 if (view) { 4974 view.setLayout(layout); 4975 } 4976 }); 4977 } 4978 } 4979 4980 updateWritingMode(mode) { 4981 this.writingMode = mode; 4982 } 4983 4984 updateAxis(axis, forceUpdate) { 4985 if (!forceUpdate && axis === this.settings.axis) { 4986 return; 4987 } 4988 4989 this.settings.axis = axis; 4990 this.stage && this.stage.axis(axis); 4991 this.viewSettings.axis = axis; 4992 4993 if (this.mapping) { 4994 this.mapping = new mapping["a" /* default */](this.layout.props, this.settings.direction, this.settings.axis); 4995 } 4996 4997 if (this.layout) { 4998 if (axis === "vertical") { 4999 this.layout.spread("none"); 5000 } else { 5001 this.layout.spread(this.layout.settings.spread); 5002 } 5003 } 5004 } 5005 5006 updateFlow(flow, defaultScrolledOverflow = "auto") { 5007 let isPaginated = flow === "paginated" || flow === "auto"; 5008 this.isPaginated = isPaginated; 5009 5010 if (flow === "scrolled-doc" || flow === "scrolled-continuous" || flow === "scrolled") { 5011 this.updateAxis("vertical"); 5012 } else { 5013 this.updateAxis("horizontal"); 5014 } 5015 5016 this.viewSettings.flow = flow; 5017 5018 if (!this.settings.overflow) { 5019 this.overflow = isPaginated ? "hidden" : defaultScrolledOverflow; 5020 } else { 5021 this.overflow = this.settings.overflow; 5022 } 5023 5024 this.stage && this.stage.overflow(this.overflow); 5025 this.updateLayout(); 5026 } 5027 5028 getContents() { 5029 var contents = []; 5030 5031 if (!this.views) { 5032 return contents; 5033 } 5034 5035 this.views.forEach(function (view) { 5036 const viewContents = view && view.contents; 5037 5038 if (viewContents) { 5039 contents.push(viewContents); 5040 } 5041 }); 5042 return contents; 5043 } 5044 5045 direction(dir = "ltr") { 5046 this.settings.direction = dir; 5047 this.stage && this.stage.direction(dir); 5048 this.viewSettings.direction = dir; 5049 this.updateLayout(); 5050 } 5051 5052 isRendered() { 5053 return this.rendered; 5054 } 5055 5056 } //-- Enable binding events to Manager 5057 5058 5059 event_emitter_default()(default_DefaultViewManager.prototype); 5060 /* harmony default export */ var managers_default = __webpack_exports__["a"] = (default_DefaultViewManager); 5061 5062 /***/ }), 5063 /* 11 */ 5064 /***/ (function(module, __webpack_exports__, __webpack_require__) { 5065 5066 "use strict"; 5067 /* harmony import */ var _epubcfi__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); 5068 /* harmony import */ var _utils_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0); 5069 5070 5071 /** 5072 * Map text locations to CFI ranges 5073 * @class 5074 * @param {Layout} layout Layout to apply 5075 * @param {string} [direction="ltr"] Text direction 5076 * @param {string} [axis="horizontal"] vertical or horizontal axis 5077 * @param {boolean} [dev] toggle developer highlighting 5078 */ 5079 5080 class Mapping { 5081 constructor(layout, direction, axis, dev = false) { 5082 this.layout = layout; 5083 this.horizontal = axis === "horizontal" ? true : false; 5084 this.direction = direction || "ltr"; 5085 this._dev = dev; 5086 } 5087 /** 5088 * Find CFI pairs for entire section at once 5089 */ 5090 5091 5092 section(view) { 5093 var ranges = this.findRanges(view); 5094 var map = this.rangeListToCfiList(view.section.cfiBase, ranges); 5095 return map; 5096 } 5097 /** 5098 * Find CFI pairs for a page 5099 * @param {Contents} contents Contents from view 5100 * @param {string} cfiBase string of the base for a cfi 5101 * @param {number} start position to start at 5102 * @param {number} end position to end at 5103 */ 5104 5105 5106 page(contents, cfiBase, start, end) { 5107 var root = contents && contents.document ? contents.document.body : false; 5108 var result; 5109 5110 if (!root) { 5111 return; 5112 } 5113 5114 result = this.rangePairToCfiPair(cfiBase, { 5115 start: this.findStart(root, start, end), 5116 end: this.findEnd(root, start, end) 5117 }); 5118 5119 if (this._dev === true) { 5120 let doc = contents.document; 5121 let startRange = new _epubcfi__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](result.start).toRange(doc); 5122 let endRange = new _epubcfi__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](result.end).toRange(doc); 5123 let selection = doc.defaultView.getSelection(); 5124 let r = doc.createRange(); 5125 selection.removeAllRanges(); 5126 r.setStart(startRange.startContainer, startRange.startOffset); 5127 r.setEnd(endRange.endContainer, endRange.endOffset); 5128 selection.addRange(r); 5129 } 5130 5131 return result; 5132 } 5133 /** 5134 * Walk a node, preforming a function on each node it finds 5135 * @private 5136 * @param {Node} root Node to walkToNode 5137 * @param {function} func walk function 5138 * @return {*} returns the result of the walk function 5139 */ 5140 5141 5142 walk(root, func) { 5143 // IE11 has strange issue, if root is text node IE throws exception on 5144 // calling treeWalker.nextNode(), saying 5145 // Unexpected call to method or property access instead of returning null value 5146 if (root && root.nodeType === Node.TEXT_NODE) { 5147 return; 5148 } // safeFilter is required so that it can work in IE as filter is a function for IE 5149 // and for other browser filter is an object. 5150 5151 5152 var filter = { 5153 acceptNode: function (node) { 5154 if (node.data.trim().length > 0) { 5155 return NodeFilter.FILTER_ACCEPT; 5156 } else { 5157 return NodeFilter.FILTER_REJECT; 5158 } 5159 } 5160 }; 5161 var safeFilter = filter.acceptNode; 5162 safeFilter.acceptNode = filter.acceptNode; 5163 var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, safeFilter, false); 5164 var node; 5165 var result; 5166 5167 while (node = treeWalker.nextNode()) { 5168 result = func(node); 5169 if (result) break; 5170 } 5171 5172 return result; 5173 } 5174 5175 findRanges(view) { 5176 var columns = []; 5177 var scrollWidth = view.contents.scrollWidth(); 5178 var spreads = Math.ceil(scrollWidth / this.layout.spreadWidth); 5179 var count = spreads * this.layout.divisor; 5180 var columnWidth = this.layout.columnWidth; 5181 var gap = this.layout.gap; 5182 var start, end; 5183 5184 for (var i = 0; i < count.pages; i++) { 5185 start = (columnWidth + gap) * i; 5186 end = columnWidth * (i + 1) + gap * i; 5187 columns.push({ 5188 start: this.findStart(view.document.body, start, end), 5189 end: this.findEnd(view.document.body, start, end) 5190 }); 5191 } 5192 5193 return columns; 5194 } 5195 /** 5196 * Find Start Range 5197 * @private 5198 * @param {Node} root root node 5199 * @param {number} start position to start at 5200 * @param {number} end position to end at 5201 * @return {Range} 5202 */ 5203 5204 5205 findStart(root, start, end) { 5206 var stack = [root]; 5207 var $el; 5208 var found; 5209 var $prev = root; 5210 5211 while (stack.length) { 5212 $el = stack.shift(); 5213 found = this.walk($el, node => { 5214 var left, right, top, bottom; 5215 var elPos; 5216 var elRange; 5217 elPos = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["nodeBounds"])(node); 5218 5219 if (this.horizontal && this.direction === "ltr") { 5220 left = this.horizontal ? elPos.left : elPos.top; 5221 right = this.horizontal ? elPos.right : elPos.bottom; 5222 5223 if (left >= start && left <= end) { 5224 return node; 5225 } else if (right > start) { 5226 return node; 5227 } else { 5228 $prev = node; 5229 stack.push(node); 5230 } 5231 } else if (this.horizontal && this.direction === "rtl") { 5232 left = elPos.left; 5233 right = elPos.right; 5234 5235 if (right <= end && right >= start) { 5236 return node; 5237 } else if (left < end) { 5238 return node; 5239 } else { 5240 $prev = node; 5241 stack.push(node); 5242 } 5243 } else { 5244 top = elPos.top; 5245 bottom = elPos.bottom; 5246 5247 if (top >= start && top <= end) { 5248 return node; 5249 } else if (bottom > start) { 5250 return node; 5251 } else { 5252 $prev = node; 5253 stack.push(node); 5254 } 5255 } 5256 }); 5257 5258 if (found) { 5259 return this.findTextStartRange(found, start, end); 5260 } 5261 } // Return last element 5262 5263 5264 return this.findTextStartRange($prev, start, end); 5265 } 5266 /** 5267 * Find End Range 5268 * @private 5269 * @param {Node} root root node 5270 * @param {number} start position to start at 5271 * @param {number} end position to end at 5272 * @return {Range} 5273 */ 5274 5275 5276 findEnd(root, start, end) { 5277 var stack = [root]; 5278 var $el; 5279 var $prev = root; 5280 var found; 5281 5282 while (stack.length) { 5283 $el = stack.shift(); 5284 found = this.walk($el, node => { 5285 var left, right, top, bottom; 5286 var elPos; 5287 var elRange; 5288 elPos = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["nodeBounds"])(node); 5289 5290 if (this.horizontal && this.direction === "ltr") { 5291 left = Math.round(elPos.left); 5292 right = Math.round(elPos.right); 5293 5294 if (left > end && $prev) { 5295 return $prev; 5296 } else if (right > end) { 5297 return node; 5298 } else { 5299 $prev = node; 5300 stack.push(node); 5301 } 5302 } else if (this.horizontal && this.direction === "rtl") { 5303 left = Math.round(this.horizontal ? elPos.left : elPos.top); 5304 right = Math.round(this.horizontal ? elPos.right : elPos.bottom); 5305 5306 if (right < start && $prev) { 5307 return $prev; 5308 } else if (left < start) { 5309 return node; 5310 } else { 5311 $prev = node; 5312 stack.push(node); 5313 } 5314 } else { 5315 top = Math.round(elPos.top); 5316 bottom = Math.round(elPos.bottom); 5317 5318 if (top > end && $prev) { 5319 return $prev; 5320 } else if (bottom > end) { 5321 return node; 5322 } else { 5323 $prev = node; 5324 stack.push(node); 5325 } 5326 } 5327 }); 5328 5329 if (found) { 5330 return this.findTextEndRange(found, start, end); 5331 } 5332 } // end of chapter 5333 5334 5335 return this.findTextEndRange($prev, start, end); 5336 } 5337 /** 5338 * Find Text Start Range 5339 * @private 5340 * @param {Node} root root node 5341 * @param {number} start position to start at 5342 * @param {number} end position to end at 5343 * @return {Range} 5344 */ 5345 5346 5347 findTextStartRange(node, start, end) { 5348 var ranges = this.splitTextNodeIntoRanges(node); 5349 var range; 5350 var pos; 5351 var left, top, right; 5352 5353 for (var i = 0; i < ranges.length; i++) { 5354 range = ranges[i]; 5355 pos = range.getBoundingClientRect(); 5356 5357 if (this.horizontal && this.direction === "ltr") { 5358 left = pos.left; 5359 5360 if (left >= start) { 5361 return range; 5362 } 5363 } else if (this.horizontal && this.direction === "rtl") { 5364 right = pos.right; 5365 5366 if (right <= end) { 5367 return range; 5368 } 5369 } else { 5370 top = pos.top; 5371 5372 if (top >= start) { 5373 return range; 5374 } 5375 } // prev = range; 5376 5377 } 5378 5379 return ranges[0]; 5380 } 5381 /** 5382 * Find Text End Range 5383 * @private 5384 * @param {Node} root root node 5385 * @param {number} start position to start at 5386 * @param {number} end position to end at 5387 * @return {Range} 5388 */ 5389 5390 5391 findTextEndRange(node, start, end) { 5392 var ranges = this.splitTextNodeIntoRanges(node); 5393 var prev; 5394 var range; 5395 var pos; 5396 var left, right, top, bottom; 5397 5398 for (var i = 0; i < ranges.length; i++) { 5399 range = ranges[i]; 5400 pos = range.getBoundingClientRect(); 5401 5402 if (this.horizontal && this.direction === "ltr") { 5403 left = pos.left; 5404 right = pos.right; 5405 5406 if (left > end && prev) { 5407 return prev; 5408 } else if (right > end) { 5409 return range; 5410 } 5411 } else if (this.horizontal && this.direction === "rtl") { 5412 left = pos.left; 5413 right = pos.right; 5414 5415 if (right < start && prev) { 5416 return prev; 5417 } else if (left < start) { 5418 return range; 5419 } 5420 } else { 5421 top = pos.top; 5422 bottom = pos.bottom; 5423 5424 if (top > end && prev) { 5425 return prev; 5426 } else if (bottom > end) { 5427 return range; 5428 } 5429 } 5430 5431 prev = range; 5432 } // Ends before limit 5433 5434 5435 return ranges[ranges.length - 1]; 5436 } 5437 /** 5438 * Split up a text node into ranges for each word 5439 * @private 5440 * @param {Node} root root node 5441 * @param {string} [_splitter] what to split on 5442 * @return {Range[]} 5443 */ 5444 5445 5446 splitTextNodeIntoRanges(node, _splitter) { 5447 var ranges = []; 5448 var textContent = node.textContent || ""; 5449 var text = textContent.trim(); 5450 var range; 5451 var doc = node.ownerDocument; 5452 var splitter = _splitter || " "; 5453 var pos = text.indexOf(splitter); 5454 5455 if (pos === -1 || node.nodeType != Node.TEXT_NODE) { 5456 range = doc.createRange(); 5457 range.selectNodeContents(node); 5458 return [range]; 5459 } 5460 5461 range = doc.createRange(); 5462 range.setStart(node, 0); 5463 range.setEnd(node, pos); 5464 ranges.push(range); 5465 range = false; 5466 5467 while (pos != -1) { 5468 pos = text.indexOf(splitter, pos + 1); 5469 5470 if (pos > 0) { 5471 if (range) { 5472 range.setEnd(node, pos); 5473 ranges.push(range); 5474 } 5475 5476 range = doc.createRange(); 5477 range.setStart(node, pos + 1); 5478 } 5479 } 5480 5481 if (range) { 5482 range.setEnd(node, text.length); 5483 ranges.push(range); 5484 } 5485 5486 return ranges; 5487 } 5488 /** 5489 * Turn a pair of ranges into a pair of CFIs 5490 * @private 5491 * @param {string} cfiBase base string for an EpubCFI 5492 * @param {object} rangePair { start: Range, end: Range } 5493 * @return {object} { start: "epubcfi(...)", end: "epubcfi(...)" } 5494 */ 5495 5496 5497 rangePairToCfiPair(cfiBase, rangePair) { 5498 var startRange = rangePair.start; 5499 var endRange = rangePair.end; 5500 startRange.collapse(true); 5501 endRange.collapse(false); 5502 let startCfi = new _epubcfi__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](startRange, cfiBase).toString(); 5503 let endCfi = new _epubcfi__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](endRange, cfiBase).toString(); 5504 return { 5505 start: startCfi, 5506 end: endCfi 5507 }; 5508 } 5509 5510 rangeListToCfiList(cfiBase, columns) { 5511 var map = []; 5512 var cifPair; 5513 5514 for (var i = 0; i < columns.length; i++) { 5515 cifPair = this.rangePairToCfiPair(cfiBase, columns[i]); 5516 map.push(cifPair); 5517 } 5518 5519 return map; 5520 } 5521 /** 5522 * Set the axis for mapping 5523 * @param {string} axis horizontal | vertical 5524 * @return {boolean} is it horizontal? 5525 */ 5526 5527 5528 axis(axis) { 5529 if (axis) { 5530 this.horizontal = axis === "horizontal" ? true : false; 5531 } 5532 5533 return this.horizontal; 5534 } 5535 5536 } 5537 5538 /* harmony default export */ __webpack_exports__["a"] = (Mapping); 5539 5540 /***/ }), 5541 /* 12 */ 5542 /***/ (function(module, __webpack_exports__, __webpack_require__) { 5543 5544 "use strict"; 5545 /* harmony import */ var event_emitter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); 5546 /* harmony import */ var event_emitter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(event_emitter__WEBPACK_IMPORTED_MODULE_0__); 5547 /* harmony import */ var _utils_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0); 5548 /* harmony import */ var _epubcfi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2); 5549 /* harmony import */ var _mapping__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); 5550 /* harmony import */ var _utils_replacements__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(8); 5551 /* harmony import */ var _utils_constants__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(1); 5552 5553 5554 5555 5556 5557 5558 const hasNavigator = typeof navigator !== "undefined"; 5559 const isChrome = hasNavigator && /Chrome/.test(navigator.userAgent); 5560 const isWebkit = hasNavigator && !isChrome && /AppleWebKit/.test(navigator.userAgent); 5561 const ELEMENT_NODE = 1; 5562 const TEXT_NODE = 3; 5563 /** 5564 * Handles DOM manipulation, queries and events for View contents 5565 * @class 5566 * @param {document} doc Document 5567 * @param {element} content Parent Element (typically Body) 5568 * @param {string} cfiBase Section component of CFIs 5569 * @param {number} sectionIndex Index in Spine of Conntent's Section 5570 */ 5571 5572 class Contents { 5573 constructor(doc, content, cfiBase, sectionIndex) { 5574 // Blank Cfi for Parsing 5575 this.epubcfi = new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](); 5576 this.document = doc; 5577 this.documentElement = this.document.documentElement; 5578 this.content = content || this.document.body; 5579 this.window = this.document.defaultView; 5580 this._size = { 5581 width: 0, 5582 height: 0 5583 }; 5584 this.sectionIndex = sectionIndex || 0; 5585 this.cfiBase = cfiBase || ""; 5586 this.epubReadingSystem("epub.js", _utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EPUBJS_VERSION */ "b"]); 5587 this.called = 0; 5588 this.active = true; 5589 this.listeners(); 5590 } 5591 /** 5592 * Get DOM events that are listened for and passed along 5593 */ 5594 5595 5596 static get listenedEvents() { 5597 return _utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* DOM_EVENTS */ "a"]; 5598 } 5599 /** 5600 * Get or Set width 5601 * @param {number} [w] 5602 * @returns {number} width 5603 */ 5604 5605 5606 width(w) { 5607 // var frame = this.documentElement; 5608 var frame = this.content; 5609 5610 if (w && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(w)) { 5611 w = w + "px"; 5612 } 5613 5614 if (w) { 5615 frame.style.width = w; // this.content.style.width = w; 5616 } 5617 5618 return parseInt(this.window.getComputedStyle(frame)["width"]); 5619 } 5620 /** 5621 * Get or Set height 5622 * @param {number} [h] 5623 * @returns {number} height 5624 */ 5625 5626 5627 height(h) { 5628 // var frame = this.documentElement; 5629 var frame = this.content; 5630 5631 if (h && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(h)) { 5632 h = h + "px"; 5633 } 5634 5635 if (h) { 5636 frame.style.height = h; // this.content.style.height = h; 5637 } 5638 5639 return parseInt(this.window.getComputedStyle(frame)["height"]); 5640 } 5641 /** 5642 * Get or Set width of the contents 5643 * @param {number} [w] 5644 * @returns {number} width 5645 */ 5646 5647 5648 contentWidth(w) { 5649 var content = this.content || this.document.body; 5650 5651 if (w && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(w)) { 5652 w = w + "px"; 5653 } 5654 5655 if (w) { 5656 content.style.width = w; 5657 } 5658 5659 return parseInt(this.window.getComputedStyle(content)["width"]); 5660 } 5661 /** 5662 * Get or Set height of the contents 5663 * @param {number} [h] 5664 * @returns {number} height 5665 */ 5666 5667 5668 contentHeight(h) { 5669 var content = this.content || this.document.body; 5670 5671 if (h && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(h)) { 5672 h = h + "px"; 5673 } 5674 5675 if (h) { 5676 content.style.height = h; 5677 } 5678 5679 return parseInt(this.window.getComputedStyle(content)["height"]); 5680 } 5681 /** 5682 * Get the width of the text using Range 5683 * @returns {number} width 5684 */ 5685 5686 5687 textWidth() { 5688 let rect; 5689 let width; 5690 let range = this.document.createRange(); 5691 let content = this.content || this.document.body; 5692 let border = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["borders"])(content); // Select the contents of frame 5693 5694 range.selectNodeContents(content); // get the width of the text content 5695 5696 rect = range.getBoundingClientRect(); 5697 width = rect.width; 5698 5699 if (border && border.width) { 5700 width += border.width; 5701 } 5702 5703 return Math.round(width); 5704 } 5705 /** 5706 * Get the height of the text using Range 5707 * @returns {number} height 5708 */ 5709 5710 5711 textHeight() { 5712 let rect; 5713 let height; 5714 let range = this.document.createRange(); 5715 let content = this.content || this.document.body; 5716 range.selectNodeContents(content); 5717 rect = range.getBoundingClientRect(); 5718 height = rect.bottom; 5719 return Math.round(height); 5720 } 5721 /** 5722 * Get documentElement scrollWidth 5723 * @returns {number} width 5724 */ 5725 5726 5727 scrollWidth() { 5728 var width = this.documentElement.scrollWidth; 5729 return width; 5730 } 5731 /** 5732 * Get documentElement scrollHeight 5733 * @returns {number} height 5734 */ 5735 5736 5737 scrollHeight() { 5738 var height = this.documentElement.scrollHeight; 5739 return height; 5740 } 5741 /** 5742 * Set overflow css style of the contents 5743 * @param {string} [overflow] 5744 */ 5745 5746 5747 overflow(overflow) { 5748 if (overflow) { 5749 this.documentElement.style.overflow = overflow; 5750 } 5751 5752 return this.window.getComputedStyle(this.documentElement)["overflow"]; 5753 } 5754 /** 5755 * Set overflowX css style of the documentElement 5756 * @param {string} [overflow] 5757 */ 5758 5759 5760 overflowX(overflow) { 5761 if (overflow) { 5762 this.documentElement.style.overflowX = overflow; 5763 } 5764 5765 return this.window.getComputedStyle(this.documentElement)["overflowX"]; 5766 } 5767 /** 5768 * Set overflowY css style of the documentElement 5769 * @param {string} [overflow] 5770 */ 5771 5772 5773 overflowY(overflow) { 5774 if (overflow) { 5775 this.documentElement.style.overflowY = overflow; 5776 } 5777 5778 return this.window.getComputedStyle(this.documentElement)["overflowY"]; 5779 } 5780 /** 5781 * Set Css styles on the contents element (typically Body) 5782 * @param {string} property 5783 * @param {string} value 5784 * @param {boolean} [priority] set as "important" 5785 */ 5786 5787 5788 css(property, value, priority) { 5789 var content = this.content || this.document.body; 5790 5791 if (value) { 5792 content.style.setProperty(property, value, priority ? "important" : ""); 5793 } else { 5794 content.style.removeProperty(property); 5795 } 5796 5797 return this.window.getComputedStyle(content)[property]; 5798 } 5799 /** 5800 * Get or Set the viewport element 5801 * @param {object} [options] 5802 * @param {string} [options.width] 5803 * @param {string} [options.height] 5804 * @param {string} [options.scale] 5805 * @param {string} [options.minimum] 5806 * @param {string} [options.maximum] 5807 * @param {string} [options.scalable] 5808 */ 5809 5810 5811 viewport(options) { 5812 var _width, _height, _scale, _minimum, _maximum, _scalable; // var width, height, scale, minimum, maximum, scalable; 5813 5814 5815 var $viewport = this.document.querySelector("meta[name='viewport']"); 5816 var parsed = { 5817 "width": undefined, 5818 "height": undefined, 5819 "scale": undefined, 5820 "minimum": undefined, 5821 "maximum": undefined, 5822 "scalable": undefined 5823 }; 5824 var newContent = []; 5825 var settings = {}; 5826 /* 5827 * check for the viewport size 5828 * <meta name="viewport" content="width=1024,height=697" /> 5829 */ 5830 5831 if ($viewport && $viewport.hasAttribute("content")) { 5832 let content = $viewport.getAttribute("content"); 5833 5834 let _width = content.match(/width\s*=\s*([^,]*)/); 5835 5836 let _height = content.match(/height\s*=\s*([^,]*)/); 5837 5838 let _scale = content.match(/initial-scale\s*=\s*([^,]*)/); 5839 5840 let _minimum = content.match(/minimum-scale\s*=\s*([^,]*)/); 5841 5842 let _maximum = content.match(/maximum-scale\s*=\s*([^,]*)/); 5843 5844 let _scalable = content.match(/user-scalable\s*=\s*([^,]*)/); 5845 5846 if (_width && _width.length && typeof _width[1] !== "undefined") { 5847 parsed.width = _width[1]; 5848 } 5849 5850 if (_height && _height.length && typeof _height[1] !== "undefined") { 5851 parsed.height = _height[1]; 5852 } 5853 5854 if (_scale && _scale.length && typeof _scale[1] !== "undefined") { 5855 parsed.scale = _scale[1]; 5856 } 5857 5858 if (_minimum && _minimum.length && typeof _minimum[1] !== "undefined") { 5859 parsed.minimum = _minimum[1]; 5860 } 5861 5862 if (_maximum && _maximum.length && typeof _maximum[1] !== "undefined") { 5863 parsed.maximum = _maximum[1]; 5864 } 5865 5866 if (_scalable && _scalable.length && typeof _scalable[1] !== "undefined") { 5867 parsed.scalable = _scalable[1]; 5868 } 5869 } 5870 5871 settings = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["defaults"])(options || {}, parsed); 5872 5873 if (options) { 5874 if (settings.width) { 5875 newContent.push("width=" + settings.width); 5876 } 5877 5878 if (settings.height) { 5879 newContent.push("height=" + settings.height); 5880 } 5881 5882 if (settings.scale) { 5883 newContent.push("initial-scale=" + settings.scale); 5884 } 5885 5886 if (settings.scalable === "no") { 5887 newContent.push("minimum-scale=" + settings.scale); 5888 newContent.push("maximum-scale=" + settings.scale); 5889 newContent.push("user-scalable=" + settings.scalable); 5890 } else { 5891 if (settings.scalable) { 5892 newContent.push("user-scalable=" + settings.scalable); 5893 } 5894 5895 if (settings.minimum) { 5896 newContent.push("minimum-scale=" + settings.minimum); 5897 } 5898 5899 if (settings.maximum) { 5900 newContent.push("minimum-scale=" + settings.maximum); 5901 } 5902 } 5903 5904 if (!$viewport) { 5905 $viewport = this.document.createElement("meta"); 5906 $viewport.setAttribute("name", "viewport"); 5907 this.document.querySelector("head").appendChild($viewport); 5908 } 5909 5910 $viewport.setAttribute("content", newContent.join(", ")); 5911 this.window.scrollTo(0, 0); 5912 } 5913 5914 return settings; 5915 } 5916 /** 5917 * Event emitter for when the contents has expanded 5918 * @private 5919 */ 5920 5921 5922 expand() { 5923 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EVENTS */ "c"].CONTENTS.EXPAND); 5924 } 5925 /** 5926 * Add DOM listeners 5927 * @private 5928 */ 5929 5930 5931 listeners() { 5932 this.imageLoadListeners(); 5933 this.mediaQueryListeners(); // this.fontLoadListeners(); 5934 5935 this.addEventListeners(); 5936 this.addSelectionListeners(); // this.transitionListeners(); 5937 5938 if (typeof ResizeObserver === "undefined") { 5939 this.resizeListeners(); 5940 this.visibilityListeners(); 5941 } else { 5942 this.resizeObservers(); 5943 } // this.mutationObservers(); 5944 5945 5946 this.linksHandler(); 5947 } 5948 /** 5949 * Remove DOM listeners 5950 * @private 5951 */ 5952 5953 5954 removeListeners() { 5955 this.removeEventListeners(); 5956 this.removeSelectionListeners(); 5957 5958 if (this.observer) { 5959 this.observer.disconnect(); 5960 } 5961 5962 clearTimeout(this.expanding); 5963 } 5964 /** 5965 * Check if size of contents has changed and 5966 * emit 'resize' event if it has. 5967 * @private 5968 */ 5969 5970 5971 resizeCheck() { 5972 let width = this.textWidth(); 5973 let height = this.textHeight(); 5974 5975 if (width != this._size.width || height != this._size.height) { 5976 this._size = { 5977 width: width, 5978 height: height 5979 }; 5980 this.onResize && this.onResize(this._size); 5981 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EVENTS */ "c"].CONTENTS.RESIZE, this._size); 5982 } 5983 } 5984 /** 5985 * Poll for resize detection 5986 * @private 5987 */ 5988 5989 5990 resizeListeners() { 5991 var width, height; // Test size again 5992 5993 clearTimeout(this.expanding); 5994 requestAnimationFrame(this.resizeCheck.bind(this)); 5995 this.expanding = setTimeout(this.resizeListeners.bind(this), 350); 5996 } 5997 /** 5998 * Listen for visibility of tab to change 5999 * @private 6000 */ 6001 6002 6003 visibilityListeners() { 6004 document.addEventListener("visibilitychange", () => { 6005 if (document.visibilityState === "visible" && this.active === false) { 6006 this.active = true; 6007 this.resizeListeners(); 6008 } else { 6009 this.active = false; 6010 clearTimeout(this.expanding); 6011 } 6012 }); 6013 } 6014 /** 6015 * Use css transitions to detect resize 6016 * @private 6017 */ 6018 6019 6020 transitionListeners() { 6021 let body = this.content; 6022 body.style['transitionProperty'] = "font, font-size, font-size-adjust, font-stretch, font-variation-settings, font-weight, width, height"; 6023 body.style['transitionDuration'] = "0.001ms"; 6024 body.style['transitionTimingFunction'] = "linear"; 6025 body.style['transitionDelay'] = "0"; 6026 this._resizeCheck = this.resizeCheck.bind(this); 6027 this.document.addEventListener('transitionend', this._resizeCheck); 6028 } 6029 /** 6030 * Listen for media query changes and emit 'expand' event 6031 * Adapted from: https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js 6032 * @private 6033 */ 6034 6035 6036 mediaQueryListeners() { 6037 var sheets = this.document.styleSheets; 6038 6039 var mediaChangeHandler = function (m) { 6040 if (m.matches && !this._expanding) { 6041 setTimeout(this.expand.bind(this), 1); 6042 } 6043 }.bind(this); 6044 6045 for (var i = 0; i < sheets.length; i += 1) { 6046 var rules; // Firefox errors if we access cssRules cross-domain 6047 6048 try { 6049 rules = sheets[i].cssRules; 6050 } catch (e) { 6051 return; 6052 } 6053 6054 if (!rules) return; // Stylesheets changed 6055 6056 for (var j = 0; j < rules.length; j += 1) { 6057 //if (rules[j].constructor === CSSMediaRule) { 6058 if (rules[j].media) { 6059 var mql = this.window.matchMedia(rules[j].media.mediaText); 6060 mql.addListener(mediaChangeHandler); //mql.onchange = mediaChangeHandler; 6061 } 6062 } 6063 } 6064 } 6065 /** 6066 * Use ResizeObserver to listen for changes in the DOM and check for resize 6067 * @private 6068 */ 6069 6070 6071 resizeObservers() { 6072 // create an observer instance 6073 this.observer = new ResizeObserver(e => { 6074 requestAnimationFrame(this.resizeCheck.bind(this)); 6075 }); // pass in the target node 6076 6077 this.observer.observe(this.document.documentElement); 6078 } 6079 /** 6080 * Use MutationObserver to listen for changes in the DOM and check for resize 6081 * @private 6082 */ 6083 6084 6085 mutationObservers() { 6086 // create an observer instance 6087 this.observer = new MutationObserver(mutations => { 6088 this.resizeCheck(); 6089 }); // configuration of the observer: 6090 6091 let config = { 6092 attributes: true, 6093 childList: true, 6094 characterData: true, 6095 subtree: true 6096 }; // pass in the target node, as well as the observer options 6097 6098 this.observer.observe(this.document, config); 6099 } 6100 /** 6101 * Test if images are loaded or add listener for when they load 6102 * @private 6103 */ 6104 6105 6106 imageLoadListeners() { 6107 var images = this.document.querySelectorAll("img"); 6108 var img; 6109 6110 for (var i = 0; i < images.length; i++) { 6111 img = images[i]; 6112 6113 if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) { 6114 img.onload = this.expand.bind(this); 6115 } 6116 } 6117 } 6118 /** 6119 * Listen for font load and check for resize when loaded 6120 * @private 6121 */ 6122 6123 6124 fontLoadListeners() { 6125 if (!this.document || !this.document.fonts) { 6126 return; 6127 } 6128 6129 this.document.fonts.ready.then(function () { 6130 this.resizeCheck(); 6131 }.bind(this)); 6132 } 6133 /** 6134 * Get the documentElement 6135 * @returns {element} documentElement 6136 */ 6137 6138 6139 root() { 6140 if (!this.document) return null; 6141 return this.document.documentElement; 6142 } 6143 /** 6144 * Get the location offset of a EpubCFI or an #id 6145 * @param {string | EpubCFI} target 6146 * @param {string} [ignoreClass] for the cfi 6147 * @returns { {left: Number, top: Number } 6148 */ 6149 6150 6151 locationOf(target, ignoreClass) { 6152 var position; 6153 var targetPos = { 6154 "left": 0, 6155 "top": 0 6156 }; 6157 if (!this.document) return targetPos; 6158 6159 if (this.epubcfi.isCfiString(target)) { 6160 let range = new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](target).toRange(this.document, ignoreClass); 6161 6162 if (range) { 6163 try { 6164 if (!range.endContainer || range.startContainer == range.endContainer && range.startOffset == range.endOffset) { 6165 // If the end for the range is not set, it results in collapsed becoming 6166 // true. This in turn leads to inconsistent behaviour when calling 6167 // getBoundingRect. Wrong bounds lead to the wrong page being displayed. 6168 // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15684911/ 6169 let pos = range.startContainer.textContent.indexOf(" ", range.startOffset); 6170 6171 if (pos == -1) { 6172 pos = range.startContainer.textContent.length; 6173 } 6174 6175 range.setEnd(range.startContainer, pos); 6176 } 6177 } catch (e) { 6178 console.error("setting end offset to start container length failed", e); 6179 } 6180 6181 if (range.startContainer.nodeType === Node.ELEMENT_NODE) { 6182 position = range.startContainer.getBoundingClientRect(); 6183 targetPos.left = position.left; 6184 targetPos.top = position.top; 6185 } else { 6186 // Webkit does not handle collapsed range bounds correctly 6187 // https://bugs.webkit.org/show_bug.cgi?id=138949 6188 // Construct a new non-collapsed range 6189 if (isWebkit) { 6190 let container = range.startContainer; 6191 let newRange = new Range(); 6192 6193 try { 6194 if (container.nodeType === ELEMENT_NODE) { 6195 position = container.getBoundingClientRect(); 6196 } else if (range.startOffset + 2 < container.length) { 6197 newRange.setStart(container, range.startOffset); 6198 newRange.setEnd(container, range.startOffset + 2); 6199 position = newRange.getBoundingClientRect(); 6200 } else if (range.startOffset - 2 > 0) { 6201 newRange.setStart(container, range.startOffset - 2); 6202 newRange.setEnd(container, range.startOffset); 6203 position = newRange.getBoundingClientRect(); 6204 } else { 6205 // empty, return the parent element 6206 position = container.parentNode.getBoundingClientRect(); 6207 } 6208 } catch (e) { 6209 console.error(e, e.stack); 6210 } 6211 } else { 6212 position = range.getBoundingClientRect(); 6213 } 6214 } 6215 } 6216 } else if (typeof target === "string" && target.indexOf("#") > -1) { 6217 let id = target.substring(target.indexOf("#") + 1); 6218 let el = this.document.getElementById(id); 6219 6220 if (el) { 6221 if (isWebkit) { 6222 // Webkit reports incorrect bounding rects in Columns 6223 let newRange = new Range(); 6224 newRange.selectNode(el); 6225 position = newRange.getBoundingClientRect(); 6226 } else { 6227 position = el.getBoundingClientRect(); 6228 } 6229 } 6230 } 6231 6232 if (position) { 6233 targetPos.left = position.left; 6234 targetPos.top = position.top; 6235 } 6236 6237 return targetPos; 6238 } 6239 /** 6240 * Append a stylesheet link to the document head 6241 * @param {string} src url 6242 */ 6243 6244 6245 addStylesheet(src) { 6246 return new Promise(function (resolve, reject) { 6247 var $stylesheet; 6248 var ready = false; 6249 6250 if (!this.document) { 6251 resolve(false); 6252 return; 6253 } // Check if link already exists 6254 6255 6256 $stylesheet = this.document.querySelector("link[href='" + src + "']"); 6257 6258 if ($stylesheet) { 6259 resolve(true); 6260 return; // already present 6261 } 6262 6263 $stylesheet = this.document.createElement("link"); 6264 $stylesheet.type = "text/css"; 6265 $stylesheet.rel = "stylesheet"; 6266 $stylesheet.href = src; 6267 6268 $stylesheet.onload = $stylesheet.onreadystatechange = function () { 6269 if (!ready && (!this.readyState || this.readyState == "complete")) { 6270 ready = true; // Let apply 6271 6272 setTimeout(() => { 6273 resolve(true); 6274 }, 1); 6275 } 6276 }; 6277 6278 this.document.head.appendChild($stylesheet); 6279 }.bind(this)); 6280 } 6281 6282 _getStylesheetNode(key) { 6283 var styleEl; 6284 key = "epubjs-inserted-css-" + (key || ''); 6285 if (!this.document) return false; // Check if link already exists 6286 6287 styleEl = this.document.getElementById(key); 6288 6289 if (!styleEl) { 6290 styleEl = this.document.createElement("style"); 6291 styleEl.id = key; // Append style element to head 6292 6293 this.document.head.appendChild(styleEl); 6294 } 6295 6296 return styleEl; 6297 } 6298 /** 6299 * Append stylesheet css 6300 * @param {string} serializedCss 6301 * @param {string} key If the key is the same, the CSS will be replaced instead of inserted 6302 */ 6303 6304 6305 addStylesheetCss(serializedCss, key) { 6306 if (!this.document || !serializedCss) return false; 6307 var styleEl; 6308 styleEl = this._getStylesheetNode(key); 6309 styleEl.innerHTML = serializedCss; 6310 return true; 6311 } 6312 /** 6313 * Append stylesheet rules to a generate stylesheet 6314 * Array: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule 6315 * Object: https://github.com/desirable-objects/json-to-css 6316 * @param {array | object} rules 6317 * @param {string} key If the key is the same, the CSS will be replaced instead of inserted 6318 */ 6319 6320 6321 addStylesheetRules(rules, key) { 6322 var styleSheet; 6323 if (!this.document || !rules || rules.length === 0) return; // Grab style sheet 6324 6325 styleSheet = this._getStylesheetNode(key).sheet; 6326 6327 if (Object.prototype.toString.call(rules) === "[object Array]") { 6328 for (var i = 0, rl = rules.length; i < rl; i++) { 6329 var j = 1, 6330 rule = rules[i], 6331 selector = rules[i][0], 6332 propStr = ""; // If the second argument of a rule is an array of arrays, correct our variables. 6333 6334 if (Object.prototype.toString.call(rule[1][0]) === "[object Array]") { 6335 rule = rule[1]; 6336 j = 0; 6337 } 6338 6339 for (var pl = rule.length; j < pl; j++) { 6340 var prop = rule[j]; 6341 propStr += prop[0] + ":" + prop[1] + (prop[2] ? " !important" : "") + ";\n"; 6342 } // Insert CSS Rule 6343 6344 6345 styleSheet.insertRule(selector + "{" + propStr + "}", styleSheet.cssRules.length); 6346 } 6347 } else { 6348 const selectors = Object.keys(rules); 6349 selectors.forEach(selector => { 6350 const definition = rules[selector]; 6351 6352 if (Array.isArray(definition)) { 6353 definition.forEach(item => { 6354 const _rules = Object.keys(item); 6355 6356 const result = _rules.map(rule => { 6357 return `${rule}:${item[rule]}`; 6358 }).join(';'); 6359 6360 styleSheet.insertRule(`${selector}{${result}}`, styleSheet.cssRules.length); 6361 }); 6362 } else { 6363 const _rules = Object.keys(definition); 6364 6365 const result = _rules.map(rule => { 6366 return `${rule}:${definition[rule]}`; 6367 }).join(';'); 6368 6369 styleSheet.insertRule(`${selector}{${result}}`, styleSheet.cssRules.length); 6370 } 6371 }); 6372 } 6373 } 6374 /** 6375 * Append a script tag to the document head 6376 * @param {string} src url 6377 * @returns {Promise} loaded 6378 */ 6379 6380 6381 addScript(src) { 6382 return new Promise(function (resolve, reject) { 6383 var $script; 6384 var ready = false; 6385 6386 if (!this.document) { 6387 resolve(false); 6388 return; 6389 } 6390 6391 $script = this.document.createElement("script"); 6392 $script.type = "text/javascript"; 6393 $script.async = true; 6394 $script.src = src; 6395 6396 $script.onload = $script.onreadystatechange = function () { 6397 if (!ready && (!this.readyState || this.readyState == "complete")) { 6398 ready = true; 6399 setTimeout(function () { 6400 resolve(true); 6401 }, 1); 6402 } 6403 }; 6404 6405 this.document.head.appendChild($script); 6406 }.bind(this)); 6407 } 6408 /** 6409 * Add a class to the contents container 6410 * @param {string} className 6411 */ 6412 6413 6414 addClass(className) { 6415 var content; 6416 if (!this.document) return; 6417 content = this.content || this.document.body; 6418 6419 if (content) { 6420 content.classList.add(className); 6421 } 6422 } 6423 /** 6424 * Remove a class from the contents container 6425 * @param {string} removeClass 6426 */ 6427 6428 6429 removeClass(className) { 6430 var content; 6431 if (!this.document) return; 6432 content = this.content || this.document.body; 6433 6434 if (content) { 6435 content.classList.remove(className); 6436 } 6437 } 6438 /** 6439 * Add DOM event listeners 6440 * @private 6441 */ 6442 6443 6444 addEventListeners() { 6445 if (!this.document) { 6446 return; 6447 } 6448 6449 this._triggerEvent = this.triggerEvent.bind(this); 6450 _utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* DOM_EVENTS */ "a"].forEach(function (eventName) { 6451 this.document.addEventListener(eventName, this._triggerEvent, { 6452 passive: true 6453 }); 6454 }, this); 6455 } 6456 /** 6457 * Remove DOM event listeners 6458 * @private 6459 */ 6460 6461 6462 removeEventListeners() { 6463 if (!this.document) { 6464 return; 6465 } 6466 6467 _utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* DOM_EVENTS */ "a"].forEach(function (eventName) { 6468 this.document.removeEventListener(eventName, this._triggerEvent, { 6469 passive: true 6470 }); 6471 }, this); 6472 this._triggerEvent = undefined; 6473 } 6474 /** 6475 * Emit passed browser events 6476 * @private 6477 */ 6478 6479 6480 triggerEvent(e) { 6481 this.emit(e.type, e); 6482 } 6483 /** 6484 * Add listener for text selection 6485 * @private 6486 */ 6487 6488 6489 addSelectionListeners() { 6490 if (!this.document) { 6491 return; 6492 } 6493 6494 this._onSelectionChange = this.onSelectionChange.bind(this); 6495 this.document.addEventListener("selectionchange", this._onSelectionChange, { 6496 passive: true 6497 }); 6498 } 6499 /** 6500 * Remove listener for text selection 6501 * @private 6502 */ 6503 6504 6505 removeSelectionListeners() { 6506 if (!this.document) { 6507 return; 6508 } 6509 6510 this.document.removeEventListener("selectionchange", this._onSelectionChange, { 6511 passive: true 6512 }); 6513 this._onSelectionChange = undefined; 6514 } 6515 /** 6516 * Handle getting text on selection 6517 * @private 6518 */ 6519 6520 6521 onSelectionChange(e) { 6522 if (this.selectionEndTimeout) { 6523 clearTimeout(this.selectionEndTimeout); 6524 } 6525 6526 this.selectionEndTimeout = setTimeout(function () { 6527 var selection = this.window.getSelection(); 6528 this.triggerSelectedEvent(selection); 6529 }.bind(this), 250); 6530 } 6531 /** 6532 * Emit event on text selection 6533 * @private 6534 */ 6535 6536 6537 triggerSelectedEvent(selection) { 6538 var range, cfirange; 6539 6540 if (selection && selection.rangeCount > 0) { 6541 range = selection.getRangeAt(0); 6542 6543 if (!range.collapsed) { 6544 // cfirange = this.section.cfiFromRange(range); 6545 cfirange = new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](range, this.cfiBase).toString(); 6546 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EVENTS */ "c"].CONTENTS.SELECTED, cfirange); 6547 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EVENTS */ "c"].CONTENTS.SELECTED_RANGE, range); 6548 } 6549 } 6550 } 6551 /** 6552 * Get a Dom Range from EpubCFI 6553 * @param {EpubCFI} _cfi 6554 * @param {string} [ignoreClass] 6555 * @returns {Range} range 6556 */ 6557 6558 6559 range(_cfi, ignoreClass) { 6560 var cfi = new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](_cfi); 6561 return cfi.toRange(this.document, ignoreClass); 6562 } 6563 /** 6564 * Get an EpubCFI from a Dom Range 6565 * @param {Range} range 6566 * @param {string} [ignoreClass] 6567 * @returns {EpubCFI} cfi 6568 */ 6569 6570 6571 cfiFromRange(range, ignoreClass) { 6572 return new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](range, this.cfiBase, ignoreClass).toString(); 6573 } 6574 /** 6575 * Get an EpubCFI from a Dom node 6576 * @param {node} node 6577 * @param {string} [ignoreClass] 6578 * @returns {EpubCFI} cfi 6579 */ 6580 6581 6582 cfiFromNode(node, ignoreClass) { 6583 return new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](node, this.cfiBase, ignoreClass).toString(); 6584 } // TODO: find where this is used - remove? 6585 6586 6587 map(layout) { 6588 var map = new _mapping__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"](layout); 6589 return map.section(); 6590 } 6591 /** 6592 * Size the contents to a given width and height 6593 * @param {number} [width] 6594 * @param {number} [height] 6595 */ 6596 6597 6598 size(width, height) { 6599 var viewport = { 6600 scale: 1.0, 6601 scalable: "no" 6602 }; 6603 this.layoutStyle("scrolling"); 6604 6605 if (width >= 0) { 6606 this.width(width); 6607 viewport.width = width; 6608 this.css("padding", "0 " + width / 12 + "px"); 6609 } 6610 6611 if (height >= 0) { 6612 this.height(height); 6613 viewport.height = height; 6614 } 6615 6616 this.css("margin", "0"); 6617 this.css("box-sizing", "border-box"); 6618 this.viewport(viewport); 6619 } 6620 /** 6621 * Apply columns to the contents for pagination 6622 * @param {number} width 6623 * @param {number} height 6624 * @param {number} columnWidth 6625 * @param {number} gap 6626 */ 6627 6628 6629 columns(width, height, columnWidth, gap, dir) { 6630 let COLUMN_AXIS = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["prefixed"])("column-axis"); 6631 let COLUMN_GAP = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["prefixed"])("column-gap"); 6632 let COLUMN_WIDTH = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["prefixed"])("column-width"); 6633 let COLUMN_FILL = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["prefixed"])("column-fill"); 6634 let writingMode = this.writingMode(); 6635 let axis = writingMode.indexOf("vertical") === 0 ? "vertical" : "horizontal"; 6636 this.layoutStyle("paginated"); 6637 6638 if (dir === "rtl" && axis === "horizontal") { 6639 this.direction(dir); 6640 } 6641 6642 this.width(width); 6643 this.height(height); // Deal with Mobile trying to scale to viewport 6644 6645 this.viewport({ 6646 width: width, 6647 height: height, 6648 scale: 1.0, 6649 scalable: "no" 6650 }); // TODO: inline-block needs more testing 6651 // Fixes Safari column cut offs, but causes RTL issues 6652 // this.css("display", "inline-block"); 6653 6654 this.css("overflow-y", "hidden"); 6655 this.css("margin", "0", true); 6656 6657 if (axis === "vertical") { 6658 this.css("padding-top", gap / 2 + "px", true); 6659 this.css("padding-bottom", gap / 2 + "px", true); 6660 this.css("padding-left", "20px"); 6661 this.css("padding-right", "20px"); 6662 this.css(COLUMN_AXIS, "vertical"); 6663 } else { 6664 this.css("padding-top", "20px"); 6665 this.css("padding-bottom", "20px"); 6666 this.css("padding-left", gap / 2 + "px", true); 6667 this.css("padding-right", gap / 2 + "px", true); 6668 this.css(COLUMN_AXIS, "horizontal"); 6669 } 6670 6671 this.css("box-sizing", "border-box"); 6672 this.css("max-width", "inherit"); 6673 this.css(COLUMN_FILL, "auto"); 6674 this.css(COLUMN_GAP, gap + "px"); 6675 this.css(COLUMN_WIDTH, columnWidth + "px"); // Fix glyph clipping in WebKit 6676 // https://github.com/futurepress/epub.js/issues/983 6677 6678 this.css("-webkit-line-box-contain", "block glyphs replaced"); 6679 } 6680 /** 6681 * Scale contents from center 6682 * @param {number} scale 6683 * @param {number} offsetX 6684 * @param {number} offsetY 6685 */ 6686 6687 6688 scaler(scale, offsetX, offsetY) { 6689 var scaleStr = "scale(" + scale + ")"; 6690 var translateStr = ""; // this.css("position", "absolute")); 6691 6692 this.css("transform-origin", "top left"); 6693 6694 if (offsetX >= 0 || offsetY >= 0) { 6695 translateStr = " translate(" + (offsetX || 0) + "px, " + (offsetY || 0) + "px )"; 6696 } 6697 6698 this.css("transform", scaleStr + translateStr); 6699 } 6700 /** 6701 * Fit contents into a fixed width and height 6702 * @param {number} width 6703 * @param {number} height 6704 */ 6705 6706 6707 fit(width, height, section) { 6708 var viewport = this.viewport(); 6709 var viewportWidth = parseInt(viewport.width); 6710 var viewportHeight = parseInt(viewport.height); 6711 var widthScale = width / viewportWidth; 6712 var heightScale = height / viewportHeight; 6713 var scale = widthScale < heightScale ? widthScale : heightScale; // the translate does not work as intended, elements can end up unaligned 6714 // var offsetY = (height - (viewportHeight * scale)) / 2; 6715 // var offsetX = 0; 6716 // if (this.sectionIndex % 2 === 1) { 6717 // offsetX = width - (viewportWidth * scale); 6718 // } 6719 6720 this.layoutStyle("paginated"); // scale needs width and height to be set 6721 6722 this.width(viewportWidth); 6723 this.height(viewportHeight); 6724 this.overflow("hidden"); // Scale to the correct size 6725 6726 this.scaler(scale, 0, 0); // this.scaler(scale, offsetX > 0 ? offsetX : 0, offsetY); 6727 // background images are not scaled by transform 6728 6729 this.css("background-size", viewportWidth * scale + "px " + viewportHeight * scale + "px"); 6730 this.css("background-color", "transparent"); 6731 6732 if (section && section.properties.includes("page-spread-left")) { 6733 // set margin since scale is weird 6734 var marginLeft = width - viewportWidth * scale; 6735 this.css("margin-left", marginLeft + "px"); 6736 } 6737 } 6738 /** 6739 * Set the direction of the text 6740 * @param {string} [dir="ltr"] "rtl" | "ltr" 6741 */ 6742 6743 6744 direction(dir) { 6745 if (this.documentElement) { 6746 this.documentElement.style["direction"] = dir; 6747 } 6748 } 6749 6750 mapPage(cfiBase, layout, start, end, dev) { 6751 var mapping = new _mapping__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"](layout, dev); 6752 return mapping.page(this, cfiBase, start, end); 6753 } 6754 /** 6755 * Emit event when link in content is clicked 6756 * @private 6757 */ 6758 6759 6760 linksHandler() { 6761 Object(_utils_replacements__WEBPACK_IMPORTED_MODULE_4__[/* replaceLinks */ "c"])(this.content, href => { 6762 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EVENTS */ "c"].CONTENTS.LINK_CLICKED, href); 6763 }); 6764 } 6765 /** 6766 * Set the writingMode of the text 6767 * @param {string} [mode="horizontal-tb"] "horizontal-tb" | "vertical-rl" | "vertical-lr" 6768 */ 6769 6770 6771 writingMode(mode) { 6772 let WRITING_MODE = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["prefixed"])("writing-mode"); 6773 6774 if (mode && this.documentElement) { 6775 this.documentElement.style[WRITING_MODE] = mode; 6776 } 6777 6778 return this.window.getComputedStyle(this.documentElement)[WRITING_MODE] || ''; 6779 } 6780 /** 6781 * Set the layoutStyle of the content 6782 * @param {string} [style="paginated"] "scrolling" | "paginated" 6783 * @private 6784 */ 6785 6786 6787 layoutStyle(style) { 6788 if (style) { 6789 this._layoutStyle = style; 6790 navigator.epubReadingSystem.layoutStyle = this._layoutStyle; 6791 } 6792 6793 return this._layoutStyle || "paginated"; 6794 } 6795 /** 6796 * Add the epubReadingSystem object to the navigator 6797 * @param {string} name 6798 * @param {string} version 6799 * @private 6800 */ 6801 6802 6803 epubReadingSystem(name, version) { 6804 navigator.epubReadingSystem = { 6805 name: name, 6806 version: version, 6807 layoutStyle: this.layoutStyle(), 6808 hasFeature: function (feature) { 6809 switch (feature) { 6810 case "dom-manipulation": 6811 return true; 6812 6813 case "layout-changes": 6814 return true; 6815 6816 case "touch-events": 6817 return true; 6818 6819 case "mouse-events": 6820 return true; 6821 6822 case "keyboard-events": 6823 return true; 6824 6825 case "spine-scripting": 6826 return false; 6827 6828 default: 6829 return false; 6830 } 6831 } 6832 }; 6833 return navigator.epubReadingSystem; 6834 } 6835 6836 destroy() { 6837 // this.document.removeEventListener('transitionend', this._resizeCheck); 6838 this.removeListeners(); 6839 } 6840 6841 } 6842 6843 event_emitter__WEBPACK_IMPORTED_MODULE_0___default()(Contents.prototype); 6844 /* harmony default export */ __webpack_exports__["a"] = (Contents); 6845 6846 /***/ }), 6847 /* 13 */ 6848 /***/ (function(module, exports, __webpack_require__) { 6849 6850 "use strict"; 6851 6852 6853 Object.defineProperty(exports, "__esModule", { 6854 value: true 6855 }); 6856 exports.Underline = exports.Highlight = exports.Mark = exports.Pane = undefined; 6857 6858 var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 6859 6860 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; }; }(); 6861 6862 var _svg = __webpack_require__(49); 6863 6864 var _svg2 = _interopRequireDefault(_svg); 6865 6866 var _events = __webpack_require__(50); 6867 6868 var _events2 = _interopRequireDefault(_events); 6869 6870 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 6871 6872 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6873 6874 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 6875 6876 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6877 6878 var Pane = exports.Pane = function () { 6879 function Pane(target) { 6880 var container = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.body; 6881 6882 _classCallCheck(this, Pane); 6883 6884 this.target = target; 6885 this.element = _svg2.default.createElement('svg'); 6886 this.marks = []; 6887 6888 // Match the coordinates of the target element 6889 this.element.style.position = 'absolute'; 6890 // Disable pointer events 6891 this.element.setAttribute('pointer-events', 'none'); 6892 6893 // Set up mouse event proxying between the target element and the marks 6894 _events2.default.proxyMouse(this.target, this.marks); 6895 6896 this.container = container; 6897 this.container.appendChild(this.element); 6898 6899 this.render(); 6900 } 6901 6902 _createClass(Pane, [{ 6903 key: 'addMark', 6904 value: function addMark(mark) { 6905 var g = _svg2.default.createElement('g'); 6906 this.element.appendChild(g); 6907 mark.bind(g, this.container); 6908 6909 this.marks.push(mark); 6910 6911 mark.render(); 6912 return mark; 6913 } 6914 }, { 6915 key: 'removeMark', 6916 value: function removeMark(mark) { 6917 var idx = this.marks.indexOf(mark); 6918 if (idx === -1) { 6919 return; 6920 } 6921 var el = mark.unbind(); 6922 this.element.removeChild(el); 6923 this.marks.splice(idx, 1); 6924 } 6925 }, { 6926 key: 'render', 6927 value: function render() { 6928 setCoords(this.element, coords(this.target, this.container)); 6929 var _iteratorNormalCompletion = true; 6930 var _didIteratorError = false; 6931 var _iteratorError = undefined; 6932 6933 try { 6934 for (var _iterator = this.marks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { 6935 var m = _step.value; 6936 6937 m.render(); 6938 } 6939 } catch (err) { 6940 _didIteratorError = true; 6941 _iteratorError = err; 6942 } finally { 6943 try { 6944 if (!_iteratorNormalCompletion && _iterator.return) { 6945 _iterator.return(); 6946 } 6947 } finally { 6948 if (_didIteratorError) { 6949 throw _iteratorError; 6950 } 6951 } 6952 } 6953 } 6954 }]); 6955 6956 return Pane; 6957 }(); 6958 6959 var Mark = exports.Mark = function () { 6960 function Mark() { 6961 _classCallCheck(this, Mark); 6962 6963 this.element = null; 6964 } 6965 6966 _createClass(Mark, [{ 6967 key: 'bind', 6968 value: function bind(element, container) { 6969 this.element = element; 6970 this.container = container; 6971 } 6972 }, { 6973 key: 'unbind', 6974 value: function unbind() { 6975 var el = this.element; 6976 this.element = null; 6977 return el; 6978 } 6979 }, { 6980 key: 'render', 6981 value: function render() {} 6982 }, { 6983 key: 'dispatchEvent', 6984 value: function dispatchEvent(e) { 6985 if (!this.element) return; 6986 this.element.dispatchEvent(e); 6987 } 6988 }, { 6989 key: 'getBoundingClientRect', 6990 value: function getBoundingClientRect() { 6991 return this.element.getBoundingClientRect(); 6992 } 6993 }, { 6994 key: 'getClientRects', 6995 value: function getClientRects() { 6996 var rects = []; 6997 var el = this.element.firstChild; 6998 while (el) { 6999 rects.push(el.getBoundingClientRect()); 7000 el = el.nextSibling; 7001 } 7002 return rects; 7003 } 7004 }, { 7005 key: 'filteredRanges', 7006 value: function filteredRanges() { 7007 var rects = Array.from(this.range.getClientRects()); 7008 7009 // De-duplicate the boxes 7010 return rects.filter(function (box) { 7011 for (var i = 0; i < rects.length; i++) { 7012 if (rects[i] === box) { 7013 return true; 7014 } 7015 var contained = contains(rects[i], box); 7016 if (contained) { 7017 return false; 7018 } 7019 } 7020 return true; 7021 }); 7022 } 7023 }]); 7024 7025 return Mark; 7026 }(); 7027 7028 var Highlight = exports.Highlight = function (_Mark) { 7029 _inherits(Highlight, _Mark); 7030 7031 function Highlight(range, className, data, attributes) { 7032 _classCallCheck(this, Highlight); 7033 7034 var _this = _possibleConstructorReturn(this, (Highlight.__proto__ || Object.getPrototypeOf(Highlight)).call(this)); 7035 7036 _this.range = range; 7037 _this.className = className; 7038 _this.data = data || {}; 7039 _this.attributes = attributes || {}; 7040 return _this; 7041 } 7042 7043 _createClass(Highlight, [{ 7044 key: 'bind', 7045 value: function bind(element, container) { 7046 _get(Highlight.prototype.__proto__ || Object.getPrototypeOf(Highlight.prototype), 'bind', this).call(this, element, container); 7047 7048 for (var attr in this.data) { 7049 if (this.data.hasOwnProperty(attr)) { 7050 this.element.dataset[attr] = this.data[attr]; 7051 } 7052 } 7053 7054 for (var attr in this.attributes) { 7055 if (this.attributes.hasOwnProperty(attr)) { 7056 this.element.setAttribute(attr, this.attributes[attr]); 7057 } 7058 } 7059 7060 if (this.className) { 7061 this.element.classList.add(this.className); 7062 } 7063 } 7064 }, { 7065 key: 'render', 7066 value: function render() { 7067 // Empty element 7068 while (this.element.firstChild) { 7069 this.element.removeChild(this.element.firstChild); 7070 } 7071 7072 var docFrag = this.element.ownerDocument.createDocumentFragment(); 7073 var filtered = this.filteredRanges(); 7074 var offset = this.element.getBoundingClientRect(); 7075 var container = this.container.getBoundingClientRect(); 7076 7077 for (var i = 0, len = filtered.length; i < len; i++) { 7078 var r = filtered[i]; 7079 var el = _svg2.default.createElement('rect'); 7080 el.setAttribute('x', r.left - offset.left + container.left); 7081 el.setAttribute('y', r.top - offset.top + container.top); 7082 el.setAttribute('height', r.height); 7083 el.setAttribute('width', r.width); 7084 docFrag.appendChild(el); 7085 } 7086 7087 this.element.appendChild(docFrag); 7088 } 7089 }]); 7090 7091 return Highlight; 7092 }(Mark); 7093 7094 var Underline = exports.Underline = function (_Highlight) { 7095 _inherits(Underline, _Highlight); 7096 7097 function Underline(range, className, data, attributes) { 7098 _classCallCheck(this, Underline); 7099 7100 return _possibleConstructorReturn(this, (Underline.__proto__ || Object.getPrototypeOf(Underline)).call(this, range, className, data, attributes)); 7101 } 7102 7103 _createClass(Underline, [{ 7104 key: 'render', 7105 value: function render() { 7106 // Empty element 7107 while (this.element.firstChild) { 7108 this.element.removeChild(this.element.firstChild); 7109 } 7110 7111 var docFrag = this.element.ownerDocument.createDocumentFragment(); 7112 var filtered = this.filteredRanges(); 7113 var offset = this.element.getBoundingClientRect(); 7114 var container = this.container.getBoundingClientRect(); 7115 7116 for (var i = 0, len = filtered.length; i < len; i++) { 7117 var r = filtered[i]; 7118 7119 var rect = _svg2.default.createElement('rect'); 7120 rect.setAttribute('x', r.left - offset.left + container.left); 7121 rect.setAttribute('y', r.top - offset.top + container.top); 7122 rect.setAttribute('height', r.height); 7123 rect.setAttribute('width', r.width); 7124 rect.setAttribute('fill', 'none'); 7125 7126 var line = _svg2.default.createElement('line'); 7127 line.setAttribute('x1', r.left - offset.left + container.left); 7128 line.setAttribute('x2', r.left - offset.left + container.left + r.width); 7129 line.setAttribute('y1', r.top - offset.top + container.top + r.height - 1); 7130 line.setAttribute('y2', r.top - offset.top + container.top + r.height - 1); 7131 7132 line.setAttribute('stroke-width', 1); 7133 line.setAttribute('stroke', 'black'); //TODO: match text color? 7134 line.setAttribute('stroke-linecap', 'square'); 7135 7136 docFrag.appendChild(rect); 7137 7138 docFrag.appendChild(line); 7139 } 7140 7141 this.element.appendChild(docFrag); 7142 } 7143 }]); 7144 7145 return Underline; 7146 }(Highlight); 7147 7148 function coords(el, container) { 7149 var offset = container.getBoundingClientRect(); 7150 var rect = el.getBoundingClientRect(); 7151 7152 return { 7153 top: rect.top - offset.top, 7154 left: rect.left - offset.left, 7155 height: el.scrollHeight, 7156 width: el.scrollWidth 7157 }; 7158 } 7159 7160 function setCoords(el, coords) { 7161 el.style.setProperty('top', coords.top + 'px', 'important'); 7162 el.style.setProperty('left', coords.left + 'px', 'important'); 7163 el.style.setProperty('height', coords.height + 'px', 'important'); 7164 el.style.setProperty('width', coords.width + 'px', 'important'); 7165 } 7166 7167 function contains(rect1, rect2) { 7168 return rect2.right <= rect1.right && rect2.left >= rect1.left && rect2.top >= rect1.top && rect2.bottom <= rect1.bottom; 7169 } 7170 7171 7172 /***/ }), 7173 /* 14 */ 7174 /***/ (function(module, exports, __webpack_require__) { 7175 7176 "use strict"; 7177 7178 7179 /** 7180 * "Shallow freezes" an object to render it immutable. 7181 * Uses `Object.freeze` if available, 7182 * otherwise the immutability is only in the type. 7183 * 7184 * Is used to create "enum like" objects. 7185 * 7186 * @template T 7187 * @param {T} object the object to freeze 7188 * @param {Pick<ObjectConstructor, 'freeze'> = Object} oc `Object` by default, 7189 * allows to inject custom object constructor for tests 7190 * @returns {Readonly<T>} 7191 * 7192 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze 7193 */ 7194 function freeze(object, oc) { 7195 if (oc === undefined) { 7196 oc = Object 7197 } 7198 return oc && typeof oc.freeze === 'function' ? oc.freeze(object) : object 7199 } 7200 7201 /** 7202 * All mime types that are allowed as input to `DOMParser.parseFromString` 7203 * 7204 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString#Argument02 MDN 7205 * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#domparsersupportedtype WHATWG HTML Spec 7206 * @see DOMParser.prototype.parseFromString 7207 */ 7208 var MIME_TYPE = freeze({ 7209 /** 7210 * `text/html`, the only mime type that triggers treating an XML document as HTML. 7211 * 7212 * @see DOMParser.SupportedType.isHTML 7213 * @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration 7214 * @see https://en.wikipedia.org/wiki/HTML Wikipedia 7215 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN 7216 * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring WHATWG HTML Spec 7217 */ 7218 HTML: 'text/html', 7219 7220 /** 7221 * Helper method to check a mime type if it indicates an HTML document 7222 * 7223 * @param {string} [value] 7224 * @returns {boolean} 7225 * 7226 * @see https://www.iana.org/assignments/media-types/text/html IANA MimeType registration 7227 * @see https://en.wikipedia.org/wiki/HTML Wikipedia 7228 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString MDN 7229 * @see https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-domparser-parsefromstring */ 7230 isHTML: function (value) { 7231 return value === MIME_TYPE.HTML 7232 }, 7233 7234 /** 7235 * `application/xml`, the standard mime type for XML documents. 7236 * 7237 * @see https://www.iana.org/assignments/media-types/application/xml IANA MimeType registration 7238 * @see https://tools.ietf.org/html/rfc7303#section-9.1 RFC 7303 7239 * @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia 7240 */ 7241 XML_APPLICATION: 'application/xml', 7242 7243 /** 7244 * `text/html`, an alias for `application/xml`. 7245 * 7246 * @see https://tools.ietf.org/html/rfc7303#section-9.2 RFC 7303 7247 * @see https://www.iana.org/assignments/media-types/text/xml IANA MimeType registration 7248 * @see https://en.wikipedia.org/wiki/XML_and_MIME Wikipedia 7249 */ 7250 XML_TEXT: 'text/xml', 7251 7252 /** 7253 * `application/xhtml+xml`, indicates an XML document that has the default HTML namespace, 7254 * but is parsed as an XML document. 7255 * 7256 * @see https://www.iana.org/assignments/media-types/application/xhtml+xml IANA MimeType registration 7257 * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument WHATWG DOM Spec 7258 * @see https://en.wikipedia.org/wiki/XHTML Wikipedia 7259 */ 7260 XML_XHTML_APPLICATION: 'application/xhtml+xml', 7261 7262 /** 7263 * `image/svg+xml`, 7264 * 7265 * @see https://www.iana.org/assignments/media-types/image/svg+xml IANA MimeType registration 7266 * @see https://www.w3.org/TR/SVG11/ W3C SVG 1.1 7267 * @see https://en.wikipedia.org/wiki/Scalable_Vector_Graphics Wikipedia 7268 */ 7269 XML_SVG_IMAGE: 'image/svg+xml', 7270 }) 7271 7272 /** 7273 * Namespaces that are used in this code base. 7274 * 7275 * @see http://www.w3.org/TR/REC-xml-names 7276 */ 7277 var NAMESPACE = freeze({ 7278 /** 7279 * The XHTML namespace. 7280 * 7281 * @see http://www.w3.org/1999/xhtml 7282 */ 7283 HTML: 'http://www.w3.org/1999/xhtml', 7284 7285 /** 7286 * Checks if `uri` equals `NAMESPACE.HTML`. 7287 * 7288 * @param {string} [uri] 7289 * 7290 * @see NAMESPACE.HTML 7291 */ 7292 isHTML: function (uri) { 7293 return uri === NAMESPACE.HTML 7294 }, 7295 7296 /** 7297 * The SVG namespace. 7298 * 7299 * @see http://www.w3.org/2000/svg 7300 */ 7301 SVG: 'http://www.w3.org/2000/svg', 7302 7303 /** 7304 * The `xml:` namespace. 7305 * 7306 * @see http://www.w3.org/XML/1998/namespace 7307 */ 7308 XML: 'http://www.w3.org/XML/1998/namespace', 7309 7310 /** 7311 * The `xmlns:` namespace 7312 * 7313 * @see https://www.w3.org/2000/xmlns/ 7314 */ 7315 XMLNS: 'http://www.w3.org/2000/xmlns/', 7316 }) 7317 7318 exports.freeze = freeze; 7319 exports.MIME_TYPE = MIME_TYPE; 7320 exports.NAMESPACE = NAMESPACE; 7321 7322 7323 /***/ }), 7324 /* 15 */ 7325 /***/ (function(module, exports, __webpack_require__) { 7326 7327 var dom = __webpack_require__(25) 7328 exports.DOMImplementation = dom.DOMImplementation 7329 exports.XMLSerializer = dom.XMLSerializer 7330 exports.DOMParser = __webpack_require__(46).DOMParser 7331 7332 7333 /***/ }), 7334 /* 16 */ 7335 /***/ (function(module, __webpack_exports__, __webpack_require__) { 7336 7337 "use strict"; 7338 7339 // EXTERNAL MODULE: ./node_modules/event-emitter/index.js 7340 var event_emitter = __webpack_require__(3); 7341 var event_emitter_default = /*#__PURE__*/__webpack_require__.n(event_emitter); 7342 7343 // EXTERNAL MODULE: ./src/utils/core.js 7344 var core = __webpack_require__(0); 7345 7346 // EXTERNAL MODULE: ./src/utils/hook.js 7347 var hook = __webpack_require__(6); 7348 7349 // EXTERNAL MODULE: ./src/epubcfi.js 7350 var src_epubcfi = __webpack_require__(2); 7351 7352 // EXTERNAL MODULE: ./src/utils/queue.js 7353 var queue = __webpack_require__(9); 7354 7355 // EXTERNAL MODULE: ./src/utils/constants.js 7356 var constants = __webpack_require__(1); 7357 7358 // CONCATENATED MODULE: ./src/layout.js 7359 7360 7361 7362 /** 7363 * Figures out the CSS values to apply for a layout 7364 * @class 7365 * @param {object} settings 7366 * @param {string} [settings.layout='reflowable'] 7367 * @param {string} [settings.spread] 7368 * @param {number} [settings.minSpreadWidth=800] 7369 * @param {boolean} [settings.evenSpreads=false] 7370 */ 7371 7372 class layout_Layout { 7373 constructor(settings) { 7374 this.settings = settings; 7375 this.name = settings.layout || "reflowable"; 7376 this._spread = settings.spread === "none" ? false : true; 7377 this._minSpreadWidth = settings.minSpreadWidth || 800; 7378 this._evenSpreads = settings.evenSpreads || false; 7379 7380 if (settings.flow === "scrolled" || settings.flow === "scrolled-continuous" || settings.flow === "scrolled-doc") { 7381 this._flow = "scrolled"; 7382 } else { 7383 this._flow = "paginated"; 7384 } 7385 7386 this.width = 0; 7387 this.height = 0; 7388 this.spreadWidth = 0; 7389 this.delta = 0; 7390 this.columnWidth = 0; 7391 this.gap = 0; 7392 this.divisor = 1; 7393 this.props = { 7394 name: this.name, 7395 spread: this._spread, 7396 flow: this._flow, 7397 width: 0, 7398 height: 0, 7399 spreadWidth: 0, 7400 delta: 0, 7401 columnWidth: 0, 7402 gap: 0, 7403 divisor: 1 7404 }; 7405 } 7406 /** 7407 * Switch the flow between paginated and scrolled 7408 * @param {string} flow paginated | scrolled 7409 * @return {string} simplified flow 7410 */ 7411 7412 7413 flow(flow) { 7414 if (typeof flow != "undefined") { 7415 if (flow === "scrolled" || flow === "scrolled-continuous" || flow === "scrolled-doc") { 7416 this._flow = "scrolled"; 7417 } else { 7418 this._flow = "paginated"; 7419 } // this.props.flow = this._flow; 7420 7421 7422 this.update({ 7423 flow: this._flow 7424 }); 7425 } 7426 7427 return this._flow; 7428 } 7429 /** 7430 * Switch between using spreads or not, and set the 7431 * width at which they switch to single. 7432 * @param {string} spread "none" | "always" | "auto" 7433 * @param {number} min integer in pixels 7434 * @return {boolean} spread true | false 7435 */ 7436 7437 7438 spread(spread, min) { 7439 if (spread) { 7440 this._spread = spread === "none" ? false : true; // this.props.spread = this._spread; 7441 7442 this.update({ 7443 spread: this._spread 7444 }); 7445 } 7446 7447 if (min >= 0) { 7448 this._minSpreadWidth = min; 7449 } 7450 7451 return this._spread; 7452 } 7453 /** 7454 * Calculate the dimensions of the pagination 7455 * @param {number} _width width of the rendering 7456 * @param {number} _height height of the rendering 7457 * @param {number} _gap width of the gap between columns 7458 */ 7459 7460 7461 calculate(_width, _height, _gap) { 7462 var divisor = 1; 7463 var gap = _gap || 0; //-- Check the width and create even width columns 7464 // var fullWidth = Math.floor(_width); 7465 7466 var width = _width; 7467 var height = _height; 7468 var section = Math.floor(width / 12); 7469 var columnWidth; 7470 var spreadWidth; 7471 var pageWidth; 7472 var delta; 7473 7474 if (this._spread && width >= this._minSpreadWidth) { 7475 divisor = 2; 7476 } else { 7477 divisor = 1; 7478 } 7479 7480 if (this.name === "reflowable" && this._flow === "paginated" && !(_gap >= 0)) { 7481 gap = section % 2 === 0 ? section : section - 1; 7482 } 7483 7484 if (this.name === "pre-paginated") { 7485 gap = 0; 7486 } //-- Double Page 7487 7488 7489 if (divisor > 1) { 7490 // width = width - gap; 7491 // columnWidth = (width - gap) / divisor; 7492 // gap = gap / divisor; 7493 columnWidth = width / divisor - gap; 7494 pageWidth = columnWidth + gap; 7495 } else { 7496 columnWidth = width; 7497 pageWidth = width; 7498 } 7499 7500 if (this.name === "pre-paginated" && divisor > 1) { 7501 width = columnWidth; 7502 } 7503 7504 spreadWidth = columnWidth * divisor + gap; 7505 delta = width; 7506 this.width = width; 7507 this.height = height; 7508 this.spreadWidth = spreadWidth; 7509 this.pageWidth = pageWidth; 7510 this.delta = delta; 7511 this.columnWidth = columnWidth; 7512 this.gap = gap; 7513 this.divisor = divisor; // this.props.width = width; 7514 // this.props.height = _height; 7515 // this.props.spreadWidth = spreadWidth; 7516 // this.props.pageWidth = pageWidth; 7517 // this.props.delta = delta; 7518 // 7519 // this.props.columnWidth = colWidth; 7520 // this.props.gap = gap; 7521 // this.props.divisor = divisor; 7522 7523 this.update({ 7524 width, 7525 height, 7526 spreadWidth, 7527 pageWidth, 7528 delta, 7529 columnWidth, 7530 gap, 7531 divisor 7532 }); 7533 } 7534 /** 7535 * Apply Css to a Document 7536 * @param {Contents} contents 7537 * @return {Promise} 7538 */ 7539 7540 7541 format(contents, section, axis) { 7542 var formating; 7543 7544 if (this.name === "pre-paginated") { 7545 formating = contents.fit(this.columnWidth, this.height, section); 7546 } else if (this._flow === "paginated") { 7547 formating = contents.columns(this.width, this.height, this.columnWidth, this.gap, this.settings.direction); 7548 } else if (axis && axis === "horizontal") { 7549 formating = contents.size(null, this.height); 7550 } else { 7551 formating = contents.size(this.width, null); 7552 } 7553 7554 return formating; // might be a promise in some View Managers 7555 } 7556 /** 7557 * Count number of pages 7558 * @param {number} totalLength 7559 * @param {number} pageLength 7560 * @return {{spreads: Number, pages: Number}} 7561 */ 7562 7563 7564 count(totalLength, pageLength) { 7565 let spreads, pages; 7566 7567 if (this.name === "pre-paginated") { 7568 spreads = 1; 7569 pages = 1; 7570 } else if (this._flow === "paginated") { 7571 pageLength = pageLength || this.delta; 7572 spreads = Math.ceil(totalLength / pageLength); 7573 pages = spreads * this.divisor; 7574 } else { 7575 // scrolled 7576 pageLength = pageLength || this.height; 7577 spreads = Math.ceil(totalLength / pageLength); 7578 pages = spreads; 7579 } 7580 7581 return { 7582 spreads, 7583 pages 7584 }; 7585 } 7586 /** 7587 * Update props that have changed 7588 * @private 7589 * @param {object} props 7590 */ 7591 7592 7593 update(props) { 7594 // Remove props that haven't changed 7595 Object.keys(props).forEach(propName => { 7596 if (this.props[propName] === props[propName]) { 7597 delete props[propName]; 7598 } 7599 }); 7600 7601 if (Object.keys(props).length > 0) { 7602 let newProps = Object(core["extend"])(this.props, props); 7603 this.emit(constants["c" /* EVENTS */].LAYOUT.UPDATED, newProps, props); 7604 } 7605 } 7606 7607 } 7608 7609 event_emitter_default()(layout_Layout.prototype); 7610 /* harmony default export */ var layout = (layout_Layout); 7611 // EXTERNAL MODULE: ./src/utils/url.js 7612 var utils_url = __webpack_require__(5); 7613 7614 // CONCATENATED MODULE: ./src/themes.js 7615 7616 /** 7617 * Themes to apply to displayed content 7618 * @class 7619 * @param {Rendition} rendition 7620 */ 7621 7622 class themes_Themes { 7623 constructor(rendition) { 7624 this.rendition = rendition; 7625 this._themes = { 7626 "default": { 7627 "rules": {}, 7628 "url": "", 7629 "serialized": "" 7630 } 7631 }; 7632 this._overrides = {}; 7633 this._current = "default"; 7634 this._injected = []; 7635 this.rendition.hooks.content.register(this.inject.bind(this)); 7636 this.rendition.hooks.content.register(this.overrides.bind(this)); 7637 } 7638 /** 7639 * Add themes to be used by a rendition 7640 * @param {object | Array<object> | string} 7641 * @example themes.register("light", "http://example.com/light.css") 7642 * @example themes.register("light", { "body": { "color": "purple"}}) 7643 * @example themes.register({ "light" : {...}, "dark" : {...}}) 7644 */ 7645 7646 7647 register() { 7648 if (arguments.length === 0) { 7649 return; 7650 } 7651 7652 if (arguments.length === 1 && typeof arguments[0] === "object") { 7653 return this.registerThemes(arguments[0]); 7654 } 7655 7656 if (arguments.length === 1 && typeof arguments[0] === "string") { 7657 return this.default(arguments[0]); 7658 } 7659 7660 if (arguments.length === 2 && typeof arguments[1] === "string") { 7661 return this.registerUrl(arguments[0], arguments[1]); 7662 } 7663 7664 if (arguments.length === 2 && typeof arguments[1] === "object") { 7665 return this.registerRules(arguments[0], arguments[1]); 7666 } 7667 } 7668 /** 7669 * Add a default theme to be used by a rendition 7670 * @param {object | string} theme 7671 * @example themes.register("http://example.com/default.css") 7672 * @example themes.register({ "body": { "color": "purple"}}) 7673 */ 7674 7675 7676 default(theme) { 7677 if (!theme) { 7678 return; 7679 } 7680 7681 if (typeof theme === "string") { 7682 return this.registerUrl("default", theme); 7683 } 7684 7685 if (typeof theme === "object") { 7686 return this.registerRules("default", theme); 7687 } 7688 } 7689 /** 7690 * Register themes object 7691 * @param {object} themes 7692 */ 7693 7694 7695 registerThemes(themes) { 7696 for (var theme in themes) { 7697 if (themes.hasOwnProperty(theme)) { 7698 if (typeof themes[theme] === "string") { 7699 this.registerUrl(theme, themes[theme]); 7700 } else { 7701 this.registerRules(theme, themes[theme]); 7702 } 7703 } 7704 } 7705 } 7706 /** 7707 * Register a theme by passing its css as string 7708 * @param {string} name 7709 * @param {string} css 7710 */ 7711 7712 7713 registerCss(name, css) { 7714 this._themes[name] = { 7715 "serialized": css 7716 }; 7717 7718 if (this._injected[name] || name == 'default') { 7719 this.update(name); 7720 } 7721 } 7722 /** 7723 * Register a url 7724 * @param {string} name 7725 * @param {string} input 7726 */ 7727 7728 7729 registerUrl(name, input) { 7730 var url = new utils_url["a" /* default */](input); 7731 this._themes[name] = { 7732 "url": url.toString() 7733 }; 7734 7735 if (this._injected[name] || name == 'default') { 7736 this.update(name); 7737 } 7738 } 7739 /** 7740 * Register rule 7741 * @param {string} name 7742 * @param {object} rules 7743 */ 7744 7745 7746 registerRules(name, rules) { 7747 this._themes[name] = { 7748 "rules": rules 7749 }; // TODO: serialize css rules 7750 7751 if (this._injected[name] || name == 'default') { 7752 this.update(name); 7753 } 7754 } 7755 /** 7756 * Select a theme 7757 * @param {string} name 7758 */ 7759 7760 7761 select(name) { 7762 var prev = this._current; 7763 var contents; 7764 this._current = name; 7765 this.update(name); 7766 contents = this.rendition.getContents(); 7767 contents.forEach(content => { 7768 content.removeClass(prev); 7769 content.addClass(name); 7770 }); 7771 } 7772 /** 7773 * Update a theme 7774 * @param {string} name 7775 */ 7776 7777 7778 update(name) { 7779 var contents = this.rendition.getContents(); 7780 contents.forEach(content => { 7781 this.add(name, content); 7782 }); 7783 } 7784 /** 7785 * Inject all themes into contents 7786 * @param {Contents} contents 7787 */ 7788 7789 7790 inject(contents) { 7791 var links = []; 7792 var themes = this._themes; 7793 var theme; 7794 7795 for (var name in themes) { 7796 if (themes.hasOwnProperty(name) && (name === this._current || name === "default")) { 7797 theme = themes[name]; 7798 7799 if (theme.rules && Object.keys(theme.rules).length > 0 || theme.url && links.indexOf(theme.url) === -1) { 7800 this.add(name, contents); 7801 } 7802 7803 this._injected.push(name); 7804 } 7805 } 7806 7807 if (this._current != "default") { 7808 contents.addClass(this._current); 7809 } 7810 } 7811 /** 7812 * Add Theme to contents 7813 * @param {string} name 7814 * @param {Contents} contents 7815 */ 7816 7817 7818 add(name, contents) { 7819 var theme = this._themes[name]; 7820 7821 if (!theme || !contents) { 7822 return; 7823 } 7824 7825 if (theme.url) { 7826 contents.addStylesheet(theme.url); 7827 } else if (theme.serialized) { 7828 contents.addStylesheetCss(theme.serialized, name); 7829 theme.injected = true; 7830 } else if (theme.rules) { 7831 contents.addStylesheetRules(theme.rules, name); 7832 theme.injected = true; 7833 } 7834 } 7835 /** 7836 * Add override 7837 * @param {string} name 7838 * @param {string} value 7839 * @param {boolean} priority 7840 */ 7841 7842 7843 override(name, value, priority) { 7844 var contents = this.rendition.getContents(); 7845 this._overrides[name] = { 7846 value: value, 7847 priority: priority === true 7848 }; 7849 contents.forEach(content => { 7850 content.css(name, this._overrides[name].value, this._overrides[name].priority); 7851 }); 7852 } 7853 7854 removeOverride(name) { 7855 var contents = this.rendition.getContents(); 7856 delete this._overrides[name]; 7857 contents.forEach(content => { 7858 content.css(name); 7859 }); 7860 } 7861 /** 7862 * Add all overrides 7863 * @param {Content} content 7864 */ 7865 7866 7867 overrides(contents) { 7868 var overrides = this._overrides; 7869 7870 for (var rule in overrides) { 7871 if (overrides.hasOwnProperty(rule)) { 7872 contents.css(rule, overrides[rule].value, overrides[rule].priority); 7873 } 7874 } 7875 } 7876 /** 7877 * Adjust the font size of a rendition 7878 * @param {number} size 7879 */ 7880 7881 7882 fontSize(size) { 7883 this.override("font-size", size); 7884 } 7885 /** 7886 * Adjust the font-family of a rendition 7887 * @param {string} f 7888 */ 7889 7890 7891 font(f) { 7892 this.override("font-family", f, true); 7893 } 7894 7895 destroy() { 7896 this.rendition = undefined; 7897 this._themes = undefined; 7898 this._overrides = undefined; 7899 this._current = undefined; 7900 this._injected = undefined; 7901 } 7902 7903 } 7904 7905 /* harmony default export */ var themes = (themes_Themes); 7906 // EXTERNAL MODULE: ./src/contents.js 7907 var src_contents = __webpack_require__(12); 7908 7909 // CONCATENATED MODULE: ./src/annotations.js 7910 7911 7912 7913 /** 7914 * Handles managing adding & removing Annotations 7915 * @param {Rendition} rendition 7916 * @class 7917 */ 7918 7919 class annotations_Annotations { 7920 constructor(rendition) { 7921 this.rendition = rendition; 7922 this.highlights = []; 7923 this.underlines = []; 7924 this.marks = []; 7925 this._annotations = {}; 7926 this._annotationsBySectionIndex = {}; 7927 this.rendition.hooks.render.register(this.inject.bind(this)); 7928 this.rendition.hooks.unloaded.register(this.clear.bind(this)); 7929 } 7930 /** 7931 * Add an annotation to store 7932 * @param {string} type Type of annotation to add: "highlight", "underline", "mark" 7933 * @param {EpubCFI} cfiRange EpubCFI range to attach annotation to 7934 * @param {object} data Data to assign to annotation 7935 * @param {function} [cb] Callback after annotation is added 7936 * @param {string} className CSS class to assign to annotation 7937 * @param {object} styles CSS styles to assign to annotation 7938 * @returns {Annotation} annotation 7939 */ 7940 7941 7942 add(type, cfiRange, data, cb, className, styles) { 7943 let hash = encodeURI(cfiRange + type); 7944 let cfi = new src_epubcfi["a" /* default */](cfiRange); 7945 let sectionIndex = cfi.spinePos; 7946 let annotation = new annotations_Annotation({ 7947 type, 7948 cfiRange, 7949 data, 7950 sectionIndex, 7951 cb, 7952 className, 7953 styles 7954 }); 7955 this._annotations[hash] = annotation; 7956 7957 if (sectionIndex in this._annotationsBySectionIndex) { 7958 this._annotationsBySectionIndex[sectionIndex].push(hash); 7959 } else { 7960 this._annotationsBySectionIndex[sectionIndex] = [hash]; 7961 } 7962 7963 let views = this.rendition.views(); 7964 views.forEach(view => { 7965 if (annotation.sectionIndex === view.index) { 7966 annotation.attach(view); 7967 } 7968 }); 7969 return annotation; 7970 } 7971 /** 7972 * Remove an annotation from store 7973 * @param {EpubCFI} cfiRange EpubCFI range the annotation is attached to 7974 * @param {string} type Type of annotation to add: "highlight", "underline", "mark" 7975 */ 7976 7977 7978 remove(cfiRange, type) { 7979 let hash = encodeURI(cfiRange + type); 7980 7981 if (hash in this._annotations) { 7982 let annotation = this._annotations[hash]; 7983 7984 if (type && annotation.type !== type) { 7985 return; 7986 } 7987 7988 let views = this.rendition.views(); 7989 views.forEach(view => { 7990 this._removeFromAnnotationBySectionIndex(annotation.sectionIndex, hash); 7991 7992 if (annotation.sectionIndex === view.index) { 7993 annotation.detach(view); 7994 } 7995 }); 7996 delete this._annotations[hash]; 7997 } 7998 } 7999 /** 8000 * Remove an annotations by Section Index 8001 * @private 8002 */ 8003 8004 8005 _removeFromAnnotationBySectionIndex(sectionIndex, hash) { 8006 this._annotationsBySectionIndex[sectionIndex] = this._annotationsAt(sectionIndex).filter(h => h !== hash); 8007 } 8008 /** 8009 * Get annotations by Section Index 8010 * @private 8011 */ 8012 8013 8014 _annotationsAt(index) { 8015 return this._annotationsBySectionIndex[index]; 8016 } 8017 /** 8018 * Add a highlight to the store 8019 * @param {EpubCFI} cfiRange EpubCFI range to attach annotation to 8020 * @param {object} data Data to assign to annotation 8021 * @param {function} cb Callback after annotation is clicked 8022 * @param {string} className CSS class to assign to annotation 8023 * @param {object} styles CSS styles to assign to annotation 8024 */ 8025 8026 8027 highlight(cfiRange, data, cb, className, styles) { 8028 return this.add("highlight", cfiRange, data, cb, className, styles); 8029 } 8030 /** 8031 * Add a underline to the store 8032 * @param {EpubCFI} cfiRange EpubCFI range to attach annotation to 8033 * @param {object} data Data to assign to annotation 8034 * @param {function} cb Callback after annotation is clicked 8035 * @param {string} className CSS class to assign to annotation 8036 * @param {object} styles CSS styles to assign to annotation 8037 */ 8038 8039 8040 underline(cfiRange, data, cb, className, styles) { 8041 return this.add("underline", cfiRange, data, cb, className, styles); 8042 } 8043 /** 8044 * Add a mark to the store 8045 * @param {EpubCFI} cfiRange EpubCFI range to attach annotation to 8046 * @param {object} data Data to assign to annotation 8047 * @param {function} cb Callback after annotation is clicked 8048 */ 8049 8050 8051 mark(cfiRange, data, cb) { 8052 return this.add("mark", cfiRange, data, cb); 8053 } 8054 /** 8055 * iterate over annotations in the store 8056 */ 8057 8058 8059 each() { 8060 return this._annotations.forEach.apply(this._annotations, arguments); 8061 } 8062 /** 8063 * Hook for injecting annotation into a view 8064 * @param {View} view 8065 * @private 8066 */ 8067 8068 8069 inject(view) { 8070 let sectionIndex = view.index; 8071 8072 if (sectionIndex in this._annotationsBySectionIndex) { 8073 let annotations = this._annotationsBySectionIndex[sectionIndex]; 8074 annotations.forEach(hash => { 8075 let annotation = this._annotations[hash]; 8076 annotation.attach(view); 8077 }); 8078 } 8079 } 8080 /** 8081 * Hook for removing annotation from a view 8082 * @param {View} view 8083 * @private 8084 */ 8085 8086 8087 clear(view) { 8088 let sectionIndex = view.index; 8089 8090 if (sectionIndex in this._annotationsBySectionIndex) { 8091 let annotations = this._annotationsBySectionIndex[sectionIndex]; 8092 annotations.forEach(hash => { 8093 let annotation = this._annotations[hash]; 8094 annotation.detach(view); 8095 }); 8096 } 8097 } 8098 /** 8099 * [Not Implemented] Show annotations 8100 * @TODO: needs implementation in View 8101 */ 8102 8103 8104 show() {} 8105 /** 8106 * [Not Implemented] Hide annotations 8107 * @TODO: needs implementation in View 8108 */ 8109 8110 8111 hide() {} 8112 8113 } 8114 /** 8115 * Annotation object 8116 * @class 8117 * @param {object} options 8118 * @param {string} options.type Type of annotation to add: "highlight", "underline", "mark" 8119 * @param {EpubCFI} options.cfiRange EpubCFI range to attach annotation to 8120 * @param {object} options.data Data to assign to annotation 8121 * @param {int} options.sectionIndex Index in the Spine of the Section annotation belongs to 8122 * @param {function} [options.cb] Callback after annotation is clicked 8123 * @param {string} className CSS class to assign to annotation 8124 * @param {object} styles CSS styles to assign to annotation 8125 * @returns {Annotation} annotation 8126 */ 8127 8128 8129 class annotations_Annotation { 8130 constructor({ 8131 type, 8132 cfiRange, 8133 data, 8134 sectionIndex, 8135 cb, 8136 className, 8137 styles 8138 }) { 8139 this.type = type; 8140 this.cfiRange = cfiRange; 8141 this.data = data; 8142 this.sectionIndex = sectionIndex; 8143 this.mark = undefined; 8144 this.cb = cb; 8145 this.className = className; 8146 this.styles = styles; 8147 } 8148 /** 8149 * Update stored data 8150 * @param {object} data 8151 */ 8152 8153 8154 update(data) { 8155 this.data = data; 8156 } 8157 /** 8158 * Add to a view 8159 * @param {View} view 8160 */ 8161 8162 8163 attach(view) { 8164 let { 8165 cfiRange, 8166 data, 8167 type, 8168 mark, 8169 cb, 8170 className, 8171 styles 8172 } = this; 8173 let result; 8174 8175 if (type === "highlight") { 8176 result = view.highlight(cfiRange, data, cb, className, styles); 8177 } else if (type === "underline") { 8178 result = view.underline(cfiRange, data, cb, className, styles); 8179 } else if (type === "mark") { 8180 result = view.mark(cfiRange, data, cb); 8181 } 8182 8183 this.mark = result; 8184 this.emit(constants["c" /* EVENTS */].ANNOTATION.ATTACH, result); 8185 return result; 8186 } 8187 /** 8188 * Remove from a view 8189 * @param {View} view 8190 */ 8191 8192 8193 detach(view) { 8194 let { 8195 cfiRange, 8196 type 8197 } = this; 8198 let result; 8199 8200 if (view) { 8201 if (type === "highlight") { 8202 result = view.unhighlight(cfiRange); 8203 } else if (type === "underline") { 8204 result = view.ununderline(cfiRange); 8205 } else if (type === "mark") { 8206 result = view.unmark(cfiRange); 8207 } 8208 } 8209 8210 this.mark = undefined; 8211 this.emit(constants["c" /* EVENTS */].ANNOTATION.DETACH, result); 8212 return result; 8213 } 8214 /** 8215 * [Not Implemented] Get text of an annotation 8216 * @TODO: needs implementation in contents 8217 */ 8218 8219 8220 text() {} 8221 8222 } 8223 8224 event_emitter_default()(annotations_Annotation.prototype); 8225 /* harmony default export */ var annotations = (annotations_Annotations); 8226 // EXTERNAL MODULE: ./src/managers/views/iframe.js 8227 var iframe = __webpack_require__(20); 8228 8229 // EXTERNAL MODULE: ./src/managers/default/index.js + 3 modules 8230 var managers_default = __webpack_require__(10); 8231 8232 // EXTERNAL MODULE: ./src/managers/continuous/index.js + 1 modules 8233 var continuous = __webpack_require__(22); 8234 8235 // CONCATENATED MODULE: ./src/rendition.js 8236 8237 8238 8239 8240 8241 // import Mapping from "./mapping"; 8242 8243 8244 8245 8246 // Default Views 8247 8248 // Default View Managers 8249 8250 8251 8252 /** 8253 * Displays an Epub as a series of Views for each Section. 8254 * Requires Manager and View class to handle specifics of rendering 8255 * the section content. 8256 * @class 8257 * @param {Book} book 8258 * @param {object} [options] 8259 * @param {number} [options.width] 8260 * @param {number} [options.height] 8261 * @param {string} [options.ignoreClass] class for the cfi parser to ignore 8262 * @param {string | function | object} [options.manager='default'] 8263 * @param {string | function} [options.view='iframe'] 8264 * @param {string} [options.layout] layout to force 8265 * @param {string} [options.spread] force spread value 8266 * @param {number} [options.minSpreadWidth] overridden by spread: none (never) / both (always) 8267 * @param {string} [options.stylesheet] url of stylesheet to be injected 8268 * @param {boolean} [options.resizeOnOrientationChange] false to disable orientation events 8269 * @param {string} [options.script] url of script to be injected 8270 * @param {boolean | object} [options.snap=false] use snap scrolling 8271 * @param {string} [options.defaultDirection='ltr'] default text direction 8272 * @param {boolean} [options.allowScriptedContent=false] enable running scripts in content 8273 * @param {boolean} [options.allowPopups=false] enable opening popup in content 8274 */ 8275 8276 class rendition_Rendition { 8277 constructor(book, options) { 8278 this.settings = Object(core["extend"])(this.settings || {}, { 8279 width: null, 8280 height: null, 8281 ignoreClass: "", 8282 manager: "default", 8283 view: "iframe", 8284 flow: null, 8285 layout: null, 8286 spread: null, 8287 minSpreadWidth: 800, 8288 stylesheet: null, 8289 resizeOnOrientationChange: true, 8290 script: null, 8291 snap: false, 8292 defaultDirection: "ltr", 8293 allowScriptedContent: false, 8294 allowPopups: false 8295 }); 8296 Object(core["extend"])(this.settings, options); 8297 8298 if (typeof this.settings.manager === "object") { 8299 this.manager = this.settings.manager; 8300 } 8301 8302 this.book = book; 8303 /** 8304 * Adds Hook methods to the Rendition prototype 8305 * @member {object} hooks 8306 * @property {Hook} hooks.content 8307 * @memberof Rendition 8308 */ 8309 8310 this.hooks = {}; 8311 this.hooks.display = new hook["a" /* default */](this); 8312 this.hooks.serialize = new hook["a" /* default */](this); 8313 this.hooks.content = new hook["a" /* default */](this); 8314 this.hooks.unloaded = new hook["a" /* default */](this); 8315 this.hooks.layout = new hook["a" /* default */](this); 8316 this.hooks.render = new hook["a" /* default */](this); 8317 this.hooks.show = new hook["a" /* default */](this); 8318 this.hooks.content.register(this.handleLinks.bind(this)); 8319 this.hooks.content.register(this.passEvents.bind(this)); 8320 this.hooks.content.register(this.adjustImages.bind(this)); 8321 this.book.spine.hooks.content.register(this.injectIdentifier.bind(this)); 8322 8323 if (this.settings.stylesheet) { 8324 this.book.spine.hooks.content.register(this.injectStylesheet.bind(this)); 8325 } 8326 8327 if (this.settings.script) { 8328 this.book.spine.hooks.content.register(this.injectScript.bind(this)); 8329 } 8330 /** 8331 * @member {Themes} themes 8332 * @memberof Rendition 8333 */ 8334 8335 8336 this.themes = new themes(this); 8337 /** 8338 * @member {Annotations} annotations 8339 * @memberof Rendition 8340 */ 8341 8342 this.annotations = new annotations(this); 8343 this.epubcfi = new src_epubcfi["a" /* default */](); 8344 this.q = new queue["a" /* default */](this); 8345 /** 8346 * A Rendered Location Range 8347 * @typedef location 8348 * @type {Object} 8349 * @property {object} start 8350 * @property {string} start.index 8351 * @property {string} start.href 8352 * @property {object} start.displayed 8353 * @property {EpubCFI} start.cfi 8354 * @property {number} start.location 8355 * @property {number} start.percentage 8356 * @property {number} start.displayed.page 8357 * @property {number} start.displayed.total 8358 * @property {object} end 8359 * @property {string} end.index 8360 * @property {string} end.href 8361 * @property {object} end.displayed 8362 * @property {EpubCFI} end.cfi 8363 * @property {number} end.location 8364 * @property {number} end.percentage 8365 * @property {number} end.displayed.page 8366 * @property {number} end.displayed.total 8367 * @property {boolean} atStart 8368 * @property {boolean} atEnd 8369 * @memberof Rendition 8370 */ 8371 8372 this.location = undefined; // Hold queue until book is opened 8373 8374 this.q.enqueue(this.book.opened); 8375 this.starting = new core["defer"](); 8376 /** 8377 * @member {promise} started returns after the rendition has started 8378 * @memberof Rendition 8379 */ 8380 8381 this.started = this.starting.promise; // Block the queue until rendering is started 8382 8383 this.q.enqueue(this.start); 8384 } 8385 /** 8386 * Set the manager function 8387 * @param {function} manager 8388 */ 8389 8390 8391 setManager(manager) { 8392 this.manager = manager; 8393 } 8394 /** 8395 * Require the manager from passed string, or as a class function 8396 * @param {string|object} manager [description] 8397 * @return {method} 8398 */ 8399 8400 8401 requireManager(manager) { 8402 var viewManager; // If manager is a string, try to load from imported managers 8403 8404 if (typeof manager === "string" && manager === "default") { 8405 viewManager = managers_default["a" /* default */]; 8406 } else if (typeof manager === "string" && manager === "continuous") { 8407 viewManager = continuous["a" /* default */]; 8408 } else { 8409 // otherwise, assume we were passed a class function 8410 viewManager = manager; 8411 } 8412 8413 return viewManager; 8414 } 8415 /** 8416 * Require the view from passed string, or as a class function 8417 * @param {string|object} view 8418 * @return {view} 8419 */ 8420 8421 8422 requireView(view) { 8423 var View; // If view is a string, try to load from imported views, 8424 8425 if (typeof view == "string" && view === "iframe") { 8426 View = iframe["a" /* default */]; 8427 } else { 8428 // otherwise, assume we were passed a class function 8429 View = view; 8430 } 8431 8432 return View; 8433 } 8434 /** 8435 * Start the rendering 8436 * @return {Promise} rendering has started 8437 */ 8438 8439 8440 start() { 8441 if (!this.settings.layout && (this.book.package.metadata.layout === "pre-paginated" || this.book.displayOptions.fixedLayout === "true")) { 8442 this.settings.layout = "pre-paginated"; 8443 } 8444 8445 switch (this.book.package.metadata.spread) { 8446 case 'none': 8447 this.settings.spread = 'none'; 8448 break; 8449 8450 case 'both': 8451 this.settings.spread = true; 8452 break; 8453 } 8454 8455 if (!this.manager) { 8456 this.ViewManager = this.requireManager(this.settings.manager); 8457 this.View = this.requireView(this.settings.view); 8458 this.manager = new this.ViewManager({ 8459 view: this.View, 8460 queue: this.q, 8461 request: this.book.load.bind(this.book), 8462 settings: this.settings 8463 }); 8464 } 8465 8466 this.direction(this.book.package.metadata.direction || this.settings.defaultDirection); // Parse metadata to get layout props 8467 8468 this.settings.globalLayoutProperties = this.determineLayoutProperties(this.book.package.metadata); 8469 this.flow(this.settings.globalLayoutProperties.flow); 8470 this.layout(this.settings.globalLayoutProperties); // Listen for displayed views 8471 8472 this.manager.on(constants["c" /* EVENTS */].MANAGERS.ADDED, this.afterDisplayed.bind(this)); 8473 this.manager.on(constants["c" /* EVENTS */].MANAGERS.REMOVED, this.afterRemoved.bind(this)); // Listen for resizing 8474 8475 this.manager.on(constants["c" /* EVENTS */].MANAGERS.RESIZED, this.onResized.bind(this)); // Listen for rotation 8476 8477 this.manager.on(constants["c" /* EVENTS */].MANAGERS.ORIENTATION_CHANGE, this.onOrientationChange.bind(this)); // Listen for scroll changes 8478 8479 this.manager.on(constants["c" /* EVENTS */].MANAGERS.SCROLLED, this.reportLocation.bind(this)); 8480 /** 8481 * Emit that rendering has started 8482 * @event started 8483 * @memberof Rendition 8484 */ 8485 8486 this.emit(constants["c" /* EVENTS */].RENDITION.STARTED); // Start processing queue 8487 8488 this.starting.resolve(); 8489 } 8490 /** 8491 * Call to attach the container to an element in the dom 8492 * Container must be attached before rendering can begin 8493 * @param {element} element to attach to 8494 * @return {Promise} 8495 */ 8496 8497 8498 attachTo(element) { 8499 return this.q.enqueue(function () { 8500 // Start rendering 8501 this.manager.render(element, { 8502 "width": this.settings.width, 8503 "height": this.settings.height 8504 }); 8505 /** 8506 * Emit that rendering has attached to an element 8507 * @event attached 8508 * @memberof Rendition 8509 */ 8510 8511 this.emit(constants["c" /* EVENTS */].RENDITION.ATTACHED); 8512 }.bind(this)); 8513 } 8514 /** 8515 * Display a point in the book 8516 * The request will be added to the rendering Queue, 8517 * so it will wait until book is opened, rendering started 8518 * and all other rendering tasks have finished to be called. 8519 * @param {string} target Url or EpubCFI 8520 * @return {Promise} 8521 */ 8522 8523 8524 display(target) { 8525 if (this.displaying) { 8526 this.displaying.resolve(); 8527 } 8528 8529 return this.q.enqueue(this._display, target); 8530 } 8531 /** 8532 * Tells the manager what to display immediately 8533 * @private 8534 * @param {string} target Url or EpubCFI 8535 * @return {Promise} 8536 */ 8537 8538 8539 _display(target) { 8540 if (!this.book) { 8541 return; 8542 } 8543 8544 var isCfiString = this.epubcfi.isCfiString(target); 8545 var displaying = new core["defer"](); 8546 var displayed = displaying.promise; 8547 var section; 8548 var moveTo; 8549 this.displaying = displaying; // Check if this is a book percentage 8550 8551 if (this.book.locations.length() && Object(core["isFloat"])(target)) { 8552 target = this.book.locations.cfiFromPercentage(parseFloat(target)); 8553 } 8554 8555 section = this.book.spine.get(target); 8556 8557 if (!section) { 8558 displaying.reject(new Error("No Section Found")); 8559 return displayed; 8560 } 8561 8562 this.manager.display(section, target).then(() => { 8563 displaying.resolve(section); 8564 this.displaying = undefined; 8565 /** 8566 * Emit that a section has been displayed 8567 * @event displayed 8568 * @param {Section} section 8569 * @memberof Rendition 8570 */ 8571 8572 this.emit(constants["c" /* EVENTS */].RENDITION.DISPLAYED, section); 8573 this.reportLocation(); 8574 }, err => { 8575 /** 8576 * Emit that has been an error displaying 8577 * @event displayError 8578 * @param {Section} section 8579 * @memberof Rendition 8580 */ 8581 this.emit(constants["c" /* EVENTS */].RENDITION.DISPLAY_ERROR, err); 8582 }); 8583 return displayed; 8584 } 8585 /* 8586 render(view, show) { 8587 // view.onLayout = this.layout.format.bind(this.layout); 8588 view.create(); 8589 // Fit to size of the container, apply padding 8590 this.manager.resizeView(view); 8591 // Render Chain 8592 return view.section.render(this.book.request) 8593 .then(function(contents){ 8594 return view.load(contents); 8595 }.bind(this)) 8596 .then(function(doc){ 8597 return this.hooks.content.trigger(view, this); 8598 }.bind(this)) 8599 .then(function(){ 8600 this.layout.format(view.contents); 8601 return this.hooks.layout.trigger(view, this); 8602 }.bind(this)) 8603 .then(function(){ 8604 return view.display(); 8605 }.bind(this)) 8606 .then(function(){ 8607 return this.hooks.render.trigger(view, this); 8608 }.bind(this)) 8609 .then(function(){ 8610 if(show !== false) { 8611 this.q.enqueue(function(view){ 8612 view.show(); 8613 }, view); 8614 } 8615 // this.map = new Map(view, this.layout); 8616 this.hooks.show.trigger(view, this); 8617 this.trigger("rendered", view.section); 8618 }.bind(this)) 8619 .catch(function(e){ 8620 this.trigger("loaderror", e); 8621 }.bind(this)); 8622 } 8623 */ 8624 8625 /** 8626 * Report what section has been displayed 8627 * @private 8628 * @param {*} view 8629 */ 8630 8631 8632 afterDisplayed(view) { 8633 view.on(constants["c" /* EVENTS */].VIEWS.MARK_CLICKED, (cfiRange, data) => this.triggerMarkEvent(cfiRange, data, view.contents)); 8634 this.hooks.render.trigger(view, this).then(() => { 8635 if (view.contents) { 8636 this.hooks.content.trigger(view.contents, this).then(() => { 8637 /** 8638 * Emit that a section has been rendered 8639 * @event rendered 8640 * @param {Section} section 8641 * @param {View} view 8642 * @memberof Rendition 8643 */ 8644 this.emit(constants["c" /* EVENTS */].RENDITION.RENDERED, view.section, view); 8645 }); 8646 } else { 8647 this.emit(constants["c" /* EVENTS */].RENDITION.RENDERED, view.section, view); 8648 } 8649 }); 8650 } 8651 /** 8652 * Report what has been removed 8653 * @private 8654 * @param {*} view 8655 */ 8656 8657 8658 afterRemoved(view) { 8659 this.hooks.unloaded.trigger(view, this).then(() => { 8660 /** 8661 * Emit that a section has been removed 8662 * @event removed 8663 * @param {Section} section 8664 * @param {View} view 8665 * @memberof Rendition 8666 */ 8667 this.emit(constants["c" /* EVENTS */].RENDITION.REMOVED, view.section, view); 8668 }); 8669 } 8670 /** 8671 * Report resize events and display the last seen location 8672 * @private 8673 */ 8674 8675 8676 onResized(size, epubcfi) { 8677 /** 8678 * Emit that the rendition has been resized 8679 * @event resized 8680 * @param {number} width 8681 * @param {height} height 8682 * @param {string} epubcfi (optional) 8683 * @memberof Rendition 8684 */ 8685 this.emit(constants["c" /* EVENTS */].RENDITION.RESIZED, { 8686 width: size.width, 8687 height: size.height 8688 }, epubcfi); 8689 8690 if (this.location && this.location.start) { 8691 this.display(epubcfi || this.location.start.cfi); 8692 } 8693 } 8694 /** 8695 * Report orientation events and display the last seen location 8696 * @private 8697 */ 8698 8699 8700 onOrientationChange(orientation) { 8701 /** 8702 * Emit that the rendition has been rotated 8703 * @event orientationchange 8704 * @param {string} orientation 8705 * @memberof Rendition 8706 */ 8707 this.emit(constants["c" /* EVENTS */].RENDITION.ORIENTATION_CHANGE, orientation); 8708 } 8709 /** 8710 * Move the Rendition to a specific offset 8711 * Usually you would be better off calling display() 8712 * @param {object} offset 8713 */ 8714 8715 8716 moveTo(offset) { 8717 this.manager.moveTo(offset); 8718 } 8719 /** 8720 * Trigger a resize of the views 8721 * @param {number} [width] 8722 * @param {number} [height] 8723 * @param {string} [epubcfi] (optional) 8724 */ 8725 8726 8727 resize(width, height, epubcfi) { 8728 if (width) { 8729 this.settings.width = width; 8730 } 8731 8732 if (height) { 8733 this.settings.height = height; 8734 } 8735 8736 this.manager.resize(width, height, epubcfi); 8737 } 8738 /** 8739 * Clear all rendered views 8740 */ 8741 8742 8743 clear() { 8744 this.manager.clear(); 8745 } 8746 /** 8747 * Go to the next "page" in the rendition 8748 * @return {Promise} 8749 */ 8750 8751 8752 next() { 8753 return this.q.enqueue(this.manager.next.bind(this.manager)).then(this.reportLocation.bind(this)); 8754 } 8755 /** 8756 * Go to the previous "page" in the rendition 8757 * @return {Promise} 8758 */ 8759 8760 8761 prev() { 8762 return this.q.enqueue(this.manager.prev.bind(this.manager)).then(this.reportLocation.bind(this)); 8763 } //-- http://www.idpf.org/epub/301/spec/epub-publications.html#meta-properties-rendering 8764 8765 /** 8766 * Determine the Layout properties from metadata and settings 8767 * @private 8768 * @param {object} metadata 8769 * @return {object} properties 8770 */ 8771 8772 8773 determineLayoutProperties(metadata) { 8774 var properties; 8775 var layout = this.settings.layout || metadata.layout || "reflowable"; 8776 var spread = this.settings.spread || metadata.spread || "auto"; 8777 var orientation = this.settings.orientation || metadata.orientation || "auto"; 8778 var flow = this.settings.flow || metadata.flow || "auto"; 8779 var viewport = metadata.viewport || ""; 8780 var minSpreadWidth = this.settings.minSpreadWidth || metadata.minSpreadWidth || 800; 8781 var direction = this.settings.direction || metadata.direction || "ltr"; 8782 8783 if ((this.settings.width === 0 || this.settings.width > 0) && (this.settings.height === 0 || this.settings.height > 0)) {// viewport = "width="+this.settings.width+", height="+this.settings.height+""; 8784 } 8785 8786 properties = { 8787 layout: layout, 8788 spread: spread, 8789 orientation: orientation, 8790 flow: flow, 8791 viewport: viewport, 8792 minSpreadWidth: minSpreadWidth, 8793 direction: direction 8794 }; 8795 return properties; 8796 } 8797 /** 8798 * Adjust the flow of the rendition to paginated or scrolled 8799 * (scrolled-continuous vs scrolled-doc are handled by different view managers) 8800 * @param {string} flow 8801 */ 8802 8803 8804 flow(flow) { 8805 var _flow = flow; 8806 8807 if (flow === "scrolled" || flow === "scrolled-doc" || flow === "scrolled-continuous") { 8808 _flow = "scrolled"; 8809 } 8810 8811 if (flow === "auto" || flow === "paginated") { 8812 _flow = "paginated"; 8813 } 8814 8815 this.settings.flow = flow; 8816 8817 if (this._layout) { 8818 this._layout.flow(_flow); 8819 } 8820 8821 if (this.manager && this._layout) { 8822 this.manager.applyLayout(this._layout); 8823 } 8824 8825 if (this.manager) { 8826 this.manager.updateFlow(_flow); 8827 } 8828 8829 if (this.manager && this.manager.isRendered() && this.location) { 8830 this.manager.clear(); 8831 this.display(this.location.start.cfi); 8832 } 8833 } 8834 /** 8835 * Adjust the layout of the rendition to reflowable or pre-paginated 8836 * @param {object} settings 8837 */ 8838 8839 8840 layout(settings) { 8841 if (settings) { 8842 this._layout = new layout(settings); 8843 8844 this._layout.spread(settings.spread, this.settings.minSpreadWidth); // this.mapping = new Mapping(this._layout.props); 8845 8846 8847 this._layout.on(constants["c" /* EVENTS */].LAYOUT.UPDATED, (props, changed) => { 8848 this.emit(constants["c" /* EVENTS */].RENDITION.LAYOUT, props, changed); 8849 }); 8850 } 8851 8852 if (this.manager && this._layout) { 8853 this.manager.applyLayout(this._layout); 8854 } 8855 8856 return this._layout; 8857 } 8858 /** 8859 * Adjust if the rendition uses spreads 8860 * @param {string} spread none | auto (TODO: implement landscape, portrait, both) 8861 * @param {int} [min] min width to use spreads at 8862 */ 8863 8864 8865 spread(spread, min) { 8866 this.settings.spread = spread; 8867 8868 if (min) { 8869 this.settings.minSpreadWidth = min; 8870 } 8871 8872 if (this._layout) { 8873 this._layout.spread(spread, min); 8874 } 8875 8876 if (this.manager && this.manager.isRendered()) { 8877 this.manager.updateLayout(); 8878 } 8879 } 8880 /** 8881 * Adjust the direction of the rendition 8882 * @param {string} dir 8883 */ 8884 8885 8886 direction(dir) { 8887 this.settings.direction = dir || "ltr"; 8888 8889 if (this.manager) { 8890 this.manager.direction(this.settings.direction); 8891 } 8892 8893 if (this.manager && this.manager.isRendered() && this.location) { 8894 this.manager.clear(); 8895 this.display(this.location.start.cfi); 8896 } 8897 } 8898 /** 8899 * Report the current location 8900 * @fires relocated 8901 * @fires locationChanged 8902 */ 8903 8904 8905 reportLocation() { 8906 return this.q.enqueue(function reportedLocation() { 8907 requestAnimationFrame(function reportedLocationAfterRAF() { 8908 var location = this.manager.currentLocation(); 8909 8910 if (location && location.then && typeof location.then === "function") { 8911 location.then(function (result) { 8912 let located = this.located(result); 8913 8914 if (!located || !located.start || !located.end) { 8915 return; 8916 } 8917 8918 this.location = located; 8919 this.emit(constants["c" /* EVENTS */].RENDITION.LOCATION_CHANGED, { 8920 index: this.location.start.index, 8921 href: this.location.start.href, 8922 start: this.location.start.cfi, 8923 end: this.location.end.cfi, 8924 percentage: this.location.start.percentage 8925 }); 8926 this.emit(constants["c" /* EVENTS */].RENDITION.RELOCATED, this.location); 8927 }.bind(this)); 8928 } else if (location) { 8929 let located = this.located(location); 8930 8931 if (!located || !located.start || !located.end) { 8932 return; 8933 } 8934 8935 this.location = located; 8936 /** 8937 * @event locationChanged 8938 * @deprecated 8939 * @type {object} 8940 * @property {number} index 8941 * @property {string} href 8942 * @property {EpubCFI} start 8943 * @property {EpubCFI} end 8944 * @property {number} percentage 8945 * @memberof Rendition 8946 */ 8947 8948 this.emit(constants["c" /* EVENTS */].RENDITION.LOCATION_CHANGED, { 8949 index: this.location.start.index, 8950 href: this.location.start.href, 8951 start: this.location.start.cfi, 8952 end: this.location.end.cfi, 8953 percentage: this.location.start.percentage 8954 }); 8955 /** 8956 * @event relocated 8957 * @type {displayedLocation} 8958 * @memberof Rendition 8959 */ 8960 8961 this.emit(constants["c" /* EVENTS */].RENDITION.RELOCATED, this.location); 8962 } 8963 }.bind(this)); 8964 }.bind(this)); 8965 } 8966 /** 8967 * Get the Current Location object 8968 * @return {displayedLocation | promise} location (may be a promise) 8969 */ 8970 8971 8972 currentLocation() { 8973 var location = this.manager.currentLocation(); 8974 8975 if (location && location.then && typeof location.then === "function") { 8976 location.then(function (result) { 8977 let located = this.located(result); 8978 return located; 8979 }.bind(this)); 8980 } else if (location) { 8981 let located = this.located(location); 8982 return located; 8983 } 8984 } 8985 /** 8986 * Creates a Rendition#locationRange from location 8987 * passed by the Manager 8988 * @returns {displayedLocation} 8989 * @private 8990 */ 8991 8992 8993 located(location) { 8994 if (!location.length) { 8995 return {}; 8996 } 8997 8998 let start = location[0]; 8999 let end = location[location.length - 1]; 9000 let located = { 9001 start: { 9002 index: start.index, 9003 href: start.href, 9004 cfi: start.mapping.start, 9005 displayed: { 9006 page: start.pages[0] || 1, 9007 total: start.totalPages 9008 } 9009 }, 9010 end: { 9011 index: end.index, 9012 href: end.href, 9013 cfi: end.mapping.end, 9014 displayed: { 9015 page: end.pages[end.pages.length - 1] || 1, 9016 total: end.totalPages 9017 } 9018 } 9019 }; 9020 let locationStart = this.book.locations.locationFromCfi(start.mapping.start); 9021 let locationEnd = this.book.locations.locationFromCfi(end.mapping.end); 9022 9023 if (locationStart != null) { 9024 located.start.location = locationStart; 9025 located.start.percentage = this.book.locations.percentageFromLocation(locationStart); 9026 } 9027 9028 if (locationEnd != null) { 9029 located.end.location = locationEnd; 9030 located.end.percentage = this.book.locations.percentageFromLocation(locationEnd); 9031 } 9032 9033 let pageStart = this.book.pageList.pageFromCfi(start.mapping.start); 9034 let pageEnd = this.book.pageList.pageFromCfi(end.mapping.end); 9035 9036 if (pageStart != -1) { 9037 located.start.page = pageStart; 9038 } 9039 9040 if (pageEnd != -1) { 9041 located.end.page = pageEnd; 9042 } 9043 9044 if (end.index === this.book.spine.last().index && located.end.displayed.page >= located.end.displayed.total) { 9045 located.atEnd = true; 9046 } 9047 9048 if (start.index === this.book.spine.first().index && located.start.displayed.page === 1) { 9049 located.atStart = true; 9050 } 9051 9052 return located; 9053 } 9054 /** 9055 * Remove and Clean Up the Rendition 9056 */ 9057 9058 9059 destroy() { 9060 // Clear the queue 9061 // this.q.clear(); 9062 // this.q = undefined; 9063 this.manager && this.manager.destroy(); 9064 this.book = undefined; // this.views = null; 9065 // this.hooks.display.clear(); 9066 // this.hooks.serialize.clear(); 9067 // this.hooks.content.clear(); 9068 // this.hooks.layout.clear(); 9069 // this.hooks.render.clear(); 9070 // this.hooks.show.clear(); 9071 // this.hooks = {}; 9072 // this.themes.destroy(); 9073 // this.themes = undefined; 9074 // this.epubcfi = undefined; 9075 // this.starting = undefined; 9076 // this.started = undefined; 9077 } 9078 /** 9079 * Pass the events from a view's Contents 9080 * @private 9081 * @param {Contents} view contents 9082 */ 9083 9084 9085 passEvents(contents) { 9086 constants["a" /* DOM_EVENTS */].forEach(e => { 9087 contents.on(e, ev => this.triggerViewEvent(ev, contents)); 9088 }); 9089 contents.on(constants["c" /* EVENTS */].CONTENTS.SELECTED, e => this.triggerSelectedEvent(e, contents)); 9090 } 9091 /** 9092 * Emit events passed by a view 9093 * @private 9094 * @param {event} e 9095 */ 9096 9097 9098 triggerViewEvent(e, contents) { 9099 this.emit(e.type, e, contents); 9100 } 9101 /** 9102 * Emit a selection event's CFI Range passed from a a view 9103 * @private 9104 * @param {string} cfirange 9105 */ 9106 9107 9108 triggerSelectedEvent(cfirange, contents) { 9109 /** 9110 * Emit that a text selection has occurred 9111 * @event selected 9112 * @param {string} cfirange 9113 * @param {Contents} contents 9114 * @memberof Rendition 9115 */ 9116 this.emit(constants["c" /* EVENTS */].RENDITION.SELECTED, cfirange, contents); 9117 } 9118 /** 9119 * Emit a markClicked event with the cfiRange and data from a mark 9120 * @private 9121 * @param {EpubCFI} cfirange 9122 */ 9123 9124 9125 triggerMarkEvent(cfiRange, data, contents) { 9126 /** 9127 * Emit that a mark was clicked 9128 * @event markClicked 9129 * @param {EpubCFI} cfirange 9130 * @param {object} data 9131 * @param {Contents} contents 9132 * @memberof Rendition 9133 */ 9134 this.emit(constants["c" /* EVENTS */].RENDITION.MARK_CLICKED, cfiRange, data, contents); 9135 } 9136 /** 9137 * Get a Range from a Visible CFI 9138 * @param {string} cfi EpubCfi String 9139 * @param {string} ignoreClass 9140 * @return {range} 9141 */ 9142 9143 9144 getRange(cfi, ignoreClass) { 9145 var _cfi = new src_epubcfi["a" /* default */](cfi); 9146 9147 var found = this.manager.visible().filter(function (view) { 9148 if (_cfi.spinePos === view.index) return true; 9149 }); // Should only every return 1 item 9150 9151 if (found.length) { 9152 return found[0].contents.range(_cfi, ignoreClass); 9153 } 9154 } 9155 /** 9156 * Hook to adjust images to fit in columns 9157 * @param {Contents} contents 9158 * @private 9159 */ 9160 9161 9162 adjustImages(contents) { 9163 if (this._layout.name === "pre-paginated") { 9164 return new Promise(function (resolve) { 9165 resolve(); 9166 }); 9167 } 9168 9169 let computed = contents.window.getComputedStyle(contents.content, null); 9170 let height = (contents.content.offsetHeight - (parseFloat(computed.paddingTop) + parseFloat(computed.paddingBottom))) * .95; 9171 let horizontalPadding = parseFloat(computed.paddingLeft) + parseFloat(computed.paddingRight); 9172 contents.addStylesheetRules({ 9173 "img": { 9174 "max-width": (this._layout.columnWidth ? this._layout.columnWidth - horizontalPadding + "px" : "100%") + "!important", 9175 "max-height": height + "px" + "!important", 9176 "object-fit": "contain", 9177 "page-break-inside": "avoid", 9178 "break-inside": "avoid", 9179 "box-sizing": "border-box" 9180 }, 9181 "svg": { 9182 "max-width": (this._layout.columnWidth ? this._layout.columnWidth - horizontalPadding + "px" : "100%") + "!important", 9183 "max-height": height + "px" + "!important", 9184 "page-break-inside": "avoid", 9185 "break-inside": "avoid" 9186 } 9187 }); 9188 return new Promise(function (resolve, reject) { 9189 // Wait to apply 9190 setTimeout(function () { 9191 resolve(); 9192 }, 1); 9193 }); 9194 } 9195 /** 9196 * Get the Contents object of each rendered view 9197 * @returns {Contents[]} 9198 */ 9199 9200 9201 getContents() { 9202 return this.manager ? this.manager.getContents() : []; 9203 } 9204 /** 9205 * Get the views member from the manager 9206 * @returns {Views} 9207 */ 9208 9209 9210 views() { 9211 let views = this.manager ? this.manager.views : undefined; 9212 return views || []; 9213 } 9214 /** 9215 * Hook to handle link clicks in rendered content 9216 * @param {Contents} contents 9217 * @private 9218 */ 9219 9220 9221 handleLinks(contents) { 9222 if (contents) { 9223 contents.on(constants["c" /* EVENTS */].CONTENTS.LINK_CLICKED, href => { 9224 let relative = this.book.path.relative(href); 9225 this.display(relative); 9226 }); 9227 } 9228 } 9229 /** 9230 * Hook to handle injecting stylesheet before 9231 * a Section is serialized 9232 * @param {document} doc 9233 * @param {Section} section 9234 * @private 9235 */ 9236 9237 9238 injectStylesheet(doc, section) { 9239 let style = doc.createElement("link"); 9240 style.setAttribute("type", "text/css"); 9241 style.setAttribute("rel", "stylesheet"); 9242 style.setAttribute("href", this.settings.stylesheet); 9243 doc.getElementsByTagName("head")[0].appendChild(style); 9244 } 9245 /** 9246 * Hook to handle injecting scripts before 9247 * a Section is serialized 9248 * @param {document} doc 9249 * @param {Section} section 9250 * @private 9251 */ 9252 9253 9254 injectScript(doc, section) { 9255 let script = doc.createElement("script"); 9256 script.setAttribute("type", "text/javascript"); 9257 script.setAttribute("src", this.settings.script); 9258 script.textContent = " "; // Needed to prevent self closing tag 9259 9260 doc.getElementsByTagName("head")[0].appendChild(script); 9261 } 9262 /** 9263 * Hook to handle the document identifier before 9264 * a Section is serialized 9265 * @param {document} doc 9266 * @param {Section} section 9267 * @private 9268 */ 9269 9270 9271 injectIdentifier(doc, section) { 9272 let ident = this.book.packaging.metadata.identifier; 9273 let meta = doc.createElement("meta"); 9274 meta.setAttribute("name", "dc.relation.ispartof"); 9275 9276 if (ident) { 9277 meta.setAttribute("content", ident); 9278 } 9279 9280 doc.getElementsByTagName("head")[0].appendChild(meta); 9281 } 9282 9283 } //-- Enable binding events to Renderer 9284 9285 9286 event_emitter_default()(rendition_Rendition.prototype); 9287 /* harmony default export */ var rendition = __webpack_exports__["a"] = (rendition_Rendition); 9288 9289 /***/ }), 9290 /* 17 */ 9291 /***/ (function(module, exports) { 9292 9293 var g; 9294 9295 // This works in non-strict mode 9296 g = (function() { 9297 return this; 9298 })(); 9299 9300 try { 9301 // This works if eval is allowed (see CSP) 9302 g = g || new Function("return this")(); 9303 } catch (e) { 9304 // This works if the window reference is available 9305 if (typeof window === "object") g = window; 9306 } 9307 9308 // g can still be undefined, but nothing to do about it... 9309 // We return undefined, instead of nothing here, so it's 9310 // easier to handle this case. if(!global) { ...} 9311 9312 module.exports = g; 9313 9314 9315 /***/ }), 9316 /* 18 */ 9317 /***/ (function(module, exports, __webpack_require__) { 9318 9319 "use strict"; 9320 9321 9322 var _undefined = __webpack_require__(38)(); // Support ES3 engines 9323 9324 module.exports = function (val) { 9325 return (val !== _undefined) && (val !== null); 9326 }; 9327 9328 9329 /***/ }), 9330 /* 19 */ 9331 /***/ (function(module, exports) { 9332 9333 /** 9334 * Checks if `value` is the 9335 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) 9336 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 9337 * 9338 * @static 9339 * @memberOf _ 9340 * @since 0.1.0 9341 * @category Lang 9342 * @param {*} value The value to check. 9343 * @returns {boolean} Returns `true` if `value` is an object, else `false`. 9344 * @example 9345 * 9346 * _.isObject({}); 9347 * // => true 9348 * 9349 * _.isObject([1, 2, 3]); 9350 * // => true 9351 * 9352 * _.isObject(_.noop); 9353 * // => true 9354 * 9355 * _.isObject(null); 9356 * // => false 9357 */ 9358 function isObject(value) { 9359 var type = typeof value; 9360 return value != null && (type == 'object' || type == 'function'); 9361 } 9362 9363 module.exports = isObject; 9364 9365 9366 /***/ }), 9367 /* 20 */ 9368 /***/ (function(module, __webpack_exports__, __webpack_require__) { 9369 9370 "use strict"; 9371 /* harmony import */ var event_emitter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); 9372 /* harmony import */ var event_emitter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(event_emitter__WEBPACK_IMPORTED_MODULE_0__); 9373 /* harmony import */ var _utils_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0); 9374 /* harmony import */ var _epubcfi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2); 9375 /* harmony import */ var _contents__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); 9376 /* harmony import */ var _utils_constants__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(1); 9377 /* harmony import */ var marks_pane__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(13); 9378 /* harmony import */ var marks_pane__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(marks_pane__WEBPACK_IMPORTED_MODULE_5__); 9379 9380 9381 9382 9383 9384 9385 9386 class IframeView { 9387 constructor(section, options) { 9388 this.settings = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["extend"])({ 9389 ignoreClass: "", 9390 axis: undefined, 9391 //options.layout && options.layout.props.flow === "scrolled" ? "vertical" : "horizontal", 9392 direction: undefined, 9393 width: 0, 9394 height: 0, 9395 layout: undefined, 9396 globalLayoutProperties: {}, 9397 method: undefined, 9398 forceRight: false, 9399 allowScriptedContent: false, 9400 allowPopups: false 9401 }, options || {}); 9402 this.id = "epubjs-view-" + Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["uuid"])(); 9403 this.section = section; 9404 this.index = section.index; 9405 this.element = this.container(this.settings.axis); 9406 this.added = false; 9407 this.displayed = false; 9408 this.rendered = false; // this.width = this.settings.width; 9409 // this.height = this.settings.height; 9410 9411 this.fixedWidth = 0; 9412 this.fixedHeight = 0; // Blank Cfi for Parsing 9413 9414 this.epubcfi = new _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"](); 9415 this.layout = this.settings.layout; // Dom events to listen for 9416 // this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"]; 9417 9418 this.pane = undefined; 9419 this.highlights = {}; 9420 this.underlines = {}; 9421 this.marks = {}; 9422 } 9423 9424 container(axis) { 9425 var element = document.createElement("div"); 9426 element.classList.add("epub-view"); // this.element.style.minHeight = "100px"; 9427 9428 element.style.height = "0px"; 9429 element.style.width = "0px"; 9430 element.style.overflow = "hidden"; 9431 element.style.position = "relative"; 9432 element.style.display = "block"; 9433 9434 if (axis && axis == "horizontal") { 9435 element.style.flex = "none"; 9436 } else { 9437 element.style.flex = "initial"; 9438 } 9439 9440 return element; 9441 } 9442 9443 create() { 9444 if (this.iframe) { 9445 return this.iframe; 9446 } 9447 9448 if (!this.element) { 9449 this.element = this.createContainer(); 9450 } 9451 9452 this.iframe = document.createElement("iframe"); 9453 this.iframe.id = this.id; 9454 this.iframe.scrolling = "no"; // Might need to be removed: breaks ios width calculations 9455 9456 this.iframe.style.overflow = "hidden"; 9457 this.iframe.seamless = "seamless"; // Back up if seamless isn't supported 9458 9459 this.iframe.style.border = "none"; // sandbox 9460 9461 this.iframe.sandbox = "allow-same-origin"; 9462 9463 if (this.settings.allowScriptedContent) { 9464 this.iframe.sandbox += " allow-scripts"; 9465 } 9466 9467 if (this.settings.allowPopups) { 9468 this.iframe.sandbox += " allow-popups"; 9469 } 9470 9471 this.iframe.setAttribute("enable-annotation", "true"); 9472 this.resizing = true; // this.iframe.style.display = "none"; 9473 9474 this.element.style.visibility = "hidden"; 9475 this.iframe.style.visibility = "hidden"; 9476 this.iframe.style.width = "0"; 9477 this.iframe.style.height = "0"; 9478 this._width = 0; 9479 this._height = 0; 9480 this.element.setAttribute("ref", this.index); 9481 this.added = true; 9482 this.elementBounds = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["bounds"])(this.element); // if(width || height){ 9483 // this.resize(width, height); 9484 // } else if(this.width && this.height){ 9485 // this.resize(this.width, this.height); 9486 // } else { 9487 // this.iframeBounds = bounds(this.iframe); 9488 // } 9489 9490 if ("srcdoc" in this.iframe) { 9491 this.supportsSrcdoc = true; 9492 } else { 9493 this.supportsSrcdoc = false; 9494 } 9495 9496 if (!this.settings.method) { 9497 this.settings.method = this.supportsSrcdoc ? "srcdoc" : "write"; 9498 } 9499 9500 return this.iframe; 9501 } 9502 9503 render(request, show) { 9504 // view.onLayout = this.layout.format.bind(this.layout); 9505 this.create(); // Fit to size of the container, apply padding 9506 9507 this.size(); 9508 9509 if (!this.sectionRender) { 9510 this.sectionRender = this.section.render(request); 9511 } // Render Chain 9512 9513 9514 return this.sectionRender.then(function (contents) { 9515 return this.load(contents); 9516 }.bind(this)).then(function () { 9517 // find and report the writingMode axis 9518 let writingMode = this.contents.writingMode(); // Set the axis based on the flow and writing mode 9519 9520 let axis; 9521 9522 if (this.settings.flow === "scrolled") { 9523 axis = writingMode.indexOf("vertical") === 0 ? "horizontal" : "vertical"; 9524 } else { 9525 axis = writingMode.indexOf("vertical") === 0 ? "vertical" : "horizontal"; 9526 } 9527 9528 if (writingMode.indexOf("vertical") === 0 && this.settings.flow === "paginated") { 9529 this.layout.delta = this.layout.height; 9530 } 9531 9532 this.setAxis(axis); 9533 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.AXIS, axis); 9534 this.setWritingMode(writingMode); 9535 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.WRITING_MODE, writingMode); // apply the layout function to the contents 9536 9537 this.layout.format(this.contents, this.section, this.axis); // Listen for events that require an expansion of the iframe 9538 9539 this.addListeners(); 9540 return new Promise((resolve, reject) => { 9541 // Expand the iframe to the full size of the content 9542 this.expand(); 9543 9544 if (this.settings.forceRight) { 9545 this.element.style.marginLeft = this.width() + "px"; 9546 } 9547 9548 resolve(); 9549 }); 9550 }.bind(this), function (e) { 9551 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.LOAD_ERROR, e); 9552 return new Promise((resolve, reject) => { 9553 reject(e); 9554 }); 9555 }.bind(this)).then(function () { 9556 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.RENDERED, this.section); 9557 }.bind(this)); 9558 } 9559 9560 reset() { 9561 if (this.iframe) { 9562 this.iframe.style.width = "0"; 9563 this.iframe.style.height = "0"; 9564 this._width = 0; 9565 this._height = 0; 9566 this._textWidth = undefined; 9567 this._contentWidth = undefined; 9568 this._textHeight = undefined; 9569 this._contentHeight = undefined; 9570 } 9571 9572 this._needsReframe = true; 9573 } // Determine locks base on settings 9574 9575 9576 size(_width, _height) { 9577 var width = _width || this.settings.width; 9578 var height = _height || this.settings.height; 9579 9580 if (this.layout.name === "pre-paginated") { 9581 this.lock("both", width, height); 9582 } else if (this.settings.axis === "horizontal") { 9583 this.lock("height", width, height); 9584 } else { 9585 this.lock("width", width, height); 9586 } 9587 9588 this.settings.width = width; 9589 this.settings.height = height; 9590 } // Lock an axis to element dimensions, taking borders into account 9591 9592 9593 lock(what, width, height) { 9594 var elBorders = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["borders"])(this.element); 9595 var iframeBorders; 9596 9597 if (this.iframe) { 9598 iframeBorders = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["borders"])(this.iframe); 9599 } else { 9600 iframeBorders = { 9601 width: 0, 9602 height: 0 9603 }; 9604 } 9605 9606 if (what == "width" && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(width)) { 9607 this.lockedWidth = width - elBorders.width - iframeBorders.width; // this.resize(this.lockedWidth, width); // width keeps ratio correct 9608 } 9609 9610 if (what == "height" && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(height)) { 9611 this.lockedHeight = height - elBorders.height - iframeBorders.height; // this.resize(width, this.lockedHeight); 9612 } 9613 9614 if (what === "both" && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(width) && Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(height)) { 9615 this.lockedWidth = width - elBorders.width - iframeBorders.width; 9616 this.lockedHeight = height - elBorders.height - iframeBorders.height; // this.resize(this.lockedWidth, this.lockedHeight); 9617 } 9618 9619 if (this.displayed && this.iframe) { 9620 // this.contents.layout(); 9621 this.expand(); 9622 } 9623 } // Resize a single axis based on content dimensions 9624 9625 9626 expand(force) { 9627 var width = this.lockedWidth; 9628 var height = this.lockedHeight; 9629 var columns; 9630 var textWidth, textHeight; 9631 if (!this.iframe || this._expanding) return; 9632 this._expanding = true; 9633 9634 if (this.layout.name === "pre-paginated") { 9635 width = this.layout.columnWidth; 9636 height = this.layout.height; 9637 } // Expand Horizontally 9638 else if (this.settings.axis === "horizontal") { 9639 // Get the width of the text 9640 width = this.contents.textWidth(); 9641 9642 if (width % this.layout.pageWidth > 0) { 9643 width = Math.ceil(width / this.layout.pageWidth) * this.layout.pageWidth; 9644 } 9645 9646 if (this.settings.forceEvenPages) { 9647 columns = width / this.layout.pageWidth; 9648 9649 if (this.layout.divisor > 1 && this.layout.name === "reflowable" && columns % 2 > 0) { 9650 // add a blank page 9651 width += this.layout.pageWidth; 9652 } 9653 } 9654 } // Expand Vertically 9655 else if (this.settings.axis === "vertical") { 9656 height = this.contents.textHeight(); 9657 9658 if (this.settings.flow === "paginated" && height % this.layout.height > 0) { 9659 height = Math.ceil(height / this.layout.height) * this.layout.height; 9660 } 9661 } // Only Resize if dimensions have changed or 9662 // if Frame is still hidden, so needs reframing 9663 9664 9665 if (this._needsReframe || width != this._width || height != this._height) { 9666 this.reframe(width, height); 9667 } 9668 9669 this._expanding = false; 9670 } 9671 9672 reframe(width, height) { 9673 var size; 9674 9675 if (Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(width)) { 9676 this.element.style.width = width + "px"; 9677 this.iframe.style.width = width + "px"; 9678 this._width = width; 9679 } 9680 9681 if (Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["isNumber"])(height)) { 9682 this.element.style.height = height + "px"; 9683 this.iframe.style.height = height + "px"; 9684 this._height = height; 9685 } 9686 9687 let widthDelta = this.prevBounds ? width - this.prevBounds.width : width; 9688 let heightDelta = this.prevBounds ? height - this.prevBounds.height : height; 9689 size = { 9690 width: width, 9691 height: height, 9692 widthDelta: widthDelta, 9693 heightDelta: heightDelta 9694 }; 9695 this.pane && this.pane.render(); 9696 requestAnimationFrame(() => { 9697 let mark; 9698 9699 for (let m in this.marks) { 9700 if (this.marks.hasOwnProperty(m)) { 9701 mark = this.marks[m]; 9702 this.placeMark(mark.element, mark.range); 9703 } 9704 } 9705 }); 9706 this.onResize(this, size); 9707 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.RESIZED, size); 9708 this.prevBounds = size; 9709 this.elementBounds = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["bounds"])(this.element); 9710 } 9711 9712 load(contents) { 9713 var loading = new _utils_core__WEBPACK_IMPORTED_MODULE_1__["defer"](); 9714 var loaded = loading.promise; 9715 9716 if (!this.iframe) { 9717 loading.reject(new Error("No Iframe Available")); 9718 return loaded; 9719 } 9720 9721 this.iframe.onload = function (event) { 9722 this.onLoad(event, loading); 9723 }.bind(this); 9724 9725 if (this.settings.method === "blobUrl") { 9726 this.blobUrl = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["createBlobUrl"])(contents, "application/xhtml+xml"); 9727 this.iframe.src = this.blobUrl; 9728 this.element.appendChild(this.iframe); 9729 } else if (this.settings.method === "srcdoc") { 9730 this.iframe.srcdoc = contents; 9731 this.element.appendChild(this.iframe); 9732 } else { 9733 this.element.appendChild(this.iframe); 9734 this.document = this.iframe.contentDocument; 9735 9736 if (!this.document) { 9737 loading.reject(new Error("No Document Available")); 9738 return loaded; 9739 } 9740 9741 this.iframe.contentDocument.open(); // For Cordova windows platform 9742 9743 if (window.MSApp && MSApp.execUnsafeLocalFunction) { 9744 var outerThis = this; 9745 MSApp.execUnsafeLocalFunction(function () { 9746 outerThis.iframe.contentDocument.write(contents); 9747 }); 9748 } else { 9749 this.iframe.contentDocument.write(contents); 9750 } 9751 9752 this.iframe.contentDocument.close(); 9753 } 9754 9755 return loaded; 9756 } 9757 9758 onLoad(event, promise) { 9759 this.window = this.iframe.contentWindow; 9760 this.document = this.iframe.contentDocument; 9761 this.contents = new _contents__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"](this.document, this.document.body, this.section.cfiBase, this.section.index); 9762 this.rendering = false; 9763 var link = this.document.querySelector("link[rel='canonical']"); 9764 9765 if (link) { 9766 link.setAttribute("href", this.section.canonical); 9767 } else { 9768 link = this.document.createElement("link"); 9769 link.setAttribute("rel", "canonical"); 9770 link.setAttribute("href", this.section.canonical); 9771 this.document.querySelector("head").appendChild(link); 9772 } 9773 9774 this.contents.on(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].CONTENTS.EXPAND, () => { 9775 if (this.displayed && this.iframe) { 9776 this.expand(); 9777 9778 if (this.contents) { 9779 this.layout.format(this.contents); 9780 } 9781 } 9782 }); 9783 this.contents.on(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].CONTENTS.RESIZE, e => { 9784 if (this.displayed && this.iframe) { 9785 this.expand(); 9786 9787 if (this.contents) { 9788 this.layout.format(this.contents); 9789 } 9790 } 9791 }); 9792 promise.resolve(this.contents); 9793 } 9794 9795 setLayout(layout) { 9796 this.layout = layout; 9797 9798 if (this.contents) { 9799 this.layout.format(this.contents); 9800 this.expand(); 9801 } 9802 } 9803 9804 setAxis(axis) { 9805 this.settings.axis = axis; 9806 9807 if (axis == "horizontal") { 9808 this.element.style.flex = "none"; 9809 } else { 9810 this.element.style.flex = "initial"; 9811 } 9812 9813 this.size(); 9814 } 9815 9816 setWritingMode(mode) { 9817 // this.element.style.writingMode = writingMode; 9818 this.writingMode = mode; 9819 } 9820 9821 addListeners() {//TODO: Add content listeners for expanding 9822 } 9823 9824 removeListeners(layoutFunc) {//TODO: remove content listeners for expanding 9825 } 9826 9827 display(request) { 9828 var displayed = new _utils_core__WEBPACK_IMPORTED_MODULE_1__["defer"](); 9829 9830 if (!this.displayed) { 9831 this.render(request).then(function () { 9832 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.DISPLAYED, this); 9833 this.onDisplayed(this); 9834 this.displayed = true; 9835 displayed.resolve(this); 9836 }.bind(this), function (err) { 9837 displayed.reject(err, this); 9838 }); 9839 } else { 9840 displayed.resolve(this); 9841 } 9842 9843 return displayed.promise; 9844 } 9845 9846 show() { 9847 this.element.style.visibility = "visible"; 9848 9849 if (this.iframe) { 9850 this.iframe.style.visibility = "visible"; // Remind Safari to redraw the iframe 9851 9852 this.iframe.style.transform = "translateZ(0)"; 9853 this.iframe.offsetWidth; 9854 this.iframe.style.transform = null; 9855 } 9856 9857 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.SHOWN, this); 9858 } 9859 9860 hide() { 9861 // this.iframe.style.display = "none"; 9862 this.element.style.visibility = "hidden"; 9863 this.iframe.style.visibility = "hidden"; 9864 this.stopExpanding = true; 9865 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.HIDDEN, this); 9866 } 9867 9868 offset() { 9869 return { 9870 top: this.element.offsetTop, 9871 left: this.element.offsetLeft 9872 }; 9873 } 9874 9875 width() { 9876 return this._width; 9877 } 9878 9879 height() { 9880 return this._height; 9881 } 9882 9883 position() { 9884 return this.element.getBoundingClientRect(); 9885 } 9886 9887 locationOf(target) { 9888 var parentPos = this.iframe.getBoundingClientRect(); 9889 var targetPos = this.contents.locationOf(target, this.settings.ignoreClass); 9890 return { 9891 "left": targetPos.left, 9892 "top": targetPos.top 9893 }; 9894 } 9895 9896 onDisplayed(view) {// Stub, override with a custom functions 9897 } 9898 9899 onResize(view, e) {// Stub, override with a custom functions 9900 } 9901 9902 bounds(force) { 9903 if (force || !this.elementBounds) { 9904 this.elementBounds = Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["bounds"])(this.element); 9905 } 9906 9907 return this.elementBounds; 9908 } 9909 9910 highlight(cfiRange, data = {}, cb, className = "epubjs-hl", styles = {}) { 9911 if (!this.contents) { 9912 return; 9913 } 9914 9915 const attributes = Object.assign({ 9916 "fill": "yellow", 9917 "fill-opacity": "0.3", 9918 "mix-blend-mode": "multiply" 9919 }, styles); 9920 let range = this.contents.range(cfiRange); 9921 9922 let emitter = () => { 9923 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.MARK_CLICKED, cfiRange, data); 9924 }; 9925 9926 data["epubcfi"] = cfiRange; 9927 9928 if (!this.pane) { 9929 this.pane = new marks_pane__WEBPACK_IMPORTED_MODULE_5__["Pane"](this.iframe, this.element); 9930 } 9931 9932 let m = new marks_pane__WEBPACK_IMPORTED_MODULE_5__["Highlight"](range, className, data, attributes); 9933 let h = this.pane.addMark(m); 9934 this.highlights[cfiRange] = { 9935 "mark": h, 9936 "element": h.element, 9937 "listeners": [emitter, cb] 9938 }; 9939 h.element.setAttribute("ref", className); 9940 h.element.addEventListener("click", emitter); 9941 h.element.addEventListener("touchstart", emitter); 9942 9943 if (cb) { 9944 h.element.addEventListener("click", cb); 9945 h.element.addEventListener("touchstart", cb); 9946 } 9947 9948 return h; 9949 } 9950 9951 underline(cfiRange, data = {}, cb, className = "epubjs-ul", styles = {}) { 9952 if (!this.contents) { 9953 return; 9954 } 9955 9956 const attributes = Object.assign({ 9957 "stroke": "black", 9958 "stroke-opacity": "0.3", 9959 "mix-blend-mode": "multiply" 9960 }, styles); 9961 let range = this.contents.range(cfiRange); 9962 9963 let emitter = () => { 9964 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.MARK_CLICKED, cfiRange, data); 9965 }; 9966 9967 data["epubcfi"] = cfiRange; 9968 9969 if (!this.pane) { 9970 this.pane = new marks_pane__WEBPACK_IMPORTED_MODULE_5__["Pane"](this.iframe, this.element); 9971 } 9972 9973 let m = new marks_pane__WEBPACK_IMPORTED_MODULE_5__["Underline"](range, className, data, attributes); 9974 let h = this.pane.addMark(m); 9975 this.underlines[cfiRange] = { 9976 "mark": h, 9977 "element": h.element, 9978 "listeners": [emitter, cb] 9979 }; 9980 h.element.setAttribute("ref", className); 9981 h.element.addEventListener("click", emitter); 9982 h.element.addEventListener("touchstart", emitter); 9983 9984 if (cb) { 9985 h.element.addEventListener("click", cb); 9986 h.element.addEventListener("touchstart", cb); 9987 } 9988 9989 return h; 9990 } 9991 9992 mark(cfiRange, data = {}, cb) { 9993 if (!this.contents) { 9994 return; 9995 } 9996 9997 if (cfiRange in this.marks) { 9998 let item = this.marks[cfiRange]; 9999 return item; 10000 } 10001 10002 let range = this.contents.range(cfiRange); 10003 10004 if (!range) { 10005 return; 10006 } 10007 10008 let container = range.commonAncestorContainer; 10009 let parent = container.nodeType === 1 ? container : container.parentNode; 10010 10011 let emitter = e => { 10012 this.emit(_utils_constants__WEBPACK_IMPORTED_MODULE_4__[/* EVENTS */ "c"].VIEWS.MARK_CLICKED, cfiRange, data); 10013 }; 10014 10015 if (range.collapsed && container.nodeType === 1) { 10016 range = new Range(); 10017 range.selectNodeContents(container); 10018 } else if (range.collapsed) { 10019 // Webkit doesn't like collapsed ranges 10020 range = new Range(); 10021 range.selectNodeContents(parent); 10022 } 10023 10024 let mark = this.document.createElement("a"); 10025 mark.setAttribute("ref", "epubjs-mk"); 10026 mark.style.position = "absolute"; 10027 mark.dataset["epubcfi"] = cfiRange; 10028 10029 if (data) { 10030 Object.keys(data).forEach(key => { 10031 mark.dataset[key] = data[key]; 10032 }); 10033 } 10034 10035 if (cb) { 10036 mark.addEventListener("click", cb); 10037 mark.addEventListener("touchstart", cb); 10038 } 10039 10040 mark.addEventListener("click", emitter); 10041 mark.addEventListener("touchstart", emitter); 10042 this.placeMark(mark, range); 10043 this.element.appendChild(mark); 10044 this.marks[cfiRange] = { 10045 "element": mark, 10046 "range": range, 10047 "listeners": [emitter, cb] 10048 }; 10049 return parent; 10050 } 10051 10052 placeMark(element, range) { 10053 let top, right, left; 10054 10055 if (this.layout.name === "pre-paginated" || this.settings.axis !== "horizontal") { 10056 let pos = range.getBoundingClientRect(); 10057 top = pos.top; 10058 right = pos.right; 10059 } else { 10060 // Element might break columns, so find the left most element 10061 let rects = range.getClientRects(); 10062 let rect; 10063 10064 for (var i = 0; i != rects.length; i++) { 10065 rect = rects[i]; 10066 10067 if (!left || rect.left < left) { 10068 left = rect.left; // right = rect.right; 10069 10070 right = Math.ceil(left / this.layout.props.pageWidth) * this.layout.props.pageWidth - this.layout.gap / 2; 10071 top = rect.top; 10072 } 10073 } 10074 } 10075 10076 element.style.top = `${top}px`; 10077 element.style.left = `${right}px`; 10078 } 10079 10080 unhighlight(cfiRange) { 10081 let item; 10082 10083 if (cfiRange in this.highlights) { 10084 item = this.highlights[cfiRange]; 10085 this.pane.removeMark(item.mark); 10086 item.listeners.forEach(l => { 10087 if (l) { 10088 item.element.removeEventListener("click", l); 10089 item.element.removeEventListener("touchstart", l); 10090 } 10091 10092 ; 10093 }); 10094 delete this.highlights[cfiRange]; 10095 } 10096 } 10097 10098 ununderline(cfiRange) { 10099 let item; 10100 10101 if (cfiRange in this.underlines) { 10102 item = this.underlines[cfiRange]; 10103 this.pane.removeMark(item.mark); 10104 item.listeners.forEach(l => { 10105 if (l) { 10106 item.element.removeEventListener("click", l); 10107 item.element.removeEventListener("touchstart", l); 10108 } 10109 10110 ; 10111 }); 10112 delete this.underlines[cfiRange]; 10113 } 10114 } 10115 10116 unmark(cfiRange) { 10117 let item; 10118 10119 if (cfiRange in this.marks) { 10120 item = this.marks[cfiRange]; 10121 this.element.removeChild(item.element); 10122 item.listeners.forEach(l => { 10123 if (l) { 10124 item.element.removeEventListener("click", l); 10125 item.element.removeEventListener("touchstart", l); 10126 } 10127 10128 ; 10129 }); 10130 delete this.marks[cfiRange]; 10131 } 10132 } 10133 10134 destroy() { 10135 for (let cfiRange in this.highlights) { 10136 this.unhighlight(cfiRange); 10137 } 10138 10139 for (let cfiRange in this.underlines) { 10140 this.ununderline(cfiRange); 10141 } 10142 10143 for (let cfiRange in this.marks) { 10144 this.unmark(cfiRange); 10145 } 10146 10147 if (this.blobUrl) { 10148 Object(_utils_core__WEBPACK_IMPORTED_MODULE_1__["revokeBlobUrl"])(this.blobUrl); 10149 } 10150 10151 if (this.displayed) { 10152 this.displayed = false; 10153 this.removeListeners(); 10154 this.contents.destroy(); 10155 this.stopExpanding = true; 10156 this.element.removeChild(this.iframe); 10157 10158 if (this.pane) { 10159 this.pane.element.remove(); 10160 this.pane = undefined; 10161 } 10162 10163 this.iframe = undefined; 10164 this.contents = undefined; 10165 this._textWidth = null; 10166 this._textHeight = null; 10167 this._width = null; 10168 this._height = null; 10169 } // this.element.style.height = "0px"; 10170 // this.element.style.width = "0px"; 10171 10172 } 10173 10174 } 10175 10176 event_emitter__WEBPACK_IMPORTED_MODULE_0___default()(IframeView.prototype); 10177 /* harmony default export */ __webpack_exports__["a"] = (IframeView); 10178 10179 /***/ }), 10180 /* 21 */ 10181 /***/ (function(module, exports, __webpack_require__) { 10182 10183 var isObject = __webpack_require__(19), 10184 now = __webpack_require__(51), 10185 toNumber = __webpack_require__(53); 10186 10187 /** Error message constants. */ 10188 var FUNC_ERROR_TEXT = 'Expected a function'; 10189 10190 /* Built-in method references for those with the same name as other `lodash` methods. */ 10191 var nativeMax = Math.max, 10192 nativeMin = Math.min; 10193 10194 /** 10195 * Creates a debounced function that delays invoking `func` until after `wait` 10196 * milliseconds have elapsed since the last time the debounced function was 10197 * invoked. The debounced function comes with a `cancel` method to cancel 10198 * delayed `func` invocations and a `flush` method to immediately invoke them. 10199 * Provide `options` to indicate whether `func` should be invoked on the 10200 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked 10201 * with the last arguments provided to the debounced function. Subsequent 10202 * calls to the debounced function return the result of the last `func` 10203 * invocation. 10204 * 10205 * **Note:** If `leading` and `trailing` options are `true`, `func` is 10206 * invoked on the trailing edge of the timeout only if the debounced function 10207 * is invoked more than once during the `wait` timeout. 10208 * 10209 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred 10210 * until to the next tick, similar to `setTimeout` with a timeout of `0`. 10211 * 10212 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) 10213 * for details over the differences between `_.debounce` and `_.throttle`. 10214 * 10215 * @static 10216 * @memberOf _ 10217 * @since 0.1.0 10218 * @category Function 10219 * @param {Function} func The function to debounce. 10220 * @param {number} [wait=0] The number of milliseconds to delay. 10221 * @param {Object} [options={}] The options object. 10222 * @param {boolean} [options.leading=false] 10223 * Specify invoking on the leading edge of the timeout. 10224 * @param {number} [options.maxWait] 10225 * The maximum time `func` is allowed to be delayed before it's invoked. 10226 * @param {boolean} [options.trailing=true] 10227 * Specify invoking on the trailing edge of the timeout. 10228 * @returns {Function} Returns the new debounced function. 10229 * @example 10230 * 10231 * // Avoid costly calculations while the window size is in flux. 10232 * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); 10233 * 10234 * // Invoke `sendMail` when clicked, debouncing subsequent calls. 10235 * jQuery(element).on('click', _.debounce(sendMail, 300, { 10236 * 'leading': true, 10237 * 'trailing': false 10238 * })); 10239 * 10240 * // Ensure `batchLog` is invoked once after 1 second of debounced calls. 10241 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); 10242 * var source = new EventSource('/stream'); 10243 * jQuery(source).on('message', debounced); 10244 * 10245 * // Cancel the trailing debounced invocation. 10246 * jQuery(window).on('popstate', debounced.cancel); 10247 */ 10248 function debounce(func, wait, options) { 10249 var lastArgs, 10250 lastThis, 10251 maxWait, 10252 result, 10253 timerId, 10254 lastCallTime, 10255 lastInvokeTime = 0, 10256 leading = false, 10257 maxing = false, 10258 trailing = true; 10259 10260 if (typeof func != 'function') { 10261 throw new TypeError(FUNC_ERROR_TEXT); 10262 } 10263 wait = toNumber(wait) || 0; 10264 if (isObject(options)) { 10265 leading = !!options.leading; 10266 maxing = 'maxWait' in options; 10267 maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; 10268 trailing = 'trailing' in options ? !!options.trailing : trailing; 10269 } 10270 10271 function invokeFunc(time) { 10272 var args = lastArgs, 10273 thisArg = lastThis; 10274 10275 lastArgs = lastThis = undefined; 10276 lastInvokeTime = time; 10277 result = func.apply(thisArg, args); 10278 return result; 10279 } 10280 10281 function leadingEdge(time) { 10282 // Reset any `maxWait` timer. 10283 lastInvokeTime = time; 10284 // Start the timer for the trailing edge. 10285 timerId = setTimeout(timerExpired, wait); 10286 // Invoke the leading edge. 10287 return leading ? invokeFunc(time) : result; 10288 } 10289 10290 function remainingWait(time) { 10291 var timeSinceLastCall = time - lastCallTime, 10292 timeSinceLastInvoke = time - lastInvokeTime, 10293 timeWaiting = wait - timeSinceLastCall; 10294 10295 return maxing 10296 ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) 10297 : timeWaiting; 10298 } 10299 10300 function shouldInvoke(time) { 10301 var timeSinceLastCall = time - lastCallTime, 10302 timeSinceLastInvoke = time - lastInvokeTime; 10303 10304 // Either this is the first call, activity has stopped and we're at the 10305 // trailing edge, the system time has gone backwards and we're treating 10306 // it as the trailing edge, or we've hit the `maxWait` limit. 10307 return (lastCallTime === undefined || (timeSinceLastCall >= wait) || 10308 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); 10309 } 10310 10311 function timerExpired() { 10312 var time = now(); 10313 if (shouldInvoke(time)) { 10314 return trailingEdge(time); 10315 } 10316 // Restart the timer. 10317 timerId = setTimeout(timerExpired, remainingWait(time)); 10318 } 10319 10320 function trailingEdge(time) { 10321 timerId = undefined; 10322 10323 // Only invoke if we have `lastArgs` which means `func` has been 10324 // debounced at least once. 10325 if (trailing && lastArgs) { 10326 return invokeFunc(time); 10327 } 10328 lastArgs = lastThis = undefined; 10329 return result; 10330 } 10331 10332 function cancel() { 10333 if (timerId !== undefined) { 10334 clearTimeout(timerId); 10335 } 10336 lastInvokeTime = 0; 10337 lastArgs = lastCallTime = lastThis = timerId = undefined; 10338 } 10339 10340 function flush() { 10341 return timerId === undefined ? result : trailingEdge(now()); 10342 } 10343 10344 function debounced() { 10345 var time = now(), 10346 isInvoking = shouldInvoke(time); 10347 10348 lastArgs = arguments; 10349 lastThis = this; 10350 lastCallTime = time; 10351 10352 if (isInvoking) { 10353 if (timerId === undefined) { 10354 return leadingEdge(lastCallTime); 10355 } 10356 if (maxing) { 10357 // Handle invocations in a tight loop. 10358 clearTimeout(timerId); 10359 timerId = setTimeout(timerExpired, wait); 10360 return invokeFunc(lastCallTime); 10361 } 10362 } 10363 if (timerId === undefined) { 10364 timerId = setTimeout(timerExpired, wait); 10365 } 10366 return result; 10367 } 10368 debounced.cancel = cancel; 10369 debounced.flush = flush; 10370 return debounced; 10371 } 10372 10373 module.exports = debounce; 10374 10375 10376 /***/ }), 10377 /* 22 */ 10378 /***/ (function(module, __webpack_exports__, __webpack_require__) { 10379 10380 "use strict"; 10381 10382 // EXTERNAL MODULE: ./src/utils/core.js 10383 var core = __webpack_require__(0); 10384 10385 // EXTERNAL MODULE: ./src/managers/default/index.js + 3 modules 10386 var managers_default = __webpack_require__(10); 10387 10388 // EXTERNAL MODULE: ./src/utils/constants.js 10389 var constants = __webpack_require__(1); 10390 10391 // EXTERNAL MODULE: ./node_modules/event-emitter/index.js 10392 var event_emitter = __webpack_require__(3); 10393 var event_emitter_default = /*#__PURE__*/__webpack_require__.n(event_emitter); 10394 10395 // CONCATENATED MODULE: ./src/managers/helpers/snap.js 10396 10397 10398 // easing equations from https://github.com/danro/easing-js/blob/master/easing.js 10399 10400 const PI_D2 = Math.PI / 2; 10401 const EASING_EQUATIONS = { 10402 easeOutSine: function (pos) { 10403 return Math.sin(pos * PI_D2); 10404 }, 10405 easeInOutSine: function (pos) { 10406 return -0.5 * (Math.cos(Math.PI * pos) - 1); 10407 }, 10408 easeInOutQuint: function (pos) { 10409 if ((pos /= 0.5) < 1) { 10410 return 0.5 * Math.pow(pos, 5); 10411 } 10412 10413 return 0.5 * (Math.pow(pos - 2, 5) + 2); 10414 }, 10415 easeInCubic: function (pos) { 10416 return Math.pow(pos, 3); 10417 } 10418 }; 10419 10420 class snap_Snap { 10421 constructor(manager, options) { 10422 this.settings = Object(core["extend"])({ 10423 duration: 80, 10424 minVelocity: 0.2, 10425 minDistance: 10, 10426 easing: EASING_EQUATIONS['easeInCubic'] 10427 }, options || {}); 10428 this.supportsTouch = this.supportsTouch(); 10429 10430 if (this.supportsTouch) { 10431 this.setup(manager); 10432 } 10433 } 10434 10435 setup(manager) { 10436 this.manager = manager; 10437 this.layout = this.manager.layout; 10438 this.fullsize = this.manager.settings.fullsize; 10439 10440 if (this.fullsize) { 10441 this.element = this.manager.stage.element; 10442 this.scroller = window; 10443 this.disableScroll(); 10444 } else { 10445 this.element = this.manager.stage.container; 10446 this.scroller = this.element; 10447 this.element.style["WebkitOverflowScrolling"] = "touch"; 10448 } // this.overflow = this.manager.overflow; 10449 // set lookahead offset to page width 10450 10451 10452 this.manager.settings.offset = this.layout.width; 10453 this.manager.settings.afterScrolledTimeout = this.settings.duration * 2; 10454 this.isVertical = this.manager.settings.axis === "vertical"; // disable snapping if not paginated or axis in not horizontal 10455 10456 if (!this.manager.isPaginated || this.isVertical) { 10457 return; 10458 } 10459 10460 this.touchCanceler = false; 10461 this.resizeCanceler = false; 10462 this.snapping = false; 10463 this.scrollLeft; 10464 this.scrollTop; 10465 this.startTouchX = undefined; 10466 this.startTouchY = undefined; 10467 this.startTime = undefined; 10468 this.endTouchX = undefined; 10469 this.endTouchY = undefined; 10470 this.endTime = undefined; 10471 this.addListeners(); 10472 } 10473 10474 supportsTouch() { 10475 if ('ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch) { 10476 return true; 10477 } 10478 10479 return false; 10480 } 10481 10482 disableScroll() { 10483 this.element.style.overflow = "hidden"; 10484 } 10485 10486 enableScroll() { 10487 this.element.style.overflow = ""; 10488 } 10489 10490 addListeners() { 10491 this._onResize = this.onResize.bind(this); 10492 window.addEventListener('resize', this._onResize); 10493 this._onScroll = this.onScroll.bind(this); 10494 this.scroller.addEventListener('scroll', this._onScroll); 10495 this._onTouchStart = this.onTouchStart.bind(this); 10496 this.scroller.addEventListener('touchstart', this._onTouchStart, { 10497 passive: true 10498 }); 10499 this.on('touchstart', this._onTouchStart); 10500 this._onTouchMove = this.onTouchMove.bind(this); 10501 this.scroller.addEventListener('touchmove', this._onTouchMove, { 10502 passive: true 10503 }); 10504 this.on('touchmove', this._onTouchMove); 10505 this._onTouchEnd = this.onTouchEnd.bind(this); 10506 this.scroller.addEventListener('touchend', this._onTouchEnd, { 10507 passive: true 10508 }); 10509 this.on('touchend', this._onTouchEnd); 10510 this._afterDisplayed = this.afterDisplayed.bind(this); 10511 this.manager.on(constants["c" /* EVENTS */].MANAGERS.ADDED, this._afterDisplayed); 10512 } 10513 10514 removeListeners() { 10515 window.removeEventListener('resize', this._onResize); 10516 this._onResize = undefined; 10517 this.scroller.removeEventListener('scroll', this._onScroll); 10518 this._onScroll = undefined; 10519 this.scroller.removeEventListener('touchstart', this._onTouchStart, { 10520 passive: true 10521 }); 10522 this.off('touchstart', this._onTouchStart); 10523 this._onTouchStart = undefined; 10524 this.scroller.removeEventListener('touchmove', this._onTouchMove, { 10525 passive: true 10526 }); 10527 this.off('touchmove', this._onTouchMove); 10528 this._onTouchMove = undefined; 10529 this.scroller.removeEventListener('touchend', this._onTouchEnd, { 10530 passive: true 10531 }); 10532 this.off('touchend', this._onTouchEnd); 10533 this._onTouchEnd = undefined; 10534 this.manager.off(constants["c" /* EVENTS */].MANAGERS.ADDED, this._afterDisplayed); 10535 this._afterDisplayed = undefined; 10536 } 10537 10538 afterDisplayed(view) { 10539 let contents = view.contents; 10540 ["touchstart", "touchmove", "touchend"].forEach(e => { 10541 contents.on(e, ev => this.triggerViewEvent(ev, contents)); 10542 }); 10543 } 10544 10545 triggerViewEvent(e, contents) { 10546 this.emit(e.type, e, contents); 10547 } 10548 10549 onScroll(e) { 10550 this.scrollLeft = this.fullsize ? window.scrollX : this.scroller.scrollLeft; 10551 this.scrollTop = this.fullsize ? window.scrollY : this.scroller.scrollTop; 10552 } 10553 10554 onResize(e) { 10555 this.resizeCanceler = true; 10556 } 10557 10558 onTouchStart(e) { 10559 let { 10560 screenX, 10561 screenY 10562 } = e.touches[0]; 10563 10564 if (this.fullsize) { 10565 this.enableScroll(); 10566 } 10567 10568 this.touchCanceler = true; 10569 10570 if (!this.startTouchX) { 10571 this.startTouchX = screenX; 10572 this.startTouchY = screenY; 10573 this.startTime = this.now(); 10574 } 10575 10576 this.endTouchX = screenX; 10577 this.endTouchY = screenY; 10578 this.endTime = this.now(); 10579 } 10580 10581 onTouchMove(e) { 10582 let { 10583 screenX, 10584 screenY 10585 } = e.touches[0]; 10586 let deltaY = Math.abs(screenY - this.endTouchY); 10587 this.touchCanceler = true; 10588 10589 if (!this.fullsize && deltaY < 10) { 10590 this.element.scrollLeft -= screenX - this.endTouchX; 10591 } 10592 10593 this.endTouchX = screenX; 10594 this.endTouchY = screenY; 10595 this.endTime = this.now(); 10596 } 10597 10598 onTouchEnd(e) { 10599 if (this.fullsize) { 10600 this.disableScroll(); 10601 } 10602 10603 this.touchCanceler = false; 10604 let swipped = this.wasSwiped(); 10605 10606 if (swipped !== 0) { 10607 this.snap(swipped); 10608 } else { 10609 this.snap(); 10610 } 10611 10612 this.startTouchX = undefined; 10613 this.startTouchY = undefined; 10614 this.startTime = undefined; 10615 this.endTouchX = undefined; 10616 this.endTouchY = undefined; 10617 this.endTime = undefined; 10618 } 10619 10620 wasSwiped() { 10621 let snapWidth = this.layout.pageWidth * this.layout.divisor; 10622 let distance = this.endTouchX - this.startTouchX; 10623 let absolute = Math.abs(distance); 10624 let time = this.endTime - this.startTime; 10625 let velocity = distance / time; 10626 let minVelocity = this.settings.minVelocity; 10627 10628 if (absolute <= this.settings.minDistance || absolute >= snapWidth) { 10629 return 0; 10630 } 10631 10632 if (velocity > minVelocity) { 10633 // previous 10634 return -1; 10635 } else if (velocity < -minVelocity) { 10636 // next 10637 return 1; 10638 } 10639 } 10640 10641 needsSnap() { 10642 let left = this.scrollLeft; 10643 let snapWidth = this.layout.pageWidth * this.layout.divisor; 10644 return left % snapWidth !== 0; 10645 } 10646 10647 snap(howMany = 0) { 10648 let left = this.scrollLeft; 10649 let snapWidth = this.layout.pageWidth * this.layout.divisor; 10650 let snapTo = Math.round(left / snapWidth) * snapWidth; 10651 10652 if (howMany) { 10653 snapTo += howMany * snapWidth; 10654 } 10655 10656 return this.smoothScrollTo(snapTo); 10657 } 10658 10659 smoothScrollTo(destination) { 10660 const deferred = new core["defer"](); 10661 const start = this.scrollLeft; 10662 const startTime = this.now(); 10663 const duration = this.settings.duration; 10664 const easing = this.settings.easing; 10665 this.snapping = true; // add animation loop 10666 10667 function tick() { 10668 const now = this.now(); 10669 const time = Math.min(1, (now - startTime) / duration); 10670 const timeFunction = easing(time); 10671 10672 if (this.touchCanceler || this.resizeCanceler) { 10673 this.resizeCanceler = false; 10674 this.snapping = false; 10675 deferred.resolve(); 10676 return; 10677 } 10678 10679 if (time < 1) { 10680 window.requestAnimationFrame(tick.bind(this)); 10681 this.scrollTo(start + (destination - start) * time, 0); 10682 } else { 10683 this.scrollTo(destination, 0); 10684 this.snapping = false; 10685 deferred.resolve(); 10686 } 10687 } 10688 10689 tick.call(this); 10690 return deferred.promise; 10691 } 10692 10693 scrollTo(left = 0, top = 0) { 10694 if (this.fullsize) { 10695 window.scroll(left, top); 10696 } else { 10697 this.scroller.scrollLeft = left; 10698 this.scroller.scrollTop = top; 10699 } 10700 } 10701 10702 now() { 10703 return 'now' in window.performance ? performance.now() : new Date().getTime(); 10704 } 10705 10706 destroy() { 10707 if (!this.scroller) { 10708 return; 10709 } 10710 10711 if (this.fullsize) { 10712 this.enableScroll(); 10713 } 10714 10715 this.removeListeners(); 10716 this.scroller = undefined; 10717 } 10718 10719 } 10720 10721 event_emitter_default()(snap_Snap.prototype); 10722 /* harmony default export */ var snap = (snap_Snap); 10723 // EXTERNAL MODULE: ./node_modules/lodash/debounce.js 10724 var debounce = __webpack_require__(21); 10725 var debounce_default = /*#__PURE__*/__webpack_require__.n(debounce); 10726 10727 // CONCATENATED MODULE: ./src/managers/continuous/index.js 10728 10729 10730 10731 10732 10733 10734 class continuous_ContinuousViewManager extends managers_default["a" /* default */] { 10735 constructor(options) { 10736 super(options); 10737 this.name = "continuous"; 10738 this.settings = Object(core["extend"])(this.settings || {}, { 10739 infinite: true, 10740 overflow: undefined, 10741 axis: undefined, 10742 writingMode: undefined, 10743 flow: "scrolled", 10744 offset: 500, 10745 offsetDelta: 250, 10746 width: undefined, 10747 height: undefined, 10748 snap: false, 10749 afterScrolledTimeout: 10, 10750 allowScriptedContent: false, 10751 allowPopups: false 10752 }); 10753 Object(core["extend"])(this.settings, options.settings || {}); // Gap can be 0, but defaults doesn't handle that 10754 10755 if (options.settings.gap != "undefined" && options.settings.gap === 0) { 10756 this.settings.gap = options.settings.gap; 10757 } 10758 10759 this.viewSettings = { 10760 ignoreClass: this.settings.ignoreClass, 10761 axis: this.settings.axis, 10762 flow: this.settings.flow, 10763 layout: this.layout, 10764 width: 0, 10765 height: 0, 10766 forceEvenPages: false, 10767 allowScriptedContent: this.settings.allowScriptedContent, 10768 allowPopups: this.settings.allowPopups 10769 }; 10770 this.scrollTop = 0; 10771 this.scrollLeft = 0; 10772 } 10773 10774 display(section, target) { 10775 return managers_default["a" /* default */].prototype.display.call(this, section, target).then(function () { 10776 return this.fill(); 10777 }.bind(this)); 10778 } 10779 10780 fill(_full) { 10781 var full = _full || new core["defer"](); 10782 this.q.enqueue(() => { 10783 return this.check(); 10784 }).then(result => { 10785 if (result) { 10786 this.fill(full); 10787 } else { 10788 full.resolve(); 10789 } 10790 }); 10791 return full.promise; 10792 } 10793 10794 moveTo(offset) { 10795 // var bounds = this.stage.bounds(); 10796 // var dist = Math.floor(offset.top / bounds.height) * bounds.height; 10797 var distX = 0, 10798 distY = 0; 10799 var offsetX = 0, 10800 offsetY = 0; 10801 10802 if (!this.isPaginated) { 10803 distY = offset.top; 10804 offsetY = offset.top + this.settings.offsetDelta; 10805 } else { 10806 distX = Math.floor(offset.left / this.layout.delta) * this.layout.delta; 10807 offsetX = distX + this.settings.offsetDelta; 10808 } 10809 10810 if (distX > 0 || distY > 0) { 10811 this.scrollBy(distX, distY, true); 10812 } 10813 } 10814 10815 afterResized(view) { 10816 this.emit(constants["c" /* EVENTS */].MANAGERS.RESIZE, view.section); 10817 } // Remove Previous Listeners if present 10818 10819 10820 removeShownListeners(view) { 10821 // view.off("shown", this.afterDisplayed); 10822 // view.off("shown", this.afterDisplayedAbove); 10823 view.onDisplayed = function () {}; 10824 } 10825 10826 add(section) { 10827 var view = this.createView(section); 10828 this.views.append(view); 10829 view.on(constants["c" /* EVENTS */].VIEWS.RESIZED, bounds => { 10830 view.expanded = true; 10831 }); 10832 view.on(constants["c" /* EVENTS */].VIEWS.AXIS, axis => { 10833 this.updateAxis(axis); 10834 }); 10835 view.on(constants["c" /* EVENTS */].VIEWS.WRITING_MODE, mode => { 10836 this.updateWritingMode(mode); 10837 }); // view.on(EVENTS.VIEWS.SHOWN, this.afterDisplayed.bind(this)); 10838 10839 view.onDisplayed = this.afterDisplayed.bind(this); 10840 view.onResize = this.afterResized.bind(this); 10841 return view.display(this.request); 10842 } 10843 10844 append(section) { 10845 var view = this.createView(section); 10846 view.on(constants["c" /* EVENTS */].VIEWS.RESIZED, bounds => { 10847 view.expanded = true; 10848 }); 10849 view.on(constants["c" /* EVENTS */].VIEWS.AXIS, axis => { 10850 this.updateAxis(axis); 10851 }); 10852 view.on(constants["c" /* EVENTS */].VIEWS.WRITING_MODE, mode => { 10853 this.updateWritingMode(mode); 10854 }); 10855 this.views.append(view); 10856 view.onDisplayed = this.afterDisplayed.bind(this); 10857 return view; 10858 } 10859 10860 prepend(section) { 10861 var view = this.createView(section); 10862 view.on(constants["c" /* EVENTS */].VIEWS.RESIZED, bounds => { 10863 this.counter(bounds); 10864 view.expanded = true; 10865 }); 10866 view.on(constants["c" /* EVENTS */].VIEWS.AXIS, axis => { 10867 this.updateAxis(axis); 10868 }); 10869 view.on(constants["c" /* EVENTS */].VIEWS.WRITING_MODE, mode => { 10870 this.updateWritingMode(mode); 10871 }); 10872 this.views.prepend(view); 10873 view.onDisplayed = this.afterDisplayed.bind(this); 10874 return view; 10875 } 10876 10877 counter(bounds) { 10878 if (this.settings.axis === "vertical") { 10879 this.scrollBy(0, bounds.heightDelta, true); 10880 } else { 10881 this.scrollBy(bounds.widthDelta, 0, true); 10882 } 10883 } 10884 10885 update(_offset) { 10886 var container = this.bounds(); 10887 var views = this.views.all(); 10888 var viewsLength = views.length; 10889 var visible = []; 10890 var offset = typeof _offset != "undefined" ? _offset : this.settings.offset || 0; 10891 var isVisible; 10892 var view; 10893 var updating = new core["defer"](); 10894 var promises = []; 10895 10896 for (var i = 0; i < viewsLength; i++) { 10897 view = views[i]; 10898 isVisible = this.isVisible(view, offset, offset, container); 10899 10900 if (isVisible === true) { 10901 // console.log("visible " + view.index, view.displayed); 10902 if (!view.displayed) { 10903 let displayed = view.display(this.request).then(function (view) { 10904 view.show(); 10905 }, err => { 10906 view.hide(); 10907 }); 10908 promises.push(displayed); 10909 } else { 10910 view.show(); 10911 } 10912 10913 visible.push(view); 10914 } else { 10915 this.q.enqueue(view.destroy.bind(view)); // console.log("hidden " + view.index, view.displayed); 10916 10917 clearTimeout(this.trimTimeout); 10918 this.trimTimeout = setTimeout(function () { 10919 this.q.enqueue(this.trim.bind(this)); 10920 }.bind(this), 250); 10921 } 10922 } 10923 10924 if (promises.length) { 10925 return Promise.all(promises).catch(err => { 10926 updating.reject(err); 10927 }); 10928 } else { 10929 updating.resolve(); 10930 return updating.promise; 10931 } 10932 } 10933 10934 check(_offsetLeft, _offsetTop) { 10935 var checking = new core["defer"](); 10936 var newViews = []; 10937 var horizontal = this.settings.axis === "horizontal"; 10938 var delta = this.settings.offset || 0; 10939 10940 if (_offsetLeft && horizontal) { 10941 delta = _offsetLeft; 10942 } 10943 10944 if (_offsetTop && !horizontal) { 10945 delta = _offsetTop; 10946 } 10947 10948 var bounds = this._bounds; // bounds saved this until resize 10949 10950 let offset = horizontal ? this.scrollLeft : this.scrollTop; 10951 let visibleLength = horizontal ? Math.floor(bounds.width) : bounds.height; 10952 let contentLength = horizontal ? this.container.scrollWidth : this.container.scrollHeight; 10953 let writingMode = this.writingMode && this.writingMode.indexOf("vertical") === 0 ? "vertical" : "horizontal"; 10954 let rtlScrollType = this.settings.rtlScrollType; 10955 let rtl = this.settings.direction === "rtl"; 10956 10957 if (!this.settings.fullsize) { 10958 // Scroll offset starts at width of element 10959 if (rtl && rtlScrollType === "default" && writingMode === "horizontal") { 10960 offset = contentLength - visibleLength - offset; 10961 } // Scroll offset starts at 0 and goes negative 10962 10963 10964 if (rtl && rtlScrollType === "negative" && writingMode === "horizontal") { 10965 offset = offset * -1; 10966 } 10967 } else { 10968 // Scroll offset starts at 0 and goes negative 10969 if (horizontal && rtl && rtlScrollType === "negative" || !horizontal && rtl && rtlScrollType === "default") { 10970 offset = offset * -1; 10971 } 10972 } 10973 10974 let prepend = () => { 10975 let first = this.views.first(); 10976 let prev = first && first.section.prev(); 10977 10978 if (prev) { 10979 newViews.push(this.prepend(prev)); 10980 } 10981 }; 10982 10983 let append = () => { 10984 let last = this.views.last(); 10985 let next = last && last.section.next(); 10986 10987 if (next) { 10988 newViews.push(this.append(next)); 10989 } 10990 }; 10991 10992 let end = offset + visibleLength + delta; 10993 let start = offset - delta; 10994 10995 if (end >= contentLength) { 10996 append(); 10997 } 10998 10999 if (start < 0) { 11000 prepend(); 11001 } 11002 11003 let promises = newViews.map(view => { 11004 return view.display(this.request); 11005 }); 11006 11007 if (newViews.length) { 11008 return Promise.all(promises).then(() => { 11009 return this.check(); 11010 }).then(() => { 11011 // Check to see if anything new is on screen after rendering 11012 return this.update(delta); 11013 }, err => { 11014 return err; 11015 }); 11016 } else { 11017 this.q.enqueue(function () { 11018 this.update(); 11019 }.bind(this)); 11020 checking.resolve(false); 11021 return checking.promise; 11022 } 11023 } 11024 11025 trim() { 11026 var task = new core["defer"](); 11027 var displayed = this.views.displayed(); 11028 var first = displayed[0]; 11029 var last = displayed[displayed.length - 1]; 11030 var firstIndex = this.views.indexOf(first); 11031 var lastIndex = this.views.indexOf(last); 11032 var above = this.views.slice(0, firstIndex); 11033 var below = this.views.slice(lastIndex + 1); // Erase all but last above 11034 11035 for (var i = 0; i < above.length - 1; i++) { 11036 this.erase(above[i], above); 11037 } // Erase all except first below 11038 11039 11040 for (var j = 1; j < below.length; j++) { 11041 this.erase(below[j]); 11042 } 11043 11044 task.resolve(); 11045 return task.promise; 11046 } 11047 11048 erase(view, above) { 11049 //Trim 11050 var prevTop; 11051 var prevLeft; 11052 11053 if (!this.settings.fullsize) { 11054 prevTop = this.container.scrollTop; 11055 prevLeft = this.container.scrollLeft; 11056 } else { 11057 prevTop = window.scrollY; 11058 prevLeft = window.scrollX; 11059 } 11060 11061 var bounds = view.bounds(); 11062 this.views.remove(view); 11063 11064 if (above) { 11065 if (this.settings.axis === "vertical") { 11066 this.scrollTo(0, prevTop - bounds.height, true); 11067 } else { 11068 if (this.settings.direction === 'rtl') { 11069 if (!this.settings.fullsize) { 11070 this.scrollTo(prevLeft, 0, true); 11071 } else { 11072 this.scrollTo(prevLeft + Math.floor(bounds.width), 0, true); 11073 } 11074 } else { 11075 this.scrollTo(prevLeft - Math.floor(bounds.width), 0, true); 11076 } 11077 } 11078 } 11079 } 11080 11081 addEventListeners(stage) { 11082 window.addEventListener("unload", function (e) { 11083 this.ignore = true; // this.scrollTo(0,0); 11084 11085 this.destroy(); 11086 }.bind(this)); 11087 this.addScrollListeners(); 11088 11089 if (this.isPaginated && this.settings.snap) { 11090 this.snapper = new snap(this, this.settings.snap && typeof this.settings.snap === "object" && this.settings.snap); 11091 } 11092 } 11093 11094 addScrollListeners() { 11095 var scroller; 11096 this.tick = core["requestAnimationFrame"]; 11097 let dir = this.settings.direction === "rtl" && this.settings.rtlScrollType === "default" ? -1 : 1; 11098 this.scrollDeltaVert = 0; 11099 this.scrollDeltaHorz = 0; 11100 11101 if (!this.settings.fullsize) { 11102 scroller = this.container; 11103 this.scrollTop = this.container.scrollTop; 11104 this.scrollLeft = this.container.scrollLeft; 11105 } else { 11106 scroller = window; 11107 this.scrollTop = window.scrollY * dir; 11108 this.scrollLeft = window.scrollX * dir; 11109 } 11110 11111 this._onScroll = this.onScroll.bind(this); 11112 scroller.addEventListener("scroll", this._onScroll); 11113 this._scrolled = debounce_default()(this.scrolled.bind(this), 30); // this.tick.call(window, this.onScroll.bind(this)); 11114 11115 this.didScroll = false; 11116 } 11117 11118 removeEventListeners() { 11119 var scroller; 11120 11121 if (!this.settings.fullsize) { 11122 scroller = this.container; 11123 } else { 11124 scroller = window; 11125 } 11126 11127 scroller.removeEventListener("scroll", this._onScroll); 11128 this._onScroll = undefined; 11129 } 11130 11131 onScroll() { 11132 let scrollTop; 11133 let scrollLeft; 11134 let dir = this.settings.direction === "rtl" && this.settings.rtlScrollType === "default" ? -1 : 1; 11135 11136 if (!this.settings.fullsize) { 11137 scrollTop = this.container.scrollTop; 11138 scrollLeft = this.container.scrollLeft; 11139 } else { 11140 scrollTop = window.scrollY * dir; 11141 scrollLeft = window.scrollX * dir; 11142 } 11143 11144 this.scrollTop = scrollTop; 11145 this.scrollLeft = scrollLeft; 11146 11147 if (!this.ignore) { 11148 this._scrolled(); 11149 } else { 11150 this.ignore = false; 11151 } 11152 11153 this.scrollDeltaVert += Math.abs(scrollTop - this.prevScrollTop); 11154 this.scrollDeltaHorz += Math.abs(scrollLeft - this.prevScrollLeft); 11155 this.prevScrollTop = scrollTop; 11156 this.prevScrollLeft = scrollLeft; 11157 clearTimeout(this.scrollTimeout); 11158 this.scrollTimeout = setTimeout(function () { 11159 this.scrollDeltaVert = 0; 11160 this.scrollDeltaHorz = 0; 11161 }.bind(this), 150); 11162 clearTimeout(this.afterScrolled); 11163 this.didScroll = false; 11164 } 11165 11166 scrolled() { 11167 this.q.enqueue(function () { 11168 return this.check(); 11169 }.bind(this)); 11170 this.emit(constants["c" /* EVENTS */].MANAGERS.SCROLL, { 11171 top: this.scrollTop, 11172 left: this.scrollLeft 11173 }); 11174 clearTimeout(this.afterScrolled); 11175 this.afterScrolled = setTimeout(function () { 11176 // Don't report scroll if we are about the snap 11177 if (this.snapper && this.snapper.supportsTouch && this.snapper.needsSnap()) { 11178 return; 11179 } 11180 11181 this.emit(constants["c" /* EVENTS */].MANAGERS.SCROLLED, { 11182 top: this.scrollTop, 11183 left: this.scrollLeft 11184 }); 11185 }.bind(this), this.settings.afterScrolledTimeout); 11186 } 11187 11188 next() { 11189 let delta = this.layout.props.name === "pre-paginated" && this.layout.props.spread ? this.layout.props.delta * 2 : this.layout.props.delta; 11190 if (!this.views.length) return; 11191 11192 if (this.isPaginated && this.settings.axis === "horizontal") { 11193 this.scrollBy(delta, 0, true); 11194 } else { 11195 this.scrollBy(0, this.layout.height, true); 11196 } 11197 11198 this.q.enqueue(function () { 11199 return this.check(); 11200 }.bind(this)); 11201 } 11202 11203 prev() { 11204 let delta = this.layout.props.name === "pre-paginated" && this.layout.props.spread ? this.layout.props.delta * 2 : this.layout.props.delta; 11205 if (!this.views.length) return; 11206 11207 if (this.isPaginated && this.settings.axis === "horizontal") { 11208 this.scrollBy(-delta, 0, true); 11209 } else { 11210 this.scrollBy(0, -this.layout.height, true); 11211 } 11212 11213 this.q.enqueue(function () { 11214 return this.check(); 11215 }.bind(this)); 11216 } 11217 11218 updateFlow(flow) { 11219 if (this.rendered && this.snapper) { 11220 this.snapper.destroy(); 11221 this.snapper = undefined; 11222 } 11223 11224 super.updateFlow(flow, "scroll"); 11225 11226 if (this.rendered && this.isPaginated && this.settings.snap) { 11227 this.snapper = new snap(this, this.settings.snap && typeof this.settings.snap === "object" && this.settings.snap); 11228 } 11229 } 11230 11231 destroy() { 11232 super.destroy(); 11233 11234 if (this.snapper) { 11235 this.snapper.destroy(); 11236 } 11237 } 11238 11239 } 11240 11241 /* harmony default export */ var continuous = __webpack_exports__["a"] = (continuous_ContinuousViewManager); 11242 11243 /***/ }), 11244 /* 23 */ 11245 /***/ (function(module, exports, __webpack_require__) { 11246 11247 /* WEBPACK VAR INJECTION */(function(global) {var require;var require;/*! 11248 localForage -- Offline Storage, Improved 11249 Version 1.10.0 11250 https://localforage.github.io/localForage 11251 (c) 2013-2017 Mozilla, Apache License 2.0 11252 */ 11253 (function(f){if(true){module.exports=f()}else { var g; }})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return require(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw (f.code="MODULE_NOT_FOUND", f)}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ 11254 (function (global){ 11255 'use strict'; 11256 var Mutation = global.MutationObserver || global.WebKitMutationObserver; 11257 11258 var scheduleDrain; 11259 11260 { 11261 if (Mutation) { 11262 var called = 0; 11263 var observer = new Mutation(nextTick); 11264 var element = global.document.createTextNode(''); 11265 observer.observe(element, { 11266 characterData: true 11267 }); 11268 scheduleDrain = function () { 11269 element.data = (called = ++called % 2); 11270 }; 11271 } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { 11272 var channel = new global.MessageChannel(); 11273 channel.port1.onmessage = nextTick; 11274 scheduleDrain = function () { 11275 channel.port2.postMessage(0); 11276 }; 11277 } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { 11278 scheduleDrain = function () { 11279 11280 // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted 11281 // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called. 11282 var scriptEl = global.document.createElement('script'); 11283 scriptEl.onreadystatechange = function () { 11284 nextTick(); 11285 11286 scriptEl.onreadystatechange = null; 11287 scriptEl.parentNode.removeChild(scriptEl); 11288 scriptEl = null; 11289 }; 11290 global.document.documentElement.appendChild(scriptEl); 11291 }; 11292 } else { 11293 scheduleDrain = function () { 11294 setTimeout(nextTick, 0); 11295 }; 11296 } 11297 } 11298 11299 var draining; 11300 var queue = []; 11301 //named nextTick for less confusing stack traces 11302 function nextTick() { 11303 draining = true; 11304 var i, oldQueue; 11305 var len = queue.length; 11306 while (len) { 11307 oldQueue = queue; 11308 queue = []; 11309 i = -1; 11310 while (++i < len) { 11311 oldQueue[i](); 11312 } 11313 len = queue.length; 11314 } 11315 draining = false; 11316 } 11317 11318 module.exports = immediate; 11319 function immediate(task) { 11320 if (queue.push(task) === 1 && !draining) { 11321 scheduleDrain(); 11322 } 11323 } 11324 11325 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 11326 },{}],2:[function(_dereq_,module,exports){ 11327 'use strict'; 11328 var immediate = _dereq_(1); 11329 11330 /* istanbul ignore next */ 11331 function INTERNAL() {} 11332 11333 var handlers = {}; 11334 11335 var REJECTED = ['REJECTED']; 11336 var FULFILLED = ['FULFILLED']; 11337 var PENDING = ['PENDING']; 11338 11339 module.exports = Promise; 11340 11341 function Promise(resolver) { 11342 if (typeof resolver !== 'function') { 11343 throw new TypeError('resolver must be a function'); 11344 } 11345 this.state = PENDING; 11346 this.queue = []; 11347 this.outcome = void 0; 11348 if (resolver !== INTERNAL) { 11349 safelyResolveThenable(this, resolver); 11350 } 11351 } 11352 11353 Promise.prototype["catch"] = function (onRejected) { 11354 return this.then(null, onRejected); 11355 }; 11356 Promise.prototype.then = function (onFulfilled, onRejected) { 11357 if (typeof onFulfilled !== 'function' && this.state === FULFILLED || 11358 typeof onRejected !== 'function' && this.state === REJECTED) { 11359 return this; 11360 } 11361 var promise = new this.constructor(INTERNAL); 11362 if (this.state !== PENDING) { 11363 var resolver = this.state === FULFILLED ? onFulfilled : onRejected; 11364 unwrap(promise, resolver, this.outcome); 11365 } else { 11366 this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); 11367 } 11368 11369 return promise; 11370 }; 11371 function QueueItem(promise, onFulfilled, onRejected) { 11372 this.promise = promise; 11373 if (typeof onFulfilled === 'function') { 11374 this.onFulfilled = onFulfilled; 11375 this.callFulfilled = this.otherCallFulfilled; 11376 } 11377 if (typeof onRejected === 'function') { 11378 this.onRejected = onRejected; 11379 this.callRejected = this.otherCallRejected; 11380 } 11381 } 11382 QueueItem.prototype.callFulfilled = function (value) { 11383 handlers.resolve(this.promise, value); 11384 }; 11385 QueueItem.prototype.otherCallFulfilled = function (value) { 11386 unwrap(this.promise, this.onFulfilled, value); 11387 }; 11388 QueueItem.prototype.callRejected = function (value) { 11389 handlers.reject(this.promise, value); 11390 }; 11391 QueueItem.prototype.otherCallRejected = function (value) { 11392 unwrap(this.promise, this.onRejected, value); 11393 }; 11394 11395 function unwrap(promise, func, value) { 11396 immediate(function () { 11397 var returnValue; 11398 try { 11399 returnValue = func(value); 11400 } catch (e) { 11401 return handlers.reject(promise, e); 11402 } 11403 if (returnValue === promise) { 11404 handlers.reject(promise, new TypeError('Cannot resolve promise with itself')); 11405 } else { 11406 handlers.resolve(promise, returnValue); 11407 } 11408 }); 11409 } 11410 11411 handlers.resolve = function (self, value) { 11412 var result = tryCatch(getThen, value); 11413 if (result.status === 'error') { 11414 return handlers.reject(self, result.value); 11415 } 11416 var thenable = result.value; 11417 11418 if (thenable) { 11419 safelyResolveThenable(self, thenable); 11420 } else { 11421 self.state = FULFILLED; 11422 self.outcome = value; 11423 var i = -1; 11424 var len = self.queue.length; 11425 while (++i < len) { 11426 self.queue[i].callFulfilled(value); 11427 } 11428 } 11429 return self; 11430 }; 11431 handlers.reject = function (self, error) { 11432 self.state = REJECTED; 11433 self.outcome = error; 11434 var i = -1; 11435 var len = self.queue.length; 11436 while (++i < len) { 11437 self.queue[i].callRejected(error); 11438 } 11439 return self; 11440 }; 11441 11442 function getThen(obj) { 11443 // Make sure we only access the accessor once as required by the spec 11444 var then = obj && obj.then; 11445 if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') { 11446 return function appyThen() { 11447 then.apply(obj, arguments); 11448 }; 11449 } 11450 } 11451 11452 function safelyResolveThenable(self, thenable) { 11453 // Either fulfill, reject or reject with error 11454 var called = false; 11455 function onError(value) { 11456 if (called) { 11457 return; 11458 } 11459 called = true; 11460 handlers.reject(self, value); 11461 } 11462 11463 function onSuccess(value) { 11464 if (called) { 11465 return; 11466 } 11467 called = true; 11468 handlers.resolve(self, value); 11469 } 11470 11471 function tryToUnwrap() { 11472 thenable(onSuccess, onError); 11473 } 11474 11475 var result = tryCatch(tryToUnwrap); 11476 if (result.status === 'error') { 11477 onError(result.value); 11478 } 11479 } 11480 11481 function tryCatch(func, value) { 11482 var out = {}; 11483 try { 11484 out.value = func(value); 11485 out.status = 'success'; 11486 } catch (e) { 11487 out.status = 'error'; 11488 out.value = e; 11489 } 11490 return out; 11491 } 11492 11493 Promise.resolve = resolve; 11494 function resolve(value) { 11495 if (value instanceof this) { 11496 return value; 11497 } 11498 return handlers.resolve(new this(INTERNAL), value); 11499 } 11500 11501 Promise.reject = reject; 11502 function reject(reason) { 11503 var promise = new this(INTERNAL); 11504 return handlers.reject(promise, reason); 11505 } 11506 11507 Promise.all = all; 11508 function all(iterable) { 11509 var self = this; 11510 if (Object.prototype.toString.call(iterable) !== '[object Array]') { 11511 return this.reject(new TypeError('must be an array')); 11512 } 11513 11514 var len = iterable.length; 11515 var called = false; 11516 if (!len) { 11517 return this.resolve([]); 11518 } 11519 11520 var values = new Array(len); 11521 var resolved = 0; 11522 var i = -1; 11523 var promise = new this(INTERNAL); 11524 11525 while (++i < len) { 11526 allResolver(iterable[i], i); 11527 } 11528 return promise; 11529 function allResolver(value, i) { 11530 self.resolve(value).then(resolveFromAll, function (error) { 11531 if (!called) { 11532 called = true; 11533 handlers.reject(promise, error); 11534 } 11535 }); 11536 function resolveFromAll(outValue) { 11537 values[i] = outValue; 11538 if (++resolved === len && !called) { 11539 called = true; 11540 handlers.resolve(promise, values); 11541 } 11542 } 11543 } 11544 } 11545 11546 Promise.race = race; 11547 function race(iterable) { 11548 var self = this; 11549 if (Object.prototype.toString.call(iterable) !== '[object Array]') { 11550 return this.reject(new TypeError('must be an array')); 11551 } 11552 11553 var len = iterable.length; 11554 var called = false; 11555 if (!len) { 11556 return this.resolve([]); 11557 } 11558 11559 var i = -1; 11560 var promise = new this(INTERNAL); 11561 11562 while (++i < len) { 11563 resolver(iterable[i]); 11564 } 11565 return promise; 11566 function resolver(value) { 11567 self.resolve(value).then(function (response) { 11568 if (!called) { 11569 called = true; 11570 handlers.resolve(promise, response); 11571 } 11572 }, function (error) { 11573 if (!called) { 11574 called = true; 11575 handlers.reject(promise, error); 11576 } 11577 }); 11578 } 11579 } 11580 11581 },{"1":1}],3:[function(_dereq_,module,exports){ 11582 (function (global){ 11583 'use strict'; 11584 if (typeof global.Promise !== 'function') { 11585 global.Promise = _dereq_(2); 11586 } 11587 11588 }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 11589 },{"2":2}],4:[function(_dereq_,module,exports){ 11590 'use strict'; 11591 11592 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 11593 11594 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 11595 11596 function getIDB() { 11597 /* global indexedDB,webkitIndexedDB,mozIndexedDB,OIndexedDB,msIndexedDB */ 11598 try { 11599 if (typeof indexedDB !== 'undefined') { 11600 return indexedDB; 11601 } 11602 if (typeof webkitIndexedDB !== 'undefined') { 11603 return webkitIndexedDB; 11604 } 11605 if (typeof mozIndexedDB !== 'undefined') { 11606 return mozIndexedDB; 11607 } 11608 if (typeof OIndexedDB !== 'undefined') { 11609 return OIndexedDB; 11610 } 11611 if (typeof msIndexedDB !== 'undefined') { 11612 return msIndexedDB; 11613 } 11614 } catch (e) { 11615 return; 11616 } 11617 } 11618 11619 var idb = getIDB(); 11620 11621 function isIndexedDBValid() { 11622 try { 11623 // Initialize IndexedDB; fall back to vendor-prefixed versions 11624 // if needed. 11625 if (!idb || !idb.open) { 11626 return false; 11627 } 11628 // We mimic PouchDB here; 11629 // 11630 // We test for openDatabase because IE Mobile identifies itself 11631 // as Safari. Oh the lulz... 11632 var isSafari = typeof openDatabase !== 'undefined' && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform); 11633 11634 var hasFetch = typeof fetch === 'function' && fetch.toString().indexOf('[native code') !== -1; 11635 11636 // Safari <10.1 does not meet our requirements for IDB support 11637 // (see: https://github.com/pouchdb/pouchdb/issues/5572). 11638 // Safari 10.1 shipped with fetch, we can use that to detect it. 11639 // Note: this creates issues with `window.fetch` polyfills and 11640 // overrides; see: 11641 // https://github.com/localForage/localForage/issues/856 11642 return (!isSafari || hasFetch) && typeof indexedDB !== 'undefined' && 11643 // some outdated implementations of IDB that appear on Samsung 11644 // and HTC Android devices <4.4 are missing IDBKeyRange 11645 // See: https://github.com/mozilla/localForage/issues/128 11646 // See: https://github.com/mozilla/localForage/issues/272 11647 typeof IDBKeyRange !== 'undefined'; 11648 } catch (e) { 11649 return false; 11650 } 11651 } 11652 11653 // Abstracts constructing a Blob object, so it also works in older 11654 // browsers that don't support the native Blob constructor. (i.e. 11655 // old QtWebKit versions, at least). 11656 // Abstracts constructing a Blob object, so it also works in older 11657 // browsers that don't support the native Blob constructor. (i.e. 11658 // old QtWebKit versions, at least). 11659 function createBlob(parts, properties) { 11660 /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */ 11661 parts = parts || []; 11662 properties = properties || {}; 11663 try { 11664 return new Blob(parts, properties); 11665 } catch (e) { 11666 if (e.name !== 'TypeError') { 11667 throw e; 11668 } 11669 var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder; 11670 var builder = new Builder(); 11671 for (var i = 0; i < parts.length; i += 1) { 11672 builder.append(parts[i]); 11673 } 11674 return builder.getBlob(properties.type); 11675 } 11676 } 11677 11678 // This is CommonJS because lie is an external dependency, so Rollup 11679 // can just ignore it. 11680 if (typeof Promise === 'undefined') { 11681 // In the "nopromises" build this will just throw if you don't have 11682 // a global promise object, but it would throw anyway later. 11683 _dereq_(3); 11684 } 11685 var Promise$1 = Promise; 11686 11687 function executeCallback(promise, callback) { 11688 if (callback) { 11689 promise.then(function (result) { 11690 callback(null, result); 11691 }, function (error) { 11692 callback(error); 11693 }); 11694 } 11695 } 11696 11697 function executeTwoCallbacks(promise, callback, errorCallback) { 11698 if (typeof callback === 'function') { 11699 promise.then(callback); 11700 } 11701 11702 if (typeof errorCallback === 'function') { 11703 promise["catch"](errorCallback); 11704 } 11705 } 11706 11707 function normalizeKey(key) { 11708 // Cast the key to a string, as that's all we can set as a key. 11709 if (typeof key !== 'string') { 11710 console.warn(key + ' used as a key, but it is not a string.'); 11711 key = String(key); 11712 } 11713 11714 return key; 11715 } 11716 11717 function getCallback() { 11718 if (arguments.length && typeof arguments[arguments.length - 1] === 'function') { 11719 return arguments[arguments.length - 1]; 11720 } 11721 } 11722 11723 // Some code originally from async_storage.js in 11724 // [Gaia](https://github.com/mozilla-b2g/gaia). 11725 11726 var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support'; 11727 var supportsBlobs = void 0; 11728 var dbContexts = {}; 11729 var toString = Object.prototype.toString; 11730 11731 // Transaction Modes 11732 var READ_ONLY = 'readonly'; 11733 var READ_WRITE = 'readwrite'; 11734 11735 // Transform a binary string to an array buffer, because otherwise 11736 // weird stuff happens when you try to work with the binary string directly. 11737 // It is known. 11738 // From http://stackoverflow.com/questions/14967647/ (continues on next line) 11739 // encode-decode-image-with-base64-breaks-image (2013-04-21) 11740 function _binStringToArrayBuffer(bin) { 11741 var length = bin.length; 11742 var buf = new ArrayBuffer(length); 11743 var arr = new Uint8Array(buf); 11744 for (var i = 0; i < length; i++) { 11745 arr[i] = bin.charCodeAt(i); 11746 } 11747 return buf; 11748 } 11749 11750 // 11751 // Blobs are not supported in all versions of IndexedDB, notably 11752 // Chrome <37 and Android <5. In those versions, storing a blob will throw. 11753 // 11754 // Various other blob bugs exist in Chrome v37-42 (inclusive). 11755 // Detecting them is expensive and confusing to users, and Chrome 37-42 11756 // is at very low usage worldwide, so we do a hacky userAgent check instead. 11757 // 11758 // content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120 11759 // 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916 11760 // FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836 11761 // 11762 // Code borrowed from PouchDB. See: 11763 // https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js 11764 // 11765 function _checkBlobSupportWithoutCaching(idb) { 11766 return new Promise$1(function (resolve) { 11767 var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE); 11768 var blob = createBlob(['']); 11769 txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key'); 11770 11771 txn.onabort = function (e) { 11772 // If the transaction aborts now its due to not being able to 11773 // write to the database, likely due to the disk being full 11774 e.preventDefault(); 11775 e.stopPropagation(); 11776 resolve(false); 11777 }; 11778 11779 txn.oncomplete = function () { 11780 var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/); 11781 var matchedEdge = navigator.userAgent.match(/Edge\//); 11782 // MS Edge pretends to be Chrome 42: 11783 // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx 11784 resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43); 11785 }; 11786 })["catch"](function () { 11787 return false; // error, so assume unsupported 11788 }); 11789 } 11790 11791 function _checkBlobSupport(idb) { 11792 if (typeof supportsBlobs === 'boolean') { 11793 return Promise$1.resolve(supportsBlobs); 11794 } 11795 return _checkBlobSupportWithoutCaching(idb).then(function (value) { 11796 supportsBlobs = value; 11797 return supportsBlobs; 11798 }); 11799 } 11800 11801 function _deferReadiness(dbInfo) { 11802 var dbContext = dbContexts[dbInfo.name]; 11803 11804 // Create a deferred object representing the current database operation. 11805 var deferredOperation = {}; 11806 11807 deferredOperation.promise = new Promise$1(function (resolve, reject) { 11808 deferredOperation.resolve = resolve; 11809 deferredOperation.reject = reject; 11810 }); 11811 11812 // Enqueue the deferred operation. 11813 dbContext.deferredOperations.push(deferredOperation); 11814 11815 // Chain its promise to the database readiness. 11816 if (!dbContext.dbReady) { 11817 dbContext.dbReady = deferredOperation.promise; 11818 } else { 11819 dbContext.dbReady = dbContext.dbReady.then(function () { 11820 return deferredOperation.promise; 11821 }); 11822 } 11823 } 11824 11825 function _advanceReadiness(dbInfo) { 11826 var dbContext = dbContexts[dbInfo.name]; 11827 11828 // Dequeue a deferred operation. 11829 var deferredOperation = dbContext.deferredOperations.pop(); 11830 11831 // Resolve its promise (which is part of the database readiness 11832 // chain of promises). 11833 if (deferredOperation) { 11834 deferredOperation.resolve(); 11835 return deferredOperation.promise; 11836 } 11837 } 11838 11839 function _rejectReadiness(dbInfo, err) { 11840 var dbContext = dbContexts[dbInfo.name]; 11841 11842 // Dequeue a deferred operation. 11843 var deferredOperation = dbContext.deferredOperations.pop(); 11844 11845 // Reject its promise (which is part of the database readiness 11846 // chain of promises). 11847 if (deferredOperation) { 11848 deferredOperation.reject(err); 11849 return deferredOperation.promise; 11850 } 11851 } 11852 11853 function _getConnection(dbInfo, upgradeNeeded) { 11854 return new Promise$1(function (resolve, reject) { 11855 dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext(); 11856 11857 if (dbInfo.db) { 11858 if (upgradeNeeded) { 11859 _deferReadiness(dbInfo); 11860 dbInfo.db.close(); 11861 } else { 11862 return resolve(dbInfo.db); 11863 } 11864 } 11865 11866 var dbArgs = [dbInfo.name]; 11867 11868 if (upgradeNeeded) { 11869 dbArgs.push(dbInfo.version); 11870 } 11871 11872 var openreq = idb.open.apply(idb, dbArgs); 11873 11874 if (upgradeNeeded) { 11875 openreq.onupgradeneeded = function (e) { 11876 var db = openreq.result; 11877 try { 11878 db.createObjectStore(dbInfo.storeName); 11879 if (e.oldVersion <= 1) { 11880 // Added when support for blob shims was added 11881 db.createObjectStore(DETECT_BLOB_SUPPORT_STORE); 11882 } 11883 } catch (ex) { 11884 if (ex.name === 'ConstraintError') { 11885 console.warn('The database "' + dbInfo.name + '"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.'); 11886 } else { 11887 throw ex; 11888 } 11889 } 11890 }; 11891 } 11892 11893 openreq.onerror = function (e) { 11894 e.preventDefault(); 11895 reject(openreq.error); 11896 }; 11897 11898 openreq.onsuccess = function () { 11899 var db = openreq.result; 11900 db.onversionchange = function (e) { 11901 // Triggered when the database is modified (e.g. adding an objectStore) or 11902 // deleted (even when initiated by other sessions in different tabs). 11903 // Closing the connection here prevents those operations from being blocked. 11904 // If the database is accessed again later by this instance, the connection 11905 // will be reopened or the database recreated as needed. 11906 e.target.close(); 11907 }; 11908 resolve(db); 11909 _advanceReadiness(dbInfo); 11910 }; 11911 }); 11912 } 11913 11914 function _getOriginalConnection(dbInfo) { 11915 return _getConnection(dbInfo, false); 11916 } 11917 11918 function _getUpgradedConnection(dbInfo) { 11919 return _getConnection(dbInfo, true); 11920 } 11921 11922 function _isUpgradeNeeded(dbInfo, defaultVersion) { 11923 if (!dbInfo.db) { 11924 return true; 11925 } 11926 11927 var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName); 11928 var isDowngrade = dbInfo.version < dbInfo.db.version; 11929 var isUpgrade = dbInfo.version > dbInfo.db.version; 11930 11931 if (isDowngrade) { 11932 // If the version is not the default one 11933 // then warn for impossible downgrade. 11934 if (dbInfo.version !== defaultVersion) { 11935 console.warn('The database "' + dbInfo.name + '"' + " can't be downgraded from version " + dbInfo.db.version + ' to version ' + dbInfo.version + '.'); 11936 } 11937 // Align the versions to prevent errors. 11938 dbInfo.version = dbInfo.db.version; 11939 } 11940 11941 if (isUpgrade || isNewStore) { 11942 // If the store is new then increment the version (if needed). 11943 // This will trigger an "upgradeneeded" event which is required 11944 // for creating a store. 11945 if (isNewStore) { 11946 var incVersion = dbInfo.db.version + 1; 11947 if (incVersion > dbInfo.version) { 11948 dbInfo.version = incVersion; 11949 } 11950 } 11951 11952 return true; 11953 } 11954 11955 return false; 11956 } 11957 11958 // encode a blob for indexeddb engines that don't support blobs 11959 function _encodeBlob(blob) { 11960 return new Promise$1(function (resolve, reject) { 11961 var reader = new FileReader(); 11962 reader.onerror = reject; 11963 reader.onloadend = function (e) { 11964 var base64 = btoa(e.target.result || ''); 11965 resolve({ 11966 __local_forage_encoded_blob: true, 11967 data: base64, 11968 type: blob.type 11969 }); 11970 }; 11971 reader.readAsBinaryString(blob); 11972 }); 11973 } 11974 11975 // decode an encoded blob 11976 function _decodeBlob(encodedBlob) { 11977 var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data)); 11978 return createBlob([arrayBuff], { type: encodedBlob.type }); 11979 } 11980 11981 // is this one of our fancy encoded blobs? 11982 function _isEncodedBlob(value) { 11983 return value && value.__local_forage_encoded_blob; 11984 } 11985 11986 // Specialize the default `ready()` function by making it dependent 11987 // on the current database operations. Thus, the driver will be actually 11988 // ready when it's been initialized (default) *and* there are no pending 11989 // operations on the database (initiated by some other instances). 11990 function _fullyReady(callback) { 11991 var self = this; 11992 11993 var promise = self._initReady().then(function () { 11994 var dbContext = dbContexts[self._dbInfo.name]; 11995 11996 if (dbContext && dbContext.dbReady) { 11997 return dbContext.dbReady; 11998 } 11999 }); 12000 12001 executeTwoCallbacks(promise, callback, callback); 12002 return promise; 12003 } 12004 12005 // Try to establish a new db connection to replace the 12006 // current one which is broken (i.e. experiencing 12007 // InvalidStateError while creating a transaction). 12008 function _tryReconnect(dbInfo) { 12009 _deferReadiness(dbInfo); 12010 12011 var dbContext = dbContexts[dbInfo.name]; 12012 var forages = dbContext.forages; 12013 12014 for (var i = 0; i < forages.length; i++) { 12015 var forage = forages[i]; 12016 if (forage._dbInfo.db) { 12017 forage._dbInfo.db.close(); 12018 forage._dbInfo.db = null; 12019 } 12020 } 12021 dbInfo.db = null; 12022 12023 return _getOriginalConnection(dbInfo).then(function (db) { 12024 dbInfo.db = db; 12025 if (_isUpgradeNeeded(dbInfo)) { 12026 // Reopen the database for upgrading. 12027 return _getUpgradedConnection(dbInfo); 12028 } 12029 return db; 12030 }).then(function (db) { 12031 // store the latest db reference 12032 // in case the db was upgraded 12033 dbInfo.db = dbContext.db = db; 12034 for (var i = 0; i < forages.length; i++) { 12035 forages[i]._dbInfo.db = db; 12036 } 12037 })["catch"](function (err) { 12038 _rejectReadiness(dbInfo, err); 12039 throw err; 12040 }); 12041 } 12042 12043 // FF doesn't like Promises (micro-tasks) and IDDB store operations, 12044 // so we have to do it with callbacks 12045 function createTransaction(dbInfo, mode, callback, retries) { 12046 if (retries === undefined) { 12047 retries = 1; 12048 } 12049 12050 try { 12051 var tx = dbInfo.db.transaction(dbInfo.storeName, mode); 12052 callback(null, tx); 12053 } catch (err) { 12054 if (retries > 0 && (!dbInfo.db || err.name === 'InvalidStateError' || err.name === 'NotFoundError')) { 12055 return Promise$1.resolve().then(function () { 12056 if (!dbInfo.db || err.name === 'NotFoundError' && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) { 12057 // increase the db version, to create the new ObjectStore 12058 if (dbInfo.db) { 12059 dbInfo.version = dbInfo.db.version + 1; 12060 } 12061 // Reopen the database for upgrading. 12062 return _getUpgradedConnection(dbInfo); 12063 } 12064 }).then(function () { 12065 return _tryReconnect(dbInfo).then(function () { 12066 createTransaction(dbInfo, mode, callback, retries - 1); 12067 }); 12068 })["catch"](callback); 12069 } 12070 12071 callback(err); 12072 } 12073 } 12074 12075 function createDbContext() { 12076 return { 12077 // Running localForages sharing a database. 12078 forages: [], 12079 // Shared database. 12080 db: null, 12081 // Database readiness (promise). 12082 dbReady: null, 12083 // Deferred operations on the database. 12084 deferredOperations: [] 12085 }; 12086 } 12087 12088 // Open the IndexedDB database (automatically creates one if one didn't 12089 // previously exist), using any options set in the config. 12090 function _initStorage(options) { 12091 var self = this; 12092 var dbInfo = { 12093 db: null 12094 }; 12095 12096 if (options) { 12097 for (var i in options) { 12098 dbInfo[i] = options[i]; 12099 } 12100 } 12101 12102 // Get the current context of the database; 12103 var dbContext = dbContexts[dbInfo.name]; 12104 12105 // ...or create a new context. 12106 if (!dbContext) { 12107 dbContext = createDbContext(); 12108 // Register the new context in the global container. 12109 dbContexts[dbInfo.name] = dbContext; 12110 } 12111 12112 // Register itself as a running localForage in the current context. 12113 dbContext.forages.push(self); 12114 12115 // Replace the default `ready()` function with the specialized one. 12116 if (!self._initReady) { 12117 self._initReady = self.ready; 12118 self.ready = _fullyReady; 12119 } 12120 12121 // Create an array of initialization states of the related localForages. 12122 var initPromises = []; 12123 12124 function ignoreErrors() { 12125 // Don't handle errors here, 12126 // just makes sure related localForages aren't pending. 12127 return Promise$1.resolve(); 12128 } 12129 12130 for (var j = 0; j < dbContext.forages.length; j++) { 12131 var forage = dbContext.forages[j]; 12132 if (forage !== self) { 12133 // Don't wait for itself... 12134 initPromises.push(forage._initReady()["catch"](ignoreErrors)); 12135 } 12136 } 12137 12138 // Take a snapshot of the related localForages. 12139 var forages = dbContext.forages.slice(0); 12140 12141 // Initialize the connection process only when 12142 // all the related localForages aren't pending. 12143 return Promise$1.all(initPromises).then(function () { 12144 dbInfo.db = dbContext.db; 12145 // Get the connection or open a new one without upgrade. 12146 return _getOriginalConnection(dbInfo); 12147 }).then(function (db) { 12148 dbInfo.db = db; 12149 if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) { 12150 // Reopen the database for upgrading. 12151 return _getUpgradedConnection(dbInfo); 12152 } 12153 return db; 12154 }).then(function (db) { 12155 dbInfo.db = dbContext.db = db; 12156 self._dbInfo = dbInfo; 12157 // Share the final connection amongst related localForages. 12158 for (var k = 0; k < forages.length; k++) { 12159 var forage = forages[k]; 12160 if (forage !== self) { 12161 // Self is already up-to-date. 12162 forage._dbInfo.db = dbInfo.db; 12163 forage._dbInfo.version = dbInfo.version; 12164 } 12165 } 12166 }); 12167 } 12168 12169 function getItem(key, callback) { 12170 var self = this; 12171 12172 key = normalizeKey(key); 12173 12174 var promise = new Promise$1(function (resolve, reject) { 12175 self.ready().then(function () { 12176 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) { 12177 if (err) { 12178 return reject(err); 12179 } 12180 12181 try { 12182 var store = transaction.objectStore(self._dbInfo.storeName); 12183 var req = store.get(key); 12184 12185 req.onsuccess = function () { 12186 var value = req.result; 12187 if (value === undefined) { 12188 value = null; 12189 } 12190 if (_isEncodedBlob(value)) { 12191 value = _decodeBlob(value); 12192 } 12193 resolve(value); 12194 }; 12195 12196 req.onerror = function () { 12197 reject(req.error); 12198 }; 12199 } catch (e) { 12200 reject(e); 12201 } 12202 }); 12203 })["catch"](reject); 12204 }); 12205 12206 executeCallback(promise, callback); 12207 return promise; 12208 } 12209 12210 // Iterate over all items stored in database. 12211 function iterate(iterator, callback) { 12212 var self = this; 12213 12214 var promise = new Promise$1(function (resolve, reject) { 12215 self.ready().then(function () { 12216 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) { 12217 if (err) { 12218 return reject(err); 12219 } 12220 12221 try { 12222 var store = transaction.objectStore(self._dbInfo.storeName); 12223 var req = store.openCursor(); 12224 var iterationNumber = 1; 12225 12226 req.onsuccess = function () { 12227 var cursor = req.result; 12228 12229 if (cursor) { 12230 var value = cursor.value; 12231 if (_isEncodedBlob(value)) { 12232 value = _decodeBlob(value); 12233 } 12234 var result = iterator(value, cursor.key, iterationNumber++); 12235 12236 // when the iterator callback returns any 12237 // (non-`undefined`) value, then we stop 12238 // the iteration immediately 12239 if (result !== void 0) { 12240 resolve(result); 12241 } else { 12242 cursor["continue"](); 12243 } 12244 } else { 12245 resolve(); 12246 } 12247 }; 12248 12249 req.onerror = function () { 12250 reject(req.error); 12251 }; 12252 } catch (e) { 12253 reject(e); 12254 } 12255 }); 12256 })["catch"](reject); 12257 }); 12258 12259 executeCallback(promise, callback); 12260 12261 return promise; 12262 } 12263 12264 function setItem(key, value, callback) { 12265 var self = this; 12266 12267 key = normalizeKey(key); 12268 12269 var promise = new Promise$1(function (resolve, reject) { 12270 var dbInfo; 12271 self.ready().then(function () { 12272 dbInfo = self._dbInfo; 12273 if (toString.call(value) === '[object Blob]') { 12274 return _checkBlobSupport(dbInfo.db).then(function (blobSupport) { 12275 if (blobSupport) { 12276 return value; 12277 } 12278 return _encodeBlob(value); 12279 }); 12280 } 12281 return value; 12282 }).then(function (value) { 12283 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) { 12284 if (err) { 12285 return reject(err); 12286 } 12287 12288 try { 12289 var store = transaction.objectStore(self._dbInfo.storeName); 12290 12291 // The reason we don't _save_ null is because IE 10 does 12292 // not support saving the `null` type in IndexedDB. How 12293 // ironic, given the bug below! 12294 // See: https://github.com/mozilla/localForage/issues/161 12295 if (value === null) { 12296 value = undefined; 12297 } 12298 12299 var req = store.put(value, key); 12300 12301 transaction.oncomplete = function () { 12302 // Cast to undefined so the value passed to 12303 // callback/promise is the same as what one would get out 12304 // of `getItem()` later. This leads to some weirdness 12305 // (setItem('foo', undefined) will return `null`), but 12306 // it's not my fault localStorage is our baseline and that 12307 // it's weird. 12308 if (value === undefined) { 12309 value = null; 12310 } 12311 12312 resolve(value); 12313 }; 12314 transaction.onabort = transaction.onerror = function () { 12315 var err = req.error ? req.error : req.transaction.error; 12316 reject(err); 12317 }; 12318 } catch (e) { 12319 reject(e); 12320 } 12321 }); 12322 })["catch"](reject); 12323 }); 12324 12325 executeCallback(promise, callback); 12326 return promise; 12327 } 12328 12329 function removeItem(key, callback) { 12330 var self = this; 12331 12332 key = normalizeKey(key); 12333 12334 var promise = new Promise$1(function (resolve, reject) { 12335 self.ready().then(function () { 12336 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) { 12337 if (err) { 12338 return reject(err); 12339 } 12340 12341 try { 12342 var store = transaction.objectStore(self._dbInfo.storeName); 12343 // We use a Grunt task to make this safe for IE and some 12344 // versions of Android (including those used by Cordova). 12345 // Normally IE won't like `.delete()` and will insist on 12346 // using `['delete']()`, but we have a build step that 12347 // fixes this for us now. 12348 var req = store["delete"](key); 12349 transaction.oncomplete = function () { 12350 resolve(); 12351 }; 12352 12353 transaction.onerror = function () { 12354 reject(req.error); 12355 }; 12356 12357 // The request will be also be aborted if we've exceeded our storage 12358 // space. 12359 transaction.onabort = function () { 12360 var err = req.error ? req.error : req.transaction.error; 12361 reject(err); 12362 }; 12363 } catch (e) { 12364 reject(e); 12365 } 12366 }); 12367 })["catch"](reject); 12368 }); 12369 12370 executeCallback(promise, callback); 12371 return promise; 12372 } 12373 12374 function clear(callback) { 12375 var self = this; 12376 12377 var promise = new Promise$1(function (resolve, reject) { 12378 self.ready().then(function () { 12379 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) { 12380 if (err) { 12381 return reject(err); 12382 } 12383 12384 try { 12385 var store = transaction.objectStore(self._dbInfo.storeName); 12386 var req = store.clear(); 12387 12388 transaction.oncomplete = function () { 12389 resolve(); 12390 }; 12391 12392 transaction.onabort = transaction.onerror = function () { 12393 var err = req.error ? req.error : req.transaction.error; 12394 reject(err); 12395 }; 12396 } catch (e) { 12397 reject(e); 12398 } 12399 }); 12400 })["catch"](reject); 12401 }); 12402 12403 executeCallback(promise, callback); 12404 return promise; 12405 } 12406 12407 function length(callback) { 12408 var self = this; 12409 12410 var promise = new Promise$1(function (resolve, reject) { 12411 self.ready().then(function () { 12412 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) { 12413 if (err) { 12414 return reject(err); 12415 } 12416 12417 try { 12418 var store = transaction.objectStore(self._dbInfo.storeName); 12419 var req = store.count(); 12420 12421 req.onsuccess = function () { 12422 resolve(req.result); 12423 }; 12424 12425 req.onerror = function () { 12426 reject(req.error); 12427 }; 12428 } catch (e) { 12429 reject(e); 12430 } 12431 }); 12432 })["catch"](reject); 12433 }); 12434 12435 executeCallback(promise, callback); 12436 return promise; 12437 } 12438 12439 function key(n, callback) { 12440 var self = this; 12441 12442 var promise = new Promise$1(function (resolve, reject) { 12443 if (n < 0) { 12444 resolve(null); 12445 12446 return; 12447 } 12448 12449 self.ready().then(function () { 12450 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) { 12451 if (err) { 12452 return reject(err); 12453 } 12454 12455 try { 12456 var store = transaction.objectStore(self._dbInfo.storeName); 12457 var advanced = false; 12458 var req = store.openKeyCursor(); 12459 12460 req.onsuccess = function () { 12461 var cursor = req.result; 12462 if (!cursor) { 12463 // this means there weren't enough keys 12464 resolve(null); 12465 12466 return; 12467 } 12468 12469 if (n === 0) { 12470 // We have the first key, return it if that's what they 12471 // wanted. 12472 resolve(cursor.key); 12473 } else { 12474 if (!advanced) { 12475 // Otherwise, ask the cursor to skip ahead n 12476 // records. 12477 advanced = true; 12478 cursor.advance(n); 12479 } else { 12480 // When we get here, we've got the nth key. 12481 resolve(cursor.key); 12482 } 12483 } 12484 }; 12485 12486 req.onerror = function () { 12487 reject(req.error); 12488 }; 12489 } catch (e) { 12490 reject(e); 12491 } 12492 }); 12493 })["catch"](reject); 12494 }); 12495 12496 executeCallback(promise, callback); 12497 return promise; 12498 } 12499 12500 function keys(callback) { 12501 var self = this; 12502 12503 var promise = new Promise$1(function (resolve, reject) { 12504 self.ready().then(function () { 12505 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) { 12506 if (err) { 12507 return reject(err); 12508 } 12509 12510 try { 12511 var store = transaction.objectStore(self._dbInfo.storeName); 12512 var req = store.openKeyCursor(); 12513 var keys = []; 12514 12515 req.onsuccess = function () { 12516 var cursor = req.result; 12517 12518 if (!cursor) { 12519 resolve(keys); 12520 return; 12521 } 12522 12523 keys.push(cursor.key); 12524 cursor["continue"](); 12525 }; 12526 12527 req.onerror = function () { 12528 reject(req.error); 12529 }; 12530 } catch (e) { 12531 reject(e); 12532 } 12533 }); 12534 })["catch"](reject); 12535 }); 12536 12537 executeCallback(promise, callback); 12538 return promise; 12539 } 12540 12541 function dropInstance(options, callback) { 12542 callback = getCallback.apply(this, arguments); 12543 12544 var currentConfig = this.config(); 12545 options = typeof options !== 'function' && options || {}; 12546 if (!options.name) { 12547 options.name = options.name || currentConfig.name; 12548 options.storeName = options.storeName || currentConfig.storeName; 12549 } 12550 12551 var self = this; 12552 var promise; 12553 if (!options.name) { 12554 promise = Promise$1.reject('Invalid arguments'); 12555 } else { 12556 var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db; 12557 12558 var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function (db) { 12559 var dbContext = dbContexts[options.name]; 12560 var forages = dbContext.forages; 12561 dbContext.db = db; 12562 for (var i = 0; i < forages.length; i++) { 12563 forages[i]._dbInfo.db = db; 12564 } 12565 return db; 12566 }); 12567 12568 if (!options.storeName) { 12569 promise = dbPromise.then(function (db) { 12570 _deferReadiness(options); 12571 12572 var dbContext = dbContexts[options.name]; 12573 var forages = dbContext.forages; 12574 12575 db.close(); 12576 for (var i = 0; i < forages.length; i++) { 12577 var forage = forages[i]; 12578 forage._dbInfo.db = null; 12579 } 12580 12581 var dropDBPromise = new Promise$1(function (resolve, reject) { 12582 var req = idb.deleteDatabase(options.name); 12583 12584 req.onerror = function () { 12585 var db = req.result; 12586 if (db) { 12587 db.close(); 12588 } 12589 reject(req.error); 12590 }; 12591 12592 req.onblocked = function () { 12593 // Closing all open connections in onversionchange handler should prevent this situation, but if 12594 // we do get here, it just means the request remains pending - eventually it will succeed or error 12595 console.warn('dropInstance blocked for database "' + options.name + '" until all open connections are closed'); 12596 }; 12597 12598 req.onsuccess = function () { 12599 var db = req.result; 12600 if (db) { 12601 db.close(); 12602 } 12603 resolve(db); 12604 }; 12605 }); 12606 12607 return dropDBPromise.then(function (db) { 12608 dbContext.db = db; 12609 for (var i = 0; i < forages.length; i++) { 12610 var _forage = forages[i]; 12611 _advanceReadiness(_forage._dbInfo); 12612 } 12613 })["catch"](function (err) { 12614 (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {}); 12615 throw err; 12616 }); 12617 }); 12618 } else { 12619 promise = dbPromise.then(function (db) { 12620 if (!db.objectStoreNames.contains(options.storeName)) { 12621 return; 12622 } 12623 12624 var newVersion = db.version + 1; 12625 12626 _deferReadiness(options); 12627 12628 var dbContext = dbContexts[options.name]; 12629 var forages = dbContext.forages; 12630 12631 db.close(); 12632 for (var i = 0; i < forages.length; i++) { 12633 var forage = forages[i]; 12634 forage._dbInfo.db = null; 12635 forage._dbInfo.version = newVersion; 12636 } 12637 12638 var dropObjectPromise = new Promise$1(function (resolve, reject) { 12639 var req = idb.open(options.name, newVersion); 12640 12641 req.onerror = function (err) { 12642 var db = req.result; 12643 db.close(); 12644 reject(err); 12645 }; 12646 12647 req.onupgradeneeded = function () { 12648 var db = req.result; 12649 db.deleteObjectStore(options.storeName); 12650 }; 12651 12652 req.onsuccess = function () { 12653 var db = req.result; 12654 db.close(); 12655 resolve(db); 12656 }; 12657 }); 12658 12659 return dropObjectPromise.then(function (db) { 12660 dbContext.db = db; 12661 for (var j = 0; j < forages.length; j++) { 12662 var _forage2 = forages[j]; 12663 _forage2._dbInfo.db = db; 12664 _advanceReadiness(_forage2._dbInfo); 12665 } 12666 })["catch"](function (err) { 12667 (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {}); 12668 throw err; 12669 }); 12670 }); 12671 } 12672 } 12673 12674 executeCallback(promise, callback); 12675 return promise; 12676 } 12677 12678 var asyncStorage = { 12679 _driver: 'asyncStorage', 12680 _initStorage: _initStorage, 12681 _support: isIndexedDBValid(), 12682 iterate: iterate, 12683 getItem: getItem, 12684 setItem: setItem, 12685 removeItem: removeItem, 12686 clear: clear, 12687 length: length, 12688 key: key, 12689 keys: keys, 12690 dropInstance: dropInstance 12691 }; 12692 12693 function isWebSQLValid() { 12694 return typeof openDatabase === 'function'; 12695 } 12696 12697 // Sadly, the best way to save binary data in WebSQL/localStorage is serializing 12698 // it to Base64, so this is how we store it to prevent very strange errors with less 12699 // verbose ways of binary <-> string data storage. 12700 var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 12701 12702 var BLOB_TYPE_PREFIX = '~~local_forage_type~'; 12703 var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/; 12704 12705 var SERIALIZED_MARKER = '__lfsc__:'; 12706 var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length; 12707 12708 // OMG the serializations! 12709 var TYPE_ARRAYBUFFER = 'arbf'; 12710 var TYPE_BLOB = 'blob'; 12711 var TYPE_INT8ARRAY = 'si08'; 12712 var TYPE_UINT8ARRAY = 'ui08'; 12713 var TYPE_UINT8CLAMPEDARRAY = 'uic8'; 12714 var TYPE_INT16ARRAY = 'si16'; 12715 var TYPE_INT32ARRAY = 'si32'; 12716 var TYPE_UINT16ARRAY = 'ur16'; 12717 var TYPE_UINT32ARRAY = 'ui32'; 12718 var TYPE_FLOAT32ARRAY = 'fl32'; 12719 var TYPE_FLOAT64ARRAY = 'fl64'; 12720 var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length; 12721 12722 var toString$1 = Object.prototype.toString; 12723 12724 function stringToBuffer(serializedString) { 12725 // Fill the string into a ArrayBuffer. 12726 var bufferLength = serializedString.length * 0.75; 12727 var len = serializedString.length; 12728 var i; 12729 var p = 0; 12730 var encoded1, encoded2, encoded3, encoded4; 12731 12732 if (serializedString[serializedString.length - 1] === '=') { 12733 bufferLength--; 12734 if (serializedString[serializedString.length - 2] === '=') { 12735 bufferLength--; 12736 } 12737 } 12738 12739 var buffer = new ArrayBuffer(bufferLength); 12740 var bytes = new Uint8Array(buffer); 12741 12742 for (i = 0; i < len; i += 4) { 12743 encoded1 = BASE_CHARS.indexOf(serializedString[i]); 12744 encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]); 12745 encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]); 12746 encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]); 12747 12748 /*jslint bitwise: true */ 12749 bytes[p++] = encoded1 << 2 | encoded2 >> 4; 12750 bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2; 12751 bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63; 12752 } 12753 return buffer; 12754 } 12755 12756 // Converts a buffer to a string to store, serialized, in the backend 12757 // storage library. 12758 function bufferToString(buffer) { 12759 // base64-arraybuffer 12760 var bytes = new Uint8Array(buffer); 12761 var base64String = ''; 12762 var i; 12763 12764 for (i = 0; i < bytes.length; i += 3) { 12765 /*jslint bitwise: true */ 12766 base64String += BASE_CHARS[bytes[i] >> 2]; 12767 base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4]; 12768 base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6]; 12769 base64String += BASE_CHARS[bytes[i + 2] & 63]; 12770 } 12771 12772 if (bytes.length % 3 === 2) { 12773 base64String = base64String.substring(0, base64String.length - 1) + '='; 12774 } else if (bytes.length % 3 === 1) { 12775 base64String = base64String.substring(0, base64String.length - 2) + '=='; 12776 } 12777 12778 return base64String; 12779 } 12780 12781 // Serialize a value, afterwards executing a callback (which usually 12782 // instructs the `setItem()` callback/promise to be executed). This is how 12783 // we store binary data with localStorage. 12784 function serialize(value, callback) { 12785 var valueType = ''; 12786 if (value) { 12787 valueType = toString$1.call(value); 12788 } 12789 12790 // Cannot use `value instanceof ArrayBuffer` or such here, as these 12791 // checks fail when running the tests using casper.js... 12792 // 12793 // TODO: See why those tests fail and use a better solution. 12794 if (value && (valueType === '[object ArrayBuffer]' || value.buffer && toString$1.call(value.buffer) === '[object ArrayBuffer]')) { 12795 // Convert binary arrays to a string and prefix the string with 12796 // a special marker. 12797 var buffer; 12798 var marker = SERIALIZED_MARKER; 12799 12800 if (value instanceof ArrayBuffer) { 12801 buffer = value; 12802 marker += TYPE_ARRAYBUFFER; 12803 } else { 12804 buffer = value.buffer; 12805 12806 if (valueType === '[object Int8Array]') { 12807 marker += TYPE_INT8ARRAY; 12808 } else if (valueType === '[object Uint8Array]') { 12809 marker += TYPE_UINT8ARRAY; 12810 } else if (valueType === '[object Uint8ClampedArray]') { 12811 marker += TYPE_UINT8CLAMPEDARRAY; 12812 } else if (valueType === '[object Int16Array]') { 12813 marker += TYPE_INT16ARRAY; 12814 } else if (valueType === '[object Uint16Array]') { 12815 marker += TYPE_UINT16ARRAY; 12816 } else if (valueType === '[object Int32Array]') { 12817 marker += TYPE_INT32ARRAY; 12818 } else if (valueType === '[object Uint32Array]') { 12819 marker += TYPE_UINT32ARRAY; 12820 } else if (valueType === '[object Float32Array]') { 12821 marker += TYPE_FLOAT32ARRAY; 12822 } else if (valueType === '[object Float64Array]') { 12823 marker += TYPE_FLOAT64ARRAY; 12824 } else { 12825 callback(new Error('Failed to get type for BinaryArray')); 12826 } 12827 } 12828 12829 callback(marker + bufferToString(buffer)); 12830 } else if (valueType === '[object Blob]') { 12831 // Conver the blob to a binaryArray and then to a string. 12832 var fileReader = new FileReader(); 12833 12834 fileReader.onload = function () { 12835 // Backwards-compatible prefix for the blob type. 12836 var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result); 12837 12838 callback(SERIALIZED_MARKER + TYPE_BLOB + str); 12839 }; 12840 12841 fileReader.readAsArrayBuffer(value); 12842 } else { 12843 try { 12844 callback(JSON.stringify(value)); 12845 } catch (e) { 12846 console.error("Couldn't convert value into a JSON string: ", value); 12847 12848 callback(null, e); 12849 } 12850 } 12851 } 12852 12853 // Deserialize data we've inserted into a value column/field. We place 12854 // special markers into our strings to mark them as encoded; this isn't 12855 // as nice as a meta field, but it's the only sane thing we can do whilst 12856 // keeping localStorage support intact. 12857 // 12858 // Oftentimes this will just deserialize JSON content, but if we have a 12859 // special marker (SERIALIZED_MARKER, defined above), we will extract 12860 // some kind of arraybuffer/binary data/typed array out of the string. 12861 function deserialize(value) { 12862 // If we haven't marked this string as being specially serialized (i.e. 12863 // something other than serialized JSON), we can just return it and be 12864 // done with it. 12865 if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) { 12866 return JSON.parse(value); 12867 } 12868 12869 // The following code deals with deserializing some kind of Blob or 12870 // TypedArray. First we separate out the type of data we're dealing 12871 // with from the data itself. 12872 var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH); 12873 var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH); 12874 12875 var blobType; 12876 // Backwards-compatible blob type serialization strategy. 12877 // DBs created with older versions of localForage will simply not have the blob type. 12878 if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) { 12879 var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX); 12880 blobType = matcher[1]; 12881 serializedString = serializedString.substring(matcher[0].length); 12882 } 12883 var buffer = stringToBuffer(serializedString); 12884 12885 // Return the right type based on the code/type set during 12886 // serialization. 12887 switch (type) { 12888 case TYPE_ARRAYBUFFER: 12889 return buffer; 12890 case TYPE_BLOB: 12891 return createBlob([buffer], { type: blobType }); 12892 case TYPE_INT8ARRAY: 12893 return new Int8Array(buffer); 12894 case TYPE_UINT8ARRAY: 12895 return new Uint8Array(buffer); 12896 case TYPE_UINT8CLAMPEDARRAY: 12897 return new Uint8ClampedArray(buffer); 12898 case TYPE_INT16ARRAY: 12899 return new Int16Array(buffer); 12900 case TYPE_UINT16ARRAY: 12901 return new Uint16Array(buffer); 12902 case TYPE_INT32ARRAY: 12903 return new Int32Array(buffer); 12904 case TYPE_UINT32ARRAY: 12905 return new Uint32Array(buffer); 12906 case TYPE_FLOAT32ARRAY: 12907 return new Float32Array(buffer); 12908 case TYPE_FLOAT64ARRAY: 12909 return new Float64Array(buffer); 12910 default: 12911 throw new Error('Unkown type: ' + type); 12912 } 12913 } 12914 12915 var localforageSerializer = { 12916 serialize: serialize, 12917 deserialize: deserialize, 12918 stringToBuffer: stringToBuffer, 12919 bufferToString: bufferToString 12920 }; 12921 12922 /* 12923 * Includes code from: 12924 * 12925 * base64-arraybuffer 12926 * https://github.com/niklasvh/base64-arraybuffer 12927 * 12928 * Copyright (c) 2012 Niklas von Hertzen 12929 * Licensed under the MIT license. 12930 */ 12931 12932 function createDbTable(t, dbInfo, callback, errorCallback) { 12933 t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' ' + '(id INTEGER PRIMARY KEY, key unique, value)', [], callback, errorCallback); 12934 } 12935 12936 // Open the WebSQL database (automatically creates one if one didn't 12937 // previously exist), using any options set in the config. 12938 function _initStorage$1(options) { 12939 var self = this; 12940 var dbInfo = { 12941 db: null 12942 }; 12943 12944 if (options) { 12945 for (var i in options) { 12946 dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i]; 12947 } 12948 } 12949 12950 var dbInfoPromise = new Promise$1(function (resolve, reject) { 12951 // Open the database; the openDatabase API will automatically 12952 // create it for us if it doesn't exist. 12953 try { 12954 dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size); 12955 } catch (e) { 12956 return reject(e); 12957 } 12958 12959 // Create our key/value table if it doesn't exist. 12960 dbInfo.db.transaction(function (t) { 12961 createDbTable(t, dbInfo, function () { 12962 self._dbInfo = dbInfo; 12963 resolve(); 12964 }, function (t, error) { 12965 reject(error); 12966 }); 12967 }, reject); 12968 }); 12969 12970 dbInfo.serializer = localforageSerializer; 12971 return dbInfoPromise; 12972 } 12973 12974 function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) { 12975 t.executeSql(sqlStatement, args, callback, function (t, error) { 12976 if (error.code === error.SYNTAX_ERR) { 12977 t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name = ?", [dbInfo.storeName], function (t, results) { 12978 if (!results.rows.length) { 12979 // if the table is missing (was deleted) 12980 // re-create it table and retry 12981 createDbTable(t, dbInfo, function () { 12982 t.executeSql(sqlStatement, args, callback, errorCallback); 12983 }, errorCallback); 12984 } else { 12985 errorCallback(t, error); 12986 } 12987 }, errorCallback); 12988 } else { 12989 errorCallback(t, error); 12990 } 12991 }, errorCallback); 12992 } 12993 12994 function getItem$1(key, callback) { 12995 var self = this; 12996 12997 key = normalizeKey(key); 12998 12999 var promise = new Promise$1(function (resolve, reject) { 13000 self.ready().then(function () { 13001 var dbInfo = self._dbInfo; 13002 dbInfo.db.transaction(function (t) { 13003 tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) { 13004 var result = results.rows.length ? results.rows.item(0).value : null; 13005 13006 // Check to see if this is serialized content we need to 13007 // unpack. 13008 if (result) { 13009 result = dbInfo.serializer.deserialize(result); 13010 } 13011 13012 resolve(result); 13013 }, function (t, error) { 13014 reject(error); 13015 }); 13016 }); 13017 })["catch"](reject); 13018 }); 13019 13020 executeCallback(promise, callback); 13021 return promise; 13022 } 13023 13024 function iterate$1(iterator, callback) { 13025 var self = this; 13026 13027 var promise = new Promise$1(function (resolve, reject) { 13028 self.ready().then(function () { 13029 var dbInfo = self._dbInfo; 13030 13031 dbInfo.db.transaction(function (t) { 13032 tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName, [], function (t, results) { 13033 var rows = results.rows; 13034 var length = rows.length; 13035 13036 for (var i = 0; i < length; i++) { 13037 var item = rows.item(i); 13038 var result = item.value; 13039 13040 // Check to see if this is serialized content 13041 // we need to unpack. 13042 if (result) { 13043 result = dbInfo.serializer.deserialize(result); 13044 } 13045 13046 result = iterator(result, item.key, i + 1); 13047 13048 // void(0) prevents problems with redefinition 13049 // of `undefined`. 13050 if (result !== void 0) { 13051 resolve(result); 13052 return; 13053 } 13054 } 13055 13056 resolve(); 13057 }, function (t, error) { 13058 reject(error); 13059 }); 13060 }); 13061 })["catch"](reject); 13062 }); 13063 13064 executeCallback(promise, callback); 13065 return promise; 13066 } 13067 13068 function _setItem(key, value, callback, retriesLeft) { 13069 var self = this; 13070 13071 key = normalizeKey(key); 13072 13073 var promise = new Promise$1(function (resolve, reject) { 13074 self.ready().then(function () { 13075 // The localStorage API doesn't return undefined values in an 13076 // "expected" way, so undefined is always cast to null in all 13077 // drivers. See: https://github.com/mozilla/localForage/pull/42 13078 if (value === undefined) { 13079 value = null; 13080 } 13081 13082 // Save the original value to pass to the callback. 13083 var originalValue = value; 13084 13085 var dbInfo = self._dbInfo; 13086 dbInfo.serializer.serialize(value, function (value, error) { 13087 if (error) { 13088 reject(error); 13089 } else { 13090 dbInfo.db.transaction(function (t) { 13091 tryExecuteSql(t, dbInfo, 'INSERT OR REPLACE INTO ' + dbInfo.storeName + ' ' + '(key, value) VALUES (?, ?)', [key, value], function () { 13092 resolve(originalValue); 13093 }, function (t, error) { 13094 reject(error); 13095 }); 13096 }, function (sqlError) { 13097 // The transaction failed; check 13098 // to see if it's a quota error. 13099 if (sqlError.code === sqlError.QUOTA_ERR) { 13100 // We reject the callback outright for now, but 13101 // it's worth trying to re-run the transaction. 13102 // Even if the user accepts the prompt to use 13103 // more storage on Safari, this error will 13104 // be called. 13105 // 13106 // Try to re-run the transaction. 13107 if (retriesLeft > 0) { 13108 resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1])); 13109 return; 13110 } 13111 reject(sqlError); 13112 } 13113 }); 13114 } 13115 }); 13116 })["catch"](reject); 13117 }); 13118 13119 executeCallback(promise, callback); 13120 return promise; 13121 } 13122 13123 function setItem$1(key, value, callback) { 13124 return _setItem.apply(this, [key, value, callback, 1]); 13125 } 13126 13127 function removeItem$1(key, callback) { 13128 var self = this; 13129 13130 key = normalizeKey(key); 13131 13132 var promise = new Promise$1(function (resolve, reject) { 13133 self.ready().then(function () { 13134 var dbInfo = self._dbInfo; 13135 dbInfo.db.transaction(function (t) { 13136 tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () { 13137 resolve(); 13138 }, function (t, error) { 13139 reject(error); 13140 }); 13141 }); 13142 })["catch"](reject); 13143 }); 13144 13145 executeCallback(promise, callback); 13146 return promise; 13147 } 13148 13149 // Deletes every item in the table. 13150 // TODO: Find out if this resets the AUTO_INCREMENT number. 13151 function clear$1(callback) { 13152 var self = this; 13153 13154 var promise = new Promise$1(function (resolve, reject) { 13155 self.ready().then(function () { 13156 var dbInfo = self._dbInfo; 13157 dbInfo.db.transaction(function (t) { 13158 tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName, [], function () { 13159 resolve(); 13160 }, function (t, error) { 13161 reject(error); 13162 }); 13163 }); 13164 })["catch"](reject); 13165 }); 13166 13167 executeCallback(promise, callback); 13168 return promise; 13169 } 13170 13171 // Does a simple `COUNT(key)` to get the number of items stored in 13172 // localForage. 13173 function length$1(callback) { 13174 var self = this; 13175 13176 var promise = new Promise$1(function (resolve, reject) { 13177 self.ready().then(function () { 13178 var dbInfo = self._dbInfo; 13179 dbInfo.db.transaction(function (t) { 13180 // Ahhh, SQL makes this one soooooo easy. 13181 tryExecuteSql(t, dbInfo, 'SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) { 13182 var result = results.rows.item(0).c; 13183 resolve(result); 13184 }, function (t, error) { 13185 reject(error); 13186 }); 13187 }); 13188 })["catch"](reject); 13189 }); 13190 13191 executeCallback(promise, callback); 13192 return promise; 13193 } 13194 13195 // Return the key located at key index X; essentially gets the key from a 13196 // `WHERE id = ?`. This is the most efficient way I can think to implement 13197 // this rarely-used (in my experience) part of the API, but it can seem 13198 // inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so 13199 // the ID of each key will change every time it's updated. Perhaps a stored 13200 // procedure for the `setItem()` SQL would solve this problem? 13201 // TODO: Don't change ID on `setItem()`. 13202 function key$1(n, callback) { 13203 var self = this; 13204 13205 var promise = new Promise$1(function (resolve, reject) { 13206 self.ready().then(function () { 13207 var dbInfo = self._dbInfo; 13208 dbInfo.db.transaction(function (t) { 13209 tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) { 13210 var result = results.rows.length ? results.rows.item(0).key : null; 13211 resolve(result); 13212 }, function (t, error) { 13213 reject(error); 13214 }); 13215 }); 13216 })["catch"](reject); 13217 }); 13218 13219 executeCallback(promise, callback); 13220 return promise; 13221 } 13222 13223 function keys$1(callback) { 13224 var self = this; 13225 13226 var promise = new Promise$1(function (resolve, reject) { 13227 self.ready().then(function () { 13228 var dbInfo = self._dbInfo; 13229 dbInfo.db.transaction(function (t) { 13230 tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName, [], function (t, results) { 13231 var keys = []; 13232 13233 for (var i = 0; i < results.rows.length; i++) { 13234 keys.push(results.rows.item(i).key); 13235 } 13236 13237 resolve(keys); 13238 }, function (t, error) { 13239 reject(error); 13240 }); 13241 }); 13242 })["catch"](reject); 13243 }); 13244 13245 executeCallback(promise, callback); 13246 return promise; 13247 } 13248 13249 // https://www.w3.org/TR/webdatabase/#databases 13250 // > There is no way to enumerate or delete the databases available for an origin from this API. 13251 function getAllStoreNames(db) { 13252 return new Promise$1(function (resolve, reject) { 13253 db.transaction(function (t) { 13254 t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function (t, results) { 13255 var storeNames = []; 13256 13257 for (var i = 0; i < results.rows.length; i++) { 13258 storeNames.push(results.rows.item(i).name); 13259 } 13260 13261 resolve({ 13262 db: db, 13263 storeNames: storeNames 13264 }); 13265 }, function (t, error) { 13266 reject(error); 13267 }); 13268 }, function (sqlError) { 13269 reject(sqlError); 13270 }); 13271 }); 13272 } 13273 13274 function dropInstance$1(options, callback) { 13275 callback = getCallback.apply(this, arguments); 13276 13277 var currentConfig = this.config(); 13278 options = typeof options !== 'function' && options || {}; 13279 if (!options.name) { 13280 options.name = options.name || currentConfig.name; 13281 options.storeName = options.storeName || currentConfig.storeName; 13282 } 13283 13284 var self = this; 13285 var promise; 13286 if (!options.name) { 13287 promise = Promise$1.reject('Invalid arguments'); 13288 } else { 13289 promise = new Promise$1(function (resolve) { 13290 var db; 13291 if (options.name === currentConfig.name) { 13292 // use the db reference of the current instance 13293 db = self._dbInfo.db; 13294 } else { 13295 db = openDatabase(options.name, '', '', 0); 13296 } 13297 13298 if (!options.storeName) { 13299 // drop all database tables 13300 resolve(getAllStoreNames(db)); 13301 } else { 13302 resolve({ 13303 db: db, 13304 storeNames: [options.storeName] 13305 }); 13306 } 13307 }).then(function (operationInfo) { 13308 return new Promise$1(function (resolve, reject) { 13309 operationInfo.db.transaction(function (t) { 13310 function dropTable(storeName) { 13311 return new Promise$1(function (resolve, reject) { 13312 t.executeSql('DROP TABLE IF EXISTS ' + storeName, [], function () { 13313 resolve(); 13314 }, function (t, error) { 13315 reject(error); 13316 }); 13317 }); 13318 } 13319 13320 var operations = []; 13321 for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) { 13322 operations.push(dropTable(operationInfo.storeNames[i])); 13323 } 13324 13325 Promise$1.all(operations).then(function () { 13326 resolve(); 13327 })["catch"](function (e) { 13328 reject(e); 13329 }); 13330 }, function (sqlError) { 13331 reject(sqlError); 13332 }); 13333 }); 13334 }); 13335 } 13336 13337 executeCallback(promise, callback); 13338 return promise; 13339 } 13340 13341 var webSQLStorage = { 13342 _driver: 'webSQLStorage', 13343 _initStorage: _initStorage$1, 13344 _support: isWebSQLValid(), 13345 iterate: iterate$1, 13346 getItem: getItem$1, 13347 setItem: setItem$1, 13348 removeItem: removeItem$1, 13349 clear: clear$1, 13350 length: length$1, 13351 key: key$1, 13352 keys: keys$1, 13353 dropInstance: dropInstance$1 13354 }; 13355 13356 function isLocalStorageValid() { 13357 try { 13358 return typeof localStorage !== 'undefined' && 'setItem' in localStorage && 13359 // in IE8 typeof localStorage.setItem === 'object' 13360 !!localStorage.setItem; 13361 } catch (e) { 13362 return false; 13363 } 13364 } 13365 13366 function _getKeyPrefix(options, defaultConfig) { 13367 var keyPrefix = options.name + '/'; 13368 13369 if (options.storeName !== defaultConfig.storeName) { 13370 keyPrefix += options.storeName + '/'; 13371 } 13372 return keyPrefix; 13373 } 13374 13375 // Check if localStorage throws when saving an item 13376 function checkIfLocalStorageThrows() { 13377 var localStorageTestKey = '_localforage_support_test'; 13378 13379 try { 13380 localStorage.setItem(localStorageTestKey, true); 13381 localStorage.removeItem(localStorageTestKey); 13382 13383 return false; 13384 } catch (e) { 13385 return true; 13386 } 13387 } 13388 13389 // Check if localStorage is usable and allows to save an item 13390 // This method checks if localStorage is usable in Safari Private Browsing 13391 // mode, or in any other case where the available quota for localStorage 13392 // is 0 and there wasn't any saved items yet. 13393 function _isLocalStorageUsable() { 13394 return !checkIfLocalStorageThrows() || localStorage.length > 0; 13395 } 13396 13397 // Config the localStorage backend, using options set in the config. 13398 function _initStorage$2(options) { 13399 var self = this; 13400 var dbInfo = {}; 13401 if (options) { 13402 for (var i in options) { 13403 dbInfo[i] = options[i]; 13404 } 13405 } 13406 13407 dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig); 13408 13409 if (!_isLocalStorageUsable()) { 13410 return Promise$1.reject(); 13411 } 13412 13413 self._dbInfo = dbInfo; 13414 dbInfo.serializer = localforageSerializer; 13415 13416 return Promise$1.resolve(); 13417 } 13418 13419 // Remove all keys from the datastore, effectively destroying all data in 13420 // the app's key/value store! 13421 function clear$2(callback) { 13422 var self = this; 13423 var promise = self.ready().then(function () { 13424 var keyPrefix = self._dbInfo.keyPrefix; 13425 13426 for (var i = localStorage.length - 1; i >= 0; i--) { 13427 var key = localStorage.key(i); 13428 13429 if (key.indexOf(keyPrefix) === 0) { 13430 localStorage.removeItem(key); 13431 } 13432 } 13433 }); 13434 13435 executeCallback(promise, callback); 13436 return promise; 13437 } 13438 13439 // Retrieve an item from the store. Unlike the original async_storage 13440 // library in Gaia, we don't modify return values at all. If a key's value 13441 // is `undefined`, we pass that value to the callback function. 13442 function getItem$2(key, callback) { 13443 var self = this; 13444 13445 key = normalizeKey(key); 13446 13447 var promise = self.ready().then(function () { 13448 var dbInfo = self._dbInfo; 13449 var result = localStorage.getItem(dbInfo.keyPrefix + key); 13450 13451 // If a result was found, parse it from the serialized 13452 // string into a JS object. If result isn't truthy, the key 13453 // is likely undefined and we'll pass it straight to the 13454 // callback. 13455 if (result) { 13456 result = dbInfo.serializer.deserialize(result); 13457 } 13458 13459 return result; 13460 }); 13461 13462 executeCallback(promise, callback); 13463 return promise; 13464 } 13465 13466 // Iterate over all items in the store. 13467 function iterate$2(iterator, callback) { 13468 var self = this; 13469 13470 var promise = self.ready().then(function () { 13471 var dbInfo = self._dbInfo; 13472 var keyPrefix = dbInfo.keyPrefix; 13473 var keyPrefixLength = keyPrefix.length; 13474 var length = localStorage.length; 13475 13476 // We use a dedicated iterator instead of the `i` variable below 13477 // so other keys we fetch in localStorage aren't counted in 13478 // the `iterationNumber` argument passed to the `iterate()` 13479 // callback. 13480 // 13481 // See: github.com/mozilla/localForage/pull/435#discussion_r38061530 13482 var iterationNumber = 1; 13483 13484 for (var i = 0; i < length; i++) { 13485 var key = localStorage.key(i); 13486 if (key.indexOf(keyPrefix) !== 0) { 13487 continue; 13488 } 13489 var value = localStorage.getItem(key); 13490 13491 // If a result was found, parse it from the serialized 13492 // string into a JS object. If result isn't truthy, the 13493 // key is likely undefined and we'll pass it straight 13494 // to the iterator. 13495 if (value) { 13496 value = dbInfo.serializer.deserialize(value); 13497 } 13498 13499 value = iterator(value, key.substring(keyPrefixLength), iterationNumber++); 13500 13501 if (value !== void 0) { 13502 return value; 13503 } 13504 } 13505 }); 13506 13507 executeCallback(promise, callback); 13508 return promise; 13509 } 13510 13511 // Same as localStorage's key() method, except takes a callback. 13512 function key$2(n, callback) { 13513 var self = this; 13514 var promise = self.ready().then(function () { 13515 var dbInfo = self._dbInfo; 13516 var result; 13517 try { 13518 result = localStorage.key(n); 13519 } catch (error) { 13520 result = null; 13521 } 13522 13523 // Remove the prefix from the key, if a key is found. 13524 if (result) { 13525 result = result.substring(dbInfo.keyPrefix.length); 13526 } 13527 13528 return result; 13529 }); 13530 13531 executeCallback(promise, callback); 13532 return promise; 13533 } 13534 13535 function keys$2(callback) { 13536 var self = this; 13537 var promise = self.ready().then(function () { 13538 var dbInfo = self._dbInfo; 13539 var length = localStorage.length; 13540 var keys = []; 13541 13542 for (var i = 0; i < length; i++) { 13543 var itemKey = localStorage.key(i); 13544 if (itemKey.indexOf(dbInfo.keyPrefix) === 0) { 13545 keys.push(itemKey.substring(dbInfo.keyPrefix.length)); 13546 } 13547 } 13548 13549 return keys; 13550 }); 13551 13552 executeCallback(promise, callback); 13553 return promise; 13554 } 13555 13556 // Supply the number of keys in the datastore to the callback function. 13557 function length$2(callback) { 13558 var self = this; 13559 var promise = self.keys().then(function (keys) { 13560 return keys.length; 13561 }); 13562 13563 executeCallback(promise, callback); 13564 return promise; 13565 } 13566 13567 // Remove an item from the store, nice and simple. 13568 function removeItem$2(key, callback) { 13569 var self = this; 13570 13571 key = normalizeKey(key); 13572 13573 var promise = self.ready().then(function () { 13574 var dbInfo = self._dbInfo; 13575 localStorage.removeItem(dbInfo.keyPrefix + key); 13576 }); 13577 13578 executeCallback(promise, callback); 13579 return promise; 13580 } 13581 13582 // Set a key's value and run an optional callback once the value is set. 13583 // Unlike Gaia's implementation, the callback function is passed the value, 13584 // in case you want to operate on that value only after you're sure it 13585 // saved, or something like that. 13586 function setItem$2(key, value, callback) { 13587 var self = this; 13588 13589 key = normalizeKey(key); 13590 13591 var promise = self.ready().then(function () { 13592 // Convert undefined values to null. 13593 // https://github.com/mozilla/localForage/pull/42 13594 if (value === undefined) { 13595 value = null; 13596 } 13597 13598 // Save the original value to pass to the callback. 13599 var originalValue = value; 13600 13601 return new Promise$1(function (resolve, reject) { 13602 var dbInfo = self._dbInfo; 13603 dbInfo.serializer.serialize(value, function (value, error) { 13604 if (error) { 13605 reject(error); 13606 } else { 13607 try { 13608 localStorage.setItem(dbInfo.keyPrefix + key, value); 13609 resolve(originalValue); 13610 } catch (e) { 13611 // localStorage capacity exceeded. 13612 // TODO: Make this a specific error/event. 13613 if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') { 13614 reject(e); 13615 } 13616 reject(e); 13617 } 13618 } 13619 }); 13620 }); 13621 }); 13622 13623 executeCallback(promise, callback); 13624 return promise; 13625 } 13626 13627 function dropInstance$2(options, callback) { 13628 callback = getCallback.apply(this, arguments); 13629 13630 options = typeof options !== 'function' && options || {}; 13631 if (!options.name) { 13632 var currentConfig = this.config(); 13633 options.name = options.name || currentConfig.name; 13634 options.storeName = options.storeName || currentConfig.storeName; 13635 } 13636 13637 var self = this; 13638 var promise; 13639 if (!options.name) { 13640 promise = Promise$1.reject('Invalid arguments'); 13641 } else { 13642 promise = new Promise$1(function (resolve) { 13643 if (!options.storeName) { 13644 resolve(options.name + '/'); 13645 } else { 13646 resolve(_getKeyPrefix(options, self._defaultConfig)); 13647 } 13648 }).then(function (keyPrefix) { 13649 for (var i = localStorage.length - 1; i >= 0; i--) { 13650 var key = localStorage.key(i); 13651 13652 if (key.indexOf(keyPrefix) === 0) { 13653 localStorage.removeItem(key); 13654 } 13655 } 13656 }); 13657 } 13658 13659 executeCallback(promise, callback); 13660 return promise; 13661 } 13662 13663 var localStorageWrapper = { 13664 _driver: 'localStorageWrapper', 13665 _initStorage: _initStorage$2, 13666 _support: isLocalStorageValid(), 13667 iterate: iterate$2, 13668 getItem: getItem$2, 13669 setItem: setItem$2, 13670 removeItem: removeItem$2, 13671 clear: clear$2, 13672 length: length$2, 13673 key: key$2, 13674 keys: keys$2, 13675 dropInstance: dropInstance$2 13676 }; 13677 13678 var sameValue = function sameValue(x, y) { 13679 return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y); 13680 }; 13681 13682 var includes = function includes(array, searchElement) { 13683 var len = array.length; 13684 var i = 0; 13685 while (i < len) { 13686 if (sameValue(array[i], searchElement)) { 13687 return true; 13688 } 13689 i++; 13690 } 13691 13692 return false; 13693 }; 13694 13695 var isArray = Array.isArray || function (arg) { 13696 return Object.prototype.toString.call(arg) === '[object Array]'; 13697 }; 13698 13699 // Drivers are stored here when `defineDriver()` is called. 13700 // They are shared across all instances of localForage. 13701 var DefinedDrivers = {}; 13702 13703 var DriverSupport = {}; 13704 13705 var DefaultDrivers = { 13706 INDEXEDDB: asyncStorage, 13707 WEBSQL: webSQLStorage, 13708 LOCALSTORAGE: localStorageWrapper 13709 }; 13710 13711 var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver]; 13712 13713 var OptionalDriverMethods = ['dropInstance']; 13714 13715 var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods); 13716 13717 var DefaultConfig = { 13718 description: '', 13719 driver: DefaultDriverOrder.slice(), 13720 name: 'localforage', 13721 // Default DB size is _JUST UNDER_ 5MB, as it's the highest size 13722 // we can use without a prompt. 13723 size: 4980736, 13724 storeName: 'keyvaluepairs', 13725 version: 1.0 13726 }; 13727 13728 function callWhenReady(localForageInstance, libraryMethod) { 13729 localForageInstance[libraryMethod] = function () { 13730 var _args = arguments; 13731 return localForageInstance.ready().then(function () { 13732 return localForageInstance[libraryMethod].apply(localForageInstance, _args); 13733 }); 13734 }; 13735 } 13736 13737 function extend() { 13738 for (var i = 1; i < arguments.length; i++) { 13739 var arg = arguments[i]; 13740 13741 if (arg) { 13742 for (var _key in arg) { 13743 if (arg.hasOwnProperty(_key)) { 13744 if (isArray(arg[_key])) { 13745 arguments[0][_key] = arg[_key].slice(); 13746 } else { 13747 arguments[0][_key] = arg[_key]; 13748 } 13749 } 13750 } 13751 } 13752 } 13753 13754 return arguments[0]; 13755 } 13756 13757 var LocalForage = function () { 13758 function LocalForage(options) { 13759 _classCallCheck(this, LocalForage); 13760 13761 for (var driverTypeKey in DefaultDrivers) { 13762 if (DefaultDrivers.hasOwnProperty(driverTypeKey)) { 13763 var driver = DefaultDrivers[driverTypeKey]; 13764 var driverName = driver._driver; 13765 this[driverTypeKey] = driverName; 13766 13767 if (!DefinedDrivers[driverName]) { 13768 // we don't need to wait for the promise, 13769 // since the default drivers can be defined 13770 // in a blocking manner 13771 this.defineDriver(driver); 13772 } 13773 } 13774 } 13775 13776 this._defaultConfig = extend({}, DefaultConfig); 13777 this._config = extend({}, this._defaultConfig, options); 13778 this._driverSet = null; 13779 this._initDriver = null; 13780 this._ready = false; 13781 this._dbInfo = null; 13782 13783 this._wrapLibraryMethodsWithReady(); 13784 this.setDriver(this._config.driver)["catch"](function () {}); 13785 } 13786 13787 // Set any config values for localForage; can be called anytime before 13788 // the first API call (e.g. `getItem`, `setItem`). 13789 // We loop through options so we don't overwrite existing config 13790 // values. 13791 13792 13793 LocalForage.prototype.config = function config(options) { 13794 // If the options argument is an object, we use it to set values. 13795 // Otherwise, we return either a specified config value or all 13796 // config values. 13797 if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') { 13798 // If localforage is ready and fully initialized, we can't set 13799 // any new configuration values. Instead, we return an error. 13800 if (this._ready) { 13801 return new Error("Can't call config() after localforage " + 'has been used.'); 13802 } 13803 13804 for (var i in options) { 13805 if (i === 'storeName') { 13806 options[i] = options[i].replace(/\W/g, '_'); 13807 } 13808 13809 if (i === 'version' && typeof options[i] !== 'number') { 13810 return new Error('Database version must be a number.'); 13811 } 13812 13813 this._config[i] = options[i]; 13814 } 13815 13816 // after all config options are set and 13817 // the driver option is used, try setting it 13818 if ('driver' in options && options.driver) { 13819 return this.setDriver(this._config.driver); 13820 } 13821 13822 return true; 13823 } else if (typeof options === 'string') { 13824 return this._config[options]; 13825 } else { 13826 return this._config; 13827 } 13828 }; 13829 13830 // Used to define a custom driver, shared across all instances of 13831 // localForage. 13832 13833 13834 LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) { 13835 var promise = new Promise$1(function (resolve, reject) { 13836 try { 13837 var driverName = driverObject._driver; 13838 var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver'); 13839 13840 // A driver name should be defined and not overlap with the 13841 // library-defined, default drivers. 13842 if (!driverObject._driver) { 13843 reject(complianceError); 13844 return; 13845 } 13846 13847 var driverMethods = LibraryMethods.concat('_initStorage'); 13848 for (var i = 0, len = driverMethods.length; i < len; i++) { 13849 var driverMethodName = driverMethods[i]; 13850 13851 // when the property is there, 13852 // it should be a method even when optional 13853 var isRequired = !includes(OptionalDriverMethods, driverMethodName); 13854 if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== 'function') { 13855 reject(complianceError); 13856 return; 13857 } 13858 } 13859 13860 var configureMissingMethods = function configureMissingMethods() { 13861 var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) { 13862 return function () { 13863 var error = new Error('Method ' + methodName + ' is not implemented by the current driver'); 13864 var promise = Promise$1.reject(error); 13865 executeCallback(promise, arguments[arguments.length - 1]); 13866 return promise; 13867 }; 13868 }; 13869 13870 for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) { 13871 var optionalDriverMethod = OptionalDriverMethods[_i]; 13872 if (!driverObject[optionalDriverMethod]) { 13873 driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod); 13874 } 13875 } 13876 }; 13877 13878 configureMissingMethods(); 13879 13880 var setDriverSupport = function setDriverSupport(support) { 13881 if (DefinedDrivers[driverName]) { 13882 console.info('Redefining LocalForage driver: ' + driverName); 13883 } 13884 DefinedDrivers[driverName] = driverObject; 13885 DriverSupport[driverName] = support; 13886 // don't use a then, so that we can define 13887 // drivers that have simple _support methods 13888 // in a blocking manner 13889 resolve(); 13890 }; 13891 13892 if ('_support' in driverObject) { 13893 if (driverObject._support && typeof driverObject._support === 'function') { 13894 driverObject._support().then(setDriverSupport, reject); 13895 } else { 13896 setDriverSupport(!!driverObject._support); 13897 } 13898 } else { 13899 setDriverSupport(true); 13900 } 13901 } catch (e) { 13902 reject(e); 13903 } 13904 }); 13905 13906 executeTwoCallbacks(promise, callback, errorCallback); 13907 return promise; 13908 }; 13909 13910 LocalForage.prototype.driver = function driver() { 13911 return this._driver || null; 13912 }; 13913 13914 LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) { 13915 var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error('Driver not found.')); 13916 13917 executeTwoCallbacks(getDriverPromise, callback, errorCallback); 13918 return getDriverPromise; 13919 }; 13920 13921 LocalForage.prototype.getSerializer = function getSerializer(callback) { 13922 var serializerPromise = Promise$1.resolve(localforageSerializer); 13923 executeTwoCallbacks(serializerPromise, callback); 13924 return serializerPromise; 13925 }; 13926 13927 LocalForage.prototype.ready = function ready(callback) { 13928 var self = this; 13929 13930 var promise = self._driverSet.then(function () { 13931 if (self._ready === null) { 13932 self._ready = self._initDriver(); 13933 } 13934 13935 return self._ready; 13936 }); 13937 13938 executeTwoCallbacks(promise, callback, callback); 13939 return promise; 13940 }; 13941 13942 LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) { 13943 var self = this; 13944 13945 if (!isArray(drivers)) { 13946 drivers = [drivers]; 13947 } 13948 13949 var supportedDrivers = this._getSupportedDrivers(drivers); 13950 13951 function setDriverToConfig() { 13952 self._config.driver = self.driver(); 13953 } 13954 13955 function extendSelfWithDriver(driver) { 13956 self._extend(driver); 13957 setDriverToConfig(); 13958 13959 self._ready = self._initStorage(self._config); 13960 return self._ready; 13961 } 13962 13963 function initDriver(supportedDrivers) { 13964 return function () { 13965 var currentDriverIndex = 0; 13966 13967 function driverPromiseLoop() { 13968 while (currentDriverIndex < supportedDrivers.length) { 13969 var driverName = supportedDrivers[currentDriverIndex]; 13970 currentDriverIndex++; 13971 13972 self._dbInfo = null; 13973 self._ready = null; 13974 13975 return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop); 13976 } 13977 13978 setDriverToConfig(); 13979 var error = new Error('No available storage method found.'); 13980 self._driverSet = Promise$1.reject(error); 13981 return self._driverSet; 13982 } 13983 13984 return driverPromiseLoop(); 13985 }; 13986 } 13987 13988 // There might be a driver initialization in progress 13989 // so wait for it to finish in order to avoid a possible 13990 // race condition to set _dbInfo 13991 var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function () { 13992 return Promise$1.resolve(); 13993 }) : Promise$1.resolve(); 13994 13995 this._driverSet = oldDriverSetDone.then(function () { 13996 var driverName = supportedDrivers[0]; 13997 self._dbInfo = null; 13998 self._ready = null; 13999 14000 return self.getDriver(driverName).then(function (driver) { 14001 self._driver = driver._driver; 14002 setDriverToConfig(); 14003 self._wrapLibraryMethodsWithReady(); 14004 self._initDriver = initDriver(supportedDrivers); 14005 }); 14006 })["catch"](function () { 14007 setDriverToConfig(); 14008 var error = new Error('No available storage method found.'); 14009 self._driverSet = Promise$1.reject(error); 14010 return self._driverSet; 14011 }); 14012 14013 executeTwoCallbacks(this._driverSet, callback, errorCallback); 14014 return this._driverSet; 14015 }; 14016 14017 LocalForage.prototype.supports = function supports(driverName) { 14018 return !!DriverSupport[driverName]; 14019 }; 14020 14021 LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) { 14022 extend(this, libraryMethodsAndProperties); 14023 }; 14024 14025 LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) { 14026 var supportedDrivers = []; 14027 for (var i = 0, len = drivers.length; i < len; i++) { 14028 var driverName = drivers[i]; 14029 if (this.supports(driverName)) { 14030 supportedDrivers.push(driverName); 14031 } 14032 } 14033 return supportedDrivers; 14034 }; 14035 14036 LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() { 14037 // Add a stub for each driver API method that delays the call to the 14038 // corresponding driver method until localForage is ready. These stubs 14039 // will be replaced by the driver methods as soon as the driver is 14040 // loaded, so there is no performance impact. 14041 for (var i = 0, len = LibraryMethods.length; i < len; i++) { 14042 callWhenReady(this, LibraryMethods[i]); 14043 } 14044 }; 14045 14046 LocalForage.prototype.createInstance = function createInstance(options) { 14047 return new LocalForage(options); 14048 }; 14049 14050 return LocalForage; 14051 }(); 14052 14053 // The actual localForage object that we expose as a module or via a 14054 // global. It's extended by pulling in one of our other libraries. 14055 14056 14057 var localforage_js = new LocalForage(); 14058 14059 module.exports = localforage_js; 14060 14061 },{"3":3}]},{},[4])(4) 14062 }); 14063 14064 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(17))) 14065 14066 /***/ }), 14067 /* 24 */ 14068 /***/ (function(module, __webpack_exports__, __webpack_require__) { 14069 14070 "use strict"; 14071 14072 // EXTERNAL MODULE: ./node_modules/event-emitter/index.js 14073 var event_emitter = __webpack_require__(3); 14074 var event_emitter_default = /*#__PURE__*/__webpack_require__.n(event_emitter); 14075 14076 // EXTERNAL MODULE: ./src/utils/core.js 14077 var core = __webpack_require__(0); 14078 14079 // EXTERNAL MODULE: ./src/utils/url.js 14080 var utils_url = __webpack_require__(5); 14081 14082 // EXTERNAL MODULE: ./src/utils/path.js 14083 var utils_path = __webpack_require__(4); 14084 14085 // EXTERNAL MODULE: ./src/epubcfi.js 14086 var epubcfi = __webpack_require__(2); 14087 14088 // EXTERNAL MODULE: ./src/utils/hook.js 14089 var hook = __webpack_require__(6); 14090 14091 // EXTERNAL MODULE: ./src/utils/replacements.js 14092 var replacements = __webpack_require__(8); 14093 14094 // CONCATENATED MODULE: ./src/utils/request.js 14095 14096 14097 14098 function request_request(url, type, withCredentials, headers) { 14099 var supportsURL = typeof window != "undefined" ? window.URL : false; // TODO: fallback for url if window isn't defined 14100 14101 var BLOB_RESPONSE = supportsURL ? "blob" : "arraybuffer"; 14102 var deferred = new core["defer"](); 14103 var xhr = new XMLHttpRequest(); //-- Check from PDF.js: 14104 // https://github.com/mozilla/pdf.js/blob/master/web/compatibility.js 14105 14106 var xhrPrototype = XMLHttpRequest.prototype; 14107 var header; 14108 14109 if (!("overrideMimeType" in xhrPrototype)) { 14110 // IE10 might have response, but not overrideMimeType 14111 Object.defineProperty(xhrPrototype, "overrideMimeType", { 14112 value: function xmlHttpRequestOverrideMimeType() {} 14113 }); 14114 } 14115 14116 if (withCredentials) { 14117 xhr.withCredentials = true; 14118 } 14119 14120 xhr.onreadystatechange = handler; 14121 xhr.onerror = err; 14122 xhr.open("GET", url, true); 14123 14124 for (header in headers) { 14125 xhr.setRequestHeader(header, headers[header]); 14126 } 14127 14128 if (type == "json") { 14129 xhr.setRequestHeader("Accept", "application/json"); 14130 } // If type isn"t set, determine it from the file extension 14131 14132 14133 if (!type) { 14134 type = new utils_path["a" /* default */](url).extension; 14135 } 14136 14137 if (type == "blob") { 14138 xhr.responseType = BLOB_RESPONSE; 14139 } 14140 14141 if (Object(core["isXml"])(type)) { 14142 // xhr.responseType = "document"; 14143 xhr.overrideMimeType("text/xml"); // for OPF parsing 14144 } 14145 14146 if (type == "xhtml") {// xhr.responseType = "document"; 14147 } 14148 14149 if (type == "html" || type == "htm") {// xhr.responseType = "document"; 14150 } 14151 14152 if (type == "binary") { 14153 xhr.responseType = "arraybuffer"; 14154 } 14155 14156 xhr.send(); 14157 14158 function err(e) { 14159 deferred.reject(e); 14160 } 14161 14162 function handler() { 14163 if (this.readyState === XMLHttpRequest.DONE) { 14164 var responseXML = false; 14165 14166 if (this.responseType === "" || this.responseType === "document") { 14167 responseXML = this.responseXML; 14168 } 14169 14170 if (this.status === 200 || this.status === 0 || responseXML) { 14171 //-- Firefox is reporting 0 for blob urls 14172 var r; 14173 14174 if (!this.response && !responseXML) { 14175 deferred.reject({ 14176 status: this.status, 14177 message: "Empty Response", 14178 stack: new Error().stack 14179 }); 14180 return deferred.promise; 14181 } 14182 14183 if (this.status === 403) { 14184 deferred.reject({ 14185 status: this.status, 14186 response: this.response, 14187 message: "Forbidden", 14188 stack: new Error().stack 14189 }); 14190 return deferred.promise; 14191 } 14192 14193 if (responseXML) { 14194 r = this.responseXML; 14195 } else if (Object(core["isXml"])(type)) { 14196 // xhr.overrideMimeType("text/xml"); // for OPF parsing 14197 // If this.responseXML wasn't set, try to parse using a DOMParser from text 14198 r = Object(core["parse"])(this.response, "text/xml"); 14199 } else if (type == "xhtml") { 14200 r = Object(core["parse"])(this.response, "application/xhtml+xml"); 14201 } else if (type == "html" || type == "htm") { 14202 r = Object(core["parse"])(this.response, "text/html"); 14203 } else if (type == "json") { 14204 r = JSON.parse(this.response); 14205 } else if (type == "blob") { 14206 if (supportsURL) { 14207 r = this.response; 14208 } else { 14209 //-- Safari doesn't support responseType blob, so create a blob from arraybuffer 14210 r = new Blob([this.response]); 14211 } 14212 } else { 14213 r = this.response; 14214 } 14215 14216 deferred.resolve(r); 14217 } else { 14218 deferred.reject({ 14219 status: this.status, 14220 message: this.response, 14221 stack: new Error().stack 14222 }); 14223 } 14224 } 14225 } 14226 14227 return deferred.promise; 14228 } 14229 14230 /* harmony default export */ var utils_request = (request_request); 14231 // EXTERNAL MODULE: ./node_modules/@xmldom/xmldom/lib/index.js 14232 var lib = __webpack_require__(15); 14233 14234 // CONCATENATED MODULE: ./src/section.js 14235 14236 14237 14238 14239 14240 14241 14242 /** 14243 * Represents a Section of the Book 14244 * 14245 * In most books this is equivalent to a Chapter 14246 * @param {object} item The spine item representing the section 14247 * @param {object} hooks hooks for serialize and content 14248 */ 14249 14250 class section_Section { 14251 constructor(item, hooks) { 14252 this.idref = item.idref; 14253 this.linear = item.linear === "yes"; 14254 this.properties = item.properties; 14255 this.index = item.index; 14256 this.href = item.href; 14257 this.url = item.url; 14258 this.canonical = item.canonical; 14259 this.next = item.next; 14260 this.prev = item.prev; 14261 this.cfiBase = item.cfiBase; 14262 14263 if (hooks) { 14264 this.hooks = hooks; 14265 } else { 14266 this.hooks = {}; 14267 this.hooks.serialize = new hook["a" /* default */](this); 14268 this.hooks.content = new hook["a" /* default */](this); 14269 } 14270 14271 this.document = undefined; 14272 this.contents = undefined; 14273 this.output = undefined; 14274 } 14275 /** 14276 * Load the section from its url 14277 * @param {method} [_request] a request method to use for loading 14278 * @return {document} a promise with the xml document 14279 */ 14280 14281 14282 load(_request) { 14283 var request = _request || this.request || utils_request; 14284 var loading = new core["defer"](); 14285 var loaded = loading.promise; 14286 14287 if (this.contents) { 14288 loading.resolve(this.contents); 14289 } else { 14290 request(this.url).then(function (xml) { 14291 // var directory = new Url(this.url).directory; 14292 this.document = xml; 14293 this.contents = xml.documentElement; 14294 return this.hooks.content.trigger(this.document, this); 14295 }.bind(this)).then(function () { 14296 loading.resolve(this.contents); 14297 }.bind(this)).catch(function (error) { 14298 loading.reject(error); 14299 }); 14300 } 14301 14302 return loaded; 14303 } 14304 /** 14305 * Adds a base tag for resolving urls in the section 14306 * @private 14307 */ 14308 14309 14310 base() { 14311 return Object(replacements["a" /* replaceBase */])(this.document, this); 14312 } 14313 /** 14314 * Render the contents of a section 14315 * @param {method} [_request] a request method to use for loading 14316 * @return {string} output a serialized XML Document 14317 */ 14318 14319 14320 render(_request) { 14321 var rendering = new core["defer"](); 14322 var rendered = rendering.promise; 14323 this.output; // TODO: better way to return this from hooks? 14324 14325 this.load(_request).then(function (contents) { 14326 var userAgent = typeof navigator !== 'undefined' && navigator.userAgent || ''; 14327 var isIE = userAgent.indexOf('Trident') >= 0; 14328 var Serializer; 14329 14330 if (typeof XMLSerializer === "undefined" || isIE) { 14331 Serializer = lib["DOMParser"]; 14332 } else { 14333 Serializer = XMLSerializer; 14334 } 14335 14336 var serializer = new Serializer(); 14337 this.output = serializer.serializeToString(contents); 14338 return this.output; 14339 }.bind(this)).then(function () { 14340 return this.hooks.serialize.trigger(this.output, this); 14341 }.bind(this)).then(function () { 14342 rendering.resolve(this.output); 14343 }.bind(this)).catch(function (error) { 14344 rendering.reject(error); 14345 }); 14346 return rendered; 14347 } 14348 /** 14349 * Find a string in a section 14350 * @param {string} _query The query string to find 14351 * @return {object[]} A list of matches, with form {cfi, excerpt} 14352 */ 14353 14354 14355 find(_query) { 14356 var section = this; 14357 var matches = []; 14358 14359 var query = _query.toLowerCase(); 14360 14361 var find = function (node) { 14362 var text = node.textContent.toLowerCase(); 14363 var range = section.document.createRange(); 14364 var cfi; 14365 var pos; 14366 var last = -1; 14367 var excerpt; 14368 var limit = 150; 14369 14370 while (pos != -1) { 14371 // Search for the query 14372 pos = text.indexOf(query, last + 1); 14373 14374 if (pos != -1) { 14375 // We found it! Generate a CFI 14376 range = section.document.createRange(); 14377 range.setStart(node, pos); 14378 range.setEnd(node, pos + query.length); 14379 cfi = section.cfiFromRange(range); // Generate the excerpt 14380 14381 if (node.textContent.length < limit) { 14382 excerpt = node.textContent; 14383 } else { 14384 excerpt = node.textContent.substring(pos - limit / 2, pos + limit / 2); 14385 excerpt = "..." + excerpt + "..."; 14386 } // Add the CFI to the matches list 14387 14388 14389 matches.push({ 14390 cfi: cfi, 14391 excerpt: excerpt 14392 }); 14393 } 14394 14395 last = pos; 14396 } 14397 }; 14398 14399 Object(core["sprint"])(section.document, function (node) { 14400 find(node); 14401 }); 14402 return matches; 14403 } 14404 14405 /** 14406 * Search a string in multiple sequential Element of the section. If the document.createTreeWalker api is missed(eg: IE8), use `find` as a fallback. 14407 * @param {string} _query The query string to search 14408 * @param {int} maxSeqEle The maximum number of Element that are combined for search, default value is 5. 14409 * @return {object[]} A list of matches, with form {cfi, excerpt} 14410 */ 14411 search(_query, maxSeqEle = 5) { 14412 if (typeof document.createTreeWalker == "undefined") { 14413 return this.find(_query); 14414 } 14415 14416 let matches = []; 14417 const excerptLimit = 150; 14418 const section = this; 14419 14420 const query = _query.toLowerCase(); 14421 14422 const search = function (nodeList) { 14423 const textWithCase = nodeList.reduce((acc, current) => { 14424 return acc + current.textContent; 14425 }, ""); 14426 const text = textWithCase.toLowerCase(); 14427 const pos = text.indexOf(query); 14428 14429 if (pos != -1) { 14430 const startNodeIndex = 0, 14431 endPos = pos + query.length; 14432 let endNodeIndex = 0, 14433 l = 0; 14434 14435 if (pos < nodeList[startNodeIndex].length) { 14436 let cfi; 14437 14438 while (endNodeIndex < nodeList.length - 1) { 14439 l += nodeList[endNodeIndex].length; 14440 14441 if (endPos <= l) { 14442 break; 14443 } 14444 14445 endNodeIndex += 1; 14446 } 14447 14448 let startNode = nodeList[startNodeIndex], 14449 endNode = nodeList[endNodeIndex]; 14450 let range = section.document.createRange(); 14451 range.setStart(startNode, pos); 14452 let beforeEndLengthCount = nodeList.slice(0, endNodeIndex).reduce((acc, current) => { 14453 return acc + current.textContent.length; 14454 }, 0); 14455 range.setEnd(endNode, beforeEndLengthCount > endPos ? endPos : endPos - beforeEndLengthCount); 14456 cfi = section.cfiFromRange(range); 14457 let excerpt = nodeList.slice(0, endNodeIndex + 1).reduce((acc, current) => { 14458 return acc + current.textContent; 14459 }, ""); 14460 14461 if (excerpt.length > excerptLimit) { 14462 excerpt = excerpt.substring(pos - excerptLimit / 2, pos + excerptLimit / 2); 14463 excerpt = "..." + excerpt + "..."; 14464 } 14465 14466 matches.push({ 14467 cfi: cfi, 14468 excerpt: excerpt 14469 }); 14470 } 14471 } 14472 }; 14473 14474 const treeWalker = document.createTreeWalker(section.document, NodeFilter.SHOW_TEXT, null, false); 14475 let node, 14476 nodeList = []; 14477 14478 while (node = treeWalker.nextNode()) { 14479 nodeList.push(node); 14480 14481 if (nodeList.length == maxSeqEle) { 14482 search(nodeList.slice(0, maxSeqEle)); 14483 nodeList = nodeList.slice(1, maxSeqEle); 14484 } 14485 } 14486 14487 if (nodeList.length > 0) { 14488 search(nodeList); 14489 } 14490 14491 return matches; 14492 } 14493 /** 14494 * Reconciles the current chapters layout properties with 14495 * the global layout properties. 14496 * @param {object} globalLayout The global layout settings object, chapter properties string 14497 * @return {object} layoutProperties Object with layout properties 14498 */ 14499 14500 14501 reconcileLayoutSettings(globalLayout) { 14502 //-- Get the global defaults 14503 var settings = { 14504 layout: globalLayout.layout, 14505 spread: globalLayout.spread, 14506 orientation: globalLayout.orientation 14507 }; //-- Get the chapter's display type 14508 14509 this.properties.forEach(function (prop) { 14510 var rendition = prop.replace("rendition:", ""); 14511 var split = rendition.indexOf("-"); 14512 var property, value; 14513 14514 if (split != -1) { 14515 property = rendition.slice(0, split); 14516 value = rendition.slice(split + 1); 14517 settings[property] = value; 14518 } 14519 }); 14520 return settings; 14521 } 14522 /** 14523 * Get a CFI from a Range in the Section 14524 * @param {range} _range 14525 * @return {string} cfi an EpubCFI string 14526 */ 14527 14528 14529 cfiFromRange(_range) { 14530 return new epubcfi["a" /* default */](_range, this.cfiBase).toString(); 14531 } 14532 /** 14533 * Get a CFI from an Element in the Section 14534 * @param {element} el 14535 * @return {string} cfi an EpubCFI string 14536 */ 14537 14538 14539 cfiFromElement(el) { 14540 return new epubcfi["a" /* default */](el, this.cfiBase).toString(); 14541 } 14542 /** 14543 * Unload the section document 14544 */ 14545 14546 14547 unload() { 14548 this.document = undefined; 14549 this.contents = undefined; 14550 this.output = undefined; 14551 } 14552 14553 destroy() { 14554 this.unload(); 14555 this.hooks.serialize.clear(); 14556 this.hooks.content.clear(); 14557 this.hooks = undefined; 14558 this.idref = undefined; 14559 this.linear = undefined; 14560 this.properties = undefined; 14561 this.index = undefined; 14562 this.href = undefined; 14563 this.url = undefined; 14564 this.next = undefined; 14565 this.prev = undefined; 14566 this.cfiBase = undefined; 14567 } 14568 14569 } 14570 14571 /* harmony default export */ var src_section = (section_Section); 14572 // CONCATENATED MODULE: ./src/spine.js 14573 14574 14575 14576 14577 /** 14578 * A collection of Spine Items 14579 */ 14580 14581 class spine_Spine { 14582 constructor() { 14583 this.spineItems = []; 14584 this.spineByHref = {}; 14585 this.spineById = {}; 14586 this.hooks = {}; 14587 this.hooks.serialize = new hook["a" /* default */](); 14588 this.hooks.content = new hook["a" /* default */](); // Register replacements 14589 14590 this.hooks.content.register(replacements["a" /* replaceBase */]); 14591 this.hooks.content.register(replacements["b" /* replaceCanonical */]); 14592 this.hooks.content.register(replacements["d" /* replaceMeta */]); 14593 this.epubcfi = new epubcfi["a" /* default */](); 14594 this.loaded = false; 14595 this.items = undefined; 14596 this.manifest = undefined; 14597 this.spineNodeIndex = undefined; 14598 this.baseUrl = undefined; 14599 this.length = undefined; 14600 } 14601 /** 14602 * Unpack items from a opf into spine items 14603 * @param {Packaging} _package 14604 * @param {method} resolver URL resolver 14605 * @param {method} canonical Resolve canonical url 14606 */ 14607 14608 14609 unpack(_package, resolver, canonical) { 14610 this.items = _package.spine; 14611 this.manifest = _package.manifest; 14612 this.spineNodeIndex = _package.spineNodeIndex; 14613 this.baseUrl = _package.baseUrl || _package.basePath || ""; 14614 this.length = this.items.length; 14615 this.items.forEach((item, index) => { 14616 var manifestItem = this.manifest[item.idref]; 14617 var spineItem; 14618 item.index = index; 14619 item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.id); 14620 14621 if (item.href) { 14622 item.url = resolver(item.href, true); 14623 item.canonical = canonical(item.href); 14624 } 14625 14626 if (manifestItem) { 14627 item.href = manifestItem.href; 14628 item.url = resolver(item.href, true); 14629 item.canonical = canonical(item.href); 14630 14631 if (manifestItem.properties.length) { 14632 item.properties.push.apply(item.properties, manifestItem.properties); 14633 } 14634 } 14635 14636 if (item.linear === "yes") { 14637 item.prev = function () { 14638 let prevIndex = item.index; 14639 14640 while (prevIndex > 0) { 14641 let prev = this.get(prevIndex - 1); 14642 14643 if (prev && prev.linear) { 14644 return prev; 14645 } 14646 14647 prevIndex -= 1; 14648 } 14649 14650 return; 14651 }.bind(this); 14652 14653 item.next = function () { 14654 let nextIndex = item.index; 14655 14656 while (nextIndex < this.spineItems.length - 1) { 14657 let next = this.get(nextIndex + 1); 14658 14659 if (next && next.linear) { 14660 return next; 14661 } 14662 14663 nextIndex += 1; 14664 } 14665 14666 return; 14667 }.bind(this); 14668 } else { 14669 item.prev = function () { 14670 return; 14671 }; 14672 14673 item.next = function () { 14674 return; 14675 }; 14676 } 14677 14678 spineItem = new src_section(item, this.hooks); 14679 this.append(spineItem); 14680 }); 14681 this.loaded = true; 14682 } 14683 /** 14684 * Get an item from the spine 14685 * @param {string|number} [target] 14686 * @return {Section} section 14687 * @example spine.get(); 14688 * @example spine.get(1); 14689 * @example spine.get("chap1.html"); 14690 * @example spine.get("#id1234"); 14691 */ 14692 14693 14694 get(target) { 14695 var index = 0; 14696 14697 if (typeof target === "undefined") { 14698 while (index < this.spineItems.length) { 14699 let next = this.spineItems[index]; 14700 14701 if (next && next.linear) { 14702 break; 14703 } 14704 14705 index += 1; 14706 } 14707 } else if (this.epubcfi.isCfiString(target)) { 14708 let cfi = new epubcfi["a" /* default */](target); 14709 index = cfi.spinePos; 14710 } else if (typeof target === "number" || isNaN(target) === false) { 14711 index = target; 14712 } else if (typeof target === "string" && target.indexOf("#") === 0) { 14713 index = this.spineById[target.substring(1)]; 14714 } else if (typeof target === "string") { 14715 // Remove fragments 14716 target = target.split("#")[0]; 14717 index = this.spineByHref[target] || this.spineByHref[encodeURI(target)]; 14718 } 14719 14720 return this.spineItems[index] || null; 14721 } 14722 /** 14723 * Append a Section to the Spine 14724 * @private 14725 * @param {Section} section 14726 */ 14727 14728 14729 append(section) { 14730 var index = this.spineItems.length; 14731 section.index = index; 14732 this.spineItems.push(section); // Encode and Decode href lookups 14733 // see pr for details: https://github.com/futurepress/epub.js/pull/358 14734 14735 this.spineByHref[decodeURI(section.href)] = index; 14736 this.spineByHref[encodeURI(section.href)] = index; 14737 this.spineByHref[section.href] = index; 14738 this.spineById[section.idref] = index; 14739 return index; 14740 } 14741 /** 14742 * Prepend a Section to the Spine 14743 * @private 14744 * @param {Section} section 14745 */ 14746 14747 14748 prepend(section) { 14749 // var index = this.spineItems.unshift(section); 14750 this.spineByHref[section.href] = 0; 14751 this.spineById[section.idref] = 0; // Re-index 14752 14753 this.spineItems.forEach(function (item, index) { 14754 item.index = index; 14755 }); 14756 return 0; 14757 } // insert(section, index) { 14758 // 14759 // }; 14760 14761 /** 14762 * Remove a Section from the Spine 14763 * @private 14764 * @param {Section} section 14765 */ 14766 14767 14768 remove(section) { 14769 var index = this.spineItems.indexOf(section); 14770 14771 if (index > -1) { 14772 delete this.spineByHref[section.href]; 14773 delete this.spineById[section.idref]; 14774 return this.spineItems.splice(index, 1); 14775 } 14776 } 14777 /** 14778 * Loop over the Sections in the Spine 14779 * @return {method} forEach 14780 */ 14781 14782 14783 each() { 14784 return this.spineItems.forEach.apply(this.spineItems, arguments); 14785 } 14786 /** 14787 * Find the first Section in the Spine 14788 * @return {Section} first section 14789 */ 14790 14791 14792 first() { 14793 let index = 0; 14794 14795 do { 14796 let next = this.get(index); 14797 14798 if (next && next.linear) { 14799 return next; 14800 } 14801 14802 index += 1; 14803 } while (index < this.spineItems.length); 14804 } 14805 /** 14806 * Find the last Section in the Spine 14807 * @return {Section} last section 14808 */ 14809 14810 14811 last() { 14812 let index = this.spineItems.length - 1; 14813 14814 do { 14815 let prev = this.get(index); 14816 14817 if (prev && prev.linear) { 14818 return prev; 14819 } 14820 14821 index -= 1; 14822 } while (index >= 0); 14823 } 14824 14825 destroy() { 14826 this.each(section => section.destroy()); 14827 this.spineItems = undefined; 14828 this.spineByHref = undefined; 14829 this.spineById = undefined; 14830 this.hooks.serialize.clear(); 14831 this.hooks.content.clear(); 14832 this.hooks = undefined; 14833 this.epubcfi = undefined; 14834 this.loaded = false; 14835 this.items = undefined; 14836 this.manifest = undefined; 14837 this.spineNodeIndex = undefined; 14838 this.baseUrl = undefined; 14839 this.length = undefined; 14840 } 14841 14842 } 14843 14844 /* harmony default export */ var src_spine = (spine_Spine); 14845 // EXTERNAL MODULE: ./src/utils/queue.js 14846 var queue = __webpack_require__(9); 14847 14848 // EXTERNAL MODULE: ./src/utils/constants.js 14849 var constants = __webpack_require__(1); 14850 14851 // CONCATENATED MODULE: ./src/locations.js 14852 14853 14854 14855 14856 14857 /** 14858 * Find Locations for a Book 14859 * @param {Spine} spine 14860 * @param {request} request 14861 * @param {number} [pause=100] 14862 */ 14863 14864 class locations_Locations { 14865 constructor(spine, request, pause) { 14866 this.spine = spine; 14867 this.request = request; 14868 this.pause = pause || 100; 14869 this.q = new queue["a" /* default */](this); 14870 this.epubcfi = new epubcfi["a" /* default */](); 14871 this._locations = []; 14872 this._locationsWords = []; 14873 this.total = 0; 14874 this.break = 150; 14875 this._current = 0; 14876 this._wordCounter = 0; 14877 this.currentLocation = ''; 14878 this._currentCfi = ''; 14879 this.processingTimeout = undefined; 14880 } 14881 /** 14882 * Load all of sections in the book to generate locations 14883 * @param {int} chars how many chars to split on 14884 * @return {Promise<Array<string>>} locations 14885 */ 14886 14887 14888 generate(chars) { 14889 if (chars) { 14890 this.break = chars; 14891 } 14892 14893 this.q.pause(); 14894 this.spine.each(function (section) { 14895 if (section.linear) { 14896 this.q.enqueue(this.process.bind(this), section); 14897 } 14898 }.bind(this)); 14899 return this.q.run().then(function () { 14900 this.total = this._locations.length - 1; 14901 14902 if (this._currentCfi) { 14903 this.currentLocation = this._currentCfi; 14904 } 14905 14906 return this._locations; // console.log(this.percentage(this.book.rendition.location.start), this.percentage(this.book.rendition.location.end)); 14907 }.bind(this)); 14908 } 14909 14910 createRange() { 14911 return { 14912 startContainer: undefined, 14913 startOffset: undefined, 14914 endContainer: undefined, 14915 endOffset: undefined 14916 }; 14917 } 14918 14919 process(section) { 14920 return section.load(this.request).then(function (contents) { 14921 var completed = new core["defer"](); 14922 var locations = this.parse(contents, section.cfiBase); 14923 this._locations = this._locations.concat(locations); 14924 section.unload(); 14925 this.processingTimeout = setTimeout(() => completed.resolve(locations), this.pause); 14926 return completed.promise; 14927 }.bind(this)); 14928 } 14929 14930 parse(contents, cfiBase, chars) { 14931 var locations = []; 14932 var range; 14933 var doc = contents.ownerDocument; 14934 var body = Object(core["qs"])(doc, "body"); 14935 var counter = 0; 14936 var prev; 14937 14938 var _break = chars || this.break; 14939 14940 var parser = function (node) { 14941 var len = node.length; 14942 var dist; 14943 var pos = 0; 14944 14945 if (node.textContent.trim().length === 0) { 14946 return false; // continue 14947 } // Start range 14948 14949 14950 if (counter == 0) { 14951 range = this.createRange(); 14952 range.startContainer = node; 14953 range.startOffset = 0; 14954 } 14955 14956 dist = _break - counter; // Node is smaller than a break, 14957 // skip over it 14958 14959 if (dist > len) { 14960 counter += len; 14961 pos = len; 14962 } 14963 14964 while (pos < len) { 14965 dist = _break - counter; 14966 14967 if (counter === 0) { 14968 // Start new range 14969 pos += 1; 14970 range = this.createRange(); 14971 range.startContainer = node; 14972 range.startOffset = pos; 14973 } // pos += dist; 14974 // Gone over 14975 14976 14977 if (pos + dist >= len) { 14978 // Continue counter for next node 14979 counter += len - pos; // break 14980 14981 pos = len; // At End 14982 } else { 14983 // Advance pos 14984 pos += dist; // End the previous range 14985 14986 range.endContainer = node; 14987 range.endOffset = pos; // cfi = section.cfiFromRange(range); 14988 14989 let cfi = new epubcfi["a" /* default */](range, cfiBase).toString(); 14990 locations.push(cfi); 14991 counter = 0; 14992 } 14993 } 14994 14995 prev = node; 14996 }; 14997 14998 Object(core["sprint"])(body, parser.bind(this)); // Close remaining 14999 15000 if (range && range.startContainer && prev) { 15001 range.endContainer = prev; 15002 range.endOffset = prev.length; 15003 let cfi = new epubcfi["a" /* default */](range, cfiBase).toString(); 15004 locations.push(cfi); 15005 counter = 0; 15006 } 15007 15008 return locations; 15009 } 15010 /** 15011 * Load all of sections in the book to generate locations 15012 * @param {string} startCfi start position 15013 * @param {int} wordCount how many words to split on 15014 * @param {int} count result count 15015 * @return {object} locations 15016 */ 15017 15018 15019 generateFromWords(startCfi, wordCount, count) { 15020 var start = startCfi ? new epubcfi["a" /* default */](startCfi) : undefined; 15021 this.q.pause(); 15022 this._locationsWords = []; 15023 this._wordCounter = 0; 15024 this.spine.each(function (section) { 15025 if (section.linear) { 15026 if (start) { 15027 if (section.index >= start.spinePos) { 15028 this.q.enqueue(this.processWords.bind(this), section, wordCount, start, count); 15029 } 15030 } else { 15031 this.q.enqueue(this.processWords.bind(this), section, wordCount, start, count); 15032 } 15033 } 15034 }.bind(this)); 15035 return this.q.run().then(function () { 15036 if (this._currentCfi) { 15037 this.currentLocation = this._currentCfi; 15038 } 15039 15040 return this._locationsWords; 15041 }.bind(this)); 15042 } 15043 15044 processWords(section, wordCount, startCfi, count) { 15045 if (count && this._locationsWords.length >= count) { 15046 return Promise.resolve(); 15047 } 15048 15049 return section.load(this.request).then(function (contents) { 15050 var completed = new core["defer"](); 15051 var locations = this.parseWords(contents, section, wordCount, startCfi); 15052 var remainingCount = count - this._locationsWords.length; 15053 this._locationsWords = this._locationsWords.concat(locations.length >= count ? locations.slice(0, remainingCount) : locations); 15054 section.unload(); 15055 this.processingTimeout = setTimeout(() => completed.resolve(locations), this.pause); 15056 return completed.promise; 15057 }.bind(this)); 15058 } //http://stackoverflow.com/questions/18679576/counting-words-in-string 15059 15060 15061 countWords(s) { 15062 s = s.replace(/(^\s*)|(\s*$)/gi, ""); //exclude start and end white-space 15063 15064 s = s.replace(/[ ]{2,}/gi, " "); //2 or more space to 1 15065 15066 s = s.replace(/\n /, "\n"); // exclude newline with a start spacing 15067 15068 return s.split(" ").length; 15069 } 15070 15071 parseWords(contents, section, wordCount, startCfi) { 15072 var cfiBase = section.cfiBase; 15073 var locations = []; 15074 var doc = contents.ownerDocument; 15075 var body = Object(core["qs"])(doc, "body"); 15076 var prev; 15077 var _break = wordCount; 15078 var foundStartNode = startCfi ? startCfi.spinePos !== section.index : true; 15079 var startNode; 15080 15081 if (startCfi && section.index === startCfi.spinePos) { 15082 startNode = startCfi.findNode(startCfi.range ? startCfi.path.steps.concat(startCfi.start.steps) : startCfi.path.steps, contents.ownerDocument); 15083 } 15084 15085 var parser = function (node) { 15086 if (!foundStartNode) { 15087 if (node === startNode) { 15088 foundStartNode = true; 15089 } else { 15090 return false; 15091 } 15092 } 15093 15094 if (node.textContent.length < 10) { 15095 if (node.textContent.trim().length === 0) { 15096 return false; 15097 } 15098 } 15099 15100 var len = this.countWords(node.textContent); 15101 var dist; 15102 var pos = 0; 15103 15104 if (len === 0) { 15105 return false; // continue 15106 } 15107 15108 dist = _break - this._wordCounter; // Node is smaller than a break, 15109 // skip over it 15110 15111 if (dist > len) { 15112 this._wordCounter += len; 15113 pos = len; 15114 } 15115 15116 while (pos < len) { 15117 dist = _break - this._wordCounter; // Gone over 15118 15119 if (pos + dist >= len) { 15120 // Continue counter for next node 15121 this._wordCounter += len - pos; // break 15122 15123 pos = len; // At End 15124 } else { 15125 // Advance pos 15126 pos += dist; 15127 let cfi = new epubcfi["a" /* default */](node, cfiBase); 15128 locations.push({ 15129 cfi: cfi.toString(), 15130 wordCount: this._wordCounter 15131 }); 15132 this._wordCounter = 0; 15133 } 15134 } 15135 15136 prev = node; 15137 }; 15138 15139 Object(core["sprint"])(body, parser.bind(this)); 15140 return locations; 15141 } 15142 /** 15143 * Get a location from an EpubCFI 15144 * @param {EpubCFI} cfi 15145 * @return {number} 15146 */ 15147 15148 15149 locationFromCfi(cfi) { 15150 let loc; 15151 15152 if (epubcfi["a" /* default */].prototype.isCfiString(cfi)) { 15153 cfi = new epubcfi["a" /* default */](cfi); 15154 } // Check if the location has not been set yet 15155 15156 15157 if (this._locations.length === 0) { 15158 return -1; 15159 } 15160 15161 loc = Object(core["locationOf"])(cfi, this._locations, this.epubcfi.compare); 15162 15163 if (loc > this.total) { 15164 return this.total; 15165 } 15166 15167 return loc; 15168 } 15169 /** 15170 * Get a percentage position in locations from an EpubCFI 15171 * @param {EpubCFI} cfi 15172 * @return {number} 15173 */ 15174 15175 15176 percentageFromCfi(cfi) { 15177 if (this._locations.length === 0) { 15178 return null; 15179 } // Find closest cfi 15180 15181 15182 var loc = this.locationFromCfi(cfi); // Get percentage in total 15183 15184 return this.percentageFromLocation(loc); 15185 } 15186 /** 15187 * Get a percentage position from a location index 15188 * @param {number} location 15189 * @return {number} 15190 */ 15191 15192 15193 percentageFromLocation(loc) { 15194 if (!loc || !this.total) { 15195 return 0; 15196 } 15197 15198 return loc / this.total; 15199 } 15200 /** 15201 * Get an EpubCFI from location index 15202 * @param {number} loc 15203 * @return {EpubCFI} cfi 15204 */ 15205 15206 15207 cfiFromLocation(loc) { 15208 var cfi = -1; // check that pg is an int 15209 15210 if (typeof loc != "number") { 15211 loc = parseInt(loc); 15212 } 15213 15214 if (loc >= 0 && loc < this._locations.length) { 15215 cfi = this._locations[loc]; 15216 } 15217 15218 return cfi; 15219 } 15220 /** 15221 * Get an EpubCFI from location percentage 15222 * @param {number} percentage 15223 * @return {EpubCFI} cfi 15224 */ 15225 15226 15227 cfiFromPercentage(percentage) { 15228 let loc; 15229 15230 if (percentage > 1) { 15231 console.warn("Normalize cfiFromPercentage value to between 0 - 1"); 15232 } // Make sure 1 goes to very end 15233 15234 15235 if (percentage >= 1) { 15236 let cfi = new epubcfi["a" /* default */](this._locations[this.total]); 15237 cfi.collapse(); 15238 return cfi.toString(); 15239 } 15240 15241 loc = Math.ceil(this.total * percentage); 15242 return this.cfiFromLocation(loc); 15243 } 15244 /** 15245 * Load locations from JSON 15246 * @param {json} locations 15247 */ 15248 15249 15250 load(locations) { 15251 if (typeof locations === "string") { 15252 this._locations = JSON.parse(locations); 15253 } else { 15254 this._locations = locations; 15255 } 15256 15257 this.total = this._locations.length - 1; 15258 return this._locations; 15259 } 15260 /** 15261 * Save locations to JSON 15262 * @return {json} 15263 */ 15264 15265 15266 save() { 15267 return JSON.stringify(this._locations); 15268 } 15269 15270 getCurrent() { 15271 return this._current; 15272 } 15273 15274 setCurrent(curr) { 15275 var loc; 15276 15277 if (typeof curr == "string") { 15278 this._currentCfi = curr; 15279 } else if (typeof curr == "number") { 15280 this._current = curr; 15281 } else { 15282 return; 15283 } 15284 15285 if (this._locations.length === 0) { 15286 return; 15287 } 15288 15289 if (typeof curr == "string") { 15290 loc = this.locationFromCfi(curr); 15291 this._current = loc; 15292 } else { 15293 loc = curr; 15294 } 15295 15296 this.emit(constants["c" /* EVENTS */].LOCATIONS.CHANGED, { 15297 percentage: this.percentageFromLocation(loc) 15298 }); 15299 } 15300 /** 15301 * Get the current location 15302 */ 15303 15304 15305 get currentLocation() { 15306 return this._current; 15307 } 15308 /** 15309 * Set the current location 15310 */ 15311 15312 15313 set currentLocation(curr) { 15314 this.setCurrent(curr); 15315 } 15316 /** 15317 * Locations length 15318 */ 15319 15320 15321 length() { 15322 return this._locations.length; 15323 } 15324 15325 destroy() { 15326 this.spine = undefined; 15327 this.request = undefined; 15328 this.pause = undefined; 15329 this.q.stop(); 15330 this.q = undefined; 15331 this.epubcfi = undefined; 15332 this._locations = undefined; 15333 this.total = undefined; 15334 this.break = undefined; 15335 this._current = undefined; 15336 this.currentLocation = undefined; 15337 this._currentCfi = undefined; 15338 clearTimeout(this.processingTimeout); 15339 } 15340 15341 } 15342 15343 event_emitter_default()(locations_Locations.prototype); 15344 /* harmony default export */ var src_locations = (locations_Locations); 15345 // EXTERNAL MODULE: ./node_modules/path-webpack/path.js 15346 var path_webpack_path = __webpack_require__(7); 15347 var path_default = /*#__PURE__*/__webpack_require__.n(path_webpack_path); 15348 15349 // CONCATENATED MODULE: ./src/container.js 15350 15351 15352 /** 15353 * Handles Parsing and Accessing an Epub Container 15354 * @class 15355 * @param {document} [containerDocument] xml document 15356 */ 15357 15358 class container_Container { 15359 constructor(containerDocument) { 15360 this.packagePath = ''; 15361 this.directory = ''; 15362 this.encoding = ''; 15363 15364 if (containerDocument) { 15365 this.parse(containerDocument); 15366 } 15367 } 15368 /** 15369 * Parse the Container XML 15370 * @param {document} containerDocument 15371 */ 15372 15373 15374 parse(containerDocument) { 15375 //-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/> 15376 var rootfile; 15377 15378 if (!containerDocument) { 15379 throw new Error("Container File Not Found"); 15380 } 15381 15382 rootfile = Object(core["qs"])(containerDocument, "rootfile"); 15383 15384 if (!rootfile) { 15385 throw new Error("No RootFile Found"); 15386 } 15387 15388 this.packagePath = rootfile.getAttribute("full-path"); 15389 this.directory = path_default.a.dirname(this.packagePath); 15390 this.encoding = containerDocument.xmlEncoding; 15391 } 15392 15393 destroy() { 15394 this.packagePath = undefined; 15395 this.directory = undefined; 15396 this.encoding = undefined; 15397 } 15398 15399 } 15400 15401 /* harmony default export */ var container = (container_Container); 15402 // CONCATENATED MODULE: ./src/packaging.js 15403 15404 /** 15405 * Open Packaging Format Parser 15406 * @class 15407 * @param {document} packageDocument OPF XML 15408 */ 15409 15410 class packaging_Packaging { 15411 constructor(packageDocument) { 15412 this.manifest = {}; 15413 this.navPath = ''; 15414 this.ncxPath = ''; 15415 this.coverPath = ''; 15416 this.spineNodeIndex = 0; 15417 this.spine = []; 15418 this.metadata = {}; 15419 15420 if (packageDocument) { 15421 this.parse(packageDocument); 15422 } 15423 } 15424 /** 15425 * Parse OPF XML 15426 * @param {document} packageDocument OPF XML 15427 * @return {object} parsed package parts 15428 */ 15429 15430 15431 parse(packageDocument) { 15432 var metadataNode, manifestNode, spineNode; 15433 15434 if (!packageDocument) { 15435 throw new Error("Package File Not Found"); 15436 } 15437 15438 metadataNode = Object(core["qs"])(packageDocument, "metadata"); 15439 15440 if (!metadataNode) { 15441 throw new Error("No Metadata Found"); 15442 } 15443 15444 manifestNode = Object(core["qs"])(packageDocument, "manifest"); 15445 15446 if (!manifestNode) { 15447 throw new Error("No Manifest Found"); 15448 } 15449 15450 spineNode = Object(core["qs"])(packageDocument, "spine"); 15451 15452 if (!spineNode) { 15453 throw new Error("No Spine Found"); 15454 } 15455 15456 this.manifest = this.parseManifest(manifestNode); 15457 this.navPath = this.findNavPath(manifestNode); 15458 this.ncxPath = this.findNcxPath(manifestNode, spineNode); 15459 this.coverPath = this.findCoverPath(packageDocument); 15460 this.spineNodeIndex = Object(core["indexOfElementNode"])(spineNode); 15461 this.spine = this.parseSpine(spineNode, this.manifest); 15462 this.uniqueIdentifier = this.findUniqueIdentifier(packageDocument); 15463 this.metadata = this.parseMetadata(metadataNode); 15464 this.metadata.direction = spineNode.getAttribute("page-progression-direction"); 15465 return { 15466 "metadata": this.metadata, 15467 "spine": this.spine, 15468 "manifest": this.manifest, 15469 "navPath": this.navPath, 15470 "ncxPath": this.ncxPath, 15471 "coverPath": this.coverPath, 15472 "spineNodeIndex": this.spineNodeIndex 15473 }; 15474 } 15475 /** 15476 * Parse Metadata 15477 * @private 15478 * @param {node} xml 15479 * @return {object} metadata 15480 */ 15481 15482 15483 parseMetadata(xml) { 15484 var metadata = {}; 15485 metadata.title = this.getElementText(xml, "title"); 15486 metadata.creator = this.getElementText(xml, "creator"); 15487 metadata.description = this.getElementText(xml, "description"); 15488 metadata.pubdate = this.getElementText(xml, "date"); 15489 metadata.publisher = this.getElementText(xml, "publisher"); 15490 metadata.identifier = this.getElementText(xml, "identifier"); 15491 metadata.language = this.getElementText(xml, "language"); 15492 metadata.rights = this.getElementText(xml, "rights"); 15493 metadata.modified_date = this.getPropertyText(xml, "dcterms:modified"); 15494 metadata.layout = this.getPropertyText(xml, "rendition:layout"); 15495 metadata.orientation = this.getPropertyText(xml, "rendition:orientation"); 15496 metadata.flow = this.getPropertyText(xml, "rendition:flow"); 15497 metadata.viewport = this.getPropertyText(xml, "rendition:viewport"); 15498 metadata.media_active_class = this.getPropertyText(xml, "media:active-class"); 15499 metadata.spread = this.getPropertyText(xml, "rendition:spread"); // metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction"); 15500 15501 return metadata; 15502 } 15503 /** 15504 * Parse Manifest 15505 * @private 15506 * @param {node} manifestXml 15507 * @return {object} manifest 15508 */ 15509 15510 15511 parseManifest(manifestXml) { 15512 var manifest = {}; //-- Turn items into an array 15513 // var selected = manifestXml.querySelectorAll("item"); 15514 15515 var selected = Object(core["qsa"])(manifestXml, "item"); 15516 var items = Array.prototype.slice.call(selected); //-- Create an object with the id as key 15517 15518 items.forEach(function (item) { 15519 var id = item.getAttribute("id"), 15520 href = item.getAttribute("href") || "", 15521 type = item.getAttribute("media-type") || "", 15522 overlay = item.getAttribute("media-overlay") || "", 15523 properties = item.getAttribute("properties") || ""; 15524 manifest[id] = { 15525 "href": href, 15526 // "url" : href, 15527 "type": type, 15528 "overlay": overlay, 15529 "properties": properties.length ? properties.split(" ") : [] 15530 }; 15531 }); 15532 return manifest; 15533 } 15534 /** 15535 * Parse Spine 15536 * @private 15537 * @param {node} spineXml 15538 * @param {Packaging.manifest} manifest 15539 * @return {object} spine 15540 */ 15541 15542 15543 parseSpine(spineXml, manifest) { 15544 var spine = []; 15545 var selected = Object(core["qsa"])(spineXml, "itemref"); 15546 var items = Array.prototype.slice.call(selected); // var epubcfi = new EpubCFI(); 15547 //-- Add to array to maintain ordering and cross reference with manifest 15548 15549 items.forEach(function (item, index) { 15550 var idref = item.getAttribute("idref"); // var cfiBase = epubcfi.generateChapterComponent(spineNodeIndex, index, Id); 15551 15552 var props = item.getAttribute("properties") || ""; 15553 var propArray = props.length ? props.split(" ") : []; // var manifestProps = manifest[Id].properties; 15554 // var manifestPropArray = manifestProps.length ? manifestProps.split(" ") : []; 15555 15556 var itemref = { 15557 "id": item.getAttribute("id"), 15558 "idref": idref, 15559 "linear": item.getAttribute("linear") || "yes", 15560 "properties": propArray, 15561 // "href" : manifest[Id].href, 15562 // "url" : manifest[Id].url, 15563 "index": index // "cfiBase" : cfiBase 15564 15565 }; 15566 spine.push(itemref); 15567 }); 15568 return spine; 15569 } 15570 /** 15571 * Find Unique Identifier 15572 * @private 15573 * @param {node} packageXml 15574 * @return {string} Unique Identifier text 15575 */ 15576 15577 15578 findUniqueIdentifier(packageXml) { 15579 var uniqueIdentifierId = packageXml.documentElement.getAttribute("unique-identifier"); 15580 15581 if (!uniqueIdentifierId) { 15582 return ""; 15583 } 15584 15585 var identifier = packageXml.getElementById(uniqueIdentifierId); 15586 15587 if (!identifier) { 15588 return ""; 15589 } 15590 15591 if (identifier.localName === "identifier" && identifier.namespaceURI === "http://purl.org/dc/elements/1.1/") { 15592 return identifier.childNodes.length > 0 ? identifier.childNodes[0].nodeValue.trim() : ""; 15593 } 15594 15595 return ""; 15596 } 15597 /** 15598 * Find TOC NAV 15599 * @private 15600 * @param {element} manifestNode 15601 * @return {string} 15602 */ 15603 15604 15605 findNavPath(manifestNode) { 15606 // Find item with property "nav" 15607 // Should catch nav regardless of order 15608 // var node = manifestNode.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']"); 15609 var node = Object(core["qsp"])(manifestNode, "item", { 15610 "properties": "nav" 15611 }); 15612 return node ? node.getAttribute("href") : false; 15613 } 15614 /** 15615 * Find TOC NCX 15616 * media-type="application/x-dtbncx+xml" href="toc.ncx" 15617 * @private 15618 * @param {element} manifestNode 15619 * @param {element} spineNode 15620 * @return {string} 15621 */ 15622 15623 15624 findNcxPath(manifestNode, spineNode) { 15625 // var node = manifestNode.querySelector("item[media-type='application/x-dtbncx+xml']"); 15626 var node = Object(core["qsp"])(manifestNode, "item", { 15627 "media-type": "application/x-dtbncx+xml" 15628 }); 15629 var tocId; // If we can't find the toc by media-type then try to look for id of the item in the spine attributes as 15630 // according to http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4.1.2, 15631 // "The item that describes the NCX must be referenced by the spine toc attribute." 15632 15633 if (!node) { 15634 tocId = spineNode.getAttribute("toc"); 15635 15636 if (tocId) { 15637 // node = manifestNode.querySelector("item[id='" + tocId + "']"); 15638 node = manifestNode.querySelector(`#${tocId}`); 15639 } 15640 } 15641 15642 return node ? node.getAttribute("href") : false; 15643 } 15644 /** 15645 * Find the Cover Path 15646 * <item properties="cover-image" id="ci" href="cover.svg" media-type="image/svg+xml" /> 15647 * Fallback for Epub 2.0 15648 * @private 15649 * @param {node} packageXml 15650 * @return {string} href 15651 */ 15652 15653 15654 findCoverPath(packageXml) { 15655 var pkg = Object(core["qs"])(packageXml, "package"); 15656 var epubVersion = pkg.getAttribute("version"); // Try parsing cover with epub 3. 15657 // var node = packageXml.querySelector("item[properties='cover-image']"); 15658 15659 var node = Object(core["qsp"])(packageXml, "item", { 15660 "properties": "cover-image" 15661 }); 15662 if (node) return node.getAttribute("href"); // Fallback to epub 2. 15663 15664 var metaCover = Object(core["qsp"])(packageXml, "meta", { 15665 "name": "cover" 15666 }); 15667 15668 if (metaCover) { 15669 var coverId = metaCover.getAttribute("content"); // var cover = packageXml.querySelector("item[id='" + coverId + "']"); 15670 15671 var cover = packageXml.getElementById(coverId); 15672 return cover ? cover.getAttribute("href") : ""; 15673 } else { 15674 return false; 15675 } 15676 } 15677 /** 15678 * Get text of a namespaced element 15679 * @private 15680 * @param {node} xml 15681 * @param {string} tag 15682 * @return {string} text 15683 */ 15684 15685 15686 getElementText(xml, tag) { 15687 var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag); 15688 var el; 15689 if (!found || found.length === 0) return ""; 15690 el = found[0]; 15691 15692 if (el.childNodes.length) { 15693 return el.childNodes[0].nodeValue; 15694 } 15695 15696 return ""; 15697 } 15698 /** 15699 * Get text by property 15700 * @private 15701 * @param {node} xml 15702 * @param {string} property 15703 * @return {string} text 15704 */ 15705 15706 15707 getPropertyText(xml, property) { 15708 var el = Object(core["qsp"])(xml, "meta", { 15709 "property": property 15710 }); 15711 15712 if (el && el.childNodes.length) { 15713 return el.childNodes[0].nodeValue; 15714 } 15715 15716 return ""; 15717 } 15718 /** 15719 * Load JSON Manifest 15720 * @param {document} packageDocument OPF XML 15721 * @return {object} parsed package parts 15722 */ 15723 15724 15725 load(json) { 15726 this.metadata = json.metadata; 15727 let spine = json.readingOrder || json.spine; 15728 this.spine = spine.map((item, index) => { 15729 item.index = index; 15730 item.linear = item.linear || "yes"; 15731 return item; 15732 }); 15733 json.resources.forEach((item, index) => { 15734 this.manifest[index] = item; 15735 15736 if (item.rel && item.rel[0] === "cover") { 15737 this.coverPath = item.href; 15738 } 15739 }); 15740 this.spineNodeIndex = 0; 15741 this.toc = json.toc.map((item, index) => { 15742 item.label = item.title; 15743 return item; 15744 }); 15745 return { 15746 "metadata": this.metadata, 15747 "spine": this.spine, 15748 "manifest": this.manifest, 15749 "navPath": this.navPath, 15750 "ncxPath": this.ncxPath, 15751 "coverPath": this.coverPath, 15752 "spineNodeIndex": this.spineNodeIndex, 15753 "toc": this.toc 15754 }; 15755 } 15756 15757 destroy() { 15758 this.manifest = undefined; 15759 this.navPath = undefined; 15760 this.ncxPath = undefined; 15761 this.coverPath = undefined; 15762 this.spineNodeIndex = undefined; 15763 this.spine = undefined; 15764 this.metadata = undefined; 15765 } 15766 15767 } 15768 15769 /* harmony default export */ var src_packaging = (packaging_Packaging); 15770 // CONCATENATED MODULE: ./src/navigation.js 15771 15772 /** 15773 * Navigation Parser 15774 * @param {document} xml navigation html / xhtml / ncx 15775 */ 15776 15777 class navigation_Navigation { 15778 constructor(xml) { 15779 this.toc = []; 15780 this.tocByHref = {}; 15781 this.tocById = {}; 15782 this.landmarks = []; 15783 this.landmarksByType = {}; 15784 this.length = 0; 15785 15786 if (xml) { 15787 this.parse(xml); 15788 } 15789 } 15790 /** 15791 * Parse out the navigation items 15792 * @param {document} xml navigation html / xhtml / ncx 15793 */ 15794 15795 15796 parse(xml) { 15797 let isXml = xml.nodeType; 15798 let html; 15799 let ncx; 15800 15801 if (isXml) { 15802 html = Object(core["qs"])(xml, "html"); 15803 ncx = Object(core["qs"])(xml, "ncx"); 15804 } 15805 15806 if (!isXml) { 15807 this.toc = this.load(xml); 15808 } else if (html) { 15809 this.toc = this.parseNav(xml); 15810 this.landmarks = this.parseLandmarks(xml); 15811 } else if (ncx) { 15812 this.toc = this.parseNcx(xml); 15813 } 15814 15815 this.length = 0; 15816 this.unpack(this.toc); 15817 } 15818 /** 15819 * Unpack navigation items 15820 * @private 15821 * @param {array} toc 15822 */ 15823 15824 15825 unpack(toc) { 15826 var item; 15827 15828 for (var i = 0; i < toc.length; i++) { 15829 item = toc[i]; 15830 15831 if (item.href) { 15832 this.tocByHref[item.href] = i; 15833 } 15834 15835 if (item.id) { 15836 this.tocById[item.id] = i; 15837 } 15838 15839 this.length++; 15840 15841 if (item.subitems.length) { 15842 this.unpack(item.subitems); 15843 } 15844 } 15845 } 15846 /** 15847 * Get an item from the navigation 15848 * @param {string} target 15849 * @return {object} navItem 15850 */ 15851 15852 15853 get(target) { 15854 var index; 15855 15856 if (!target) { 15857 return this.toc; 15858 } 15859 15860 if (target.indexOf("#") === 0) { 15861 index = this.tocById[target.substring(1)]; 15862 } else if (target in this.tocByHref) { 15863 index = this.tocByHref[target]; 15864 } 15865 15866 return this.getByIndex(target, index, this.toc); 15867 } 15868 /** 15869 * Get an item from navigation subitems recursively by index 15870 * @param {string} target 15871 * @param {number} index 15872 * @param {array} navItems 15873 * @return {object} navItem 15874 */ 15875 15876 15877 getByIndex(target, index, navItems) { 15878 if (navItems.length === 0) { 15879 return; 15880 } 15881 15882 const item = navItems[index]; 15883 15884 if (item && (target === item.id || target === item.href)) { 15885 return item; 15886 } else { 15887 let result; 15888 15889 for (let i = 0; i < navItems.length; ++i) { 15890 result = this.getByIndex(target, index, navItems[i].subitems); 15891 15892 if (result) { 15893 break; 15894 } 15895 } 15896 15897 return result; 15898 } 15899 } 15900 /** 15901 * Get a landmark by type 15902 * List of types: https://idpf.github.io/epub-vocabs/structure/ 15903 * @param {string} type 15904 * @return {object} landmarkItem 15905 */ 15906 15907 15908 landmark(type) { 15909 var index; 15910 15911 if (!type) { 15912 return this.landmarks; 15913 } 15914 15915 index = this.landmarksByType[type]; 15916 return this.landmarks[index]; 15917 } 15918 /** 15919 * Parse toc from a Epub > 3.0 Nav 15920 * @private 15921 * @param {document} navHtml 15922 * @return {array} navigation list 15923 */ 15924 15925 15926 parseNav(navHtml) { 15927 var navElement = Object(core["querySelectorByType"])(navHtml, "nav", "toc"); 15928 var list = []; 15929 if (!navElement) return list; 15930 let navList = Object(core["filterChildren"])(navElement, "ol", true); 15931 if (!navList) return list; 15932 list = this.parseNavList(navList); 15933 return list; 15934 } 15935 /** 15936 * Parses lists in the toc 15937 * @param {document} navListHtml 15938 * @param {string} parent id 15939 * @return {array} navigation list 15940 */ 15941 15942 15943 parseNavList(navListHtml, parent) { 15944 const result = []; 15945 if (!navListHtml) return result; 15946 if (!navListHtml.children) return result; 15947 15948 for (let i = 0; i < navListHtml.children.length; i++) { 15949 const item = this.navItem(navListHtml.children[i], parent); 15950 15951 if (item) { 15952 result.push(item); 15953 } 15954 } 15955 15956 return result; 15957 } 15958 /** 15959 * Create a navItem 15960 * @private 15961 * @param {element} item 15962 * @return {object} navItem 15963 */ 15964 15965 15966 navItem(item, parent) { 15967 let id = item.getAttribute("id") || undefined; 15968 let content = Object(core["filterChildren"])(item, "a", true) || Object(core["filterChildren"])(item, "span", true); 15969 15970 if (!content) { 15971 return; 15972 } 15973 15974 let src = content.getAttribute("href") || ""; 15975 15976 if (!id) { 15977 id = src; 15978 } 15979 15980 let text = content.textContent || ""; 15981 let subitems = []; 15982 let nested = Object(core["filterChildren"])(item, "ol", true); 15983 15984 if (nested) { 15985 subitems = this.parseNavList(nested, id); 15986 } 15987 15988 return { 15989 "id": id, 15990 "href": src, 15991 "label": text, 15992 "subitems": subitems, 15993 "parent": parent 15994 }; 15995 } 15996 /** 15997 * Parse landmarks from a Epub > 3.0 Nav 15998 * @private 15999 * @param {document} navHtml 16000 * @return {array} landmarks list 16001 */ 16002 16003 16004 parseLandmarks(navHtml) { 16005 var navElement = Object(core["querySelectorByType"])(navHtml, "nav", "landmarks"); 16006 var navItems = navElement ? Object(core["qsa"])(navElement, "li") : []; 16007 var length = navItems.length; 16008 var i; 16009 var list = []; 16010 var item; 16011 if (!navItems || length === 0) return list; 16012 16013 for (i = 0; i < length; ++i) { 16014 item = this.landmarkItem(navItems[i]); 16015 16016 if (item) { 16017 list.push(item); 16018 this.landmarksByType[item.type] = i; 16019 } 16020 } 16021 16022 return list; 16023 } 16024 /** 16025 * Create a landmarkItem 16026 * @private 16027 * @param {element} item 16028 * @return {object} landmarkItem 16029 */ 16030 16031 16032 landmarkItem(item) { 16033 let content = Object(core["filterChildren"])(item, "a", true); 16034 16035 if (!content) { 16036 return; 16037 } 16038 16039 let type = content.getAttributeNS("http://www.idpf.org/2007/ops", "type") || undefined; 16040 let href = content.getAttribute("href") || ""; 16041 let text = content.textContent || ""; 16042 return { 16043 "href": href, 16044 "label": text, 16045 "type": type 16046 }; 16047 } 16048 /** 16049 * Parse from a Epub > 3.0 NC 16050 * @private 16051 * @param {document} navHtml 16052 * @return {array} navigation list 16053 */ 16054 16055 16056 parseNcx(tocXml) { 16057 var navPoints = Object(core["qsa"])(tocXml, "navPoint"); 16058 var length = navPoints.length; 16059 var i; 16060 var toc = {}; 16061 var list = []; 16062 var item, parent; 16063 if (!navPoints || length === 0) return list; 16064 16065 for (i = 0; i < length; ++i) { 16066 item = this.ncxItem(navPoints[i]); 16067 toc[item.id] = item; 16068 16069 if (!item.parent) { 16070 list.push(item); 16071 } else { 16072 parent = toc[item.parent]; 16073 parent.subitems.push(item); 16074 } 16075 } 16076 16077 return list; 16078 } 16079 /** 16080 * Create a ncxItem 16081 * @private 16082 * @param {element} item 16083 * @return {object} ncxItem 16084 */ 16085 16086 16087 ncxItem(item) { 16088 var id = item.getAttribute("id") || false, 16089 content = Object(core["qs"])(item, "content"), 16090 src = content.getAttribute("src"), 16091 navLabel = Object(core["qs"])(item, "navLabel"), 16092 text = navLabel.textContent ? navLabel.textContent : "", 16093 subitems = [], 16094 parentNode = item.parentNode, 16095 parent; 16096 16097 if (parentNode && (parentNode.nodeName === "navPoint" || parentNode.nodeName.split(':').slice(-1)[0] === "navPoint")) { 16098 parent = parentNode.getAttribute("id"); 16099 } 16100 16101 return { 16102 "id": id, 16103 "href": src, 16104 "label": text, 16105 "subitems": subitems, 16106 "parent": parent 16107 }; 16108 } 16109 /** 16110 * Load Spine Items 16111 * @param {object} json the items to be loaded 16112 * @return {Array} navItems 16113 */ 16114 16115 16116 load(json) { 16117 return json.map(item => { 16118 item.label = item.title; 16119 item.subitems = item.children ? this.load(item.children) : []; 16120 return item; 16121 }); 16122 } 16123 /** 16124 * forEach pass through 16125 * @param {Function} fn function to run on each item 16126 * @return {method} forEach loop 16127 */ 16128 16129 16130 forEach(fn) { 16131 return this.toc.forEach(fn); 16132 } 16133 16134 } 16135 16136 /* harmony default export */ var navigation = (navigation_Navigation); 16137 // CONCATENATED MODULE: ./src/utils/mime.js 16138 /* 16139 From Zip.js, by Gildas Lormeau 16140 edited down 16141 */ 16142 var table = { 16143 "application": { 16144 "ecmascript": ["es", "ecma"], 16145 "javascript": "js", 16146 "ogg": "ogx", 16147 "pdf": "pdf", 16148 "postscript": ["ps", "ai", "eps", "epsi", "epsf", "eps2", "eps3"], 16149 "rdf+xml": "rdf", 16150 "smil": ["smi", "smil"], 16151 "xhtml+xml": ["xhtml", "xht"], 16152 "xml": ["xml", "xsl", "xsd", "opf", "ncx"], 16153 "zip": "zip", 16154 "x-httpd-eruby": "rhtml", 16155 "x-latex": "latex", 16156 "x-maker": ["frm", "maker", "frame", "fm", "fb", "book", "fbdoc"], 16157 "x-object": "o", 16158 "x-shockwave-flash": ["swf", "swfl"], 16159 "x-silverlight": "scr", 16160 "epub+zip": "epub", 16161 "font-tdpfr": "pfr", 16162 "inkml+xml": ["ink", "inkml"], 16163 "json": "json", 16164 "jsonml+json": "jsonml", 16165 "mathml+xml": "mathml", 16166 "metalink+xml": "metalink", 16167 "mp4": "mp4s", 16168 // "oebps-package+xml" : "opf", 16169 "omdoc+xml": "omdoc", 16170 "oxps": "oxps", 16171 "vnd.amazon.ebook": "azw", 16172 "widget": "wgt", 16173 // "x-dtbncx+xml" : "ncx", 16174 "x-dtbook+xml": "dtb", 16175 "x-dtbresource+xml": "res", 16176 "x-font-bdf": "bdf", 16177 "x-font-ghostscript": "gsf", 16178 "x-font-linux-psf": "psf", 16179 "x-font-otf": "otf", 16180 "x-font-pcf": "pcf", 16181 "x-font-snf": "snf", 16182 "x-font-ttf": ["ttf", "ttc"], 16183 "x-font-type1": ["pfa", "pfb", "pfm", "afm"], 16184 "x-font-woff": "woff", 16185 "x-mobipocket-ebook": ["prc", "mobi"], 16186 "x-mspublisher": "pub", 16187 "x-nzb": "nzb", 16188 "x-tgif": "obj", 16189 "xaml+xml": "xaml", 16190 "xml-dtd": "dtd", 16191 "xproc+xml": "xpl", 16192 "xslt+xml": "xslt", 16193 "internet-property-stream": "acx", 16194 "x-compress": "z", 16195 "x-compressed": "tgz", 16196 "x-gzip": "gz" 16197 }, 16198 "audio": { 16199 "flac": "flac", 16200 "midi": ["mid", "midi", "kar", "rmi"], 16201 "mpeg": ["mpga", "mpega", "mp2", "mp3", "m4a", "mp2a", "m2a", "m3a"], 16202 "mpegurl": "m3u", 16203 "ogg": ["oga", "ogg", "spx"], 16204 "x-aiff": ["aif", "aiff", "aifc"], 16205 "x-ms-wma": "wma", 16206 "x-wav": "wav", 16207 "adpcm": "adp", 16208 "mp4": "mp4a", 16209 "webm": "weba", 16210 "x-aac": "aac", 16211 "x-caf": "caf", 16212 "x-matroska": "mka", 16213 "x-pn-realaudio-plugin": "rmp", 16214 "xm": "xm", 16215 "mid": ["mid", "rmi"] 16216 }, 16217 "image": { 16218 "gif": "gif", 16219 "ief": "ief", 16220 "jpeg": ["jpeg", "jpg", "jpe"], 16221 "pcx": "pcx", 16222 "png": "png", 16223 "svg+xml": ["svg", "svgz"], 16224 "tiff": ["tiff", "tif"], 16225 "x-icon": "ico", 16226 "bmp": "bmp", 16227 "webp": "webp", 16228 "x-pict": ["pic", "pct"], 16229 "x-tga": "tga", 16230 "cis-cod": "cod" 16231 }, 16232 "text": { 16233 "cache-manifest": ["manifest", "appcache"], 16234 "css": "css", 16235 "csv": "csv", 16236 "html": ["html", "htm", "shtml", "stm"], 16237 "mathml": "mml", 16238 "plain": ["txt", "text", "brf", "conf", "def", "list", "log", "in", "bas"], 16239 "richtext": "rtx", 16240 "tab-separated-values": "tsv", 16241 "x-bibtex": "bib" 16242 }, 16243 "video": { 16244 "mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v", "mp2", "mpa", "mpv2"], 16245 "mp4": ["mp4", "mp4v", "mpg4"], 16246 "quicktime": ["qt", "mov"], 16247 "ogg": "ogv", 16248 "vnd.mpegurl": ["mxu", "m4u"], 16249 "x-flv": "flv", 16250 "x-la-asf": ["lsf", "lsx"], 16251 "x-mng": "mng", 16252 "x-ms-asf": ["asf", "asx", "asr"], 16253 "x-ms-wm": "wm", 16254 "x-ms-wmv": "wmv", 16255 "x-ms-wmx": "wmx", 16256 "x-ms-wvx": "wvx", 16257 "x-msvideo": "avi", 16258 "x-sgi-movie": "movie", 16259 "x-matroska": ["mpv", "mkv", "mk3d", "mks"], 16260 "3gpp2": "3g2", 16261 "h261": "h261", 16262 "h263": "h263", 16263 "h264": "h264", 16264 "jpeg": "jpgv", 16265 "jpm": ["jpm", "jpgm"], 16266 "mj2": ["mj2", "mjp2"], 16267 "vnd.ms-playready.media.pyv": "pyv", 16268 "vnd.uvvu.mp4": ["uvu", "uvvu"], 16269 "vnd.vivo": "viv", 16270 "webm": "webm", 16271 "x-f4v": "f4v", 16272 "x-m4v": "m4v", 16273 "x-ms-vob": "vob", 16274 "x-smv": "smv" 16275 } 16276 }; 16277 16278 var mime_mimeTypes = function () { 16279 var type, 16280 subtype, 16281 val, 16282 index, 16283 mimeTypes = {}; 16284 16285 for (type in table) { 16286 if (table.hasOwnProperty(type)) { 16287 for (subtype in table[type]) { 16288 if (table[type].hasOwnProperty(subtype)) { 16289 val = table[type][subtype]; 16290 16291 if (typeof val == "string") { 16292 mimeTypes[val] = type + "/" + subtype; 16293 } else { 16294 for (index = 0; index < val.length; index++) { 16295 mimeTypes[val[index]] = type + "/" + subtype; 16296 } 16297 } 16298 } 16299 } 16300 } 16301 } 16302 16303 return mimeTypes; 16304 }(); 16305 16306 var defaultValue = "text/plain"; //"application/octet-stream"; 16307 16308 function lookup(filename) { 16309 return filename && mime_mimeTypes[filename.split(".").pop().toLowerCase()] || defaultValue; 16310 } 16311 16312 ; 16313 /* harmony default export */ var mime = ({ 16314 lookup 16315 }); 16316 // CONCATENATED MODULE: ./src/resources.js 16317 16318 16319 16320 16321 16322 16323 /** 16324 * Handle Package Resources 16325 * @class 16326 * @param {Manifest} manifest 16327 * @param {object} [options] 16328 * @param {string} [options.replacements="base64"] 16329 * @param {Archive} [options.archive] 16330 * @param {method} [options.resolver] 16331 */ 16332 16333 class resources_Resources { 16334 constructor(manifest, options) { 16335 this.settings = { 16336 replacements: options && options.replacements || "base64", 16337 archive: options && options.archive, 16338 resolver: options && options.resolver, 16339 request: options && options.request 16340 }; 16341 this.process(manifest); 16342 } 16343 /** 16344 * Process resources 16345 * @param {Manifest} manifest 16346 */ 16347 16348 16349 process(manifest) { 16350 this.manifest = manifest; 16351 this.resources = Object.keys(manifest).map(function (key) { 16352 return manifest[key]; 16353 }); 16354 this.replacementUrls = []; 16355 this.html = []; 16356 this.assets = []; 16357 this.css = []; 16358 this.urls = []; 16359 this.cssUrls = []; 16360 this.split(); 16361 this.splitUrls(); 16362 } 16363 /** 16364 * Split resources by type 16365 * @private 16366 */ 16367 16368 16369 split() { 16370 // HTML 16371 this.html = this.resources.filter(function (item) { 16372 if (item.type === "application/xhtml+xml" || item.type === "text/html") { 16373 return true; 16374 } 16375 }); // Exclude HTML 16376 16377 this.assets = this.resources.filter(function (item) { 16378 if (item.type !== "application/xhtml+xml" && item.type !== "text/html") { 16379 return true; 16380 } 16381 }); // Only CSS 16382 16383 this.css = this.resources.filter(function (item) { 16384 if (item.type === "text/css") { 16385 return true; 16386 } 16387 }); 16388 } 16389 /** 16390 * Convert split resources into Urls 16391 * @private 16392 */ 16393 16394 16395 splitUrls() { 16396 // All Assets Urls 16397 this.urls = this.assets.map(function (item) { 16398 return item.href; 16399 }.bind(this)); // Css Urls 16400 16401 this.cssUrls = this.css.map(function (item) { 16402 return item.href; 16403 }); 16404 } 16405 /** 16406 * Create a url to a resource 16407 * @param {string} url 16408 * @return {Promise<string>} Promise resolves with url string 16409 */ 16410 16411 16412 createUrl(url) { 16413 var parsedUrl = new utils_url["a" /* default */](url); 16414 var mimeType = mime.lookup(parsedUrl.filename); 16415 16416 if (this.settings.archive) { 16417 return this.settings.archive.createUrl(url, { 16418 "base64": this.settings.replacements === "base64" 16419 }); 16420 } else { 16421 if (this.settings.replacements === "base64") { 16422 return this.settings.request(url, 'blob').then(blob => { 16423 return Object(core["blob2base64"])(blob); 16424 }).then(blob => { 16425 return Object(core["createBase64Url"])(blob, mimeType); 16426 }); 16427 } else { 16428 return this.settings.request(url, 'blob').then(blob => { 16429 return Object(core["createBlobUrl"])(blob, mimeType); 16430 }); 16431 } 16432 } 16433 } 16434 /** 16435 * Create blob urls for all the assets 16436 * @return {Promise} returns replacement urls 16437 */ 16438 16439 16440 replacements() { 16441 if (this.settings.replacements === "none") { 16442 return new Promise(function (resolve) { 16443 resolve(this.urls); 16444 }.bind(this)); 16445 } 16446 16447 var replacements = this.urls.map(url => { 16448 var absolute = this.settings.resolver(url); 16449 return this.createUrl(absolute).catch(err => { 16450 console.error(err); 16451 return null; 16452 }); 16453 }); 16454 return Promise.all(replacements).then(replacementUrls => { 16455 this.replacementUrls = replacementUrls.filter(url => { 16456 return typeof url === "string"; 16457 }); 16458 return replacementUrls; 16459 }); 16460 } 16461 /** 16462 * Replace URLs in CSS resources 16463 * @private 16464 * @param {Archive} [archive] 16465 * @param {method} [resolver] 16466 * @return {Promise} 16467 */ 16468 16469 16470 replaceCss(archive, resolver) { 16471 var replaced = []; 16472 archive = archive || this.settings.archive; 16473 resolver = resolver || this.settings.resolver; 16474 this.cssUrls.forEach(function (href) { 16475 var replacement = this.createCssFile(href, archive, resolver).then(function (replacementUrl) { 16476 // switch the url in the replacementUrls 16477 var indexInUrls = this.urls.indexOf(href); 16478 16479 if (indexInUrls > -1) { 16480 this.replacementUrls[indexInUrls] = replacementUrl; 16481 } 16482 }.bind(this)); 16483 replaced.push(replacement); 16484 }.bind(this)); 16485 return Promise.all(replaced); 16486 } 16487 /** 16488 * Create a new CSS file with the replaced URLs 16489 * @private 16490 * @param {string} href the original css file 16491 * @return {Promise} returns a BlobUrl to the new CSS file or a data url 16492 */ 16493 16494 16495 createCssFile(href) { 16496 var newUrl; 16497 16498 if (path_default.a.isAbsolute(href)) { 16499 return new Promise(function (resolve) { 16500 resolve(); 16501 }); 16502 } 16503 16504 var absolute = this.settings.resolver(href); // Get the text of the css file from the archive 16505 16506 var textResponse; 16507 16508 if (this.settings.archive) { 16509 textResponse = this.settings.archive.getText(absolute); 16510 } else { 16511 textResponse = this.settings.request(absolute, "text"); 16512 } // Get asset links relative to css file 16513 16514 16515 var relUrls = this.urls.map(assetHref => { 16516 var resolved = this.settings.resolver(assetHref); 16517 var relative = new utils_path["a" /* default */](absolute).relative(resolved); 16518 return relative; 16519 }); 16520 16521 if (!textResponse) { 16522 // file not found, don't replace 16523 return new Promise(function (resolve) { 16524 resolve(); 16525 }); 16526 } 16527 16528 return textResponse.then(text => { 16529 // Replacements in the css text 16530 text = Object(replacements["e" /* substitute */])(text, relUrls, this.replacementUrls); // Get the new url 16531 16532 if (this.settings.replacements === "base64") { 16533 newUrl = Object(core["createBase64Url"])(text, "text/css"); 16534 } else { 16535 newUrl = Object(core["createBlobUrl"])(text, "text/css"); 16536 } 16537 16538 return newUrl; 16539 }, err => { 16540 // handle response errors 16541 return new Promise(function (resolve) { 16542 resolve(); 16543 }); 16544 }); 16545 } 16546 /** 16547 * Resolve all resources URLs relative to an absolute URL 16548 * @param {string} absolute to be resolved to 16549 * @param {resolver} [resolver] 16550 * @return {string[]} array with relative Urls 16551 */ 16552 16553 16554 relativeTo(absolute, resolver) { 16555 resolver = resolver || this.settings.resolver; // Get Urls relative to current sections 16556 16557 return this.urls.map(function (href) { 16558 var resolved = resolver(href); 16559 var relative = new utils_path["a" /* default */](absolute).relative(resolved); 16560 return relative; 16561 }.bind(this)); 16562 } 16563 /** 16564 * Get a URL for a resource 16565 * @param {string} path 16566 * @return {string} url 16567 */ 16568 16569 16570 get(path) { 16571 var indexInUrls = this.urls.indexOf(path); 16572 16573 if (indexInUrls === -1) { 16574 return; 16575 } 16576 16577 if (this.replacementUrls.length) { 16578 return new Promise(function (resolve, reject) { 16579 resolve(this.replacementUrls[indexInUrls]); 16580 }.bind(this)); 16581 } else { 16582 return this.createUrl(path); 16583 } 16584 } 16585 /** 16586 * Substitute urls in content, with replacements, 16587 * relative to a url if provided 16588 * @param {string} content 16589 * @param {string} [url] url to resolve to 16590 * @return {string} content with urls substituted 16591 */ 16592 16593 16594 substitute(content, url) { 16595 var relUrls; 16596 16597 if (url) { 16598 relUrls = this.relativeTo(url); 16599 } else { 16600 relUrls = this.urls; 16601 } 16602 16603 return Object(replacements["e" /* substitute */])(content, relUrls, this.replacementUrls); 16604 } 16605 16606 destroy() { 16607 this.settings = undefined; 16608 this.manifest = undefined; 16609 this.resources = undefined; 16610 this.replacementUrls = undefined; 16611 this.html = undefined; 16612 this.assets = undefined; 16613 this.css = undefined; 16614 this.urls = undefined; 16615 this.cssUrls = undefined; 16616 } 16617 16618 } 16619 16620 /* harmony default export */ var resources = (resources_Resources); 16621 // CONCATENATED MODULE: ./src/pagelist.js 16622 16623 16624 /** 16625 * Page List Parser 16626 * @param {document} [xml] 16627 */ 16628 16629 class pagelist_PageList { 16630 constructor(xml) { 16631 this.pages = []; 16632 this.locations = []; 16633 this.epubcfi = new epubcfi["a" /* default */](); 16634 this.firstPage = 0; 16635 this.lastPage = 0; 16636 this.totalPages = 0; 16637 this.toc = undefined; 16638 this.ncx = undefined; 16639 16640 if (xml) { 16641 this.pageList = this.parse(xml); 16642 } 16643 16644 if (this.pageList && this.pageList.length) { 16645 this.process(this.pageList); 16646 } 16647 } 16648 /** 16649 * Parse PageList Xml 16650 * @param {document} xml 16651 */ 16652 16653 16654 parse(xml) { 16655 var html = Object(core["qs"])(xml, "html"); 16656 var ncx = Object(core["qs"])(xml, "ncx"); 16657 16658 if (html) { 16659 return this.parseNav(xml); 16660 } else if (ncx) { 16661 return this.parseNcx(xml); 16662 } 16663 } 16664 /** 16665 * Parse a Nav PageList 16666 * @private 16667 * @param {node} navHtml 16668 * @return {PageList.item[]} list 16669 */ 16670 16671 16672 parseNav(navHtml) { 16673 var navElement = Object(core["querySelectorByType"])(navHtml, "nav", "page-list"); 16674 var navItems = navElement ? Object(core["qsa"])(navElement, "li") : []; 16675 var length = navItems.length; 16676 var i; 16677 var list = []; 16678 var item; 16679 if (!navItems || length === 0) return list; 16680 16681 for (i = 0; i < length; ++i) { 16682 item = this.item(navItems[i]); 16683 list.push(item); 16684 } 16685 16686 return list; 16687 } 16688 16689 parseNcx(navXml) { 16690 var list = []; 16691 var i = 0; 16692 var item; 16693 var pageList; 16694 var pageTargets; 16695 var length = 0; 16696 pageList = Object(core["qs"])(navXml, "pageList"); 16697 if (!pageList) return list; 16698 pageTargets = Object(core["qsa"])(pageList, "pageTarget"); 16699 length = pageTargets.length; 16700 16701 if (!pageTargets || pageTargets.length === 0) { 16702 return list; 16703 } 16704 16705 for (i = 0; i < length; ++i) { 16706 item = this.ncxItem(pageTargets[i]); 16707 list.push(item); 16708 } 16709 16710 return list; 16711 } 16712 16713 ncxItem(item) { 16714 var navLabel = Object(core["qs"])(item, "navLabel"); 16715 var navLabelText = Object(core["qs"])(navLabel, "text"); 16716 var pageText = navLabelText.textContent; 16717 var content = Object(core["qs"])(item, "content"); 16718 var href = content.getAttribute("src"); 16719 var page = parseInt(pageText, 10); 16720 return { 16721 "href": href, 16722 "page": page 16723 }; 16724 } 16725 /** 16726 * Page List Item 16727 * @private 16728 * @param {node} item 16729 * @return {object} pageListItem 16730 */ 16731 16732 16733 item(item) { 16734 var content = Object(core["qs"])(item, "a"), 16735 href = content.getAttribute("href") || "", 16736 text = content.textContent || "", 16737 page = parseInt(text), 16738 isCfi = href.indexOf("epubcfi"), 16739 split, 16740 packageUrl, 16741 cfi; 16742 16743 if (isCfi != -1) { 16744 split = href.split("#"); 16745 packageUrl = split[0]; 16746 cfi = split.length > 1 ? split[1] : false; 16747 return { 16748 "cfi": cfi, 16749 "href": href, 16750 "packageUrl": packageUrl, 16751 "page": page 16752 }; 16753 } else { 16754 return { 16755 "href": href, 16756 "page": page 16757 }; 16758 } 16759 } 16760 /** 16761 * Process pageList items 16762 * @private 16763 * @param {array} pageList 16764 */ 16765 16766 16767 process(pageList) { 16768 pageList.forEach(function (item) { 16769 this.pages.push(item.page); 16770 16771 if (item.cfi) { 16772 this.locations.push(item.cfi); 16773 } 16774 }, this); 16775 this.firstPage = parseInt(this.pages[0]); 16776 this.lastPage = parseInt(this.pages[this.pages.length - 1]); 16777 this.totalPages = this.lastPage - this.firstPage; 16778 } 16779 /** 16780 * Get a PageList result from a EpubCFI 16781 * @param {string} cfi EpubCFI String 16782 * @return {number} page 16783 */ 16784 16785 16786 pageFromCfi(cfi) { 16787 var pg = -1; // Check if the pageList has not been set yet 16788 16789 if (this.locations.length === 0) { 16790 return -1; 16791 } // TODO: check if CFI is valid? 16792 // check if the cfi is in the location list 16793 // var index = this.locations.indexOf(cfi); 16794 16795 16796 var index = Object(core["indexOfSorted"])(cfi, this.locations, this.epubcfi.compare); 16797 16798 if (index != -1) { 16799 pg = this.pages[index]; 16800 } else { 16801 // Otherwise add it to the list of locations 16802 // Insert it in the correct position in the locations page 16803 //index = EPUBJS.core.insert(cfi, this.locations, this.epubcfi.compare); 16804 index = Object(core["locationOf"])(cfi, this.locations, this.epubcfi.compare); // Get the page at the location just before the new one, or return the first 16805 16806 pg = index - 1 >= 0 ? this.pages[index - 1] : this.pages[0]; 16807 16808 if (pg !== undefined) {// Add the new page in so that the locations and page array match up 16809 //this.pages.splice(index, 0, pg); 16810 } else { 16811 pg = -1; 16812 } 16813 } 16814 16815 return pg; 16816 } 16817 /** 16818 * Get an EpubCFI from a Page List Item 16819 * @param {string | number} pg 16820 * @return {string} cfi 16821 */ 16822 16823 16824 cfiFromPage(pg) { 16825 var cfi = -1; // check that pg is an int 16826 16827 if (typeof pg != "number") { 16828 pg = parseInt(pg); 16829 } // check if the cfi is in the page list 16830 // Pages could be unsorted. 16831 16832 16833 var index = this.pages.indexOf(pg); 16834 16835 if (index != -1) { 16836 cfi = this.locations[index]; 16837 } // TODO: handle pages not in the list 16838 16839 16840 return cfi; 16841 } 16842 /** 16843 * Get a Page from Book percentage 16844 * @param {number} percent 16845 * @return {number} page 16846 */ 16847 16848 16849 pageFromPercentage(percent) { 16850 var pg = Math.round(this.totalPages * percent); 16851 return pg; 16852 } 16853 /** 16854 * Returns a value between 0 - 1 corresponding to the location of a page 16855 * @param {number} pg the page 16856 * @return {number} percentage 16857 */ 16858 16859 16860 percentageFromPage(pg) { 16861 var percentage = (pg - this.firstPage) / this.totalPages; 16862 return Math.round(percentage * 1000) / 1000; 16863 } 16864 /** 16865 * Returns a value between 0 - 1 corresponding to the location of a cfi 16866 * @param {string} cfi EpubCFI String 16867 * @return {number} percentage 16868 */ 16869 16870 16871 percentageFromCfi(cfi) { 16872 var pg = this.pageFromCfi(cfi); 16873 var percentage = this.percentageFromPage(pg); 16874 return percentage; 16875 } 16876 /** 16877 * Destroy 16878 */ 16879 16880 16881 destroy() { 16882 this.pages = undefined; 16883 this.locations = undefined; 16884 this.epubcfi = undefined; 16885 this.pageList = undefined; 16886 this.toc = undefined; 16887 this.ncx = undefined; 16888 } 16889 16890 } 16891 16892 /* harmony default export */ var pagelist = (pagelist_PageList); 16893 // EXTERNAL MODULE: ./src/rendition.js + 3 modules 16894 var rendition = __webpack_require__(16); 16895 16896 // EXTERNAL MODULE: external "JSZip" 16897 var external_JSZip_ = __webpack_require__(29); 16898 var external_JSZip_default = /*#__PURE__*/__webpack_require__.n(external_JSZip_); 16899 16900 // CONCATENATED MODULE: ./src/archive.js 16901 16902 16903 16904 16905 16906 /** 16907 * Handles Unzipping a requesting files from an Epub Archive 16908 * @class 16909 */ 16910 16911 class archive_Archive { 16912 constructor() { 16913 this.zip = undefined; 16914 this.urlCache = {}; 16915 this.checkRequirements(); 16916 } 16917 /** 16918 * Checks to see if JSZip exists in global namspace, 16919 * Requires JSZip if it isn't there 16920 * @private 16921 */ 16922 16923 16924 checkRequirements() { 16925 try { 16926 this.zip = new external_JSZip_default.a(); 16927 } catch (e) { 16928 throw new Error("JSZip lib not loaded"); 16929 } 16930 } 16931 /** 16932 * Open an archive 16933 * @param {binary} input 16934 * @param {boolean} [isBase64] tells JSZip if the input data is base64 encoded 16935 * @return {Promise} zipfile 16936 */ 16937 16938 16939 open(input, isBase64) { 16940 return this.zip.loadAsync(input, { 16941 "base64": isBase64 16942 }); 16943 } 16944 /** 16945 * Load and Open an archive 16946 * @param {string} zipUrl 16947 * @param {boolean} [isBase64] tells JSZip if the input data is base64 encoded 16948 * @return {Promise} zipfile 16949 */ 16950 16951 16952 openUrl(zipUrl, isBase64) { 16953 return utils_request(zipUrl, "binary").then(function (data) { 16954 return this.zip.loadAsync(data, { 16955 "base64": isBase64 16956 }); 16957 }.bind(this)); 16958 } 16959 /** 16960 * Request a url from the archive 16961 * @param {string} url a url to request from the archive 16962 * @param {string} [type] specify the type of the returned result 16963 * @return {Promise<Blob | string | JSON | Document | XMLDocument>} 16964 */ 16965 16966 16967 request(url, type) { 16968 var deferred = new core["defer"](); 16969 var response; 16970 var path = new utils_path["a" /* default */](url); // If type isn't set, determine it from the file extension 16971 16972 if (!type) { 16973 type = path.extension; 16974 } 16975 16976 if (type == "blob") { 16977 response = this.getBlob(url); 16978 } else { 16979 response = this.getText(url); 16980 } 16981 16982 if (response) { 16983 response.then(function (r) { 16984 let result = this.handleResponse(r, type); 16985 deferred.resolve(result); 16986 }.bind(this)); 16987 } else { 16988 deferred.reject({ 16989 message: "File not found in the epub: " + url, 16990 stack: new Error().stack 16991 }); 16992 } 16993 16994 return deferred.promise; 16995 } 16996 /** 16997 * Handle the response from request 16998 * @private 16999 * @param {any} response 17000 * @param {string} [type] 17001 * @return {any} the parsed result 17002 */ 17003 17004 17005 handleResponse(response, type) { 17006 var r; 17007 17008 if (type == "json") { 17009 r = JSON.parse(response); 17010 } else if (Object(core["isXml"])(type)) { 17011 r = Object(core["parse"])(response, "text/xml"); 17012 } else if (type == "xhtml") { 17013 r = Object(core["parse"])(response, "application/xhtml+xml"); 17014 } else if (type == "html" || type == "htm") { 17015 r = Object(core["parse"])(response, "text/html"); 17016 } else { 17017 r = response; 17018 } 17019 17020 return r; 17021 } 17022 /** 17023 * Get a Blob from Archive by Url 17024 * @param {string} url 17025 * @param {string} [mimeType] 17026 * @return {Blob} 17027 */ 17028 17029 17030 getBlob(url, mimeType) { 17031 var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash 17032 17033 var entry = this.zip.file(decodededUrl); 17034 17035 if (entry) { 17036 mimeType = mimeType || mime.lookup(entry.name); 17037 return entry.async("uint8array").then(function (uint8array) { 17038 return new Blob([uint8array], { 17039 type: mimeType 17040 }); 17041 }); 17042 } 17043 } 17044 /** 17045 * Get Text from Archive by Url 17046 * @param {string} url 17047 * @param {string} [encoding] 17048 * @return {string} 17049 */ 17050 17051 17052 getText(url, encoding) { 17053 var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash 17054 17055 var entry = this.zip.file(decodededUrl); 17056 17057 if (entry) { 17058 return entry.async("string").then(function (text) { 17059 return text; 17060 }); 17061 } 17062 } 17063 /** 17064 * Get a base64 encoded result from Archive by Url 17065 * @param {string} url 17066 * @param {string} [mimeType] 17067 * @return {string} base64 encoded 17068 */ 17069 17070 17071 getBase64(url, mimeType) { 17072 var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash 17073 17074 var entry = this.zip.file(decodededUrl); 17075 17076 if (entry) { 17077 mimeType = mimeType || mime.lookup(entry.name); 17078 return entry.async("base64").then(function (data) { 17079 return "data:" + mimeType + ";base64," + data; 17080 }); 17081 } 17082 } 17083 /** 17084 * Create a Url from an unarchived item 17085 * @param {string} url 17086 * @param {object} [options.base64] use base64 encoding or blob url 17087 * @return {Promise} url promise with Url string 17088 */ 17089 17090 17091 createUrl(url, options) { 17092 var deferred = new core["defer"](); 17093 17094 var _URL = window.URL || window.webkitURL || window.mozURL; 17095 17096 var tempUrl; 17097 var response; 17098 var useBase64 = options && options.base64; 17099 17100 if (url in this.urlCache) { 17101 deferred.resolve(this.urlCache[url]); 17102 return deferred.promise; 17103 } 17104 17105 if (useBase64) { 17106 response = this.getBase64(url); 17107 17108 if (response) { 17109 response.then(function (tempUrl) { 17110 this.urlCache[url] = tempUrl; 17111 deferred.resolve(tempUrl); 17112 }.bind(this)); 17113 } 17114 } else { 17115 response = this.getBlob(url); 17116 17117 if (response) { 17118 response.then(function (blob) { 17119 tempUrl = _URL.createObjectURL(blob); 17120 this.urlCache[url] = tempUrl; 17121 deferred.resolve(tempUrl); 17122 }.bind(this)); 17123 } 17124 } 17125 17126 if (!response) { 17127 deferred.reject({ 17128 message: "File not found in the epub: " + url, 17129 stack: new Error().stack 17130 }); 17131 } 17132 17133 return deferred.promise; 17134 } 17135 /** 17136 * Revoke Temp Url for a archive item 17137 * @param {string} url url of the item in the archive 17138 */ 17139 17140 17141 revokeUrl(url) { 17142 var _URL = window.URL || window.webkitURL || window.mozURL; 17143 17144 var fromCache = this.urlCache[url]; 17145 if (fromCache) _URL.revokeObjectURL(fromCache); 17146 } 17147 17148 destroy() { 17149 var _URL = window.URL || window.webkitURL || window.mozURL; 17150 17151 for (let fromCache in this.urlCache) { 17152 _URL.revokeObjectURL(fromCache); 17153 } 17154 17155 this.zip = undefined; 17156 this.urlCache = {}; 17157 } 17158 17159 } 17160 17161 /* harmony default export */ var archive = (archive_Archive); 17162 // EXTERNAL MODULE: ./node_modules/localforage/dist/localforage.js 17163 var localforage = __webpack_require__(23); 17164 var localforage_default = /*#__PURE__*/__webpack_require__.n(localforage); 17165 17166 // CONCATENATED MODULE: ./src/store.js 17167 17168 17169 17170 17171 17172 17173 /** 17174 * Handles saving and requesting files from local storage 17175 * @class 17176 * @param {string} name This should be the name of the application for modals 17177 * @param {function} [requester] 17178 * @param {function} [resolver] 17179 */ 17180 17181 class store_Store { 17182 constructor(name, requester, resolver) { 17183 this.urlCache = {}; 17184 this.storage = undefined; 17185 this.name = name; 17186 this.requester = requester || utils_request; 17187 this.resolver = resolver; 17188 this.online = true; 17189 this.checkRequirements(); 17190 this.addListeners(); 17191 } 17192 /** 17193 * Checks to see if localForage exists in global namspace, 17194 * Requires localForage if it isn't there 17195 * @private 17196 */ 17197 17198 17199 checkRequirements() { 17200 try { 17201 let store; 17202 17203 if (typeof localforage_default.a === "undefined") { 17204 store = localforage_default.a; 17205 } 17206 17207 this.storage = store.createInstance({ 17208 name: this.name 17209 }); 17210 } catch (e) { 17211 throw new Error("localForage lib not loaded"); 17212 } 17213 } 17214 /** 17215 * Add online and offline event listeners 17216 * @private 17217 */ 17218 17219 17220 addListeners() { 17221 this._status = this.status.bind(this); 17222 window.addEventListener('online', this._status); 17223 window.addEventListener('offline', this._status); 17224 } 17225 /** 17226 * Remove online and offline event listeners 17227 * @private 17228 */ 17229 17230 17231 removeListeners() { 17232 window.removeEventListener('online', this._status); 17233 window.removeEventListener('offline', this._status); 17234 this._status = undefined; 17235 } 17236 /** 17237 * Update the online / offline status 17238 * @private 17239 */ 17240 17241 17242 status(event) { 17243 let online = navigator.onLine; 17244 this.online = online; 17245 17246 if (online) { 17247 this.emit("online", this); 17248 } else { 17249 this.emit("offline", this); 17250 } 17251 } 17252 /** 17253 * Add all of a book resources to the store 17254 * @param {Resources} resources book resources 17255 * @param {boolean} [force] force resaving resources 17256 * @return {Promise<object>} store objects 17257 */ 17258 17259 17260 add(resources, force) { 17261 let mapped = resources.resources.map(item => { 17262 let { 17263 href 17264 } = item; 17265 let url = this.resolver(href); 17266 let encodedUrl = window.encodeURIComponent(url); 17267 return this.storage.getItem(encodedUrl).then(item => { 17268 if (!item || force) { 17269 return this.requester(url, "binary").then(data => { 17270 return this.storage.setItem(encodedUrl, data); 17271 }); 17272 } else { 17273 return item; 17274 } 17275 }); 17276 }); 17277 return Promise.all(mapped); 17278 } 17279 /** 17280 * Put binary data from a url to storage 17281 * @param {string} url a url to request from storage 17282 * @param {boolean} [withCredentials] 17283 * @param {object} [headers] 17284 * @return {Promise<Blob>} 17285 */ 17286 17287 17288 put(url, withCredentials, headers) { 17289 let encodedUrl = window.encodeURIComponent(url); 17290 return this.storage.getItem(encodedUrl).then(result => { 17291 if (!result) { 17292 return this.requester(url, "binary", withCredentials, headers).then(data => { 17293 return this.storage.setItem(encodedUrl, data); 17294 }); 17295 } 17296 17297 return result; 17298 }); 17299 } 17300 /** 17301 * Request a url 17302 * @param {string} url a url to request from storage 17303 * @param {string} [type] specify the type of the returned result 17304 * @param {boolean} [withCredentials] 17305 * @param {object} [headers] 17306 * @return {Promise<Blob | string | JSON | Document | XMLDocument>} 17307 */ 17308 17309 17310 request(url, type, withCredentials, headers) { 17311 if (this.online) { 17312 // From network 17313 return this.requester(url, type, withCredentials, headers).then(data => { 17314 // save to store if not present 17315 this.put(url); 17316 return data; 17317 }); 17318 } else { 17319 // From store 17320 return this.retrieve(url, type); 17321 } 17322 } 17323 /** 17324 * Request a url from storage 17325 * @param {string} url a url to request from storage 17326 * @param {string} [type] specify the type of the returned result 17327 * @return {Promise<Blob | string | JSON | Document | XMLDocument>} 17328 */ 17329 17330 17331 retrieve(url, type) { 17332 var deferred = new core["defer"](); 17333 var response; 17334 var path = new utils_path["a" /* default */](url); // If type isn't set, determine it from the file extension 17335 17336 if (!type) { 17337 type = path.extension; 17338 } 17339 17340 if (type == "blob") { 17341 response = this.getBlob(url); 17342 } else { 17343 response = this.getText(url); 17344 } 17345 17346 return response.then(r => { 17347 var deferred = new core["defer"](); 17348 var result; 17349 17350 if (r) { 17351 result = this.handleResponse(r, type); 17352 deferred.resolve(result); 17353 } else { 17354 deferred.reject({ 17355 message: "File not found in storage: " + url, 17356 stack: new Error().stack 17357 }); 17358 } 17359 17360 return deferred.promise; 17361 }); 17362 } 17363 /** 17364 * Handle the response from request 17365 * @private 17366 * @param {any} response 17367 * @param {string} [type] 17368 * @return {any} the parsed result 17369 */ 17370 17371 17372 handleResponse(response, type) { 17373 var r; 17374 17375 if (type == "json") { 17376 r = JSON.parse(response); 17377 } else if (Object(core["isXml"])(type)) { 17378 r = Object(core["parse"])(response, "text/xml"); 17379 } else if (type == "xhtml") { 17380 r = Object(core["parse"])(response, "application/xhtml+xml"); 17381 } else if (type == "html" || type == "htm") { 17382 r = Object(core["parse"])(response, "text/html"); 17383 } else { 17384 r = response; 17385 } 17386 17387 return r; 17388 } 17389 /** 17390 * Get a Blob from Storage by Url 17391 * @param {string} url 17392 * @param {string} [mimeType] 17393 * @return {Blob} 17394 */ 17395 17396 17397 getBlob(url, mimeType) { 17398 let encodedUrl = window.encodeURIComponent(url); 17399 return this.storage.getItem(encodedUrl).then(function (uint8array) { 17400 if (!uint8array) return; 17401 mimeType = mimeType || mime.lookup(url); 17402 return new Blob([uint8array], { 17403 type: mimeType 17404 }); 17405 }); 17406 } 17407 /** 17408 * Get Text from Storage by Url 17409 * @param {string} url 17410 * @param {string} [mimeType] 17411 * @return {string} 17412 */ 17413 17414 17415 getText(url, mimeType) { 17416 let encodedUrl = window.encodeURIComponent(url); 17417 mimeType = mimeType || mime.lookup(url); 17418 return this.storage.getItem(encodedUrl).then(function (uint8array) { 17419 var deferred = new core["defer"](); 17420 var reader = new FileReader(); 17421 var blob; 17422 if (!uint8array) return; 17423 blob = new Blob([uint8array], { 17424 type: mimeType 17425 }); 17426 reader.addEventListener("loadend", () => { 17427 deferred.resolve(reader.result); 17428 }); 17429 reader.readAsText(blob, mimeType); 17430 return deferred.promise; 17431 }); 17432 } 17433 /** 17434 * Get a base64 encoded result from Storage by Url 17435 * @param {string} url 17436 * @param {string} [mimeType] 17437 * @return {string} base64 encoded 17438 */ 17439 17440 17441 getBase64(url, mimeType) { 17442 let encodedUrl = window.encodeURIComponent(url); 17443 mimeType = mimeType || mime.lookup(url); 17444 return this.storage.getItem(encodedUrl).then(uint8array => { 17445 var deferred = new core["defer"](); 17446 var reader = new FileReader(); 17447 var blob; 17448 if (!uint8array) return; 17449 blob = new Blob([uint8array], { 17450 type: mimeType 17451 }); 17452 reader.addEventListener("loadend", () => { 17453 deferred.resolve(reader.result); 17454 }); 17455 reader.readAsDataURL(blob, mimeType); 17456 return deferred.promise; 17457 }); 17458 } 17459 /** 17460 * Create a Url from a stored item 17461 * @param {string} url 17462 * @param {object} [options.base64] use base64 encoding or blob url 17463 * @return {Promise} url promise with Url string 17464 */ 17465 17466 17467 createUrl(url, options) { 17468 var deferred = new core["defer"](); 17469 17470 var _URL = window.URL || window.webkitURL || window.mozURL; 17471 17472 var tempUrl; 17473 var response; 17474 var useBase64 = options && options.base64; 17475 17476 if (url in this.urlCache) { 17477 deferred.resolve(this.urlCache[url]); 17478 return deferred.promise; 17479 } 17480 17481 if (useBase64) { 17482 response = this.getBase64(url); 17483 17484 if (response) { 17485 response.then(function (tempUrl) { 17486 this.urlCache[url] = tempUrl; 17487 deferred.resolve(tempUrl); 17488 }.bind(this)); 17489 } 17490 } else { 17491 response = this.getBlob(url); 17492 17493 if (response) { 17494 response.then(function (blob) { 17495 tempUrl = _URL.createObjectURL(blob); 17496 this.urlCache[url] = tempUrl; 17497 deferred.resolve(tempUrl); 17498 }.bind(this)); 17499 } 17500 } 17501 17502 if (!response) { 17503 deferred.reject({ 17504 message: "File not found in storage: " + url, 17505 stack: new Error().stack 17506 }); 17507 } 17508 17509 return deferred.promise; 17510 } 17511 /** 17512 * Revoke Temp Url for a archive item 17513 * @param {string} url url of the item in the store 17514 */ 17515 17516 17517 revokeUrl(url) { 17518 var _URL = window.URL || window.webkitURL || window.mozURL; 17519 17520 var fromCache = this.urlCache[url]; 17521 if (fromCache) _URL.revokeObjectURL(fromCache); 17522 } 17523 17524 destroy() { 17525 var _URL = window.URL || window.webkitURL || window.mozURL; 17526 17527 for (let fromCache in this.urlCache) { 17528 _URL.revokeObjectURL(fromCache); 17529 } 17530 17531 this.urlCache = {}; 17532 this.removeListeners(); 17533 } 17534 17535 } 17536 17537 event_emitter_default()(store_Store.prototype); 17538 /* harmony default export */ var src_store = (store_Store); 17539 // CONCATENATED MODULE: ./src/displayoptions.js 17540 17541 /** 17542 * Open DisplayOptions Format Parser 17543 * @class 17544 * @param {document} displayOptionsDocument XML 17545 */ 17546 17547 class displayoptions_DisplayOptions { 17548 constructor(displayOptionsDocument) { 17549 this.interactive = ""; 17550 this.fixedLayout = ""; 17551 this.openToSpread = ""; 17552 this.orientationLock = ""; 17553 17554 if (displayOptionsDocument) { 17555 this.parse(displayOptionsDocument); 17556 } 17557 } 17558 /** 17559 * Parse XML 17560 * @param {document} displayOptionsDocument XML 17561 * @return {DisplayOptions} self 17562 */ 17563 17564 17565 parse(displayOptionsDocument) { 17566 if (!displayOptionsDocument) { 17567 return this; 17568 } 17569 17570 const displayOptionsNode = Object(core["qs"])(displayOptionsDocument, "display_options"); 17571 17572 if (!displayOptionsNode) { 17573 return this; 17574 } 17575 17576 const options = Object(core["qsa"])(displayOptionsNode, "option"); 17577 options.forEach(el => { 17578 let value = ""; 17579 17580 if (el.childNodes.length) { 17581 value = el.childNodes[0].nodeValue; 17582 } 17583 17584 switch (el.attributes.name.value) { 17585 case "interactive": 17586 this.interactive = value; 17587 break; 17588 17589 case "fixed-layout": 17590 this.fixedLayout = value; 17591 break; 17592 17593 case "open-to-spread": 17594 this.openToSpread = value; 17595 break; 17596 17597 case "orientation-lock": 17598 this.orientationLock = value; 17599 break; 17600 } 17601 }); 17602 return this; 17603 } 17604 17605 destroy() { 17606 this.interactive = undefined; 17607 this.fixedLayout = undefined; 17608 this.openToSpread = undefined; 17609 this.orientationLock = undefined; 17610 } 17611 17612 } 17613 17614 /* harmony default export */ var displayoptions = (displayoptions_DisplayOptions); 17615 // CONCATENATED MODULE: ./src/book.js 17616 17617 17618 17619 17620 17621 17622 17623 17624 17625 17626 17627 17628 17629 17630 17631 17632 17633 17634 const CONTAINER_PATH = "META-INF/container.xml"; 17635 const IBOOKS_DISPLAY_OPTIONS_PATH = "META-INF/com.apple.ibooks.display-options.xml"; 17636 const INPUT_TYPE = { 17637 BINARY: "binary", 17638 BASE64: "base64", 17639 EPUB: "epub", 17640 OPF: "opf", 17641 MANIFEST: "json", 17642 DIRECTORY: "directory" 17643 }; 17644 /** 17645 * An Epub representation with methods for the loading, parsing and manipulation 17646 * of its contents. 17647 * @class 17648 * @param {string} [url] 17649 * @param {object} [options] 17650 * @param {method} [options.requestMethod] a request function to use instead of the default 17651 * @param {boolean} [options.requestCredentials=undefined] send the xhr request withCredentials 17652 * @param {object} [options.requestHeaders=undefined] send the xhr request headers 17653 * @param {string} [options.encoding=binary] optional to pass 'binary' or base64' for archived Epubs 17654 * @param {string} [options.replacements=none] use base64, blobUrl, or none for replacing assets in archived Epubs 17655 * @param {method} [options.canonical] optional function to determine canonical urls for a path 17656 * @param {string} [options.openAs] optional string to determine the input type 17657 * @param {string} [options.store=false] cache the contents in local storage, value should be the name of the reader 17658 * @returns {Book} 17659 * @example new Book("/path/to/book.epub", {}) 17660 * @example new Book({ replacements: "blobUrl" }) 17661 */ 17662 17663 class book_Book { 17664 constructor(url, options) { 17665 // Allow passing just options to the Book 17666 if (typeof options === "undefined" && typeof url !== "string" && url instanceof Blob === false && url instanceof ArrayBuffer === false) { 17667 options = url; 17668 url = undefined; 17669 } 17670 17671 this.settings = Object(core["extend"])(this.settings || {}, { 17672 requestMethod: undefined, 17673 requestCredentials: undefined, 17674 requestHeaders: undefined, 17675 encoding: undefined, 17676 replacements: undefined, 17677 canonical: undefined, 17678 openAs: undefined, 17679 store: undefined 17680 }); 17681 Object(core["extend"])(this.settings, options); // Promises 17682 17683 this.opening = new core["defer"](); 17684 /** 17685 * @member {promise} opened returns after the book is loaded 17686 * @memberof Book 17687 */ 17688 17689 this.opened = this.opening.promise; 17690 this.isOpen = false; 17691 this.loading = { 17692 manifest: new core["defer"](), 17693 spine: new core["defer"](), 17694 metadata: new core["defer"](), 17695 cover: new core["defer"](), 17696 navigation: new core["defer"](), 17697 pageList: new core["defer"](), 17698 resources: new core["defer"](), 17699 displayOptions: new core["defer"]() 17700 }; 17701 this.loaded = { 17702 manifest: this.loading.manifest.promise, 17703 spine: this.loading.spine.promise, 17704 metadata: this.loading.metadata.promise, 17705 cover: this.loading.cover.promise, 17706 navigation: this.loading.navigation.promise, 17707 pageList: this.loading.pageList.promise, 17708 resources: this.loading.resources.promise, 17709 displayOptions: this.loading.displayOptions.promise 17710 }; 17711 /** 17712 * @member {promise} ready returns after the book is loaded and parsed 17713 * @memberof Book 17714 * @private 17715 */ 17716 17717 this.ready = Promise.all([this.loaded.manifest, this.loaded.spine, this.loaded.metadata, this.loaded.cover, this.loaded.navigation, this.loaded.resources, this.loaded.displayOptions]); // Queue for methods used before opening 17718 17719 this.isRendered = false; // this._q = queue(this); 17720 17721 /** 17722 * @member {method} request 17723 * @memberof Book 17724 * @private 17725 */ 17726 17727 this.request = this.settings.requestMethod || utils_request; 17728 /** 17729 * @member {Spine} spine 17730 * @memberof Book 17731 */ 17732 17733 this.spine = new src_spine(); 17734 /** 17735 * @member {Locations} locations 17736 * @memberof Book 17737 */ 17738 17739 this.locations = new src_locations(this.spine, this.load.bind(this)); 17740 /** 17741 * @member {Navigation} navigation 17742 * @memberof Book 17743 */ 17744 17745 this.navigation = undefined; 17746 /** 17747 * @member {PageList} pagelist 17748 * @memberof Book 17749 */ 17750 17751 this.pageList = undefined; 17752 /** 17753 * @member {Url} url 17754 * @memberof Book 17755 * @private 17756 */ 17757 17758 this.url = undefined; 17759 /** 17760 * @member {Path} path 17761 * @memberof Book 17762 * @private 17763 */ 17764 17765 this.path = undefined; 17766 /** 17767 * @member {boolean} archived 17768 * @memberof Book 17769 * @private 17770 */ 17771 17772 this.archived = false; 17773 /** 17774 * @member {Archive} archive 17775 * @memberof Book 17776 * @private 17777 */ 17778 17779 this.archive = undefined; 17780 /** 17781 * @member {Store} storage 17782 * @memberof Book 17783 * @private 17784 */ 17785 17786 this.storage = undefined; 17787 /** 17788 * @member {Resources} resources 17789 * @memberof Book 17790 * @private 17791 */ 17792 17793 this.resources = undefined; 17794 /** 17795 * @member {Rendition} rendition 17796 * @memberof Book 17797 * @private 17798 */ 17799 17800 this.rendition = undefined; 17801 /** 17802 * @member {Container} container 17803 * @memberof Book 17804 * @private 17805 */ 17806 17807 this.container = undefined; 17808 /** 17809 * @member {Packaging} packaging 17810 * @memberof Book 17811 * @private 17812 */ 17813 17814 this.packaging = undefined; 17815 /** 17816 * @member {DisplayOptions} displayOptions 17817 * @memberof DisplayOptions 17818 * @private 17819 */ 17820 17821 this.displayOptions = undefined; // this.toc = undefined; 17822 17823 if (this.settings.store) { 17824 this.store(this.settings.store); 17825 } 17826 17827 if (url) { 17828 this.open(url, this.settings.openAs).catch(error => { 17829 var err = new Error("Cannot load book at " + url); 17830 this.emit(constants["c" /* EVENTS */].BOOK.OPEN_FAILED, err); 17831 }); 17832 } 17833 } 17834 /** 17835 * Open a epub or url 17836 * @param {string | ArrayBuffer} input Url, Path or ArrayBuffer 17837 * @param {string} [what="binary", "base64", "epub", "opf", "json", "directory"] force opening as a certain type 17838 * @returns {Promise} of when the book has been loaded 17839 * @example book.open("/path/to/book.epub") 17840 */ 17841 17842 17843 open(input, what) { 17844 var opening; 17845 var type = what || this.determineType(input); 17846 17847 if (type === INPUT_TYPE.BINARY) { 17848 this.archived = true; 17849 this.url = new utils_url["a" /* default */]("/", ""); 17850 opening = this.openEpub(input); 17851 } else if (type === INPUT_TYPE.BASE64) { 17852 this.archived = true; 17853 this.url = new utils_url["a" /* default */]("/", ""); 17854 opening = this.openEpub(input, type); 17855 } else if (type === INPUT_TYPE.EPUB) { 17856 this.archived = true; 17857 this.url = new utils_url["a" /* default */]("/", ""); 17858 opening = this.request(input, "binary", this.settings.requestCredentials, this.settings.requestHeaders).then(this.openEpub.bind(this)); 17859 } else if (type == INPUT_TYPE.OPF) { 17860 this.url = new utils_url["a" /* default */](input); 17861 opening = this.openPackaging(this.url.Path.toString()); 17862 } else if (type == INPUT_TYPE.MANIFEST) { 17863 this.url = new utils_url["a" /* default */](input); 17864 opening = this.openManifest(this.url.Path.toString()); 17865 } else { 17866 this.url = new utils_url["a" /* default */](input); 17867 opening = this.openContainer(CONTAINER_PATH).then(this.openPackaging.bind(this)); 17868 } 17869 17870 return opening; 17871 } 17872 /** 17873 * Open an archived epub 17874 * @private 17875 * @param {binary} data 17876 * @param {string} [encoding] 17877 * @return {Promise} 17878 */ 17879 17880 17881 openEpub(data, encoding) { 17882 return this.unarchive(data, encoding || this.settings.encoding).then(() => { 17883 return this.openContainer(CONTAINER_PATH); 17884 }).then(packagePath => { 17885 return this.openPackaging(packagePath); 17886 }); 17887 } 17888 /** 17889 * Open the epub container 17890 * @private 17891 * @param {string} url 17892 * @return {string} packagePath 17893 */ 17894 17895 17896 openContainer(url) { 17897 return this.load(url).then(xml => { 17898 this.container = new container(xml); 17899 return this.resolve(this.container.packagePath); 17900 }); 17901 } 17902 /** 17903 * Open the Open Packaging Format Xml 17904 * @private 17905 * @param {string} url 17906 * @return {Promise} 17907 */ 17908 17909 17910 openPackaging(url) { 17911 this.path = new utils_path["a" /* default */](url); 17912 return this.load(url).then(xml => { 17913 this.packaging = new src_packaging(xml); 17914 return this.unpack(this.packaging); 17915 }); 17916 } 17917 /** 17918 * Open the manifest JSON 17919 * @private 17920 * @param {string} url 17921 * @return {Promise} 17922 */ 17923 17924 17925 openManifest(url) { 17926 this.path = new utils_path["a" /* default */](url); 17927 return this.load(url).then(json => { 17928 this.packaging = new src_packaging(); 17929 this.packaging.load(json); 17930 return this.unpack(this.packaging); 17931 }); 17932 } 17933 /** 17934 * Load a resource from the Book 17935 * @param {string} path path to the resource to load 17936 * @return {Promise} returns a promise with the requested resource 17937 */ 17938 17939 17940 load(path) { 17941 var resolved = this.resolve(path); 17942 17943 if (this.archived) { 17944 return this.archive.request(resolved); 17945 } else { 17946 return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders); 17947 } 17948 } 17949 /** 17950 * Resolve a path to it's absolute position in the Book 17951 * @param {string} path 17952 * @param {boolean} [absolute] force resolving the full URL 17953 * @return {string} the resolved path string 17954 */ 17955 17956 17957 resolve(path, absolute) { 17958 if (!path) { 17959 return; 17960 } 17961 17962 var resolved = path; 17963 var isAbsolute = path.indexOf("://") > -1; 17964 17965 if (isAbsolute) { 17966 return path; 17967 } 17968 17969 if (this.path) { 17970 resolved = this.path.resolve(path); 17971 } 17972 17973 if (absolute != false && this.url) { 17974 resolved = this.url.resolve(resolved); 17975 } 17976 17977 return resolved; 17978 } 17979 /** 17980 * Get a canonical link to a path 17981 * @param {string} path 17982 * @return {string} the canonical path string 17983 */ 17984 17985 17986 canonical(path) { 17987 var url = path; 17988 17989 if (!path) { 17990 return ""; 17991 } 17992 17993 if (this.settings.canonical) { 17994 url = this.settings.canonical(path); 17995 } else { 17996 url = this.resolve(path, true); 17997 } 17998 17999 return url; 18000 } 18001 /** 18002 * Determine the type of they input passed to open 18003 * @private 18004 * @param {string} input 18005 * @return {string} binary | directory | epub | opf 18006 */ 18007 18008 18009 determineType(input) { 18010 var url; 18011 var path; 18012 var extension; 18013 18014 if (this.settings.encoding === "base64") { 18015 return INPUT_TYPE.BASE64; 18016 } 18017 18018 if (typeof input != "string") { 18019 return INPUT_TYPE.BINARY; 18020 } 18021 18022 url = new utils_url["a" /* default */](input); 18023 path = url.path(); 18024 extension = path.extension; // If there's a search string, remove it before determining type 18025 18026 if (extension) { 18027 extension = extension.replace(/\?.*$/, ""); 18028 } 18029 18030 if (!extension) { 18031 return INPUT_TYPE.DIRECTORY; 18032 } 18033 18034 if (extension === "epub") { 18035 return INPUT_TYPE.EPUB; 18036 } 18037 18038 if (extension === "opf") { 18039 return INPUT_TYPE.OPF; 18040 } 18041 18042 if (extension === "json") { 18043 return INPUT_TYPE.MANIFEST; 18044 } 18045 } 18046 /** 18047 * unpack the contents of the Books packaging 18048 * @private 18049 * @param {Packaging} packaging object 18050 */ 18051 18052 18053 unpack(packaging) { 18054 this.package = packaging; //TODO: deprecated this 18055 18056 if (this.packaging.metadata.layout === "") { 18057 // rendition:layout not set - check display options if book is pre-paginated 18058 this.load(this.url.resolve(IBOOKS_DISPLAY_OPTIONS_PATH)).then(xml => { 18059 this.displayOptions = new displayoptions(xml); 18060 this.loading.displayOptions.resolve(this.displayOptions); 18061 }).catch(err => { 18062 this.displayOptions = new displayoptions(); 18063 this.loading.displayOptions.resolve(this.displayOptions); 18064 }); 18065 } else { 18066 this.displayOptions = new displayoptions(); 18067 this.loading.displayOptions.resolve(this.displayOptions); 18068 } 18069 18070 this.spine.unpack(this.packaging, this.resolve.bind(this), this.canonical.bind(this)); 18071 this.resources = new resources(this.packaging.manifest, { 18072 archive: this.archive, 18073 resolver: this.resolve.bind(this), 18074 request: this.request.bind(this), 18075 replacements: this.settings.replacements || (this.archived ? "blobUrl" : "base64") 18076 }); 18077 this.loadNavigation(this.packaging).then(() => { 18078 // this.toc = this.navigation.toc; 18079 this.loading.navigation.resolve(this.navigation); 18080 }); 18081 18082 if (this.packaging.coverPath) { 18083 this.cover = this.resolve(this.packaging.coverPath); 18084 } // Resolve promises 18085 18086 18087 this.loading.manifest.resolve(this.packaging.manifest); 18088 this.loading.metadata.resolve(this.packaging.metadata); 18089 this.loading.spine.resolve(this.spine); 18090 this.loading.cover.resolve(this.cover); 18091 this.loading.resources.resolve(this.resources); 18092 this.loading.pageList.resolve(this.pageList); 18093 this.isOpen = true; 18094 18095 if (this.archived || this.settings.replacements && this.settings.replacements != "none") { 18096 this.replacements().then(() => { 18097 this.loaded.displayOptions.then(() => { 18098 this.opening.resolve(this); 18099 }); 18100 }).catch(err => { 18101 console.error(err); 18102 }); 18103 } else { 18104 // Resolve book opened promise 18105 this.loaded.displayOptions.then(() => { 18106 this.opening.resolve(this); 18107 }); 18108 } 18109 } 18110 /** 18111 * Load Navigation and PageList from package 18112 * @private 18113 * @param {Packaging} packaging 18114 */ 18115 18116 18117 loadNavigation(packaging) { 18118 let navPath = packaging.navPath || packaging.ncxPath; 18119 let toc = packaging.toc; // From json manifest 18120 18121 if (toc) { 18122 return new Promise((resolve, reject) => { 18123 this.navigation = new navigation(toc); 18124 18125 if (packaging.pageList) { 18126 this.pageList = new pagelist(packaging.pageList); // TODO: handle page lists from Manifest 18127 } 18128 18129 resolve(this.navigation); 18130 }); 18131 } 18132 18133 if (!navPath) { 18134 return new Promise((resolve, reject) => { 18135 this.navigation = new navigation(); 18136 this.pageList = new pagelist(); 18137 resolve(this.navigation); 18138 }); 18139 } 18140 18141 return this.load(navPath, "xml").then(xml => { 18142 this.navigation = new navigation(xml); 18143 this.pageList = new pagelist(xml); 18144 return this.navigation; 18145 }); 18146 } 18147 /** 18148 * Gets a Section of the Book from the Spine 18149 * Alias for `book.spine.get` 18150 * @param {string} target 18151 * @return {Section} 18152 */ 18153 18154 18155 section(target) { 18156 return this.spine.get(target); 18157 } 18158 /** 18159 * Sugar to render a book to an element 18160 * @param {element | string} element element or string to add a rendition to 18161 * @param {object} [options] 18162 * @return {Rendition} 18163 */ 18164 18165 18166 renderTo(element, options) { 18167 this.rendition = new rendition["a" /* default */](this, options); 18168 this.rendition.attachTo(element); 18169 return this.rendition; 18170 } 18171 /** 18172 * Set if request should use withCredentials 18173 * @param {boolean} credentials 18174 */ 18175 18176 18177 setRequestCredentials(credentials) { 18178 this.settings.requestCredentials = credentials; 18179 } 18180 /** 18181 * Set headers request should use 18182 * @param {object} headers 18183 */ 18184 18185 18186 setRequestHeaders(headers) { 18187 this.settings.requestHeaders = headers; 18188 } 18189 /** 18190 * Unarchive a zipped epub 18191 * @private 18192 * @param {binary} input epub data 18193 * @param {string} [encoding] 18194 * @return {Archive} 18195 */ 18196 18197 18198 unarchive(input, encoding) { 18199 this.archive = new archive(); 18200 return this.archive.open(input, encoding); 18201 } 18202 /** 18203 * Store the epubs contents 18204 * @private 18205 * @param {binary} input epub data 18206 * @param {string} [encoding] 18207 * @return {Store} 18208 */ 18209 18210 18211 store(name) { 18212 // Use "blobUrl" or "base64" for replacements 18213 let replacementsSetting = this.settings.replacements && this.settings.replacements !== "none"; // Save original url 18214 18215 let originalUrl = this.url; // Save original request method 18216 18217 let requester = this.settings.requestMethod || utils_request.bind(this); // Create new Store 18218 18219 this.storage = new src_store(name, requester, this.resolve.bind(this)); // Replace request method to go through store 18220 18221 this.request = this.storage.request.bind(this.storage); 18222 this.opened.then(() => { 18223 if (this.archived) { 18224 this.storage.requester = this.archive.request.bind(this.archive); 18225 } // Substitute hook 18226 18227 18228 let substituteResources = (output, section) => { 18229 section.output = this.resources.substitute(output, section.url); 18230 }; // Set to use replacements 18231 18232 18233 this.resources.settings.replacements = replacementsSetting || "blobUrl"; // Create replacement urls 18234 18235 this.resources.replacements().then(() => { 18236 return this.resources.replaceCss(); 18237 }); 18238 this.storage.on("offline", () => { 18239 // Remove url to use relative resolving for hrefs 18240 this.url = new utils_url["a" /* default */]("/", ""); // Add hook to replace resources in contents 18241 18242 this.spine.hooks.serialize.register(substituteResources); 18243 }); 18244 this.storage.on("online", () => { 18245 // Restore original url 18246 this.url = originalUrl; // Remove hook 18247 18248 this.spine.hooks.serialize.deregister(substituteResources); 18249 }); 18250 }); 18251 return this.storage; 18252 } 18253 /** 18254 * Get the cover url 18255 * @return {Promise<?string>} coverUrl 18256 */ 18257 18258 18259 coverUrl() { 18260 return this.loaded.cover.then(() => { 18261 if (!this.cover) { 18262 return null; 18263 } 18264 18265 if (this.archived) { 18266 return this.archive.createUrl(this.cover); 18267 } else { 18268 return this.cover; 18269 } 18270 }); 18271 } 18272 /** 18273 * Load replacement urls 18274 * @private 18275 * @return {Promise} completed loading urls 18276 */ 18277 18278 18279 replacements() { 18280 this.spine.hooks.serialize.register((output, section) => { 18281 section.output = this.resources.substitute(output, section.url); 18282 }); 18283 return this.resources.replacements().then(() => { 18284 return this.resources.replaceCss(); 18285 }); 18286 } 18287 /** 18288 * Find a DOM Range for a given CFI Range 18289 * @param {EpubCFI} cfiRange a epub cfi range 18290 * @return {Promise} 18291 */ 18292 18293 18294 getRange(cfiRange) { 18295 var cfi = new epubcfi["a" /* default */](cfiRange); 18296 var item = this.spine.get(cfi.spinePos); 18297 18298 var _request = this.load.bind(this); 18299 18300 if (!item) { 18301 return new Promise((resolve, reject) => { 18302 reject("CFI could not be found"); 18303 }); 18304 } 18305 18306 return item.load(_request).then(function (contents) { 18307 var range = cfi.toRange(item.document); 18308 return range; 18309 }); 18310 } 18311 /** 18312 * Generates the Book Key using the identifier in the manifest or other string provided 18313 * @param {string} [identifier] to use instead of metadata identifier 18314 * @return {string} key 18315 */ 18316 18317 18318 key(identifier) { 18319 var ident = identifier || this.packaging.metadata.identifier || this.url.filename; 18320 return `epubjs:${constants["b" /* EPUBJS_VERSION */]}:${ident}`; 18321 } 18322 /** 18323 * Destroy the Book and all associated objects 18324 */ 18325 18326 18327 destroy() { 18328 this.opened = undefined; 18329 this.loading = undefined; 18330 this.loaded = undefined; 18331 this.ready = undefined; 18332 this.isOpen = false; 18333 this.isRendered = false; 18334 this.spine && this.spine.destroy(); 18335 this.locations && this.locations.destroy(); 18336 this.pageList && this.pageList.destroy(); 18337 this.archive && this.archive.destroy(); 18338 this.resources && this.resources.destroy(); 18339 this.container && this.container.destroy(); 18340 this.packaging && this.packaging.destroy(); 18341 this.rendition && this.rendition.destroy(); 18342 this.displayOptions && this.displayOptions.destroy(); 18343 this.spine = undefined; 18344 this.locations = undefined; 18345 this.pageList = undefined; 18346 this.archive = undefined; 18347 this.resources = undefined; 18348 this.container = undefined; 18349 this.packaging = undefined; 18350 this.rendition = undefined; 18351 this.navigation = undefined; 18352 this.url = undefined; 18353 this.path = undefined; 18354 this.archived = false; 18355 } 18356 18357 } //-- Enable binding events to book 18358 18359 18360 event_emitter_default()(book_Book.prototype); 18361 /* harmony default export */ var book = __webpack_exports__["a"] = (book_Book); 18362 18363 /***/ }), 18364 /* 25 */ 18365 /***/ (function(module, exports, __webpack_require__) { 18366 18367 var conventions = __webpack_require__(14); 18368 18369 var NAMESPACE = conventions.NAMESPACE; 18370 18371 /** 18372 * A prerequisite for `[].filter`, to drop elements that are empty 18373 * @param {string} input 18374 * @returns {boolean} 18375 */ 18376 function notEmptyString (input) { 18377 return input !== '' 18378 } 18379 /** 18380 * @see https://infra.spec.whatwg.org/#split-on-ascii-whitespace 18381 * @see https://infra.spec.whatwg.org/#ascii-whitespace 18382 * 18383 * @param {string} input 18384 * @returns {string[]} (can be empty) 18385 */ 18386 function splitOnASCIIWhitespace(input) { 18387 // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE 18388 return input ? input.split(/[\t\n\f\r ]+/).filter(notEmptyString) : [] 18389 } 18390 18391 /** 18392 * Adds element as a key to current if it is not already present. 18393 * 18394 * @param {Record<string, boolean | undefined>} current 18395 * @param {string} element 18396 * @returns {Record<string, boolean | undefined>} 18397 */ 18398 function orderedSetReducer (current, element) { 18399 if (!current.hasOwnProperty(element)) { 18400 current[element] = true; 18401 } 18402 return current; 18403 } 18404 18405 /** 18406 * @see https://infra.spec.whatwg.org/#ordered-set 18407 * @param {string} input 18408 * @returns {string[]} 18409 */ 18410 function toOrderedSet(input) { 18411 if (!input) return []; 18412 var list = splitOnASCIIWhitespace(input); 18413 return Object.keys(list.reduce(orderedSetReducer, {})) 18414 } 18415 18416 /** 18417 * Uses `list.indexOf` to implement something like `Array.prototype.includes`, 18418 * which we can not rely on being available. 18419 * 18420 * @param {any[]} list 18421 * @returns {function(any): boolean} 18422 */ 18423 function arrayIncludes (list) { 18424 return function(element) { 18425 return list && list.indexOf(element) !== -1; 18426 } 18427 } 18428 18429 function copy(src,dest){ 18430 for(var p in src){ 18431 dest[p] = src[p]; 18432 } 18433 } 18434 18435 /** 18436 ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));? 18437 ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));? 18438 */ 18439 function _extends(Class,Super){ 18440 var pt = Class.prototype; 18441 if(!(pt instanceof Super)){ 18442 function t(){}; 18443 t.prototype = Super.prototype; 18444 t = new t(); 18445 copy(pt,t); 18446 Class.prototype = pt = t; 18447 } 18448 if(pt.constructor != Class){ 18449 if(typeof Class != 'function'){ 18450 console.error("unknown Class:"+Class) 18451 } 18452 pt.constructor = Class 18453 } 18454 } 18455 18456 // Node Types 18457 var NodeType = {} 18458 var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; 18459 var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2; 18460 var TEXT_NODE = NodeType.TEXT_NODE = 3; 18461 var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4; 18462 var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5; 18463 var ENTITY_NODE = NodeType.ENTITY_NODE = 6; 18464 var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7; 18465 var COMMENT_NODE = NodeType.COMMENT_NODE = 8; 18466 var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9; 18467 var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10; 18468 var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11; 18469 var NOTATION_NODE = NodeType.NOTATION_NODE = 12; 18470 18471 // ExceptionCode 18472 var ExceptionCode = {} 18473 var ExceptionMessage = {}; 18474 var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1); 18475 var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2); 18476 var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3); 18477 var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4); 18478 var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5); 18479 var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6); 18480 var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7); 18481 var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8); 18482 var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9); 18483 var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10); 18484 //level2 18485 var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11); 18486 var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12); 18487 var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13); 18488 var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14); 18489 var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15); 18490 18491 /** 18492 * DOM Level 2 18493 * Object DOMException 18494 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html 18495 * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html 18496 */ 18497 function DOMException(code, message) { 18498 if(message instanceof Error){ 18499 var error = message; 18500 }else{ 18501 error = this; 18502 Error.call(this, ExceptionMessage[code]); 18503 this.message = ExceptionMessage[code]; 18504 if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException); 18505 } 18506 error.code = code; 18507 if(message) this.message = this.message + ": " + message; 18508 return error; 18509 }; 18510 DOMException.prototype = Error.prototype; 18511 copy(ExceptionCode,DOMException) 18512 18513 /** 18514 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177 18515 * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live. 18516 * The items in the NodeList are accessible via an integral index, starting from 0. 18517 */ 18518 function NodeList() { 18519 }; 18520 NodeList.prototype = { 18521 /** 18522 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive. 18523 * @standard level1 18524 */ 18525 length:0, 18526 /** 18527 * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null. 18528 * @standard level1 18529 * @param index unsigned long 18530 * Index into the collection. 18531 * @return Node 18532 * The node at the indexth position in the NodeList, or null if that is not a valid index. 18533 */ 18534 item: function(index) { 18535 return this[index] || null; 18536 }, 18537 toString:function(isHTML,nodeFilter){ 18538 for(var buf = [], i = 0;i<this.length;i++){ 18539 serializeToString(this[i],buf,isHTML,nodeFilter); 18540 } 18541 return buf.join(''); 18542 } 18543 }; 18544 18545 function LiveNodeList(node,refresh){ 18546 this._node = node; 18547 this._refresh = refresh 18548 _updateLiveList(this); 18549 } 18550 function _updateLiveList(list){ 18551 var inc = list._node._inc || list._node.ownerDocument._inc; 18552 if(list._inc != inc){ 18553 var ls = list._refresh(list._node); 18554 //console.log(ls.length) 18555 __set__(list,'length',ls.length); 18556 copy(ls,list); 18557 list._inc = inc; 18558 } 18559 } 18560 LiveNodeList.prototype.item = function(i){ 18561 _updateLiveList(this); 18562 return this[i]; 18563 } 18564 18565 _extends(LiveNodeList,NodeList); 18566 18567 /** 18568 * Objects implementing the NamedNodeMap interface are used 18569 * to represent collections of nodes that can be accessed by name. 18570 * Note that NamedNodeMap does not inherit from NodeList; 18571 * NamedNodeMaps are not maintained in any particular order. 18572 * Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, 18573 * but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, 18574 * and does not imply that the DOM specifies an order to these Nodes. 18575 * NamedNodeMap objects in the DOM are live. 18576 * used for attributes or DocumentType entities 18577 */ 18578 function NamedNodeMap() { 18579 }; 18580 18581 function _findNodeIndex(list,node){ 18582 var i = list.length; 18583 while(i--){ 18584 if(list[i] === node){return i} 18585 } 18586 } 18587 18588 function _addNamedNode(el,list,newAttr,oldAttr){ 18589 if(oldAttr){ 18590 list[_findNodeIndex(list,oldAttr)] = newAttr; 18591 }else{ 18592 list[list.length++] = newAttr; 18593 } 18594 if(el){ 18595 newAttr.ownerElement = el; 18596 var doc = el.ownerDocument; 18597 if(doc){ 18598 oldAttr && _onRemoveAttribute(doc,el,oldAttr); 18599 _onAddAttribute(doc,el,newAttr); 18600 } 18601 } 18602 } 18603 function _removeNamedNode(el,list,attr){ 18604 //console.log('remove attr:'+attr) 18605 var i = _findNodeIndex(list,attr); 18606 if(i>=0){ 18607 var lastIndex = list.length-1 18608 while(i<lastIndex){ 18609 list[i] = list[++i] 18610 } 18611 list.length = lastIndex; 18612 if(el){ 18613 var doc = el.ownerDocument; 18614 if(doc){ 18615 _onRemoveAttribute(doc,el,attr); 18616 attr.ownerElement = null; 18617 } 18618 } 18619 }else{ 18620 throw DOMException(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr)) 18621 } 18622 } 18623 NamedNodeMap.prototype = { 18624 length:0, 18625 item:NodeList.prototype.item, 18626 getNamedItem: function(key) { 18627 // if(key.indexOf(':')>0 || key == 'xmlns'){ 18628 // return null; 18629 // } 18630 //console.log() 18631 var i = this.length; 18632 while(i--){ 18633 var attr = this[i]; 18634 //console.log(attr.nodeName,key) 18635 if(attr.nodeName == key){ 18636 return attr; 18637 } 18638 } 18639 }, 18640 setNamedItem: function(attr) { 18641 var el = attr.ownerElement; 18642 if(el && el!=this._ownerElement){ 18643 throw new DOMException(INUSE_ATTRIBUTE_ERR); 18644 } 18645 var oldAttr = this.getNamedItem(attr.nodeName); 18646 _addNamedNode(this._ownerElement,this,attr,oldAttr); 18647 return oldAttr; 18648 }, 18649 /* returns Node */ 18650 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR 18651 var el = attr.ownerElement, oldAttr; 18652 if(el && el!=this._ownerElement){ 18653 throw new DOMException(INUSE_ATTRIBUTE_ERR); 18654 } 18655 oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName); 18656 _addNamedNode(this._ownerElement,this,attr,oldAttr); 18657 return oldAttr; 18658 }, 18659 18660 /* returns Node */ 18661 removeNamedItem: function(key) { 18662 var attr = this.getNamedItem(key); 18663 _removeNamedNode(this._ownerElement,this,attr); 18664 return attr; 18665 18666 18667 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR 18668 18669 //for level2 18670 removeNamedItemNS:function(namespaceURI,localName){ 18671 var attr = this.getNamedItemNS(namespaceURI,localName); 18672 _removeNamedNode(this._ownerElement,this,attr); 18673 return attr; 18674 }, 18675 getNamedItemNS: function(namespaceURI, localName) { 18676 var i = this.length; 18677 while(i--){ 18678 var node = this[i]; 18679 if(node.localName == localName && node.namespaceURI == namespaceURI){ 18680 return node; 18681 } 18682 } 18683 return null; 18684 } 18685 }; 18686 18687 /** 18688 * The DOMImplementation interface represents an object providing methods 18689 * which are not dependent on any particular document. 18690 * Such an object is returned by the `Document.implementation` property. 18691 * 18692 * __The individual methods describe the differences compared to the specs.__ 18693 * 18694 * @constructor 18695 * 18696 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN 18697 * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core (Initial) 18698 * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core 18699 * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core 18700 * @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard 18701 */ 18702 function DOMImplementation() { 18703 } 18704 18705 DOMImplementation.prototype = { 18706 /** 18707 * The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given feature is supported. 18708 * The different implementations fairly diverged in what kind of features were reported. 18709 * The latest version of the spec settled to force this method to always return true, where the functionality was accurate and in use. 18710 * 18711 * @deprecated It is deprecated and modern browsers return true in all cases. 18712 * 18713 * @param {string} feature 18714 * @param {string} [version] 18715 * @returns {boolean} always true 18716 * 18717 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN 18718 * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core 18719 * @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard 18720 */ 18721 hasFeature: function(feature, version) { 18722 return true; 18723 }, 18724 /** 18725 * Creates an XML Document object of the specified type with its document element. 18726 * 18727 * __It behaves slightly different from the description in the living standard__: 18728 * - There is no interface/class `XMLDocument`, it returns a `Document` instance. 18729 * - `contentType`, `encoding`, `mode`, `origin`, `url` fields are currently not declared. 18730 * - this implementation is not validating names or qualified names 18731 * (when parsing XML strings, the SAX parser takes care of that) 18732 * 18733 * @param {string|null} namespaceURI 18734 * @param {string} qualifiedName 18735 * @param {DocumentType=null} doctype 18736 * @returns {Document} 18737 * 18738 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN 18739 * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM Level 2 Core (initial) 18740 * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Level 2 Core 18741 * 18742 * @see https://dom.spec.whatwg.org/#validate-and-extract DOM: Validate and extract 18743 * @see https://www.w3.org/TR/xml/#NT-NameStartChar XML Spec: Names 18744 * @see https://www.w3.org/TR/xml-names/#ns-qualnames XML Namespaces: Qualified names 18745 */ 18746 createDocument: function(namespaceURI, qualifiedName, doctype){ 18747 var doc = new Document(); 18748 doc.implementation = this; 18749 doc.childNodes = new NodeList(); 18750 doc.doctype = doctype || null; 18751 if (doctype){ 18752 doc.appendChild(doctype); 18753 } 18754 if (qualifiedName){ 18755 var root = doc.createElementNS(namespaceURI, qualifiedName); 18756 doc.appendChild(root); 18757 } 18758 return doc; 18759 }, 18760 /** 18761 * Returns a doctype, with the given `qualifiedName`, `publicId`, and `systemId`. 18762 * 18763 * __This behavior is slightly different from the in the specs__: 18764 * - this implementation is not validating names or qualified names 18765 * (when parsing XML strings, the SAX parser takes care of that) 18766 * 18767 * @param {string} qualifiedName 18768 * @param {string} [publicId] 18769 * @param {string} [systemId] 18770 * @returns {DocumentType} which can either be used with `DOMImplementation.createDocument` upon document creation 18771 * or can be put into the document via methods like `Node.insertBefore()` or `Node.replaceChild()` 18772 * 18773 * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType MDN 18774 * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM Level 2 Core 18775 * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living Standard 18776 * 18777 * @see https://dom.spec.whatwg.org/#validate-and-extract DOM: Validate and extract 18778 * @see https://www.w3.org/TR/xml/#NT-NameStartChar XML Spec: Names 18779 * @see https://www.w3.org/TR/xml-names/#ns-qualnames XML Namespaces: Qualified names 18780 */ 18781 createDocumentType: function(qualifiedName, publicId, systemId){ 18782 var node = new DocumentType(); 18783 node.name = qualifiedName; 18784 node.nodeName = qualifiedName; 18785 node.publicId = publicId || ''; 18786 node.systemId = systemId || ''; 18787 18788 return node; 18789 } 18790 }; 18791 18792 18793 /** 18794 * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247 18795 */ 18796 18797 function Node() { 18798 }; 18799 18800 Node.prototype = { 18801 firstChild : null, 18802 lastChild : null, 18803 previousSibling : null, 18804 nextSibling : null, 18805 attributes : null, 18806 parentNode : null, 18807 childNodes : null, 18808 ownerDocument : null, 18809 nodeValue : null, 18810 namespaceURI : null, 18811 prefix : null, 18812 localName : null, 18813 // Modified in DOM Level 2: 18814 insertBefore:function(newChild, refChild){//raises 18815 return _insertBefore(this,newChild,refChild); 18816 }, 18817 replaceChild:function(newChild, oldChild){//raises 18818 this.insertBefore(newChild,oldChild); 18819 if(oldChild){ 18820 this.removeChild(oldChild); 18821 } 18822 }, 18823 removeChild:function(oldChild){ 18824 return _removeChild(this,oldChild); 18825 }, 18826 appendChild:function(newChild){ 18827 return this.insertBefore(newChild,null); 18828 }, 18829 hasChildNodes:function(){ 18830 return this.firstChild != null; 18831 }, 18832 cloneNode:function(deep){ 18833 return cloneNode(this.ownerDocument||this,this,deep); 18834 }, 18835 // Modified in DOM Level 2: 18836 normalize:function(){ 18837 var child = this.firstChild; 18838 while(child){ 18839 var next = child.nextSibling; 18840 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){ 18841 this.removeChild(next); 18842 child.appendData(next.data); 18843 }else{ 18844 child.normalize(); 18845 child = next; 18846 } 18847 } 18848 }, 18849 // Introduced in DOM Level 2: 18850 isSupported:function(feature, version){ 18851 return this.ownerDocument.implementation.hasFeature(feature,version); 18852 }, 18853 // Introduced in DOM Level 2: 18854 hasAttributes:function(){ 18855 return this.attributes.length>0; 18856 }, 18857 /** 18858 * Look up the prefix associated to the given namespace URI, starting from this node. 18859 * **The default namespace declarations are ignored by this method.** 18860 * See Namespace Prefix Lookup for details on the algorithm used by this method. 18861 * 18862 * _Note: The implementation seems to be incomplete when compared to the algorithm described in the specs._ 18863 * 18864 * @param {string | null} namespaceURI 18865 * @returns {string | null} 18866 * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix 18867 * @see https://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo 18868 * @see https://dom.spec.whatwg.org/#dom-node-lookupprefix 18869 * @see https://github.com/xmldom/xmldom/issues/322 18870 */ 18871 lookupPrefix:function(namespaceURI){ 18872 var el = this; 18873 while(el){ 18874 var map = el._nsMap; 18875 //console.dir(map) 18876 if(map){ 18877 for(var n in map){ 18878 if(map[n] == namespaceURI){ 18879 return n; 18880 } 18881 } 18882 } 18883 el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; 18884 } 18885 return null; 18886 }, 18887 // Introduced in DOM Level 3: 18888 lookupNamespaceURI:function(prefix){ 18889 var el = this; 18890 while(el){ 18891 var map = el._nsMap; 18892 //console.dir(map) 18893 if(map){ 18894 if(prefix in map){ 18895 return map[prefix] ; 18896 } 18897 } 18898 el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; 18899 } 18900 return null; 18901 }, 18902 // Introduced in DOM Level 3: 18903 isDefaultNamespace:function(namespaceURI){ 18904 var prefix = this.lookupPrefix(namespaceURI); 18905 return prefix == null; 18906 } 18907 }; 18908 18909 18910 function _xmlEncoder(c){ 18911 return c == '<' && '<' || 18912 c == '>' && '>' || 18913 c == '&' && '&' || 18914 c == '"' && '"' || 18915 '&#'+c.charCodeAt()+';' 18916 } 18917 18918 18919 copy(NodeType,Node); 18920 copy(NodeType,Node.prototype); 18921 18922 /** 18923 * @param callback return true for continue,false for break 18924 * @return boolean true: break visit; 18925 */ 18926 function _visitNode(node,callback){ 18927 if(callback(node)){ 18928 return true; 18929 } 18930 if(node = node.firstChild){ 18931 do{ 18932 if(_visitNode(node,callback)){return true} 18933 }while(node=node.nextSibling) 18934 } 18935 } 18936 18937 18938 18939 function Document(){ 18940 } 18941 18942 function _onAddAttribute(doc,el,newAttr){ 18943 doc && doc._inc++; 18944 var ns = newAttr.namespaceURI ; 18945 if(ns === NAMESPACE.XMLNS){ 18946 //update namespace 18947 el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value 18948 } 18949 } 18950 18951 function _onRemoveAttribute(doc,el,newAttr,remove){ 18952 doc && doc._inc++; 18953 var ns = newAttr.namespaceURI ; 18954 if(ns === NAMESPACE.XMLNS){ 18955 //update namespace 18956 delete el._nsMap[newAttr.prefix?newAttr.localName:''] 18957 } 18958 } 18959 18960 function _onUpdateChild(doc,el,newChild){ 18961 if(doc && doc._inc){ 18962 doc._inc++; 18963 //update childNodes 18964 var cs = el.childNodes; 18965 if(newChild){ 18966 cs[cs.length++] = newChild; 18967 }else{ 18968 //console.log(1) 18969 var child = el.firstChild; 18970 var i = 0; 18971 while(child){ 18972 cs[i++] = child; 18973 child =child.nextSibling; 18974 } 18975 cs.length = i; 18976 } 18977 } 18978 } 18979 18980 /** 18981 * attributes; 18982 * children; 18983 * 18984 * writeable properties: 18985 * nodeValue,Attr:value,CharacterData:data 18986 * prefix 18987 */ 18988 function _removeChild(parentNode,child){ 18989 var previous = child.previousSibling; 18990 var next = child.nextSibling; 18991 if(previous){ 18992 previous.nextSibling = next; 18993 }else{ 18994 parentNode.firstChild = next 18995 } 18996 if(next){ 18997 next.previousSibling = previous; 18998 }else{ 18999 parentNode.lastChild = previous; 19000 } 19001 _onUpdateChild(parentNode.ownerDocument,parentNode); 19002 return child; 19003 } 19004 /** 19005 * preformance key(refChild == null) 19006 */ 19007 function _insertBefore(parentNode,newChild,nextChild){ 19008 var cp = newChild.parentNode; 19009 if(cp){ 19010 cp.removeChild(newChild);//remove and update 19011 } 19012 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ 19013 var newFirst = newChild.firstChild; 19014 if (newFirst == null) { 19015 return newChild; 19016 } 19017 var newLast = newChild.lastChild; 19018 }else{ 19019 newFirst = newLast = newChild; 19020 } 19021 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild; 19022 19023 newFirst.previousSibling = pre; 19024 newLast.nextSibling = nextChild; 19025 19026 19027 if(pre){ 19028 pre.nextSibling = newFirst; 19029 }else{ 19030 parentNode.firstChild = newFirst; 19031 } 19032 if(nextChild == null){ 19033 parentNode.lastChild = newLast; 19034 }else{ 19035 nextChild.previousSibling = newLast; 19036 } 19037 do{ 19038 newFirst.parentNode = parentNode; 19039 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling)) 19040 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode); 19041 //console.log(parentNode.lastChild.nextSibling == null) 19042 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) { 19043 newChild.firstChild = newChild.lastChild = null; 19044 } 19045 return newChild; 19046 } 19047 function _appendSingleChild(parentNode,newChild){ 19048 var cp = newChild.parentNode; 19049 if(cp){ 19050 var pre = parentNode.lastChild; 19051 cp.removeChild(newChild);//remove and update 19052 var pre = parentNode.lastChild; 19053 } 19054 var pre = parentNode.lastChild; 19055 newChild.parentNode = parentNode; 19056 newChild.previousSibling = pre; 19057 newChild.nextSibling = null; 19058 if(pre){ 19059 pre.nextSibling = newChild; 19060 }else{ 19061 parentNode.firstChild = newChild; 19062 } 19063 parentNode.lastChild = newChild; 19064 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild); 19065 return newChild; 19066 //console.log("__aa",parentNode.lastChild.nextSibling == null) 19067 } 19068 Document.prototype = { 19069 //implementation : null, 19070 nodeName : '#document', 19071 nodeType : DOCUMENT_NODE, 19072 /** 19073 * The DocumentType node of the document. 19074 * 19075 * @readonly 19076 * @type DocumentType 19077 */ 19078 doctype : null, 19079 documentElement : null, 19080 _inc : 1, 19081 19082 insertBefore : function(newChild, refChild){//raises 19083 if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){ 19084 var child = newChild.firstChild; 19085 while(child){ 19086 var next = child.nextSibling; 19087 this.insertBefore(child,refChild); 19088 child = next; 19089 } 19090 return newChild; 19091 } 19092 if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){ 19093 this.documentElement = newChild; 19094 } 19095 19096 return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild; 19097 }, 19098 removeChild : function(oldChild){ 19099 if(this.documentElement == oldChild){ 19100 this.documentElement = null; 19101 } 19102 return _removeChild(this,oldChild); 19103 }, 19104 // Introduced in DOM Level 2: 19105 importNode : function(importedNode,deep){ 19106 return importNode(this,importedNode,deep); 19107 }, 19108 // Introduced in DOM Level 2: 19109 getElementById : function(id){ 19110 var rtv = null; 19111 _visitNode(this.documentElement,function(node){ 19112 if(node.nodeType == ELEMENT_NODE){ 19113 if(node.getAttribute('id') == id){ 19114 rtv = node; 19115 return true; 19116 } 19117 } 19118 }) 19119 return rtv; 19120 }, 19121 19122 /** 19123 * The `getElementsByClassName` method of `Document` interface returns an array-like object 19124 * of all child elements which have **all** of the given class name(s). 19125 * 19126 * Returns an empty list if `classeNames` is an empty string or only contains HTML white space characters. 19127 * 19128 * 19129 * Warning: This is a live LiveNodeList. 19130 * Changes in the DOM will reflect in the array as the changes occur. 19131 * If an element selected by this array no longer qualifies for the selector, 19132 * it will automatically be removed. Be aware of this for iteration purposes. 19133 * 19134 * @param {string} classNames is a string representing the class name(s) to match; multiple class names are separated by (ASCII-)whitespace 19135 * 19136 * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName 19137 * @see https://dom.spec.whatwg.org/#concept-getelementsbyclassname 19138 */ 19139 getElementsByClassName: function(classNames) { 19140 var classNamesSet = toOrderedSet(classNames) 19141 return new LiveNodeList(this, function(base) { 19142 var ls = []; 19143 if (classNamesSet.length > 0) { 19144 _visitNode(base.documentElement, function(node) { 19145 if(node !== base && node.nodeType === ELEMENT_NODE) { 19146 var nodeClassNames = node.getAttribute('class') 19147 // can be null if the attribute does not exist 19148 if (nodeClassNames) { 19149 // before splitting and iterating just compare them for the most common case 19150 var matches = classNames === nodeClassNames; 19151 if (!matches) { 19152 var nodeClassNamesSet = toOrderedSet(nodeClassNames) 19153 matches = classNamesSet.every(arrayIncludes(nodeClassNamesSet)) 19154 } 19155 if(matches) { 19156 ls.push(node); 19157 } 19158 } 19159 } 19160 }); 19161 } 19162 return ls; 19163 }); 19164 }, 19165 19166 //document factory method: 19167 createElement : function(tagName){ 19168 var node = new Element(); 19169 node.ownerDocument = this; 19170 node.nodeName = tagName; 19171 node.tagName = tagName; 19172 node.localName = tagName; 19173 node.childNodes = new NodeList(); 19174 var attrs = node.attributes = new NamedNodeMap(); 19175 attrs._ownerElement = node; 19176 return node; 19177 }, 19178 createDocumentFragment : function(){ 19179 var node = new DocumentFragment(); 19180 node.ownerDocument = this; 19181 node.childNodes = new NodeList(); 19182 return node; 19183 }, 19184 createTextNode : function(data){ 19185 var node = new Text(); 19186 node.ownerDocument = this; 19187 node.appendData(data) 19188 return node; 19189 }, 19190 createComment : function(data){ 19191 var node = new Comment(); 19192 node.ownerDocument = this; 19193 node.appendData(data) 19194 return node; 19195 }, 19196 createCDATASection : function(data){ 19197 var node = new CDATASection(); 19198 node.ownerDocument = this; 19199 node.appendData(data) 19200 return node; 19201 }, 19202 createProcessingInstruction : function(target,data){ 19203 var node = new ProcessingInstruction(); 19204 node.ownerDocument = this; 19205 node.tagName = node.target = target; 19206 node.nodeValue= node.data = data; 19207 return node; 19208 }, 19209 createAttribute : function(name){ 19210 var node = new Attr(); 19211 node.ownerDocument = this; 19212 node.name = name; 19213 node.nodeName = name; 19214 node.localName = name; 19215 node.specified = true; 19216 return node; 19217 }, 19218 createEntityReference : function(name){ 19219 var node = new EntityReference(); 19220 node.ownerDocument = this; 19221 node.nodeName = name; 19222 return node; 19223 }, 19224 // Introduced in DOM Level 2: 19225 createElementNS : function(namespaceURI,qualifiedName){ 19226 var node = new Element(); 19227 var pl = qualifiedName.split(':'); 19228 var attrs = node.attributes = new NamedNodeMap(); 19229 node.childNodes = new NodeList(); 19230 node.ownerDocument = this; 19231 node.nodeName = qualifiedName; 19232 node.tagName = qualifiedName; 19233 node.namespaceURI = namespaceURI; 19234 if(pl.length == 2){ 19235 node.prefix = pl[0]; 19236 node.localName = pl[1]; 19237 }else{ 19238 //el.prefix = null; 19239 node.localName = qualifiedName; 19240 } 19241 attrs._ownerElement = node; 19242 return node; 19243 }, 19244 // Introduced in DOM Level 2: 19245 createAttributeNS : function(namespaceURI,qualifiedName){ 19246 var node = new Attr(); 19247 var pl = qualifiedName.split(':'); 19248 node.ownerDocument = this; 19249 node.nodeName = qualifiedName; 19250 node.name = qualifiedName; 19251 node.namespaceURI = namespaceURI; 19252 node.specified = true; 19253 if(pl.length == 2){ 19254 node.prefix = pl[0]; 19255 node.localName = pl[1]; 19256 }else{ 19257 //el.prefix = null; 19258 node.localName = qualifiedName; 19259 } 19260 return node; 19261 } 19262 }; 19263 _extends(Document,Node); 19264 19265 19266 function Element() { 19267 this._nsMap = {}; 19268 }; 19269 Element.prototype = { 19270 nodeType : ELEMENT_NODE, 19271 hasAttribute : function(name){ 19272 return this.getAttributeNode(name)!=null; 19273 }, 19274 getAttribute : function(name){ 19275 var attr = this.getAttributeNode(name); 19276 return attr && attr.value || ''; 19277 }, 19278 getAttributeNode : function(name){ 19279 return this.attributes.getNamedItem(name); 19280 }, 19281 setAttribute : function(name, value){ 19282 var attr = this.ownerDocument.createAttribute(name); 19283 attr.value = attr.nodeValue = "" + value; 19284 this.setAttributeNode(attr) 19285 }, 19286 removeAttribute : function(name){ 19287 var attr = this.getAttributeNode(name) 19288 attr && this.removeAttributeNode(attr); 19289 }, 19290 19291 //four real opeartion method 19292 appendChild:function(newChild){ 19293 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ 19294 return this.insertBefore(newChild,null); 19295 }else{ 19296 return _appendSingleChild(this,newChild); 19297 } 19298 }, 19299 setAttributeNode : function(newAttr){ 19300 return this.attributes.setNamedItem(newAttr); 19301 }, 19302 setAttributeNodeNS : function(newAttr){ 19303 return this.attributes.setNamedItemNS(newAttr); 19304 }, 19305 removeAttributeNode : function(oldAttr){ 19306 //console.log(this == oldAttr.ownerElement) 19307 return this.attributes.removeNamedItem(oldAttr.nodeName); 19308 }, 19309 //get real attribute name,and remove it by removeAttributeNode 19310 removeAttributeNS : function(namespaceURI, localName){ 19311 var old = this.getAttributeNodeNS(namespaceURI, localName); 19312 old && this.removeAttributeNode(old); 19313 }, 19314 19315 hasAttributeNS : function(namespaceURI, localName){ 19316 return this.getAttributeNodeNS(namespaceURI, localName)!=null; 19317 }, 19318 getAttributeNS : function(namespaceURI, localName){ 19319 var attr = this.getAttributeNodeNS(namespaceURI, localName); 19320 return attr && attr.value || ''; 19321 }, 19322 setAttributeNS : function(namespaceURI, qualifiedName, value){ 19323 var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName); 19324 attr.value = attr.nodeValue = "" + value; 19325 this.setAttributeNode(attr) 19326 }, 19327 getAttributeNodeNS : function(namespaceURI, localName){ 19328 return this.attributes.getNamedItemNS(namespaceURI, localName); 19329 }, 19330 19331 getElementsByTagName : function(tagName){ 19332 return new LiveNodeList(this,function(base){ 19333 var ls = []; 19334 _visitNode(base,function(node){ 19335 if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){ 19336 ls.push(node); 19337 } 19338 }); 19339 return ls; 19340 }); 19341 }, 19342 getElementsByTagNameNS : function(namespaceURI, localName){ 19343 return new LiveNodeList(this,function(base){ 19344 var ls = []; 19345 _visitNode(base,function(node){ 19346 if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){ 19347 ls.push(node); 19348 } 19349 }); 19350 return ls; 19351 19352 }); 19353 } 19354 }; 19355 Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName; 19356 Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS; 19357 19358 19359 _extends(Element,Node); 19360 function Attr() { 19361 }; 19362 Attr.prototype.nodeType = ATTRIBUTE_NODE; 19363 _extends(Attr,Node); 19364 19365 19366 function CharacterData() { 19367 }; 19368 CharacterData.prototype = { 19369 data : '', 19370 substringData : function(offset, count) { 19371 return this.data.substring(offset, offset+count); 19372 }, 19373 appendData: function(text) { 19374 text = this.data+text; 19375 this.nodeValue = this.data = text; 19376 this.length = text.length; 19377 }, 19378 insertData: function(offset,text) { 19379 this.replaceData(offset,0,text); 19380 19381 }, 19382 appendChild:function(newChild){ 19383 throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]) 19384 }, 19385 deleteData: function(offset, count) { 19386 this.replaceData(offset,count,""); 19387 }, 19388 replaceData: function(offset, count, text) { 19389 var start = this.data.substring(0,offset); 19390 var end = this.data.substring(offset+count); 19391 text = start + text + end; 19392 this.nodeValue = this.data = text; 19393 this.length = text.length; 19394 } 19395 } 19396 _extends(CharacterData,Node); 19397 function Text() { 19398 }; 19399 Text.prototype = { 19400 nodeName : "#text", 19401 nodeType : TEXT_NODE, 19402 splitText : function(offset) { 19403 var text = this.data; 19404 var newText = text.substring(offset); 19405 text = text.substring(0, offset); 19406 this.data = this.nodeValue = text; 19407 this.length = text.length; 19408 var newNode = this.ownerDocument.createTextNode(newText); 19409 if(this.parentNode){ 19410 this.parentNode.insertBefore(newNode, this.nextSibling); 19411 } 19412 return newNode; 19413 } 19414 } 19415 _extends(Text,CharacterData); 19416 function Comment() { 19417 }; 19418 Comment.prototype = { 19419 nodeName : "#comment", 19420 nodeType : COMMENT_NODE 19421 } 19422 _extends(Comment,CharacterData); 19423 19424 function CDATASection() { 19425 }; 19426 CDATASection.prototype = { 19427 nodeName : "#cdata-section", 19428 nodeType : CDATA_SECTION_NODE 19429 } 19430 _extends(CDATASection,CharacterData); 19431 19432 19433 function DocumentType() { 19434 }; 19435 DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE; 19436 _extends(DocumentType,Node); 19437 19438 function Notation() { 19439 }; 19440 Notation.prototype.nodeType = NOTATION_NODE; 19441 _extends(Notation,Node); 19442 19443 function Entity() { 19444 }; 19445 Entity.prototype.nodeType = ENTITY_NODE; 19446 _extends(Entity,Node); 19447 19448 function EntityReference() { 19449 }; 19450 EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE; 19451 _extends(EntityReference,Node); 19452 19453 function DocumentFragment() { 19454 }; 19455 DocumentFragment.prototype.nodeName = "#document-fragment"; 19456 DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE; 19457 _extends(DocumentFragment,Node); 19458 19459 19460 function ProcessingInstruction() { 19461 } 19462 ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE; 19463 _extends(ProcessingInstruction,Node); 19464 function XMLSerializer(){} 19465 XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){ 19466 return nodeSerializeToString.call(node,isHtml,nodeFilter); 19467 } 19468 Node.prototype.toString = nodeSerializeToString; 19469 function nodeSerializeToString(isHtml,nodeFilter){ 19470 var buf = []; 19471 var refNode = this.nodeType == 9 && this.documentElement || this; 19472 var prefix = refNode.prefix; 19473 var uri = refNode.namespaceURI; 19474 19475 if(uri && prefix == null){ 19476 //console.log(prefix) 19477 var prefix = refNode.lookupPrefix(uri); 19478 if(prefix == null){ 19479 //isHTML = true; 19480 var visibleNamespaces=[ 19481 {namespace:uri,prefix:null} 19482 //{namespace:uri,prefix:''} 19483 ] 19484 } 19485 } 19486 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces); 19487 //console.log('###',this.nodeType,uri,prefix,buf.join('')) 19488 return buf.join(''); 19489 } 19490 19491 function needNamespaceDefine(node, isHTML, visibleNamespaces) { 19492 var prefix = node.prefix || ''; 19493 var uri = node.namespaceURI; 19494 // According to [Namespaces in XML 1.0](https://www.w3.org/TR/REC-xml-names/#ns-using) , 19495 // and more specifically https://www.w3.org/TR/REC-xml-names/#nsc-NoPrefixUndecl : 19496 // > In a namespace declaration for a prefix [...], the attribute value MUST NOT be empty. 19497 // in a similar manner [Namespaces in XML 1.1](https://www.w3.org/TR/xml-names11/#ns-using) 19498 // and more specifically https://www.w3.org/TR/xml-names11/#nsc-NSDeclared : 19499 // > [...] Furthermore, the attribute value [...] must not be an empty string. 19500 // so serializing empty namespace value like xmlns:ds="" would produce an invalid XML document. 19501 if (!uri) { 19502 return false; 19503 } 19504 if (prefix === "xml" && uri === NAMESPACE.XML || uri === NAMESPACE.XMLNS) { 19505 return false; 19506 } 19507 19508 var i = visibleNamespaces.length 19509 while (i--) { 19510 var ns = visibleNamespaces[i]; 19511 // get namespace prefix 19512 if (ns.prefix === prefix) { 19513 return ns.namespace !== uri; 19514 } 19515 } 19516 return true; 19517 } 19518 /** 19519 * Well-formed constraint: No < in Attribute Values 19520 * The replacement text of any entity referred to directly or indirectly in an attribute value must not contain a <. 19521 * @see https://www.w3.org/TR/xml/#CleanAttrVals 19522 * @see https://www.w3.org/TR/xml/#NT-AttValue 19523 */ 19524 function addSerializedAttribute(buf, qualifiedName, value) { 19525 buf.push(' ', qualifiedName, '="', value.replace(/[<&"]/g,_xmlEncoder), '"') 19526 } 19527 19528 function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){ 19529 if (!visibleNamespaces) { 19530 visibleNamespaces = []; 19531 } 19532 19533 if(nodeFilter){ 19534 node = nodeFilter(node); 19535 if(node){ 19536 if(typeof node == 'string'){ 19537 buf.push(node); 19538 return; 19539 } 19540 }else{ 19541 return; 19542 } 19543 //buf.sort.apply(attrs, attributeSorter); 19544 } 19545 19546 switch(node.nodeType){ 19547 case ELEMENT_NODE: 19548 var attrs = node.attributes; 19549 var len = attrs.length; 19550 var child = node.firstChild; 19551 var nodeName = node.tagName; 19552 19553 isHTML = NAMESPACE.isHTML(node.namespaceURI) || isHTML 19554 19555 var prefixedNodeName = nodeName 19556 if (!isHTML && !node.prefix && node.namespaceURI) { 19557 var defaultNS 19558 // lookup current default ns from `xmlns` attribute 19559 for (var ai = 0; ai < attrs.length; ai++) { 19560 if (attrs.item(ai).name === 'xmlns') { 19561 defaultNS = attrs.item(ai).value 19562 break 19563 } 19564 } 19565 if (!defaultNS) { 19566 // lookup current default ns in visibleNamespaces 19567 for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) { 19568 var namespace = visibleNamespaces[nsi] 19569 if (namespace.prefix === '' && namespace.namespace === node.namespaceURI) { 19570 defaultNS = namespace.namespace 19571 break 19572 } 19573 } 19574 } 19575 if (defaultNS !== node.namespaceURI) { 19576 for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) { 19577 var namespace = visibleNamespaces[nsi] 19578 if (namespace.namespace === node.namespaceURI) { 19579 if (namespace.prefix) { 19580 prefixedNodeName = namespace.prefix + ':' + nodeName 19581 } 19582 break 19583 } 19584 } 19585 } 19586 } 19587 19588 buf.push('<', prefixedNodeName); 19589 19590 for(var i=0;i<len;i++){ 19591 // add namespaces for attributes 19592 var attr = attrs.item(i); 19593 if (attr.prefix == 'xmlns') { 19594 visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value }); 19595 }else if(attr.nodeName == 'xmlns'){ 19596 visibleNamespaces.push({ prefix: '', namespace: attr.value }); 19597 } 19598 } 19599 19600 for(var i=0;i<len;i++){ 19601 var attr = attrs.item(i); 19602 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) { 19603 var prefix = attr.prefix||''; 19604 var uri = attr.namespaceURI; 19605 addSerializedAttribute(buf, prefix ? 'xmlns:' + prefix : "xmlns", uri); 19606 visibleNamespaces.push({ prefix: prefix, namespace:uri }); 19607 } 19608 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces); 19609 } 19610 19611 // add namespace for current node 19612 if (nodeName === prefixedNodeName && needNamespaceDefine(node, isHTML, visibleNamespaces)) { 19613 var prefix = node.prefix||''; 19614 var uri = node.namespaceURI; 19615 addSerializedAttribute(buf, prefix ? 'xmlns:' + prefix : "xmlns", uri); 19616 visibleNamespaces.push({ prefix: prefix, namespace:uri }); 19617 } 19618 19619 if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){ 19620 buf.push('>'); 19621 //if is cdata child node 19622 if(isHTML && /^script$/i.test(nodeName)){ 19623 while(child){ 19624 if(child.data){ 19625 buf.push(child.data); 19626 }else{ 19627 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces.slice()); 19628 } 19629 child = child.nextSibling; 19630 } 19631 }else 19632 { 19633 while(child){ 19634 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces.slice()); 19635 child = child.nextSibling; 19636 } 19637 } 19638 buf.push('</',prefixedNodeName,'>'); 19639 }else{ 19640 buf.push('/>'); 19641 } 19642 // remove added visible namespaces 19643 //visibleNamespaces.length = startVisibleNamespaces; 19644 return; 19645 case DOCUMENT_NODE: 19646 case DOCUMENT_FRAGMENT_NODE: 19647 var child = node.firstChild; 19648 while(child){ 19649 serializeToString(child, buf, isHTML, nodeFilter, visibleNamespaces.slice()); 19650 child = child.nextSibling; 19651 } 19652 return; 19653 case ATTRIBUTE_NODE: 19654 return addSerializedAttribute(buf, node.name, node.value); 19655 case TEXT_NODE: 19656 /** 19657 * The ampersand character (&) and the left angle bracket (<) must not appear in their literal form, 19658 * except when used as markup delimiters, or within a comment, a processing instruction, or a CDATA section. 19659 * If they are needed elsewhere, they must be escaped using either numeric character references or the strings 19660 * `&` and `<` respectively. 19661 * The right angle bracket (>) may be represented using the string " > ", and must, for compatibility, 19662 * be escaped using either `>` or a character reference when it appears in the string `]]>` in content, 19663 * when that string is not marking the end of a CDATA section. 19664 * 19665 * In the content of elements, character data is any string of characters 19666 * which does not contain the start-delimiter of any markup 19667 * and does not include the CDATA-section-close delimiter, `]]>`. 19668 * 19669 * @see https://www.w3.org/TR/xml/#NT-CharData 19670 */ 19671 return buf.push(node.data 19672 .replace(/[<&]/g,_xmlEncoder) 19673 .replace(/]]>/g, ']]>') 19674 ); 19675 case CDATA_SECTION_NODE: 19676 return buf.push( '<![CDATA[',node.data,']]>'); 19677 case COMMENT_NODE: 19678 return buf.push( "<!--",node.data,"-->"); 19679 case DOCUMENT_TYPE_NODE: 19680 var pubid = node.publicId; 19681 var sysid = node.systemId; 19682 buf.push('<!DOCTYPE ',node.name); 19683 if(pubid){ 19684 buf.push(' PUBLIC ', pubid); 19685 if (sysid && sysid!='.') { 19686 buf.push(' ', sysid); 19687 } 19688 buf.push('>'); 19689 }else if(sysid && sysid!='.'){ 19690 buf.push(' SYSTEM ', sysid, '>'); 19691 }else{ 19692 var sub = node.internalSubset; 19693 if(sub){ 19694 buf.push(" [",sub,"]"); 19695 } 19696 buf.push(">"); 19697 } 19698 return; 19699 case PROCESSING_INSTRUCTION_NODE: 19700 return buf.push( "<?",node.target," ",node.data,"?>"); 19701 case ENTITY_REFERENCE_NODE: 19702 return buf.push( '&',node.nodeName,';'); 19703 //case ENTITY_NODE: 19704 //case NOTATION_NODE: 19705 default: 19706 buf.push('??',node.nodeName); 19707 } 19708 } 19709 function importNode(doc,node,deep){ 19710 var node2; 19711 switch (node.nodeType) { 19712 case ELEMENT_NODE: 19713 node2 = node.cloneNode(false); 19714 node2.ownerDocument = doc; 19715 //var attrs = node2.attributes; 19716 //var len = attrs.length; 19717 //for(var i=0;i<len;i++){ 19718 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep)); 19719 //} 19720 case DOCUMENT_FRAGMENT_NODE: 19721 break; 19722 case ATTRIBUTE_NODE: 19723 deep = true; 19724 break; 19725 //case ENTITY_REFERENCE_NODE: 19726 //case PROCESSING_INSTRUCTION_NODE: 19727 ////case TEXT_NODE: 19728 //case CDATA_SECTION_NODE: 19729 //case COMMENT_NODE: 19730 // deep = false; 19731 // break; 19732 //case DOCUMENT_NODE: 19733 //case DOCUMENT_TYPE_NODE: 19734 //cannot be imported. 19735 //case ENTITY_NODE: 19736 //case NOTATION_NODE: 19737 //can not hit in level3 19738 //default:throw e; 19739 } 19740 if(!node2){ 19741 node2 = node.cloneNode(false);//false 19742 } 19743 node2.ownerDocument = doc; 19744 node2.parentNode = null; 19745 if(deep){ 19746 var child = node.firstChild; 19747 while(child){ 19748 node2.appendChild(importNode(doc,child,deep)); 19749 child = child.nextSibling; 19750 } 19751 } 19752 return node2; 19753 } 19754 // 19755 //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1, 19756 // attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,}; 19757 function cloneNode(doc,node,deep){ 19758 var node2 = new node.constructor(); 19759 for(var n in node){ 19760 var v = node[n]; 19761 if(typeof v != 'object' ){ 19762 if(v != node2[n]){ 19763 node2[n] = v; 19764 } 19765 } 19766 } 19767 if(node.childNodes){ 19768 node2.childNodes = new NodeList(); 19769 } 19770 node2.ownerDocument = doc; 19771 switch (node2.nodeType) { 19772 case ELEMENT_NODE: 19773 var attrs = node.attributes; 19774 var attrs2 = node2.attributes = new NamedNodeMap(); 19775 var len = attrs.length 19776 attrs2._ownerElement = node2; 19777 for(var i=0;i<len;i++){ 19778 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true)); 19779 } 19780 break;; 19781 case ATTRIBUTE_NODE: 19782 deep = true; 19783 } 19784 if(deep){ 19785 var child = node.firstChild; 19786 while(child){ 19787 node2.appendChild(cloneNode(doc,child,deep)); 19788 child = child.nextSibling; 19789 } 19790 } 19791 return node2; 19792 } 19793 19794 function __set__(object,key,value){ 19795 object[key] = value 19796 } 19797 //do dynamic 19798 try{ 19799 if(Object.defineProperty){ 19800 Object.defineProperty(LiveNodeList.prototype,'length',{ 19801 get:function(){ 19802 _updateLiveList(this); 19803 return this.$$length; 19804 } 19805 }); 19806 19807 Object.defineProperty(Node.prototype,'textContent',{ 19808 get:function(){ 19809 return getTextContent(this); 19810 }, 19811 19812 set:function(data){ 19813 switch(this.nodeType){ 19814 case ELEMENT_NODE: 19815 case DOCUMENT_FRAGMENT_NODE: 19816 while(this.firstChild){ 19817 this.removeChild(this.firstChild); 19818 } 19819 if(data || String(data)){ 19820 this.appendChild(this.ownerDocument.createTextNode(data)); 19821 } 19822 break; 19823 19824 default: 19825 this.data = data; 19826 this.value = data; 19827 this.nodeValue = data; 19828 } 19829 } 19830 }) 19831 19832 function getTextContent(node){ 19833 switch(node.nodeType){ 19834 case ELEMENT_NODE: 19835 case DOCUMENT_FRAGMENT_NODE: 19836 var buf = []; 19837 node = node.firstChild; 19838 while(node){ 19839 if(node.nodeType!==7 && node.nodeType !==8){ 19840 buf.push(getTextContent(node)); 19841 } 19842 node = node.nextSibling; 19843 } 19844 return buf.join(''); 19845 default: 19846 return node.nodeValue; 19847 } 19848 } 19849 19850 __set__ = function(object,key,value){ 19851 //console.log(value) 19852 object['$$'+key] = value 19853 } 19854 } 19855 }catch(e){//ie8 19856 } 19857 19858 //if(typeof require == 'function'){ 19859 exports.DocumentType = DocumentType; 19860 exports.DOMException = DOMException; 19861 exports.DOMImplementation = DOMImplementation; 19862 exports.Element = Element; 19863 exports.Node = Node; 19864 exports.NodeList = NodeList; 19865 exports.XMLSerializer = XMLSerializer; 19866 //} 19867 19868 19869 /***/ }), 19870 /* 26 */ 19871 /***/ (function(module, exports, __webpack_require__) { 19872 19873 var freeGlobal = __webpack_require__(52); 19874 19875 /** Detect free variable `self`. */ 19876 var freeSelf = typeof self == 'object' && self && self.Object === Object && self; 19877 19878 /** Used as a reference to the global object. */ 19879 var root = freeGlobal || freeSelf || Function('return this')(); 19880 19881 module.exports = root; 19882 19883 19884 /***/ }), 19885 /* 27 */ 19886 /***/ (function(module, exports, __webpack_require__) { 19887 19888 var root = __webpack_require__(26); 19889 19890 /** Built-in value references. */ 19891 var Symbol = root.Symbol; 19892 19893 module.exports = Symbol; 19894 19895 19896 /***/ }), 19897 /* 28 */ 19898 /***/ (function(module, exports, __webpack_require__) { 19899 19900 var debounce = __webpack_require__(21), 19901 isObject = __webpack_require__(19); 19902 19903 /** Error message constants. */ 19904 var FUNC_ERROR_TEXT = 'Expected a function'; 19905 19906 /** 19907 * Creates a throttled function that only invokes `func` at most once per 19908 * every `wait` milliseconds. The throttled function comes with a `cancel` 19909 * method to cancel delayed `func` invocations and a `flush` method to 19910 * immediately invoke them. Provide `options` to indicate whether `func` 19911 * should be invoked on the leading and/or trailing edge of the `wait` 19912 * timeout. The `func` is invoked with the last arguments provided to the 19913 * throttled function. Subsequent calls to the throttled function return the 19914 * result of the last `func` invocation. 19915 * 19916 * **Note:** If `leading` and `trailing` options are `true`, `func` is 19917 * invoked on the trailing edge of the timeout only if the throttled function 19918 * is invoked more than once during the `wait` timeout. 19919 * 19920 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred 19921 * until to the next tick, similar to `setTimeout` with a timeout of `0`. 19922 * 19923 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) 19924 * for details over the differences between `_.throttle` and `_.debounce`. 19925 * 19926 * @static 19927 * @memberOf _ 19928 * @since 0.1.0 19929 * @category Function 19930 * @param {Function} func The function to throttle. 19931 * @param {number} [wait=0] The number of milliseconds to throttle invocations to. 19932 * @param {Object} [options={}] The options object. 19933 * @param {boolean} [options.leading=true] 19934 * Specify invoking on the leading edge of the timeout. 19935 * @param {boolean} [options.trailing=true] 19936 * Specify invoking on the trailing edge of the timeout. 19937 * @returns {Function} Returns the new throttled function. 19938 * @example 19939 * 19940 * // Avoid excessively updating the position while scrolling. 19941 * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); 19942 * 19943 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. 19944 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); 19945 * jQuery(element).on('click', throttled); 19946 * 19947 * // Cancel the trailing throttled invocation. 19948 * jQuery(window).on('popstate', throttled.cancel); 19949 */ 19950 function throttle(func, wait, options) { 19951 var leading = true, 19952 trailing = true; 19953 19954 if (typeof func != 'function') { 19955 throw new TypeError(FUNC_ERROR_TEXT); 19956 } 19957 if (isObject(options)) { 19958 leading = 'leading' in options ? !!options.leading : leading; 19959 trailing = 'trailing' in options ? !!options.trailing : trailing; 19960 } 19961 return debounce(func, wait, { 19962 'leading': leading, 19963 'maxWait': wait, 19964 'trailing': trailing 19965 }); 19966 } 19967 19968 module.exports = throttle; 19969 19970 19971 /***/ }), 19972 /* 29 */ 19973 /***/ (function(module, exports) { 19974 19975 module.exports = __WEBPACK_EXTERNAL_MODULE__29__; 19976 19977 /***/ }), 19978 /* 30 */ 19979 /***/ (function(module, __webpack_exports__, __webpack_require__) { 19980 19981 "use strict"; 19982 __webpack_require__.r(__webpack_exports__); 19983 /* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var _book__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(24); 19984 /* harmony import */ var _rendition__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(16); 19985 /* harmony import */ var _epubcfi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2); 19986 /* harmony import */ var _contents__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); 19987 /* harmony import */ var _utils_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(0); 19988 /* harmony import */ var _utils_constants__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(1); 19989 /* harmony import */ var _managers_views_iframe__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(20); 19990 /* harmony import */ var _managers_default__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(10); 19991 /* harmony import */ var _managers_continuous__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(22); 19992 19993 19994 19995 19996 19997 19998 19999 20000 20001 /** 20002 * Creates a new Book 20003 * @param {string|ArrayBuffer} url URL, Path or ArrayBuffer 20004 * @param {object} options to pass to the book 20005 * @returns {Book} a new Book object 20006 * @example ePub("/path/to/book.epub", {}) 20007 */ 20008 20009 function ePub(url, options) { 20010 return new _book__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"](url, options); 20011 } 20012 20013 ePub.VERSION = _utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EPUBJS_VERSION */ "b"]; 20014 20015 if (typeof global !== "undefined") { 20016 global.EPUBJS_VERSION = _utils_constants__WEBPACK_IMPORTED_MODULE_5__[/* EPUBJS_VERSION */ "b"]; 20017 } 20018 20019 ePub.Book = _book__WEBPACK_IMPORTED_MODULE_0__[/* default */ "a"]; 20020 ePub.Rendition = _rendition__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"]; 20021 ePub.Contents = _contents__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]; 20022 ePub.CFI = _epubcfi__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"]; 20023 ePub.utils = _utils_core__WEBPACK_IMPORTED_MODULE_4__; 20024 /* harmony default export */ __webpack_exports__["default"] = (ePub); 20025 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(17))) 20026 20027 /***/ }), 20028 /* 31 */ 20029 /***/ (function(module, exports, __webpack_require__) { 20030 20031 "use strict"; 20032 20033 20034 var assign = __webpack_require__(32) 20035 , normalizeOpts = __webpack_require__(40) 20036 , isCallable = __webpack_require__(41) 20037 , contains = __webpack_require__(42) 20038 20039 , d; 20040 20041 d = module.exports = function (dscr, value/*, options*/) { 20042 var c, e, w, options, desc; 20043 if ((arguments.length < 2) || (typeof dscr !== 'string')) { 20044 options = value; 20045 value = dscr; 20046 dscr = null; 20047 } else { 20048 options = arguments[2]; 20049 } 20050 if (dscr == null) { 20051 c = w = true; 20052 e = false; 20053 } else { 20054 c = contains.call(dscr, 'c'); 20055 e = contains.call(dscr, 'e'); 20056 w = contains.call(dscr, 'w'); 20057 } 20058 20059 desc = { value: value, configurable: c, enumerable: e, writable: w }; 20060 return !options ? desc : assign(normalizeOpts(options), desc); 20061 }; 20062 20063 d.gs = function (dscr, get, set/*, options*/) { 20064 var c, e, options, desc; 20065 if (typeof dscr !== 'string') { 20066 options = set; 20067 set = get; 20068 get = dscr; 20069 dscr = null; 20070 } else { 20071 options = arguments[3]; 20072 } 20073 if (get == null) { 20074 get = undefined; 20075 } else if (!isCallable(get)) { 20076 options = get; 20077 get = set = undefined; 20078 } else if (set == null) { 20079 set = undefined; 20080 } else if (!isCallable(set)) { 20081 options = set; 20082 set = undefined; 20083 } 20084 if (dscr == null) { 20085 c = true; 20086 e = false; 20087 } else { 20088 c = contains.call(dscr, 'c'); 20089 e = contains.call(dscr, 'e'); 20090 } 20091 20092 desc = { get: get, set: set, configurable: c, enumerable: e }; 20093 return !options ? desc : assign(normalizeOpts(options), desc); 20094 }; 20095 20096 20097 /***/ }), 20098 /* 32 */ 20099 /***/ (function(module, exports, __webpack_require__) { 20100 20101 "use strict"; 20102 20103 20104 module.exports = __webpack_require__(33)() 20105 ? Object.assign 20106 : __webpack_require__(34); 20107 20108 20109 /***/ }), 20110 /* 33 */ 20111 /***/ (function(module, exports, __webpack_require__) { 20112 20113 "use strict"; 20114 20115 20116 module.exports = function () { 20117 var assign = Object.assign, obj; 20118 if (typeof assign !== "function") return false; 20119 obj = { foo: "raz" }; 20120 assign(obj, { bar: "dwa" }, { trzy: "trzy" }); 20121 return (obj.foo + obj.bar + obj.trzy) === "razdwatrzy"; 20122 }; 20123 20124 20125 /***/ }), 20126 /* 34 */ 20127 /***/ (function(module, exports, __webpack_require__) { 20128 20129 "use strict"; 20130 20131 20132 var keys = __webpack_require__(35) 20133 , value = __webpack_require__(39) 20134 , max = Math.max; 20135 20136 module.exports = function (dest, src /*, …srcn*/) { 20137 var error, i, length = max(arguments.length, 2), assign; 20138 dest = Object(value(dest)); 20139 assign = function (key) { 20140 try { 20141 dest[key] = src[key]; 20142 } catch (e) { 20143 if (!error) error = e; 20144 } 20145 }; 20146 for (i = 1; i < length; ++i) { 20147 src = arguments[i]; 20148 keys(src).forEach(assign); 20149 } 20150 if (error !== undefined) throw error; 20151 return dest; 20152 }; 20153 20154 20155 /***/ }), 20156 /* 35 */ 20157 /***/ (function(module, exports, __webpack_require__) { 20158 20159 "use strict"; 20160 20161 20162 module.exports = __webpack_require__(36)() 20163 ? Object.keys 20164 : __webpack_require__(37); 20165 20166 20167 /***/ }), 20168 /* 36 */ 20169 /***/ (function(module, exports, __webpack_require__) { 20170 20171 "use strict"; 20172 20173 20174 module.exports = function () { 20175 try { 20176 Object.keys("primitive"); 20177 return true; 20178 } catch (e) { 20179 return false; 20180 } 20181 }; 20182 20183 20184 /***/ }), 20185 /* 37 */ 20186 /***/ (function(module, exports, __webpack_require__) { 20187 20188 "use strict"; 20189 20190 20191 var isValue = __webpack_require__(18); 20192 20193 var keys = Object.keys; 20194 20195 module.exports = function (object) { 20196 return keys(isValue(object) ? Object(object) : object); 20197 }; 20198 20199 20200 /***/ }), 20201 /* 38 */ 20202 /***/ (function(module, exports, __webpack_require__) { 20203 20204 "use strict"; 20205 20206 20207 // eslint-disable-next-line no-empty-function 20208 module.exports = function () {}; 20209 20210 20211 /***/ }), 20212 /* 39 */ 20213 /***/ (function(module, exports, __webpack_require__) { 20214 20215 "use strict"; 20216 20217 20218 var isValue = __webpack_require__(18); 20219 20220 module.exports = function (value) { 20221 if (!isValue(value)) throw new TypeError("Cannot use null or undefined"); 20222 return value; 20223 }; 20224 20225 20226 /***/ }), 20227 /* 40 */ 20228 /***/ (function(module, exports, __webpack_require__) { 20229 20230 "use strict"; 20231 20232 20233 var isValue = __webpack_require__(18); 20234 20235 var forEach = Array.prototype.forEach, create = Object.create; 20236 20237 var process = function (src, obj) { 20238 var key; 20239 for (key in src) obj[key] = src[key]; 20240 }; 20241 20242 // eslint-disable-next-line no-unused-vars 20243 module.exports = function (opts1 /*, …options*/) { 20244 var result = create(null); 20245 forEach.call(arguments, function (options) { 20246 if (!isValue(options)) return; 20247 process(Object(options), result); 20248 }); 20249 return result; 20250 }; 20251 20252 20253 /***/ }), 20254 /* 41 */ 20255 /***/ (function(module, exports, __webpack_require__) { 20256 20257 "use strict"; 20258 // Deprecated 20259 20260 20261 20262 module.exports = function (obj) { 20263 return typeof obj === "function"; 20264 }; 20265 20266 20267 /***/ }), 20268 /* 42 */ 20269 /***/ (function(module, exports, __webpack_require__) { 20270 20271 "use strict"; 20272 20273 20274 module.exports = __webpack_require__(43)() 20275 ? String.prototype.contains 20276 : __webpack_require__(44); 20277 20278 20279 /***/ }), 20280 /* 43 */ 20281 /***/ (function(module, exports, __webpack_require__) { 20282 20283 "use strict"; 20284 20285 20286 var str = "razdwatrzy"; 20287 20288 module.exports = function () { 20289 if (typeof str.contains !== "function") return false; 20290 return (str.contains("dwa") === true) && (str.contains("foo") === false); 20291 }; 20292 20293 20294 /***/ }), 20295 /* 44 */ 20296 /***/ (function(module, exports, __webpack_require__) { 20297 20298 "use strict"; 20299 20300 20301 var indexOf = String.prototype.indexOf; 20302 20303 module.exports = function (searchString/*, position*/) { 20304 return indexOf.call(this, searchString, arguments[1]) > -1; 20305 }; 20306 20307 20308 /***/ }), 20309 /* 45 */ 20310 /***/ (function(module, exports, __webpack_require__) { 20311 20312 "use strict"; 20313 20314 20315 module.exports = function (fn) { 20316 if (typeof fn !== "function") throw new TypeError(fn + " is not a function"); 20317 return fn; 20318 }; 20319 20320 20321 /***/ }), 20322 /* 46 */ 20323 /***/ (function(module, exports, __webpack_require__) { 20324 20325 var conventions = __webpack_require__(14); 20326 var dom = __webpack_require__(25) 20327 var entities = __webpack_require__(47); 20328 var sax = __webpack_require__(48); 20329 20330 var DOMImplementation = dom.DOMImplementation; 20331 20332 var NAMESPACE = conventions.NAMESPACE; 20333 20334 var ParseError = sax.ParseError; 20335 var XMLReader = sax.XMLReader; 20336 20337 function DOMParser(options){ 20338 this.options = options ||{locator:{}}; 20339 } 20340 20341 DOMParser.prototype.parseFromString = function(source,mimeType){ 20342 var options = this.options; 20343 var sax = new XMLReader(); 20344 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler 20345 var errorHandler = options.errorHandler; 20346 var locator = options.locator; 20347 var defaultNSMap = options.xmlns||{}; 20348 var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1; 20349 var entityMap = isHTML ? entities.HTML_ENTITIES : entities.XML_ENTITIES; 20350 if(locator){ 20351 domBuilder.setDocumentLocator(locator) 20352 } 20353 20354 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); 20355 sax.domBuilder = options.domBuilder || domBuilder; 20356 if(isHTML){ 20357 defaultNSMap[''] = NAMESPACE.HTML; 20358 } 20359 defaultNSMap.xml = defaultNSMap.xml || NAMESPACE.XML; 20360 if(source && typeof source === 'string'){ 20361 sax.parse(source,defaultNSMap,entityMap); 20362 }else{ 20363 sax.errorHandler.error("invalid doc source"); 20364 } 20365 return domBuilder.doc; 20366 } 20367 function buildErrorHandler(errorImpl,domBuilder,locator){ 20368 if(!errorImpl){ 20369 if(domBuilder instanceof DOMHandler){ 20370 return domBuilder; 20371 } 20372 errorImpl = domBuilder ; 20373 } 20374 var errorHandler = {} 20375 var isCallback = errorImpl instanceof Function; 20376 locator = locator||{} 20377 function build(key){ 20378 var fn = errorImpl[key]; 20379 if(!fn && isCallback){ 20380 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; 20381 } 20382 errorHandler[key] = fn && function(msg){ 20383 fn('[xmldom '+key+']\t'+msg+_locator(locator)); 20384 }||function(){}; 20385 } 20386 build('warning'); 20387 build('error'); 20388 build('fatalError'); 20389 return errorHandler; 20390 } 20391 20392 //console.log('#\n\n\n\n\n\n\n####') 20393 /** 20394 * +ContentHandler+ErrorHandler 20395 * +LexicalHandler+EntityResolver2 20396 * -DeclHandler-DTDHandler 20397 * 20398 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler 20399 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 20400 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html 20401 */ 20402 function DOMHandler() { 20403 this.cdata = false; 20404 } 20405 function position(locator,node){ 20406 node.lineNumber = locator.lineNumber; 20407 node.columnNumber = locator.columnNumber; 20408 } 20409 /** 20410 * @see org.xml.sax.ContentHandler#startDocument 20411 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html 20412 */ 20413 DOMHandler.prototype = { 20414 startDocument : function() { 20415 this.doc = new DOMImplementation().createDocument(null, null, null); 20416 if (this.locator) { 20417 this.doc.documentURI = this.locator.systemId; 20418 } 20419 }, 20420 startElement:function(namespaceURI, localName, qName, attrs) { 20421 var doc = this.doc; 20422 var el = doc.createElementNS(namespaceURI, qName||localName); 20423 var len = attrs.length; 20424 appendElement(this, el); 20425 this.currentElement = el; 20426 20427 this.locator && position(this.locator,el) 20428 for (var i = 0 ; i < len; i++) { 20429 var namespaceURI = attrs.getURI(i); 20430 var value = attrs.getValue(i); 20431 var qName = attrs.getQName(i); 20432 var attr = doc.createAttributeNS(namespaceURI, qName); 20433 this.locator &&position(attrs.getLocator(i),attr); 20434 attr.value = attr.nodeValue = value; 20435 el.setAttributeNode(attr) 20436 } 20437 }, 20438 endElement:function(namespaceURI, localName, qName) { 20439 var current = this.currentElement 20440 var tagName = current.tagName; 20441 this.currentElement = current.parentNode; 20442 }, 20443 startPrefixMapping:function(prefix, uri) { 20444 }, 20445 endPrefixMapping:function(prefix) { 20446 }, 20447 processingInstruction:function(target, data) { 20448 var ins = this.doc.createProcessingInstruction(target, data); 20449 this.locator && position(this.locator,ins) 20450 appendElement(this, ins); 20451 }, 20452 ignorableWhitespace:function(ch, start, length) { 20453 }, 20454 characters:function(chars, start, length) { 20455 chars = _toString.apply(this,arguments) 20456 //console.log(chars) 20457 if(chars){ 20458 if (this.cdata) { 20459 var charNode = this.doc.createCDATASection(chars); 20460 } else { 20461 var charNode = this.doc.createTextNode(chars); 20462 } 20463 if(this.currentElement){ 20464 this.currentElement.appendChild(charNode); 20465 }else if(/^\s*$/.test(chars)){ 20466 this.doc.appendChild(charNode); 20467 //process xml 20468 } 20469 this.locator && position(this.locator,charNode) 20470 } 20471 }, 20472 skippedEntity:function(name) { 20473 }, 20474 endDocument:function() { 20475 this.doc.normalize(); 20476 }, 20477 setDocumentLocator:function (locator) { 20478 if(this.locator = locator){// && !('lineNumber' in locator)){ 20479 locator.lineNumber = 0; 20480 } 20481 }, 20482 //LexicalHandler 20483 comment:function(chars, start, length) { 20484 chars = _toString.apply(this,arguments) 20485 var comm = this.doc.createComment(chars); 20486 this.locator && position(this.locator,comm) 20487 appendElement(this, comm); 20488 }, 20489 20490 startCDATA:function() { 20491 //used in characters() methods 20492 this.cdata = true; 20493 }, 20494 endCDATA:function() { 20495 this.cdata = false; 20496 }, 20497 20498 startDTD:function(name, publicId, systemId) { 20499 var impl = this.doc.implementation; 20500 if (impl && impl.createDocumentType) { 20501 var dt = impl.createDocumentType(name, publicId, systemId); 20502 this.locator && position(this.locator,dt) 20503 appendElement(this, dt); 20504 this.doc.doctype = dt; 20505 } 20506 }, 20507 /** 20508 * @see org.xml.sax.ErrorHandler 20509 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html 20510 */ 20511 warning:function(error) { 20512 console.warn('[xmldom warning]\t'+error,_locator(this.locator)); 20513 }, 20514 error:function(error) { 20515 console.error('[xmldom error]\t'+error,_locator(this.locator)); 20516 }, 20517 fatalError:function(error) { 20518 throw new ParseError(error, this.locator); 20519 } 20520 } 20521 function _locator(l){ 20522 if(l){ 20523 return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' 20524 } 20525 } 20526 function _toString(chars,start,length){ 20527 if(typeof chars == 'string'){ 20528 return chars.substr(start,length) 20529 }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") 20530 if(chars.length >= start+length || start){ 20531 return new java.lang.String(chars,start,length)+''; 20532 } 20533 return chars; 20534 } 20535 } 20536 20537 /* 20538 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html 20539 * used method of org.xml.sax.ext.LexicalHandler: 20540 * #comment(chars, start, length) 20541 * #startCDATA() 20542 * #endCDATA() 20543 * #startDTD(name, publicId, systemId) 20544 * 20545 * 20546 * IGNORED method of org.xml.sax.ext.LexicalHandler: 20547 * #endDTD() 20548 * #startEntity(name) 20549 * #endEntity(name) 20550 * 20551 * 20552 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html 20553 * IGNORED method of org.xml.sax.ext.DeclHandler 20554 * #attributeDecl(eName, aName, type, mode, value) 20555 * #elementDecl(name, model) 20556 * #externalEntityDecl(name, publicId, systemId) 20557 * #internalEntityDecl(name, value) 20558 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html 20559 * IGNORED method of org.xml.sax.EntityResolver2 20560 * #resolveEntity(String name,String publicId,String baseURI,String systemId) 20561 * #resolveEntity(publicId, systemId) 20562 * #getExternalSubset(name, baseURI) 20563 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html 20564 * IGNORED method of org.xml.sax.DTDHandler 20565 * #notationDecl(name, publicId, systemId) {}; 20566 * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; 20567 */ 20568 "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ 20569 DOMHandler.prototype[key] = function(){return null} 20570 }) 20571 20572 /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ 20573 function appendElement (hander,node) { 20574 if (!hander.currentElement) { 20575 hander.doc.appendChild(node); 20576 } else { 20577 hander.currentElement.appendChild(node); 20578 } 20579 }//appendChild and setAttributeNS are preformance key 20580 20581 exports.__DOMHandler = DOMHandler; 20582 exports.DOMParser = DOMParser; 20583 20584 /** 20585 * @deprecated Import/require from main entry point instead 20586 */ 20587 exports.DOMImplementation = dom.DOMImplementation; 20588 20589 /** 20590 * @deprecated Import/require from main entry point instead 20591 */ 20592 exports.XMLSerializer = dom.XMLSerializer; 20593 20594 20595 /***/ }), 20596 /* 47 */ 20597 /***/ (function(module, exports, __webpack_require__) { 20598 20599 var freeze = __webpack_require__(14).freeze; 20600 20601 /** 20602 * The entities that are predefined in every XML document. 20603 * 20604 * @see https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-predefined-ent W3C XML 1.1 20605 * @see https://www.w3.org/TR/2008/REC-xml-20081126/#sec-predefined-ent W3C XML 1.0 20606 * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Predefined_entities_in_XML Wikipedia 20607 */ 20608 exports.XML_ENTITIES = freeze({amp:'&', apos:"'", gt:'>', lt:'<', quot:'"'}) 20609 20610 /** 20611 * A map of currently 241 entities that are detected in an HTML document. 20612 * They contain all entries from `XML_ENTITIES`. 20613 * 20614 * @see XML_ENTITIES 20615 * @see DOMParser.parseFromString 20616 * @see DOMImplementation.prototype.createHTMLDocument 20617 * @see https://html.spec.whatwg.org/#named-character-references WHATWG HTML(5) Spec 20618 * @see https://www.w3.org/TR/xml-entity-names/ W3C XML Entity Names 20619 * @see https://www.w3.org/TR/html4/sgml/entities.html W3C HTML4/SGML 20620 * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Character_entity_references_in_HTML Wikipedia (HTML) 20621 * @see https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Entities_representing_special_characters_in_XHTML Wikpedia (XHTML) 20622 */ 20623 exports.HTML_ENTITIES = freeze({ 20624 lt: '<', 20625 gt: '>', 20626 amp: '&', 20627 quot: '"', 20628 apos: "'", 20629 Agrave: "À", 20630 Aacute: "Á", 20631 Acirc: "Â", 20632 Atilde: "Ã", 20633 Auml: "Ä", 20634 Aring: "Å", 20635 AElig: "Æ", 20636 Ccedil: "Ç", 20637 Egrave: "È", 20638 Eacute: "É", 20639 Ecirc: "Ê", 20640 Euml: "Ë", 20641 Igrave: "Ì", 20642 Iacute: "Í", 20643 Icirc: "Î", 20644 Iuml: "Ï", 20645 ETH: "Ð", 20646 Ntilde: "Ñ", 20647 Ograve: "Ò", 20648 Oacute: "Ó", 20649 Ocirc: "Ô", 20650 Otilde: "Õ", 20651 Ouml: "Ö", 20652 Oslash: "Ø", 20653 Ugrave: "Ù", 20654 Uacute: "Ú", 20655 Ucirc: "Û", 20656 Uuml: "Ü", 20657 Yacute: "Ý", 20658 THORN: "Þ", 20659 szlig: "ß", 20660 agrave: "à", 20661 aacute: "á", 20662 acirc: "â", 20663 atilde: "ã", 20664 auml: "ä", 20665 aring: "å", 20666 aelig: "æ", 20667 ccedil: "ç", 20668 egrave: "è", 20669 eacute: "é", 20670 ecirc: "ê", 20671 euml: "ë", 20672 igrave: "ì", 20673 iacute: "í", 20674 icirc: "î", 20675 iuml: "ï", 20676 eth: "ð", 20677 ntilde: "ñ", 20678 ograve: "ò", 20679 oacute: "ó", 20680 ocirc: "ô", 20681 otilde: "õ", 20682 ouml: "ö", 20683 oslash: "ø", 20684 ugrave: "ù", 20685 uacute: "ú", 20686 ucirc: "û", 20687 uuml: "ü", 20688 yacute: "ý", 20689 thorn: "þ", 20690 yuml: "ÿ", 20691 nbsp: "\u00a0", 20692 iexcl: "¡", 20693 cent: "¢", 20694 pound: "£", 20695 curren: "¤", 20696 yen: "¥", 20697 brvbar: "¦", 20698 sect: "§", 20699 uml: "¨", 20700 copy: "©", 20701 ordf: "ª", 20702 laquo: "«", 20703 not: "¬", 20704 shy: "", 20705 reg: "®", 20706 macr: "¯", 20707 deg: "°", 20708 plusmn: "±", 20709 sup2: "²", 20710 sup3: "³", 20711 acute: "´", 20712 micro: "µ", 20713 para: "¶", 20714 middot: "·", 20715 cedil: "¸", 20716 sup1: "¹", 20717 ordm: "º", 20718 raquo: "»", 20719 frac14: "¼", 20720 frac12: "½", 20721 frac34: "¾", 20722 iquest: "¿", 20723 times: "×", 20724 divide: "÷", 20725 forall: "∀", 20726 part: "∂", 20727 exist: "∃", 20728 empty: "∅", 20729 nabla: "∇", 20730 isin: "∈", 20731 notin: "∉", 20732 ni: "∋", 20733 prod: "∏", 20734 sum: "∑", 20735 minus: "−", 20736 lowast: "∗", 20737 radic: "√", 20738 prop: "∝", 20739 infin: "∞", 20740 ang: "∠", 20741 and: "∧", 20742 or: "∨", 20743 cap: "∩", 20744 cup: "∪", 20745 'int': "∫", 20746 there4: "∴", 20747 sim: "∼", 20748 cong: "≅", 20749 asymp: "≈", 20750 ne: "≠", 20751 equiv: "≡", 20752 le: "≤", 20753 ge: "≥", 20754 sub: "⊂", 20755 sup: "⊃", 20756 nsub: "⊄", 20757 sube: "⊆", 20758 supe: "⊇", 20759 oplus: "⊕", 20760 otimes: "⊗", 20761 perp: "⊥", 20762 sdot: "⋅", 20763 Alpha: "Α", 20764 Beta: "Β", 20765 Gamma: "Γ", 20766 Delta: "Δ", 20767 Epsilon: "Ε", 20768 Zeta: "Ζ", 20769 Eta: "Η", 20770 Theta: "Θ", 20771 Iota: "Ι", 20772 Kappa: "Κ", 20773 Lambda: "Λ", 20774 Mu: "Μ", 20775 Nu: "Ν", 20776 Xi: "Ξ", 20777 Omicron: "Ο", 20778 Pi: "Π", 20779 Rho: "Ρ", 20780 Sigma: "Σ", 20781 Tau: "Τ", 20782 Upsilon: "Υ", 20783 Phi: "Φ", 20784 Chi: "Χ", 20785 Psi: "Ψ", 20786 Omega: "Ω", 20787 alpha: "α", 20788 beta: "β", 20789 gamma: "γ", 20790 delta: "δ", 20791 epsilon: "ε", 20792 zeta: "ζ", 20793 eta: "η", 20794 theta: "θ", 20795 iota: "ι", 20796 kappa: "κ", 20797 lambda: "λ", 20798 mu: "μ", 20799 nu: "ν", 20800 xi: "ξ", 20801 omicron: "ο", 20802 pi: "π", 20803 rho: "ρ", 20804 sigmaf: "ς", 20805 sigma: "σ", 20806 tau: "τ", 20807 upsilon: "υ", 20808 phi: "φ", 20809 chi: "χ", 20810 psi: "ψ", 20811 omega: "ω", 20812 thetasym: "ϑ", 20813 upsih: "ϒ", 20814 piv: "ϖ", 20815 OElig: "Œ", 20816 oelig: "œ", 20817 Scaron: "Š", 20818 scaron: "š", 20819 Yuml: "Ÿ", 20820 fnof: "ƒ", 20821 circ: "ˆ", 20822 tilde: "˜", 20823 ensp: " ", 20824 emsp: " ", 20825 thinsp: " ", 20826 zwnj: "", 20827 zwj: "", 20828 lrm: "", 20829 rlm: "", 20830 ndash: "–", 20831 mdash: "—", 20832 lsquo: "‘", 20833 rsquo: "’", 20834 sbquo: "‚", 20835 ldquo: "“", 20836 rdquo: "”", 20837 bdquo: "„", 20838 dagger: "†", 20839 Dagger: "‡", 20840 bull: "•", 20841 hellip: "…", 20842 permil: "‰", 20843 prime: "′", 20844 Prime: "″", 20845 lsaquo: "‹", 20846 rsaquo: "›", 20847 oline: "‾", 20848 euro: "€", 20849 trade: "™", 20850 larr: "←", 20851 uarr: "↑", 20852 rarr: "→", 20853 darr: "↓", 20854 harr: "↔", 20855 crarr: "↵", 20856 lceil: "⌈", 20857 rceil: "⌉", 20858 lfloor: "⌊", 20859 rfloor: "⌋", 20860 loz: "◊", 20861 spades: "♠", 20862 clubs: "♣", 20863 hearts: "♥", 20864 diams: "♦" 20865 }); 20866 20867 /** 20868 * @deprecated use `HTML_ENTITIES` instead 20869 * @see HTML_ENTITIES 20870 */ 20871 exports.entityMap = exports.HTML_ENTITIES 20872 20873 20874 /***/ }), 20875 /* 48 */ 20876 /***/ (function(module, exports, __webpack_require__) { 20877 20878 var NAMESPACE = __webpack_require__(14).NAMESPACE; 20879 20880 //[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] 20881 //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] 20882 //[5] Name ::= NameStartChar (NameChar)* 20883 var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF 20884 var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); 20885 var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); 20886 //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ 20887 //var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') 20888 20889 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE 20890 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE 20891 var S_TAG = 0;//tag name offerring 20892 var S_ATTR = 1;//attr name offerring 20893 var S_ATTR_SPACE=2;//attr name end and space offer 20894 var S_EQ = 3;//=space? 20895 var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) 20896 var S_ATTR_END = 5;//attr value end and no space(quot end) 20897 var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) 20898 var S_TAG_CLOSE = 7;//closed el<el /> 20899 20900 /** 20901 * Creates an error that will not be caught by XMLReader aka the SAX parser. 20902 * 20903 * @param {string} message 20904 * @param {any?} locator Optional, can provide details about the location in the source 20905 * @constructor 20906 */ 20907 function ParseError(message, locator) { 20908 this.message = message 20909 this.locator = locator 20910 if(Error.captureStackTrace) Error.captureStackTrace(this, ParseError); 20911 } 20912 ParseError.prototype = new Error(); 20913 ParseError.prototype.name = ParseError.name 20914 20915 function XMLReader(){ 20916 20917 } 20918 20919 XMLReader.prototype = { 20920 parse:function(source,defaultNSMap,entityMap){ 20921 var domBuilder = this.domBuilder; 20922 domBuilder.startDocument(); 20923 _copy(defaultNSMap ,defaultNSMap = {}) 20924 parse(source,defaultNSMap,entityMap, 20925 domBuilder,this.errorHandler); 20926 domBuilder.endDocument(); 20927 } 20928 } 20929 function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ 20930 function fixedFromCharCode(code) { 20931 // String.prototype.fromCharCode does not supports 20932 // > 2 bytes unicode chars directly 20933 if (code > 0xffff) { 20934 code -= 0x10000; 20935 var surrogate1 = 0xd800 + (code >> 10) 20936 , surrogate2 = 0xdc00 + (code & 0x3ff); 20937 20938 return String.fromCharCode(surrogate1, surrogate2); 20939 } else { 20940 return String.fromCharCode(code); 20941 } 20942 } 20943 function entityReplacer(a){ 20944 var k = a.slice(1,-1); 20945 if(k in entityMap){ 20946 return entityMap[k]; 20947 }else if(k.charAt(0) === '#'){ 20948 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) 20949 }else{ 20950 errorHandler.error('entity not found:'+a); 20951 return a; 20952 } 20953 } 20954 function appendText(end){//has some bugs 20955 if(end>start){ 20956 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); 20957 locator&&position(start); 20958 domBuilder.characters(xt,0,end-start); 20959 start = end 20960 } 20961 } 20962 function position(p,m){ 20963 while(p>=lineEnd && (m = linePattern.exec(source))){ 20964 lineStart = m.index; 20965 lineEnd = lineStart + m[0].length; 20966 locator.lineNumber++; 20967 //console.log('line++:',locator,startPos,endPos) 20968 } 20969 locator.columnNumber = p-lineStart+1; 20970 } 20971 var lineStart = 0; 20972 var lineEnd = 0; 20973 var linePattern = /.*(?:\r\n?|\n)|.*$/g 20974 var locator = domBuilder.locator; 20975 20976 var parseStack = [{currentNSMap:defaultNSMapCopy}] 20977 var closeMap = {}; 20978 var start = 0; 20979 while(true){ 20980 try{ 20981 var tagStart = source.indexOf('<',start); 20982 if(tagStart<0){ 20983 if(!source.substr(start).match(/^\s*$/)){ 20984 var doc = domBuilder.doc; 20985 var text = doc.createTextNode(source.substr(start)); 20986 doc.appendChild(text); 20987 domBuilder.currentElement = text; 20988 } 20989 return; 20990 } 20991 if(tagStart>start){ 20992 appendText(tagStart); 20993 } 20994 switch(source.charAt(tagStart+1)){ 20995 case '/': 20996 var end = source.indexOf('>',tagStart+3); 20997 var tagName = source.substring(tagStart + 2, end).replace(/[ \t\n\r]+$/g, ''); 20998 var config = parseStack.pop(); 20999 if(end<0){ 21000 21001 tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); 21002 errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); 21003 end = tagStart+1+tagName.length; 21004 }else if(tagName.match(/\s</)){ 21005 tagName = tagName.replace(/[\s<].*/,''); 21006 errorHandler.error("end tag name: "+tagName+' maybe not complete'); 21007 end = tagStart+1+tagName.length; 21008 } 21009 var localNSMap = config.localNSMap; 21010 var endMatch = config.tagName == tagName; 21011 var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase() 21012 if(endIgnoreCaseMach){ 21013 domBuilder.endElement(config.uri,config.localName,tagName); 21014 if(localNSMap){ 21015 for(var prefix in localNSMap){ 21016 domBuilder.endPrefixMapping(prefix) ; 21017 } 21018 } 21019 if(!endMatch){ 21020 errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName ); // No known test case 21021 } 21022 }else{ 21023 parseStack.push(config) 21024 } 21025 21026 end++; 21027 break; 21028 // end elment 21029 case '?':// <?...?> 21030 locator&&position(tagStart); 21031 end = parseInstruction(source,tagStart,domBuilder); 21032 break; 21033 case '!':// <!doctype,<![CDATA,<!-- 21034 locator&&position(tagStart); 21035 end = parseDCC(source,tagStart,domBuilder,errorHandler); 21036 break; 21037 default: 21038 locator&&position(tagStart); 21039 var el = new ElementAttributes(); 21040 var currentNSMap = parseStack[parseStack.length-1].currentNSMap; 21041 //elStartEnd 21042 var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler); 21043 var len = el.length; 21044 21045 21046 if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){ 21047 el.closed = true; 21048 if(!entityMap.nbsp){ 21049 errorHandler.warning('unclosed xml attribute'); 21050 } 21051 } 21052 if(locator && len){ 21053 var locator2 = copyLocator(locator,{}); 21054 //try{//attribute position fixed 21055 for(var i = 0;i<len;i++){ 21056 var a = el[i]; 21057 position(a.offset); 21058 a.locator = copyLocator(locator,{}); 21059 } 21060 domBuilder.locator = locator2 21061 if(appendElement(el,domBuilder,currentNSMap)){ 21062 parseStack.push(el) 21063 } 21064 domBuilder.locator = locator; 21065 }else{ 21066 if(appendElement(el,domBuilder,currentNSMap)){ 21067 parseStack.push(el) 21068 } 21069 } 21070 21071 if (NAMESPACE.isHTML(el.uri) && !el.closed) { 21072 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder) 21073 } else { 21074 end++; 21075 } 21076 } 21077 }catch(e){ 21078 if (e instanceof ParseError) { 21079 throw e; 21080 } 21081 errorHandler.error('element parse error: '+e) 21082 end = -1; 21083 } 21084 if(end>start){ 21085 start = end; 21086 }else{ 21087 //TODO: 这里有可能sax回退,有位置错误风险 21088 appendText(Math.max(tagStart,start)+1); 21089 } 21090 } 21091 } 21092 function copyLocator(f,t){ 21093 t.lineNumber = f.lineNumber; 21094 t.columnNumber = f.columnNumber; 21095 return t; 21096 } 21097 21098 /** 21099 * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); 21100 * @return end of the elementStartPart(end of elementEndPart for selfClosed el) 21101 */ 21102 function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ 21103 21104 /** 21105 * @param {string} qname 21106 * @param {string} value 21107 * @param {number} startIndex 21108 */ 21109 function addAttribute(qname, value, startIndex) { 21110 if (el.attributeNames.hasOwnProperty(qname)) { 21111 errorHandler.fatalError('Attribute ' + qname + ' redefined') 21112 } 21113 el.addValue(qname, value, startIndex) 21114 } 21115 var attrName; 21116 var value; 21117 var p = ++start; 21118 var s = S_TAG;//status 21119 while(true){ 21120 var c = source.charAt(p); 21121 switch(c){ 21122 case '=': 21123 if(s === S_ATTR){//attrName 21124 attrName = source.slice(start,p); 21125 s = S_EQ; 21126 }else if(s === S_ATTR_SPACE){ 21127 s = S_EQ; 21128 }else{ 21129 //fatalError: equal must after attrName or space after attrName 21130 throw new Error('attribute equal must after attrName'); // No known test case 21131 } 21132 break; 21133 case '\'': 21134 case '"': 21135 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE 21136 ){//equal 21137 if(s === S_ATTR){ 21138 errorHandler.warning('attribute value must after "="') 21139 attrName = source.slice(start,p) 21140 } 21141 start = p+1; 21142 p = source.indexOf(c,start) 21143 if(p>0){ 21144 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); 21145 addAttribute(attrName, value, start-1); 21146 s = S_ATTR_END; 21147 }else{ 21148 //fatalError: no end quot match 21149 throw new Error('attribute value no end \''+c+'\' match'); 21150 } 21151 }else if(s == S_ATTR_NOQUOT_VALUE){ 21152 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); 21153 //console.log(attrName,value,start,p) 21154 addAttribute(attrName, value, start); 21155 //console.dir(el) 21156 errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); 21157 start = p+1; 21158 s = S_ATTR_END 21159 }else{ 21160 //fatalError: no equal before 21161 throw new Error('attribute value must after "="'); // No known test case 21162 } 21163 break; 21164 case '/': 21165 switch(s){ 21166 case S_TAG: 21167 el.setTagName(source.slice(start,p)); 21168 case S_ATTR_END: 21169 case S_TAG_SPACE: 21170 case S_TAG_CLOSE: 21171 s =S_TAG_CLOSE; 21172 el.closed = true; 21173 case S_ATTR_NOQUOT_VALUE: 21174 case S_ATTR: 21175 case S_ATTR_SPACE: 21176 break; 21177 //case S_EQ: 21178 default: 21179 throw new Error("attribute invalid close char('/')") // No known test case 21180 } 21181 break; 21182 case ''://end document 21183 errorHandler.error('unexpected end of input'); 21184 if(s == S_TAG){ 21185 el.setTagName(source.slice(start,p)); 21186 } 21187 return p; 21188 case '>': 21189 switch(s){ 21190 case S_TAG: 21191 el.setTagName(source.slice(start,p)); 21192 case S_ATTR_END: 21193 case S_TAG_SPACE: 21194 case S_TAG_CLOSE: 21195 break;//normal 21196 case S_ATTR_NOQUOT_VALUE://Compatible state 21197 case S_ATTR: 21198 value = source.slice(start,p); 21199 if(value.slice(-1) === '/'){ 21200 el.closed = true; 21201 value = value.slice(0,-1) 21202 } 21203 case S_ATTR_SPACE: 21204 if(s === S_ATTR_SPACE){ 21205 value = attrName; 21206 } 21207 if(s == S_ATTR_NOQUOT_VALUE){ 21208 errorHandler.warning('attribute "'+value+'" missed quot(")!'); 21209 addAttribute(attrName, value.replace(/&#?\w+;/g,entityReplacer), start) 21210 }else{ 21211 if(!NAMESPACE.isHTML(currentNSMap['']) || !value.match(/^(?:disabled|checked|selected)$/i)){ 21212 errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') 21213 } 21214 addAttribute(value, value, start) 21215 } 21216 break; 21217 case S_EQ: 21218 throw new Error('attribute value missed!!'); 21219 } 21220 // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) 21221 return p; 21222 /*xml space '\x20' | #x9 | #xD | #xA; */ 21223 case '\u0080': 21224 c = ' '; 21225 default: 21226 if(c<= ' '){//space 21227 switch(s){ 21228 case S_TAG: 21229 el.setTagName(source.slice(start,p));//tagName 21230 s = S_TAG_SPACE; 21231 break; 21232 case S_ATTR: 21233 attrName = source.slice(start,p) 21234 s = S_ATTR_SPACE; 21235 break; 21236 case S_ATTR_NOQUOT_VALUE: 21237 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); 21238 errorHandler.warning('attribute "'+value+'" missed quot(")!!'); 21239 addAttribute(attrName, value, start) 21240 case S_ATTR_END: 21241 s = S_TAG_SPACE; 21242 break; 21243 //case S_TAG_SPACE: 21244 //case S_EQ: 21245 //case S_ATTR_SPACE: 21246 // void();break; 21247 //case S_TAG_CLOSE: 21248 //ignore warning 21249 } 21250 }else{//not space 21251 //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE 21252 //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE 21253 switch(s){ 21254 //case S_TAG:void();break; 21255 //case S_ATTR:void();break; 21256 //case S_ATTR_NOQUOT_VALUE:void();break; 21257 case S_ATTR_SPACE: 21258 var tagName = el.tagName; 21259 if (!NAMESPACE.isHTML(currentNSMap['']) || !attrName.match(/^(?:disabled|checked|selected)$/i)) { 21260 errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') 21261 } 21262 addAttribute(attrName, attrName, start); 21263 start = p; 21264 s = S_ATTR; 21265 break; 21266 case S_ATTR_END: 21267 errorHandler.warning('attribute space is required"'+attrName+'"!!') 21268 case S_TAG_SPACE: 21269 s = S_ATTR; 21270 start = p; 21271 break; 21272 case S_EQ: 21273 s = S_ATTR_NOQUOT_VALUE; 21274 start = p; 21275 break; 21276 case S_TAG_CLOSE: 21277 throw new Error("elements closed character '/' and '>' must be connected to"); 21278 } 21279 } 21280 }//end outer switch 21281 //console.log('p++',p) 21282 p++; 21283 } 21284 } 21285 /** 21286 * @return true if has new namespace define 21287 */ 21288 function appendElement(el,domBuilder,currentNSMap){ 21289 var tagName = el.tagName; 21290 var localNSMap = null; 21291 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; 21292 var i = el.length; 21293 while(i--){ 21294 var a = el[i]; 21295 var qName = a.qName; 21296 var value = a.value; 21297 var nsp = qName.indexOf(':'); 21298 if(nsp>0){ 21299 var prefix = a.prefix = qName.slice(0,nsp); 21300 var localName = qName.slice(nsp+1); 21301 var nsPrefix = prefix === 'xmlns' && localName 21302 }else{ 21303 localName = qName; 21304 prefix = null 21305 nsPrefix = qName === 'xmlns' && '' 21306 } 21307 //can not set prefix,because prefix !== '' 21308 a.localName = localName ; 21309 //prefix == null for no ns prefix attribute 21310 if(nsPrefix !== false){//hack!! 21311 if(localNSMap == null){ 21312 localNSMap = {} 21313 //console.log(currentNSMap,0) 21314 _copy(currentNSMap,currentNSMap={}) 21315 //console.log(currentNSMap,1) 21316 } 21317 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; 21318 a.uri = NAMESPACE.XMLNS 21319 domBuilder.startPrefixMapping(nsPrefix, value) 21320 } 21321 } 21322 var i = el.length; 21323 while(i--){ 21324 a = el[i]; 21325 var prefix = a.prefix; 21326 if(prefix){//no prefix attribute has no namespace 21327 if(prefix === 'xml'){ 21328 a.uri = NAMESPACE.XML; 21329 }if(prefix !== 'xmlns'){ 21330 a.uri = currentNSMap[prefix || ''] 21331 21332 //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} 21333 } 21334 } 21335 } 21336 var nsp = tagName.indexOf(':'); 21337 if(nsp>0){ 21338 prefix = el.prefix = tagName.slice(0,nsp); 21339 localName = el.localName = tagName.slice(nsp+1); 21340 }else{ 21341 prefix = null;//important!! 21342 localName = el.localName = tagName; 21343 } 21344 //no prefix element has default namespace 21345 var ns = el.uri = currentNSMap[prefix || '']; 21346 domBuilder.startElement(ns,localName,tagName,el); 21347 //endPrefixMapping and startPrefixMapping have not any help for dom builder 21348 //localNSMap = null 21349 if(el.closed){ 21350 domBuilder.endElement(ns,localName,tagName); 21351 if(localNSMap){ 21352 for(prefix in localNSMap){ 21353 domBuilder.endPrefixMapping(prefix) 21354 } 21355 } 21356 }else{ 21357 el.currentNSMap = currentNSMap; 21358 el.localNSMap = localNSMap; 21359 //parseStack.push(el); 21360 return true; 21361 } 21362 } 21363 function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ 21364 if(/^(?:script|textarea)$/i.test(tagName)){ 21365 var elEndStart = source.indexOf('</'+tagName+'>',elStartEnd); 21366 var text = source.substring(elStartEnd+1,elEndStart); 21367 if(/[&<]/.test(text)){ 21368 if(/^script$/i.test(tagName)){ 21369 //if(!/\]\]>/.test(text)){ 21370 //lexHandler.startCDATA(); 21371 domBuilder.characters(text,0,text.length); 21372 //lexHandler.endCDATA(); 21373 return elEndStart; 21374 //} 21375 }//}else{//text area 21376 text = text.replace(/&#?\w+;/g,entityReplacer); 21377 domBuilder.characters(text,0,text.length); 21378 return elEndStart; 21379 //} 21380 21381 } 21382 } 21383 return elStartEnd+1; 21384 } 21385 function fixSelfClosed(source,elStartEnd,tagName,closeMap){ 21386 //if(tagName in closeMap){ 21387 var pos = closeMap[tagName]; 21388 if(pos == null){ 21389 //console.log(tagName) 21390 pos = source.lastIndexOf('</'+tagName+'>') 21391 if(pos<elStartEnd){//忘记闭合 21392 pos = source.lastIndexOf('</'+tagName) 21393 } 21394 closeMap[tagName] =pos 21395 } 21396 return pos<elStartEnd; 21397 //} 21398 } 21399 function _copy(source,target){ 21400 for(var n in source){target[n] = source[n]} 21401 } 21402 function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!' 21403 var next= source.charAt(start+2) 21404 switch(next){ 21405 case '-': 21406 if(source.charAt(start + 3) === '-'){ 21407 var end = source.indexOf('-->',start+4); 21408 //append comment source.substring(4,end)//<!-- 21409 if(end>start){ 21410 domBuilder.comment(source,start+4,end-start-4); 21411 return end+3; 21412 }else{ 21413 errorHandler.error("Unclosed comment"); 21414 return -1; 21415 } 21416 }else{ 21417 //error 21418 return -1; 21419 } 21420 default: 21421 if(source.substr(start+3,6) == 'CDATA['){ 21422 var end = source.indexOf(']]>',start+9); 21423 domBuilder.startCDATA(); 21424 domBuilder.characters(source,start+9,end-start-9); 21425 domBuilder.endCDATA() 21426 return end+3; 21427 } 21428 //<!DOCTYPE 21429 //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 21430 var matchs = split(source,start); 21431 var len = matchs.length; 21432 if(len>1 && /!doctype/i.test(matchs[0][0])){ 21433 var name = matchs[1][0]; 21434 var pubid = false; 21435 var sysid = false; 21436 if(len>3){ 21437 if(/^public$/i.test(matchs[2][0])){ 21438 pubid = matchs[3][0]; 21439 sysid = len>4 && matchs[4][0]; 21440 }else if(/^system$/i.test(matchs[2][0])){ 21441 sysid = matchs[3][0]; 21442 } 21443 } 21444 var lastMatch = matchs[len-1] 21445 domBuilder.startDTD(name, pubid, sysid); 21446 domBuilder.endDTD(); 21447 21448 return lastMatch.index+lastMatch[0].length 21449 } 21450 } 21451 return -1; 21452 } 21453 21454 21455 21456 function parseInstruction(source,start,domBuilder){ 21457 var end = source.indexOf('?>',start); 21458 if(end){ 21459 var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/); 21460 if(match){ 21461 var len = match[0].length; 21462 domBuilder.processingInstruction(match[1], match[2]) ; 21463 return end+2; 21464 }else{//error 21465 return -1; 21466 } 21467 } 21468 return -1; 21469 } 21470 21471 function ElementAttributes(){ 21472 this.attributeNames = {} 21473 } 21474 ElementAttributes.prototype = { 21475 setTagName:function(tagName){ 21476 if(!tagNamePattern.test(tagName)){ 21477 throw new Error('invalid tagName:'+tagName) 21478 } 21479 this.tagName = tagName 21480 }, 21481 addValue:function(qName, value, offset) { 21482 if(!tagNamePattern.test(qName)){ 21483 throw new Error('invalid attribute:'+qName) 21484 } 21485 this.attributeNames[qName] = this.length; 21486 this[this.length++] = {qName:qName,value:value,offset:offset} 21487 }, 21488 length:0, 21489 getLocalName:function(i){return this[i].localName}, 21490 getLocator:function(i){return this[i].locator}, 21491 getQName:function(i){return this[i].qName}, 21492 getURI:function(i){return this[i].uri}, 21493 getValue:function(i){return this[i].value} 21494 // ,getIndex:function(uri, localName)){ 21495 // if(localName){ 21496 // 21497 // }else{ 21498 // var qName = uri 21499 // } 21500 // }, 21501 // getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))}, 21502 // getType:function(uri,localName){} 21503 // getType:function(i){}, 21504 } 21505 21506 21507 21508 function split(source,start){ 21509 var match; 21510 var buf = []; 21511 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g; 21512 reg.lastIndex = start; 21513 reg.exec(source);//skip < 21514 while(match = reg.exec(source)){ 21515 buf.push(match); 21516 if(match[1])return buf; 21517 } 21518 } 21519 21520 exports.XMLReader = XMLReader; 21521 exports.ParseError = ParseError; 21522 21523 21524 /***/ }), 21525 /* 49 */ 21526 /***/ (function(module, exports, __webpack_require__) { 21527 21528 "use strict"; 21529 21530 21531 Object.defineProperty(exports, "__esModule", { 21532 value: true 21533 }); 21534 exports.createElement = createElement; 21535 function createElement(name) { 21536 return document.createElementNS('http://www.w3.org/2000/svg', name); 21537 } 21538 21539 exports.default = { 21540 createElement: createElement 21541 }; 21542 21543 21544 /***/ }), 21545 /* 50 */ 21546 /***/ (function(module, exports, __webpack_require__) { 21547 21548 "use strict"; 21549 21550 21551 Object.defineProperty(exports, "__esModule", { 21552 value: true 21553 }); 21554 exports.proxyMouse = proxyMouse; 21555 exports.clone = clone; 21556 // import 'babelify/polyfill'; // needed for Object.assign 21557 21558 exports.default = { 21559 proxyMouse: proxyMouse 21560 }; 21561 21562 /** 21563 * Start proxying all mouse events that occur on the target node to each node in 21564 * a set of tracked nodes. 21565 * 21566 * The items in tracked do not strictly have to be DOM Nodes, but they do have 21567 * to have dispatchEvent, getBoundingClientRect, and getClientRects methods. 21568 * 21569 * @param target {Node} The node on which to listen for mouse events. 21570 * @param tracked {Node[]} A (possibly mutable) array of nodes to which to proxy 21571 * events. 21572 */ 21573 21574 function proxyMouse(target, tracked) { 21575 function dispatch(e) { 21576 // We walk through the set of tracked elements in reverse order so that 21577 // events are sent to those most recently added first. 21578 // 21579 // This is the least surprising behaviour as it simulates the way the 21580 // browser would work if items added later were drawn "on top of" 21581 // earlier ones. 21582 for (var i = tracked.length - 1; i >= 0; i--) { 21583 var t = tracked[i]; 21584 var x = e.clientX; 21585 var y = e.clientY; 21586 21587 if (e.touches && e.touches.length) { 21588 x = e.touches[0].clientX; 21589 y = e.touches[0].clientY; 21590 } 21591 21592 if (!contains(t, target, x, y)) { 21593 continue; 21594 } 21595 21596 // The event targets this mark, so dispatch a cloned event: 21597 t.dispatchEvent(clone(e)); 21598 // We only dispatch the cloned event to the first matching mark. 21599 break; 21600 } 21601 } 21602 21603 if (target.nodeName === "iframe" || target.nodeName === "IFRAME") { 21604 21605 try { 21606 // Try to get the contents if same domain 21607 this.target = target.contentDocument; 21608 } catch (err) { 21609 this.target = target; 21610 } 21611 } else { 21612 this.target = target; 21613 } 21614 21615 var _arr = ['mouseup', 'mousedown', 'click', 'touchstart']; 21616 for (var _i = 0; _i < _arr.length; _i++) { 21617 var ev = _arr[_i]; 21618 this.target.addEventListener(ev, function (e) { 21619 return dispatch(e); 21620 }, false); 21621 } 21622 } 21623 21624 /** 21625 * Clone a mouse event object. 21626 * 21627 * @param e {MouseEvent} A mouse event object to clone. 21628 * @returns {MouseEvent} 21629 */ 21630 function clone(e) { 21631 var opts = Object.assign({}, e, { bubbles: false }); 21632 try { 21633 return new MouseEvent(e.type, opts); 21634 } catch (err) { 21635 // compat: webkit 21636 var copy = document.createEvent('MouseEvents'); 21637 copy.initMouseEvent(e.type, false, opts.cancelable, opts.view, opts.detail, opts.screenX, opts.screenY, opts.clientX, opts.clientY, opts.ctrlKey, opts.altKey, opts.shiftKey, opts.metaKey, opts.button, opts.relatedTarget); 21638 return copy; 21639 } 21640 } 21641 21642 /** 21643 * Check if the item contains the point denoted by the passed coordinates 21644 * @param item {Object} An object with getBoundingClientRect and getClientRects 21645 * methods. 21646 * @param x {Number} 21647 * @param y {Number} 21648 * @returns {Boolean} 21649 */ 21650 function contains(item, target, x, y) { 21651 // offset 21652 var offset = target.getBoundingClientRect(); 21653 21654 function rectContains(r, x, y) { 21655 var top = r.top - offset.top; 21656 var left = r.left - offset.left; 21657 var bottom = top + r.height; 21658 var right = left + r.width; 21659 return top <= y && left <= x && bottom > y && right > x; 21660 } 21661 21662 // Check overall bounding box first 21663 var rect = item.getBoundingClientRect(); 21664 if (!rectContains(rect, x, y)) { 21665 return false; 21666 } 21667 21668 // Then continue to check each child rect 21669 var rects = item.getClientRects(); 21670 for (var i = 0, len = rects.length; i < len; i++) { 21671 if (rectContains(rects[i], x, y)) { 21672 return true; 21673 } 21674 } 21675 return false; 21676 } 21677 21678 21679 /***/ }), 21680 /* 51 */ 21681 /***/ (function(module, exports, __webpack_require__) { 21682 21683 var root = __webpack_require__(26); 21684 21685 /** 21686 * Gets the timestamp of the number of milliseconds that have elapsed since 21687 * the Unix epoch (1 January 1970 00:00:00 UTC). 21688 * 21689 * @static 21690 * @memberOf _ 21691 * @since 2.4.0 21692 * @category Date 21693 * @returns {number} Returns the timestamp. 21694 * @example 21695 * 21696 * _.defer(function(stamp) { 21697 * console.log(_.now() - stamp); 21698 * }, _.now()); 21699 * // => Logs the number of milliseconds it took for the deferred invocation. 21700 */ 21701 var now = function() { 21702 return root.Date.now(); 21703 }; 21704 21705 module.exports = now; 21706 21707 21708 /***/ }), 21709 /* 52 */ 21710 /***/ (function(module, exports, __webpack_require__) { 21711 21712 /* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */ 21713 var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; 21714 21715 module.exports = freeGlobal; 21716 21717 /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(17))) 21718 21719 /***/ }), 21720 /* 53 */ 21721 /***/ (function(module, exports, __webpack_require__) { 21722 21723 var baseTrim = __webpack_require__(54), 21724 isObject = __webpack_require__(19), 21725 isSymbol = __webpack_require__(56); 21726 21727 /** Used as references for various `Number` constants. */ 21728 var NAN = 0 / 0; 21729 21730 /** Used to detect bad signed hexadecimal string values. */ 21731 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; 21732 21733 /** Used to detect binary string values. */ 21734 var reIsBinary = /^0b[01]+$/i; 21735 21736 /** Used to detect octal string values. */ 21737 var reIsOctal = /^0o[0-7]+$/i; 21738 21739 /** Built-in method references without a dependency on `root`. */ 21740 var freeParseInt = parseInt; 21741 21742 /** 21743 * Converts `value` to a number. 21744 * 21745 * @static 21746 * @memberOf _ 21747 * @since 4.0.0 21748 * @category Lang 21749 * @param {*} value The value to process. 21750 * @returns {number} Returns the number. 21751 * @example 21752 * 21753 * _.toNumber(3.2); 21754 * // => 3.2 21755 * 21756 * _.toNumber(Number.MIN_VALUE); 21757 * // => 5e-324 21758 * 21759 * _.toNumber(Infinity); 21760 * // => Infinity 21761 * 21762 * _.toNumber('3.2'); 21763 * // => 3.2 21764 */ 21765 function toNumber(value) { 21766 if (typeof value == 'number') { 21767 return value; 21768 } 21769 if (isSymbol(value)) { 21770 return NAN; 21771 } 21772 if (isObject(value)) { 21773 var other = typeof value.valueOf == 'function' ? value.valueOf() : value; 21774 value = isObject(other) ? (other + '') : other; 21775 } 21776 if (typeof value != 'string') { 21777 return value === 0 ? value : +value; 21778 } 21779 value = baseTrim(value); 21780 var isBinary = reIsBinary.test(value); 21781 return (isBinary || reIsOctal.test(value)) 21782 ? freeParseInt(value.slice(2), isBinary ? 2 : 8) 21783 : (reIsBadHex.test(value) ? NAN : +value); 21784 } 21785 21786 module.exports = toNumber; 21787 21788 21789 /***/ }), 21790 /* 54 */ 21791 /***/ (function(module, exports, __webpack_require__) { 21792 21793 var trimmedEndIndex = __webpack_require__(55); 21794 21795 /** Used to match leading whitespace. */ 21796 var reTrimStart = /^\s+/; 21797 21798 /** 21799 * The base implementation of `_.trim`. 21800 * 21801 * @private 21802 * @param {string} string The string to trim. 21803 * @returns {string} Returns the trimmed string. 21804 */ 21805 function baseTrim(string) { 21806 return string 21807 ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '') 21808 : string; 21809 } 21810 21811 module.exports = baseTrim; 21812 21813 21814 /***/ }), 21815 /* 55 */ 21816 /***/ (function(module, exports) { 21817 21818 /** Used to match a single whitespace character. */ 21819 var reWhitespace = /\s/; 21820 21821 /** 21822 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace 21823 * character of `string`. 21824 * 21825 * @private 21826 * @param {string} string The string to inspect. 21827 * @returns {number} Returns the index of the last non-whitespace character. 21828 */ 21829 function trimmedEndIndex(string) { 21830 var index = string.length; 21831 21832 while (index-- && reWhitespace.test(string.charAt(index))) {} 21833 return index; 21834 } 21835 21836 module.exports = trimmedEndIndex; 21837 21838 21839 /***/ }), 21840 /* 56 */ 21841 /***/ (function(module, exports, __webpack_require__) { 21842 21843 var baseGetTag = __webpack_require__(57), 21844 isObjectLike = __webpack_require__(60); 21845 21846 /** `Object#toString` result references. */ 21847 var symbolTag = '[object Symbol]'; 21848 21849 /** 21850 * Checks if `value` is classified as a `Symbol` primitive or object. 21851 * 21852 * @static 21853 * @memberOf _ 21854 * @since 4.0.0 21855 * @category Lang 21856 * @param {*} value The value to check. 21857 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. 21858 * @example 21859 * 21860 * _.isSymbol(Symbol.iterator); 21861 * // => true 21862 * 21863 * _.isSymbol('abc'); 21864 * // => false 21865 */ 21866 function isSymbol(value) { 21867 return typeof value == 'symbol' || 21868 (isObjectLike(value) && baseGetTag(value) == symbolTag); 21869 } 21870 21871 module.exports = isSymbol; 21872 21873 21874 /***/ }), 21875 /* 57 */ 21876 /***/ (function(module, exports, __webpack_require__) { 21877 21878 var Symbol = __webpack_require__(27), 21879 getRawTag = __webpack_require__(58), 21880 objectToString = __webpack_require__(59); 21881 21882 /** `Object#toString` result references. */ 21883 var nullTag = '[object Null]', 21884 undefinedTag = '[object Undefined]'; 21885 21886 /** Built-in value references. */ 21887 var symToStringTag = Symbol ? Symbol.toStringTag : undefined; 21888 21889 /** 21890 * The base implementation of `getTag` without fallbacks for buggy environments. 21891 * 21892 * @private 21893 * @param {*} value The value to query. 21894 * @returns {string} Returns the `toStringTag`. 21895 */ 21896 function baseGetTag(value) { 21897 if (value == null) { 21898 return value === undefined ? undefinedTag : nullTag; 21899 } 21900 return (symToStringTag && symToStringTag in Object(value)) 21901 ? getRawTag(value) 21902 : objectToString(value); 21903 } 21904 21905 module.exports = baseGetTag; 21906 21907 21908 /***/ }), 21909 /* 58 */ 21910 /***/ (function(module, exports, __webpack_require__) { 21911 21912 var Symbol = __webpack_require__(27); 21913 21914 /** Used for built-in method references. */ 21915 var objectProto = Object.prototype; 21916 21917 /** Used to check objects for own properties. */ 21918 var hasOwnProperty = objectProto.hasOwnProperty; 21919 21920 /** 21921 * Used to resolve the 21922 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) 21923 * of values. 21924 */ 21925 var nativeObjectToString = objectProto.toString; 21926 21927 /** Built-in value references. */ 21928 var symToStringTag = Symbol ? Symbol.toStringTag : undefined; 21929 21930 /** 21931 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. 21932 * 21933 * @private 21934 * @param {*} value The value to query. 21935 * @returns {string} Returns the raw `toStringTag`. 21936 */ 21937 function getRawTag(value) { 21938 var isOwn = hasOwnProperty.call(value, symToStringTag), 21939 tag = value[symToStringTag]; 21940 21941 try { 21942 value[symToStringTag] = undefined; 21943 var unmasked = true; 21944 } catch (e) {} 21945 21946 var result = nativeObjectToString.call(value); 21947 if (unmasked) { 21948 if (isOwn) { 21949 value[symToStringTag] = tag; 21950 } else { 21951 delete value[symToStringTag]; 21952 } 21953 } 21954 return result; 21955 } 21956 21957 module.exports = getRawTag; 21958 21959 21960 /***/ }), 21961 /* 59 */ 21962 /***/ (function(module, exports) { 21963 21964 /** Used for built-in method references. */ 21965 var objectProto = Object.prototype; 21966 21967 /** 21968 * Used to resolve the 21969 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) 21970 * of values. 21971 */ 21972 var nativeObjectToString = objectProto.toString; 21973 21974 /** 21975 * Converts `value` to a string using `Object.prototype.toString`. 21976 * 21977 * @private 21978 * @param {*} value The value to convert. 21979 * @returns {string} Returns the converted string. 21980 */ 21981 function objectToString(value) { 21982 return nativeObjectToString.call(value); 21983 } 21984 21985 module.exports = objectToString; 21986 21987 21988 /***/ }), 21989 /* 60 */ 21990 /***/ (function(module, exports) { 21991 21992 /** 21993 * Checks if `value` is object-like. A value is object-like if it's not `null` 21994 * and has a `typeof` result of "object". 21995 * 21996 * @static 21997 * @memberOf _ 21998 * @since 4.0.0 21999 * @category Lang 22000 * @param {*} value The value to check. 22001 * @returns {boolean} Returns `true` if `value` is object-like, else `false`. 22002 * @example 22003 * 22004 * _.isObjectLike({}); 22005 * // => true 22006 * 22007 * _.isObjectLike([1, 2, 3]); 22008 * // => true 22009 * 22010 * _.isObjectLike(_.noop); 22011 * // => false 22012 * 22013 * _.isObjectLike(null); 22014 * // => false 22015 */ 22016 function isObjectLike(value) { 22017 return value != null && typeof value == 'object'; 22018 } 22019 22020 module.exports = isObjectLike; 22021 22022 22023 /***/ }) 22024 /******/ ])["default"]; 22025 });