UNPKG

vue-files-preview-inno

Version:

A tool for previewing files such as doc, excel, pdf, image, markdown, txt, audio, and video and so on.

626 lines (625 loc) 23 kB
import _ from "../../event-emitter/index.mjs"; import { borders as R, defaults as O, prefixed as m, isNumber as v } from "./utils/core.mjs"; import l from "./epubcfi.mjs"; import C from "./mapping.mjs"; import { replaceLinks as N } from "./utils/replacements.mjs"; import { EPUBJS_VERSION as T, DOM_EVENTS as E, EVENTS as f } from "./utils/constants.mjs"; const L = typeof navigator < "u", z = L && /Chrome/.test(navigator.userAgent), x = L && !z && /AppleWebKit/.test(navigator.userAgent), k = 1; class A { constructor(t, e, i, n) { this.epubcfi = new l(), this.document = t, this.documentElement = this.document.documentElement, this.content = e || this.document.body, this.window = this.document.defaultView, this._size = { width: 0, height: 0 }, this.sectionIndex = n || 0, this.cfiBase = i || "", this.epubReadingSystem("epub.js", T), this.called = 0, this.active = !0, this.listeners(); } /** * Get DOM events that are listened for and passed along */ static get listenedEvents() { return E; } /** * Get or Set width * @param {number} [w] * @returns {number} width */ width(t) { var e = this.content; return t && v(t) && (t = t + "px"), t && (e.style.width = t), parseInt(this.window.getComputedStyle(e).width); } /** * Get or Set height * @param {number} [h] * @returns {number} height */ height(t) { var e = this.content; return t && v(t) && (t = t + "px"), t && (e.style.height = t), parseInt(this.window.getComputedStyle(e).height); } /** * Get or Set width of the contents * @param {number} [w] * @returns {number} width */ contentWidth(t) { var e = this.content || this.document.body; return t && v(t) && (t = t + "px"), t && (e.style.width = t), parseInt(this.window.getComputedStyle(e).width); } /** * Get or Set height of the contents * @param {number} [h] * @returns {number} height */ contentHeight(t) { var e = this.content || this.document.body; return t && v(t) && (t = t + "px"), t && (e.style.height = t), parseInt(this.window.getComputedStyle(e).height); } /** * Get the width of the text using Range * @returns {number} width */ textWidth() { let t, e, i = this.document.createRange(), n = this.content || this.document.body, s = R(n); return i.selectNodeContents(n), t = i.getBoundingClientRect(), e = t.width, s && s.width && (e += s.width), Math.round(e); } /** * Get the height of the text using Range * @returns {number} height */ textHeight() { let t, e, i = this.document.createRange(), n = this.content || this.document.body; return i.selectNodeContents(n), t = i.getBoundingClientRect(), e = t.bottom, Math.round(e); } /** * Get documentElement scrollWidth * @returns {number} width */ scrollWidth() { var t = this.documentElement.scrollWidth; return t; } /** * Get documentElement scrollHeight * @returns {number} height */ scrollHeight() { var t = this.documentElement.scrollHeight; return t; } /** * Set overflow css style of the contents * @param {string} [overflow] */ overflow(t) { return t && (this.documentElement.style.overflow = t), this.window.getComputedStyle(this.documentElement).overflow; } /** * Set overflowX css style of the documentElement * @param {string} [overflow] */ overflowX(t) { return t && (this.documentElement.style.overflowX = t), this.window.getComputedStyle(this.documentElement).overflowX; } /** * Set overflowY css style of the documentElement * @param {string} [overflow] */ overflowY(t) { return t && (this.documentElement.style.overflowY = t), this.window.getComputedStyle(this.documentElement).overflowY; } /** * Set Css styles on the contents element (typically Body) * @param {string} property * @param {string} value * @param {boolean} [priority] set as "important" */ css(t, e, i) { var n = this.content || this.document.body; return e ? n.style.setProperty(t, e, i ? "important" : "") : n.style.removeProperty(t), this.window.getComputedStyle(n)[t]; } /** * Get or Set the viewport element * @param {object} [options] * @param {string} [options.width] * @param {string} [options.height] * @param {string} [options.scale] * @param {string} [options.minimum] * @param {string} [options.maximum] * @param {string} [options.scalable] */ viewport(t) { var e = this.document.querySelector("meta[name='viewport']"), i = { width: void 0, height: void 0, scale: void 0, minimum: void 0, maximum: void 0, scalable: void 0 }, n = [], s = {}; if (e && e.hasAttribute("content")) { let r = e.getAttribute("content"), o = r.match(/width\s*=\s*([^,]*)/), a = r.match(/height\s*=\s*([^,]*)/), h = r.match(/initial-scale\s*=\s*([^,]*)/), c = r.match(/minimum-scale\s*=\s*([^,]*)/), d = r.match(/maximum-scale\s*=\s*([^,]*)/), u = r.match(/user-scalable\s*=\s*([^,]*)/); o && o.length && typeof o[1] < "u" && (i.width = o[1]), a && a.length && typeof a[1] < "u" && (i.height = a[1]), h && h.length && typeof h[1] < "u" && (i.scale = h[1]), c && c.length && typeof c[1] < "u" && (i.minimum = c[1]), d && d.length && typeof d[1] < "u" && (i.maximum = d[1]), u && u.length && typeof u[1] < "u" && (i.scalable = u[1]); } return s = O(t || {}, i), t && (s.width && n.push("width=" + s.width), s.height && n.push("height=" + s.height), s.scale && n.push("initial-scale=" + s.scale), s.scalable === "no" ? (n.push("minimum-scale=" + s.scale), n.push("maximum-scale=" + s.scale), n.push("user-scalable=" + s.scalable)) : (s.scalable && n.push("user-scalable=" + s.scalable), s.minimum && n.push("minimum-scale=" + s.minimum), s.maximum && n.push("minimum-scale=" + s.maximum)), e || (e = this.document.createElement("meta"), e.setAttribute("name", "viewport"), this.document.querySelector("head").appendChild(e)), e.setAttribute("content", n.join(", ")), this.window.scrollTo(0, 0)), s; } /** * Event emitter for when the contents has expanded * @private */ expand() { this.emit(f.CONTENTS.EXPAND); } /** * Add DOM listeners * @private */ listeners() { this.imageLoadListeners(), this.mediaQueryListeners(), this.addEventListeners(), this.addSelectionListeners(), typeof ResizeObserver > "u" ? (this.resizeListeners(), this.visibilityListeners()) : this.resizeObservers(), this.linksHandler(); } /** * Remove DOM listeners * @private */ removeListeners() { this.removeEventListeners(), this.removeSelectionListeners(), this.observer && this.observer.disconnect(), clearTimeout(this.expanding); } /** * Check if size of contents has changed and * emit 'resize' event if it has. * @private */ resizeCheck() { let t = this.textWidth(), e = this.textHeight(); (t != this._size.width || e != this._size.height) && (this._size = { width: t, height: e }, this.onResize && this.onResize(this._size), this.emit(f.CONTENTS.RESIZE, this._size)); } /** * Poll for resize detection * @private */ resizeListeners() { clearTimeout(this.expanding), requestAnimationFrame(this.resizeCheck.bind(this)), this.expanding = setTimeout(this.resizeListeners.bind(this), 350); } /** * Listen for visibility of tab to change * @private */ visibilityListeners() { document.addEventListener("visibilitychange", () => { document.visibilityState === "visible" && this.active === !1 ? (this.active = !0, this.resizeListeners()) : (this.active = !1, clearTimeout(this.expanding)); }); } /** * Use css transitions to detect resize * @private */ transitionListeners() { let t = this.content; t.style.transitionProperty = "font, font-size, font-size-adjust, font-stretch, font-variation-settings, font-weight, width, height", t.style.transitionDuration = "0.001ms", t.style.transitionTimingFunction = "linear", t.style.transitionDelay = "0", this._resizeCheck = this.resizeCheck.bind(this), this.document.addEventListener("transitionend", this._resizeCheck); } /** * Listen for media query changes and emit 'expand' event * Adapted from: https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js * @private */ mediaQueryListeners() { for (var t = this.document.styleSheets, e = (function(o) { o.matches && !this._expanding && setTimeout(this.expand.bind(this), 1); }).bind(this), i = 0; i < t.length; i += 1) { var n; try { n = t[i].cssRules; } catch { return; } if (!n) return; for (var s = 0; s < n.length; s += 1) if (n[s].media) { var r = this.window.matchMedia(n[s].media.mediaText); r.addListener(e); } } } /** * Use ResizeObserver to listen for changes in the DOM and check for resize * @private */ resizeObservers() { this.observer = new ResizeObserver((t) => { requestAnimationFrame(this.resizeCheck.bind(this)); }), this.observer.observe(this.document.documentElement); } /** * Use MutationObserver to listen for changes in the DOM and check for resize * @private */ mutationObservers() { this.observer = new MutationObserver((e) => { this.resizeCheck(); }); let t = { attributes: !0, childList: !0, characterData: !0, subtree: !0 }; this.observer.observe(this.document, t); } /** * Test if images are loaded or add listener for when they load * @private */ imageLoadListeners() { for (var t = this.document.querySelectorAll("img"), e, i = 0; i < t.length; i++) e = t[i], typeof e.naturalWidth < "u" && e.naturalWidth === 0 && (e.onload = this.expand.bind(this)); } /** * Listen for font load and check for resize when loaded * @private */ fontLoadListeners() { !this.document || !this.document.fonts || this.document.fonts.ready.then((function() { this.resizeCheck(); }).bind(this)); } /** * Get the documentElement * @returns {element} documentElement */ root() { return this.document ? this.document.documentElement : null; } /** * Get the location offset of a EpubCFI or an #id * @param {string | EpubCFI} target * @param {string} [ignoreClass] for the cfi * @returns { {left: Number, top: Number } */ locationOf(t, e) { var i, n = { left: 0, top: 0 }; if (!this.document) return n; if (this.epubcfi.isCfiString(t)) { let s = new l(t).toRange(this.document, e); if (s) { try { if (!s.endContainer || s.startContainer == s.endContainer && s.startOffset == s.endOffset) { let r = s.startContainer.textContent.indexOf(" ", s.startOffset); r == -1 && (r = s.startContainer.textContent.length), s.setEnd(s.startContainer, r); } } catch (r) { console.error("setting end offset to start container length failed", r); } if (s.startContainer.nodeType === Node.ELEMENT_NODE) i = s.startContainer.getBoundingClientRect(), n.left = i.left, n.top = i.top; else if (x) { let r = s.startContainer, o = new Range(); try { r.nodeType === k ? i = r.getBoundingClientRect() : s.startOffset + 2 < r.length ? (o.setStart(r, s.startOffset), o.setEnd(r, s.startOffset + 2), i = o.getBoundingClientRect()) : s.startOffset - 2 > 0 ? (o.setStart(r, s.startOffset - 2), o.setEnd(r, s.startOffset), i = o.getBoundingClientRect()) : i = r.parentNode.getBoundingClientRect(); } catch (a) { console.error(a, a.stack); } } else i = s.getBoundingClientRect(); } } else if (typeof t == "string" && t.indexOf("#") > -1) { let s = t.substring(t.indexOf("#") + 1), r = this.document.getElementById(s); if (r) if (x) { let o = new Range(); o.selectNode(r), i = o.getBoundingClientRect(); } else i = r.getBoundingClientRect(); } return i && (n.left = i.left, n.top = i.top), n; } /** * Append a stylesheet link to the document head * @param {string} src url */ addStylesheet(t) { return new Promise((function(e, i) { var n, s = !1; if (!this.document) { e(!1); return; } if (n = this.document.querySelector("link[href='" + t + "']"), n) { e(!0); return; } n = this.document.createElement("link"), n.type = "text/css", n.rel = "stylesheet", n.href = t, n.onload = n.onreadystatechange = function() { !s && (!this.readyState || this.readyState == "complete") && (s = !0, setTimeout(() => { e(!0); }, 1)); }, this.document.head.appendChild(n); }).bind(this)); } _getStylesheetNode(t) { var e; return t = "epubjs-inserted-css-" + (t || ""), this.document ? (e = this.document.getElementById(t), e || (e = this.document.createElement("style"), e.id = t, this.document.head.appendChild(e)), e) : !1; } /** * Append stylesheet css * @param {string} serializedCss * @param {string} key If the key is the same, the CSS will be replaced instead of inserted */ addStylesheetCss(t, e) { if (!this.document || !t) return !1; var i; return i = this._getStylesheetNode(e), i.innerHTML = t, !0; } /** * Append stylesheet rules to a generate stylesheet * Array: https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule * Object: https://github.com/desirable-objects/json-to-css * @param {array | object} rules * @param {string} key If the key is the same, the CSS will be replaced instead of inserted */ addStylesheetRules(t, e) { var i; if (!(!this.document || !t || t.length === 0)) if (i = this._getStylesheetNode(e).sheet, Object.prototype.toString.call(t) === "[object Array]") for (var n = 0, s = t.length; n < s; n++) { var r = 1, o = t[n], a = t[n][0], h = ""; Object.prototype.toString.call(o[1][0]) === "[object Array]" && (o = o[1], r = 0); for (var c = o.length; r < c; r++) { var d = o[r]; h += d[0] + ":" + d[1] + (d[2] ? " !important" : "") + `; `; } i.insertRule(a + "{" + h + "}", i.cssRules.length); } else Object.keys(t).forEach((y) => { const g = t[y]; if (Array.isArray(g)) g.forEach((b) => { const p = Object.keys(b).map((S) => `${S}:${b[S]}`).join(";"); i.insertRule(`${y}{${p}}`, i.cssRules.length); }); else { const w = Object.keys(g).map((p) => `${p}:${g[p]}`).join(";"); i.insertRule(`${y}{${w}}`, i.cssRules.length); } }); } /** * Append a script tag to the document head * @param {string} src url * @returns {Promise} loaded */ addScript(t) { return new Promise((function(e, i) { var n, s = !1; if (!this.document) { e(!1); return; } n = this.document.createElement("script"), n.type = "text/javascript", n.async = !0, n.src = t, n.onload = n.onreadystatechange = function() { !s && (!this.readyState || this.readyState == "complete") && (s = !0, setTimeout(function() { e(!0); }, 1)); }, this.document.head.appendChild(n); }).bind(this)); } /** * Add a class to the contents container * @param {string} className */ addClass(t) { var e; this.document && (e = this.content || this.document.body, e && e.classList.add(t)); } /** * Remove a class from the contents container * @param {string} removeClass */ removeClass(t) { var e; this.document && (e = this.content || this.document.body, e && e.classList.remove(t)); } /** * Add DOM event listeners * @private */ addEventListeners() { this.document && (this._triggerEvent = this.triggerEvent.bind(this), E.forEach(function(t) { this.document.addEventListener(t, this._triggerEvent, { passive: !0 }); }, this)); } /** * Remove DOM event listeners * @private */ removeEventListeners() { this.document && (E.forEach(function(t) { this.document.removeEventListener(t, this._triggerEvent, { passive: !0 }); }, this), this._triggerEvent = void 0); } /** * Emit passed browser events * @private */ triggerEvent(t) { this.emit(t.type, t); } /** * Add listener for text selection * @private */ addSelectionListeners() { this.document && (this._onSelectionChange = this.onSelectionChange.bind(this), this.document.addEventListener("selectionchange", this._onSelectionChange, { passive: !0 })); } /** * Remove listener for text selection * @private */ removeSelectionListeners() { this.document && (this.document.removeEventListener("selectionchange", this._onSelectionChange, { passive: !0 }), this._onSelectionChange = void 0); } /** * Handle getting text on selection * @private */ onSelectionChange(t) { this.selectionEndTimeout && clearTimeout(this.selectionEndTimeout), this.selectionEndTimeout = setTimeout((function() { var e = this.window.getSelection(); this.triggerSelectedEvent(e); }).bind(this), 250); } /** * Emit event on text selection * @private */ triggerSelectedEvent(t) { var e, i; t && t.rangeCount > 0 && (e = t.getRangeAt(0), e.collapsed || (i = new l(e, this.cfiBase).toString(), this.emit(f.CONTENTS.SELECTED, i), this.emit(f.CONTENTS.SELECTED_RANGE, e))); } /** * Get a Dom Range from EpubCFI * @param {EpubCFI} _cfi * @param {string} [ignoreClass] * @returns {Range} range */ range(t, e) { var i = new l(t); return i.toRange(this.document, e); } /** * Get an EpubCFI from a Dom Range * @param {Range} range * @param {string} [ignoreClass] * @returns {EpubCFI} cfi */ cfiFromRange(t, e) { return new l(t, this.cfiBase, e).toString(); } /** * Get an EpubCFI from a Dom node * @param {node} node * @param {string} [ignoreClass] * @returns {EpubCFI} cfi */ cfiFromNode(t, e) { return new l(t, this.cfiBase, e).toString(); } // TODO: find where this is used - remove? map(t) { var e = new C(t); return e.section(); } /** * Size the contents to a given width and height * @param {number} [width] * @param {number} [height] */ size(t, e) { var i = { scale: 1, scalable: "no" }; this.layoutStyle("scrolling"), t >= 0 && (this.width(t), i.width = t, this.css("padding", "0 " + t / 12 + "px")), e >= 0 && (this.height(e), i.height = e), this.css("margin", "0"), this.css("box-sizing", "border-box"), this.viewport(i); } /** * Apply columns to the contents for pagination * @param {number} width * @param {number} height * @param {number} columnWidth * @param {number} gap */ columns(t, e, i, n, s) { let r = m("column-axis"), o = m("column-gap"), a = m("column-width"), h = m("column-fill"), d = this.writingMode().indexOf("vertical") === 0 ? "vertical" : "horizontal"; this.layoutStyle("paginated"), s === "rtl" && d === "horizontal" && this.direction(s), this.width(t), this.height(e), this.viewport({ width: t, height: e, scale: 1, scalable: "no" }), this.css("overflow-y", "hidden"), this.css("margin", "0", !0), d === "vertical" ? (this.css("padding-top", n / 2 + "px", !0), this.css("padding-bottom", n / 2 + "px", !0), this.css("padding-left", "20px"), this.css("padding-right", "20px"), this.css(r, "vertical")) : (this.css("padding-top", "20px"), this.css("padding-bottom", "20px"), this.css("padding-left", n / 2 + "px", !0), this.css("padding-right", n / 2 + "px", !0), this.css(r, "horizontal")), this.css("box-sizing", "border-box"), this.css("max-width", "inherit"), this.css(h, "auto"), this.css(o, n + "px"), this.css(a, i + "px"), this.css("-webkit-line-box-contain", "block glyphs replaced"); } /** * Scale contents from center * @param {number} scale * @param {number} offsetX * @param {number} offsetY */ scaler(t, e, i) { var n = "scale(" + t + ")", s = ""; this.css("transform-origin", "top left"), (e >= 0 || i >= 0) && (s = " translate(" + (e || 0) + "px, " + (i || 0) + "px )"), this.css("transform", n + s); } /** * Fit contents into a fixed width and height * @param {number} width * @param {number} height */ fit(t, e, i) { var n = this.viewport(), s = parseInt(n.width), r = parseInt(n.height), o = t / s, a = e / r, h = o < a ? o : a; if (this.layoutStyle("paginated"), this.width(s), this.height(r), this.overflow("hidden"), this.scaler(h, 0, 0), this.css("background-size", s * h + "px " + r * h + "px"), this.css("background-color", "transparent"), i && i.properties.includes("page-spread-left")) { var c = t - s * h; this.css("margin-left", c + "px"); } } /** * Set the direction of the text * @param {string} [dir="ltr"] "rtl" | "ltr" */ direction(t) { this.documentElement && (this.documentElement.style.direction = t); } mapPage(t, e, i, n, s) { var r = new C(e, s); return r.page(this, t, i, n); } /** * Emit event when link in content is clicked * @private */ linksHandler() { N(this.content, (t) => { this.emit(f.CONTENTS.LINK_CLICKED, t); }); } /** * Set the writingMode of the text * @param {string} [mode="horizontal-tb"] "horizontal-tb" | "vertical-rl" | "vertical-lr" */ writingMode(t) { let e = m("writing-mode"); return t && this.documentElement && (this.documentElement.style[e] = t), this.window.getComputedStyle(this.documentElement)[e] || ""; } /** * Set the layoutStyle of the content * @param {string} [style="paginated"] "scrolling" | "paginated" * @private */ layoutStyle(t) { return t && (this._layoutStyle = t, navigator.epubReadingSystem.layoutStyle = this._layoutStyle), this._layoutStyle || "paginated"; } /** * Add the epubReadingSystem object to the navigator * @param {string} name * @param {string} version * @private */ epubReadingSystem(t, e) { return navigator.epubReadingSystem = { name: t, version: e, layoutStyle: this.layoutStyle(), hasFeature: function(i) { switch (i) { case "dom-manipulation": return !0; case "layout-changes": return !0; case "touch-events": return !0; case "mouse-events": return !0; case "keyboard-events": return !0; case "spine-scripting": return !1; default: return !1; } } }, navigator.epubReadingSystem; } destroy() { this.removeListeners(); } } _(A.prototype); export { A as default }; //# sourceMappingURL=contents.mjs.map