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.

524 lines (518 loc) 19.1 kB
import u from "../../event-emitter/index.mjs"; import { extend as d, defer as g, isFloat as m } from "./utils/core.mjs"; import o from "./utils/hook.mjs"; import p from "./epubcfi.mjs"; import f from "./utils/queue.mjs"; import E from "./layout.mjs"; import y from "./themes.mjs"; import "./contents.mjs"; import b from "./annotations.mjs"; import { EVENTS as n, DOM_EVENTS as k } from "./utils/constants.mjs"; import N from "./managers/views/iframe.mjs"; import I from "./managers/default/index.mjs"; import R from "./managers/continuous/index.mjs"; class w { constructor(t, i) { this.settings = d(this.settings || {}, { width: null, height: null, ignoreClass: "", manager: "default", view: "iframe", flow: null, layout: null, spread: null, minSpreadWidth: 800, stylesheet: null, resizeOnOrientationChange: !0, script: null, snap: !1, defaultDirection: "ltr", allowScriptedContent: !1, allowPopups: !1 }), d(this.settings, i), typeof this.settings.manager == "object" && (this.manager = this.settings.manager), this.book = t, this.hooks = {}, this.hooks.display = new o(this), this.hooks.serialize = new o(this), this.hooks.content = new o(this), this.hooks.unloaded = new o(this), this.hooks.layout = new o(this), this.hooks.render = new o(this), this.hooks.show = new o(this), this.hooks.content.register(this.handleLinks.bind(this)), this.hooks.content.register(this.passEvents.bind(this)), this.hooks.content.register(this.adjustImages.bind(this)), this.book.spine.hooks.content.register(this.injectIdentifier.bind(this)), this.settings.stylesheet && this.book.spine.hooks.content.register(this.injectStylesheet.bind(this)), this.settings.script && this.book.spine.hooks.content.register(this.injectScript.bind(this)), this.themes = new y(this), this.annotations = new b(this), this.epubcfi = new p(), this.q = new f(this), this.location = void 0, this.q.enqueue(this.book.opened), this.starting = new g(), this.started = this.starting.promise, this.q.enqueue(this.start); } /** * Set the manager function * @param {function} manager */ setManager(t) { this.manager = t; } /** * Require the manager from passed string, or as a class function * @param {string|object} manager [description] * @return {method} */ requireManager(t) { var i; return typeof t == "string" && t === "default" ? i = I : typeof t == "string" && t === "continuous" ? i = R : i = t, i; } /** * Require the view from passed string, or as a class function * @param {string|object} view * @return {view} */ requireView(t) { var i; return typeof t == "string" && t === "iframe" ? i = N : i = t, i; } /** * Start the rendering * @return {Promise} rendering has started */ start() { switch (!this.settings.layout && (this.book.package.metadata.layout === "pre-paginated" || this.book.displayOptions.fixedLayout === "true") && (this.settings.layout = "pre-paginated"), this.book.package.metadata.spread) { case "none": this.settings.spread = "none"; break; case "both": this.settings.spread = !0; break; } this.manager || (this.ViewManager = this.requireManager(this.settings.manager), this.View = this.requireView(this.settings.view), this.manager = new this.ViewManager({ view: this.View, queue: this.q, request: this.book.load.bind(this.book), settings: this.settings })), this.direction(this.book.package.metadata.direction || this.settings.defaultDirection), this.settings.globalLayoutProperties = this.determineLayoutProperties(this.book.package.metadata), this.flow(this.settings.globalLayoutProperties.flow), this.layout(this.settings.globalLayoutProperties), this.manager.on(n.MANAGERS.ADDED, this.afterDisplayed.bind(this)), this.manager.on(n.MANAGERS.REMOVED, this.afterRemoved.bind(this)), this.manager.on(n.MANAGERS.RESIZED, this.onResized.bind(this)), this.manager.on(n.MANAGERS.ORIENTATION_CHANGE, this.onOrientationChange.bind(this)), this.manager.on(n.MANAGERS.SCROLLED, this.reportLocation.bind(this)), this.emit(n.RENDITION.STARTED), this.starting.resolve(); } /** * Call to attach the container to an element in the dom * Container must be attached before rendering can begin * @param {element} element to attach to * @return {Promise} */ attachTo(t) { return this.q.enqueue((function() { this.manager.render(t, { width: this.settings.width, height: this.settings.height }), this.emit(n.RENDITION.ATTACHED); }).bind(this)); } /** * Display a point in the book * The request will be added to the rendering Queue, * so it will wait until book is opened, rendering started * and all other rendering tasks have finished to be called. * @param {string} target Url or EpubCFI * @return {Promise} */ display(t) { return this.displaying && this.displaying.resolve(), this.q.enqueue(this._display, t); } /** * Tells the manager what to display immediately * @private * @param {string} target Url or EpubCFI * @return {Promise} */ _display(t) { if (this.book) { this.epubcfi.isCfiString(t); var i = new g(), e = i.promise, s; return this.displaying = i, this.book.locations.length() && m(t) && (t = this.book.locations.cfiFromPercentage(parseFloat(t))), s = this.book.spine.get(t), s ? (this.manager.display(s, t).then(() => { i.resolve(s), this.displaying = void 0, this.emit(n.RENDITION.DISPLAYED, s), this.reportLocation(); }, (a) => { this.emit(n.RENDITION.DISPLAY_ERROR, a); }), e) : (i.reject(new Error("No Section Found")), e); } } /* render(view, show) { // view.onLayout = this.layout.format.bind(this.layout); view.create(); // Fit to size of the container, apply padding this.manager.resizeView(view); // Render Chain return view.section.render(this.book.request) .then(function(contents){ return view.load(contents); }.bind(this)) .then(function(doc){ return this.hooks.content.trigger(view, this); }.bind(this)) .then(function(){ this.layout.format(view.contents); return this.hooks.layout.trigger(view, this); }.bind(this)) .then(function(){ return view.display(); }.bind(this)) .then(function(){ return this.hooks.render.trigger(view, this); }.bind(this)) .then(function(){ if(show !== false) { this.q.enqueue(function(view){ view.show(); }, view); } // this.map = new Map(view, this.layout); this.hooks.show.trigger(view, this); this.trigger("rendered", view.section); }.bind(this)) .catch(function(e){ this.trigger("loaderror", e); }.bind(this)); } */ /** * Report what section has been displayed * @private * @param {*} view */ afterDisplayed(t) { t.on(n.VIEWS.MARK_CLICKED, (i, e) => this.triggerMarkEvent(i, e, t.contents)), this.hooks.render.trigger(t, this).then(() => { t.contents ? this.hooks.content.trigger(t.contents, this).then(() => { this.emit(n.RENDITION.RENDERED, t.section, t); }) : this.emit(n.RENDITION.RENDERED, t.section, t); }); } /** * Report what has been removed * @private * @param {*} view */ afterRemoved(t) { this.hooks.unloaded.trigger(t, this).then(() => { this.emit(n.RENDITION.REMOVED, t.section, t); }); } /** * Report resize events and display the last seen location * @private */ onResized(t, i) { this.emit(n.RENDITION.RESIZED, { width: t.width, height: t.height }, i), this.location && this.location.start && this.display(i || this.location.start.cfi); } /** * Report orientation events and display the last seen location * @private */ onOrientationChange(t) { this.emit(n.RENDITION.ORIENTATION_CHANGE, t); } /** * Move the Rendition to a specific offset * Usually you would be better off calling display() * @param {object} offset */ moveTo(t) { this.manager.moveTo(t); } /** * Trigger a resize of the views * @param {number} [width] * @param {number} [height] * @param {string} [epubcfi] (optional) */ resize(t, i, e) { t && (this.settings.width = t), i && (this.settings.height = i), this.manager.resize(t, i, e); } /** * Clear all rendered views */ clear() { this.manager.clear(); } /** * Go to the next "page" in the rendition * @return {Promise} */ next() { return this.q.enqueue(this.manager.next.bind(this.manager)).then(this.reportLocation.bind(this)); } /** * Go to the previous "page" in the rendition * @return {Promise} */ prev() { return this.q.enqueue(this.manager.prev.bind(this.manager)).then(this.reportLocation.bind(this)); } //-- http://www.idpf.org/epub/301/spec/epub-publications.html#meta-properties-rendering /** * Determine the Layout properties from metadata and settings * @private * @param {object} metadata * @return {object} properties */ determineLayoutProperties(t) { var i, e = this.settings.layout || t.layout || "reflowable", s = this.settings.spread || t.spread || "auto", a = this.settings.orientation || t.orientation || "auto", r = this.settings.flow || t.flow || "auto", h = t.viewport || "", l = this.settings.minSpreadWidth || t.minSpreadWidth || 800, c = this.settings.direction || t.direction || "ltr"; return (this.settings.width === 0 || this.settings.width > 0) && (this.settings.height === 0 || this.settings.height > 0), i = { layout: e, spread: s, orientation: a, flow: r, viewport: h, minSpreadWidth: l, direction: c }, i; } /** * Adjust the flow of the rendition to paginated or scrolled * (scrolled-continuous vs scrolled-doc are handled by different view managers) * @param {string} flow */ flow(t) { var i = t; (t === "scrolled" || t === "scrolled-doc" || t === "scrolled-continuous") && (i = "scrolled"), (t === "auto" || t === "paginated") && (i = "paginated"), this.settings.flow = t, this._layout && this._layout.flow(i), this.manager && this._layout && this.manager.applyLayout(this._layout), this.manager && this.manager.updateFlow(i), this.manager && this.manager.isRendered() && this.location && (this.manager.clear(), this.display(this.location.start.cfi)); } /** * Adjust the layout of the rendition to reflowable or pre-paginated * @param {object} settings */ layout(t) { return t && (this._layout = new E(t), this._layout.spread(t.spread, this.settings.minSpreadWidth), this._layout.on(n.LAYOUT.UPDATED, (i, e) => { this.emit(n.RENDITION.LAYOUT, i, e); })), this.manager && this._layout && this.manager.applyLayout(this._layout), this._layout; } /** * Adjust if the rendition uses spreads * @param {string} spread none | auto (TODO: implement landscape, portrait, both) * @param {int} [min] min width to use spreads at */ spread(t, i) { this.settings.spread = t, i && (this.settings.minSpreadWidth = i), this._layout && this._layout.spread(t, i), this.manager && this.manager.isRendered() && this.manager.updateLayout(); } /** * Adjust the direction of the rendition * @param {string} dir */ direction(t) { this.settings.direction = t || "ltr", this.manager && this.manager.direction(this.settings.direction), this.manager && this.manager.isRendered() && this.location && (this.manager.clear(), this.display(this.location.start.cfi)); } /** * Report the current location * @fires relocated * @fires locationChanged */ reportLocation() { return this.q.enqueue((function() { requestAnimationFrame((function() { var e = this.manager.currentLocation(); if (e && e.then && typeof e.then == "function") e.then((function(s) { let a = this.located(s); !a || !a.start || !a.end || (this.location = a, this.emit(n.RENDITION.LOCATION_CHANGED, { index: this.location.start.index, href: this.location.start.href, start: this.location.start.cfi, end: this.location.end.cfi, percentage: this.location.start.percentage }), this.emit(n.RENDITION.RELOCATED, this.location)); }).bind(this)); else if (e) { let s = this.located(e); if (!s || !s.start || !s.end) return; this.location = s, this.emit(n.RENDITION.LOCATION_CHANGED, { index: this.location.start.index, href: this.location.start.href, start: this.location.start.cfi, end: this.location.end.cfi, percentage: this.location.start.percentage }), this.emit(n.RENDITION.RELOCATED, this.location); } }).bind(this)); }).bind(this)); } /** * Get the Current Location object * @return {displayedLocation | promise} location (may be a promise) */ currentLocation() { var t = this.manager.currentLocation(); if (t && t.then && typeof t.then == "function") t.then((function(i) { return this.located(i); }).bind(this)); else if (t) return this.located(t); } /** * Creates a Rendition#locationRange from location * passed by the Manager * @returns {displayedLocation} * @private */ located(t) { if (!t.length) return {}; let i = t[0], e = t[t.length - 1], s = { start: { index: i.index, href: i.href, cfi: i.mapping.start, displayed: { page: i.pages[0] || 1, total: i.totalPages } }, end: { index: e.index, href: e.href, cfi: e.mapping.end, displayed: { page: e.pages[e.pages.length - 1] || 1, total: e.totalPages } } }, a = this.book.locations.locationFromCfi(i.mapping.start), r = this.book.locations.locationFromCfi(e.mapping.end); a != null && (s.start.location = a, s.start.percentage = this.book.locations.percentageFromLocation(a)), r != null && (s.end.location = r, s.end.percentage = this.book.locations.percentageFromLocation(r)); let h = this.book.pageList.pageFromCfi(i.mapping.start), l = this.book.pageList.pageFromCfi(e.mapping.end); return h != -1 && (s.start.page = h), l != -1 && (s.end.page = l), e.index === this.book.spine.last().index && s.end.displayed.page >= s.end.displayed.total && (s.atEnd = !0), i.index === this.book.spine.first().index && s.start.displayed.page === 1 && (s.atStart = !0), s; } /** * Remove and Clean Up the Rendition */ destroy() { this.manager && this.manager.destroy(), this.book = void 0; } /** * Pass the events from a view's Contents * @private * @param {Contents} view contents */ passEvents(t) { k.forEach((i) => { t.on(i, (e) => this.triggerViewEvent(e, t)); }), t.on(n.CONTENTS.SELECTED, (i) => this.triggerSelectedEvent(i, t)); } /** * Emit events passed by a view * @private * @param {event} e */ triggerViewEvent(t, i) { this.emit(t.type, t, i); } /** * Emit a selection event's CFI Range passed from a a view * @private * @param {string} cfirange */ triggerSelectedEvent(t, i) { this.emit(n.RENDITION.SELECTED, t, i); } /** * Emit a markClicked event with the cfiRange and data from a mark * @private * @param {EpubCFI} cfirange */ triggerMarkEvent(t, i, e) { this.emit(n.RENDITION.MARK_CLICKED, t, i, e); } /** * Get a Range from a Visible CFI * @param {string} cfi EpubCfi String * @param {string} ignoreClass * @return {range} */ getRange(t, i) { var e = new p(t), s = this.manager.visible().filter(function(a) { if (e.spinePos === a.index) return !0; }); if (s.length) return s[0].contents.range(e, i); } /** * Hook to adjust images to fit in columns * @param {Contents} contents * @private */ adjustImages(t) { if (this._layout.name === "pre-paginated") return new Promise(function(a) { a(); }); let i = t.window.getComputedStyle(t.content, null), e = (t.content.offsetHeight - (parseFloat(i.paddingTop) + parseFloat(i.paddingBottom))) * 0.95, s = parseFloat(i.paddingLeft) + parseFloat(i.paddingRight); return t.addStylesheetRules({ img: { "max-width": (this._layout.columnWidth ? this._layout.columnWidth - s + "px" : "100%") + "!important", "max-height": e + "px!important", "object-fit": "contain", "page-break-inside": "avoid", "break-inside": "avoid", "box-sizing": "border-box" }, svg: { "max-width": (this._layout.columnWidth ? this._layout.columnWidth - s + "px" : "100%") + "!important", "max-height": e + "px!important", "page-break-inside": "avoid", "break-inside": "avoid" } }), new Promise(function(a, r) { setTimeout(function() { a(); }, 1); }); } /** * Get the Contents object of each rendered view * @returns {Contents[]} */ getContents() { return this.manager ? this.manager.getContents() : []; } /** * Get the views member from the manager * @returns {Views} */ views() { return (this.manager ? this.manager.views : void 0) || []; } /** * Hook to handle link clicks in rendered content * @param {Contents} contents * @private */ handleLinks(t) { t && t.on(n.CONTENTS.LINK_CLICKED, (i) => { let e = this.book.path.relative(i); this.display(e); }); } /** * Hook to handle injecting stylesheet before * a Section is serialized * @param {document} doc * @param {Section} section * @private */ injectStylesheet(t, i) { let e = t.createElement("link"); e.setAttribute("type", "text/css"), e.setAttribute("rel", "stylesheet"), e.setAttribute("href", this.settings.stylesheet), t.getElementsByTagName("head")[0].appendChild(e); } /** * Hook to handle injecting scripts before * a Section is serialized * @param {document} doc * @param {Section} section * @private */ injectScript(t, i) { let e = t.createElement("script"); e.setAttribute("type", "text/javascript"), e.setAttribute("src", this.settings.script), e.textContent = " ", t.getElementsByTagName("head")[0].appendChild(e); } /** * Hook to handle the document identifier before * a Section is serialized * @param {document} doc * @param {Section} section * @private */ injectIdentifier(t, i) { let e = this.book.packaging.metadata.identifier, s = t.createElement("meta"); s.setAttribute("name", "dc.relation.ispartof"), e && s.setAttribute("content", e), t.getElementsByTagName("head")[0].appendChild(s); } } u(w.prototype); export { w as default }; //# sourceMappingURL=rendition.mjs.map