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
JavaScript
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