UNPKG

@teipublisher/pb-components

Version:
196 lines (164 loc) 5.53 kB
import { LitElement } from 'lit-element'; import 'tify'; import { pbMixin, waitOnce } from './pb-mixin.js'; import { resolveURL } from './utils.js'; function _injectStylesheet(path) { const style = document.querySelector(`link#pb-tify`); if (!style) { const elem = document.createElement('link'); elem.type = 'text/css'; elem.rel = 'stylesheet'; elem.id = `pb-tify`; elem.href = `${resolveURL(path)}/tify.css`; document.head.appendChild(elem); } } /** * Viewer for IIIF presentation manifests based on https://tify.rocks/. * Requires a proper manifest listing the resources to show. `pb-facs-link` * can be used to navigate to a page. * * @fires pb-refresh - fired if user navigates to another page within the viewer. Parameters to be passed * with the event will be copied from the `@id` URL declared in the `rendering` property of the canvas. * @fires pb-show-annotation - when received, opens the the manifest given in `file` and the page denoted by the * `order` property in the event (see `pb-facs-link`). Page counts start at 1. */ export class PbTify extends pbMixin(LitElement) { static get properties() { return { /** * URL pointing to a IIIF presentation manifest. Relative paths * are interpreted relative to the API endpoint. */ manifest: { type: String, }, ...super.properties, }; } constructor() { super(); this.cssPath = '../css/tify'; this._initialPages = null; this._currentPage = null; } attributeChangedCallback(name, oldVal, newVal) { super.attributeChangedCallback(name, oldVal, newVal); if (name === 'manifest' && newVal) { this.manifest = newVal; this._initViewer(); } } async connectedCallback() { super.connectedCallback(); _injectStylesheet(this.cssPath); this._container = document.createElement('div'); this._container.style.height = '100%'; this._container.style.width = '100%'; this.appendChild(this._container); this.subscribeTo('pb-show-annotation', ev => { if (ev.detail) { this._initialPages = ev.detail.order ? Number(ev.detail.order) : Number.POSITIVE_INFINITY; if (this._initialPages === Number.POSITIVE_INFINITY) { this._initialPages = 1; } const url = ev.detail.file || ev.detail.url; if (url && url !== this.manifest) { this.manifest = ev.detail.file; this._initViewer(); // check if tify is already initialized } else if (this._setPage) { this._setPage(this._initialPages); } if (ev.detail.coordinates) { this._addOverlay(ev.detail.coordinates); } } }); this.signalReady(); } firstUpdated() { super.firstUpdated(); waitOnce('pb-page-ready', () => { this._initViewer(); }); } _initViewer() { if (!this.manifest) { return; } if (this._tify) { this._tify.destroy(); } this._tify = new Tify({ manifestUrl: this.toAbsoluteURL(this.manifest, this.getEndpoint()), }); this._tify.ready.then(() => { // open initial page if set earlier via pb-load-facsimile event if (this._initialPages) { this._tify.setPage(this._initialPages); } // extend tify's setPage function to allow emitting an event const { app } = this._tify; const originalSetPage = app.setPage; app.setPage = pages => { const page = Array.isArray(pages) ? pages[0] : pages; if (this._currentPage === page) { return; } const canvas = app.$root.canvases[page - 1]; this._switchPage(canvas); originalSetPage(pages); this._currentPage = page; }; this._setPage = app.setPage; }); this._tify.mount(this._container); } _switchPage(canvas) { const { rendering } = canvas; if (rendering && rendering.length > 0) { const url = new URL(rendering[0]['@id']); const params = {}; url.searchParams.forEach((value, key) => { params[key] = value; }); console.log('<pb-tify> page changed, emitting refresh with params %o', params); this.emitTo('pb-refresh', params); } } _addOverlay(coordinates) { if (!Array.isArray(coordinates) || coordinates.length !== 4) { console.error('coords incomplete or missing (array of 4 numbers expected)', coordinates); return; } const { viewer } = this._tify; const { viewport } = viewer; const overlayId = 'runtime-overlay'; if (this.overlay) { viewer.removeOverlay(this.overlay); } const viewportBounds = viewport.getBounds(); const [x1, y1, w, h] = coordinates; const rect = viewport.imageToViewportRectangle(x1, y1, w, h); // Scroll into view if necessary if (!viewportBounds.containsPoint(rect.getTopLeft())) { viewer.viewport.panTo(rect.getCenter()); } // Add overlay to viewer const overlay = document.createElement('div'); this.overlay = overlay; overlay.id = overlayId; overlay.style.border = 'var(--pb-facsimile-border, none)'; overlay.style.outline = 'var(--pb-facsimile-outline, 4px solid rgba(0, 0, 128, 0.5))'; overlay.style.background = 'var(--pb-facsimile-background, rgba(0, 0, 128, 0.05))'; viewer.addOverlay({ element: overlay, location: rect, }); } createRenderRoot() { return this; } } customElements.define('pb-tify', PbTify);