Commit e45a0a1b authored by Robert Knight's avatar Robert Knight

Define `Integration` interface

Explicitly define the common `Integration` interface which is
implemented by both `HTMLIntegration` and `PDFIntegration`.

This makes it more obvious what is part of the common interface that
`Guest` can depend on and what are additional properties or methods that
are specific to a particular implementation.
parent 90139746
...@@ -22,8 +22,9 @@ import { normalizeURI } from './util/url'; ...@@ -22,8 +22,9 @@ import { normalizeURI } from './util/url';
* @typedef {import('./util/emitter').EventBus} EventBus * @typedef {import('./util/emitter').EventBus} EventBus
* @typedef {import('../types/annotator').AnnotationData} AnnotationData * @typedef {import('../types/annotator').AnnotationData} AnnotationData
* @typedef {import('../types/annotator').Anchor} Anchor * @typedef {import('../types/annotator').Anchor} Anchor
* @typedef {import('../types/annotator').Integration} Integration
* @typedef {import('../types/annotator').SidebarLayout} SidebarLayout
* @typedef {import('../types/api').Target} Target * @typedef {import('../types/api').Target} Target
* @typedef {import('./sidebar').LayoutState} LayoutState
*/ */
/** /**
...@@ -142,13 +143,11 @@ export default class Guest { ...@@ -142,13 +143,11 @@ export default class Guest {
/** @type {Anchor[]} */ /** @type {Anchor[]} */
this.anchors = []; this.anchors = [];
// Setup the document type-specific integration consisting of metadata extraction, /** @type {Integration} */
// anchoring module and logic to respond to activity (eg. scrolling) in the page. this.integration =
if (config.documentType === 'pdf') { config.documentType === 'pdf'
this.integration = new PDFIntegration(this); ? new PDFIntegration(this)
} else { : new HTMLIntegration(this.element);
this.integration = new HTMLIntegration(this.element);
}
// Set the frame identifier if it's available. // Set the frame identifier if it's available.
// The "top" guest instance will have this as null since it's in a top frame not a sub frame // The "top" guest instance will have this as null since it's in a top frame not a sub frame
...@@ -664,7 +663,7 @@ export default class Guest { ...@@ -664,7 +663,7 @@ export default class Guest {
/** /**
* Attempt to fit the document content alongside the sidebar. * Attempt to fit the document content alongside the sidebar.
* *
* @param {LayoutState} sidebarLayout * @param {SidebarLayout} sidebarLayout
*/ */
fitSideBySide(sidebarLayout) { fitSideBySide(sidebarLayout) {
const active = this.integration.fitSideBySide(sidebarLayout); const active = this.integration.fitSideBySide(sidebarLayout);
......
...@@ -2,6 +2,18 @@ import { anchor, describe } from '../anchoring/html'; ...@@ -2,6 +2,18 @@ import { anchor, describe } from '../anchoring/html';
import { HTMLMetadata } from './html-metadata'; import { HTMLMetadata } from './html-metadata';
/**
* @typedef {import('../../types/annotator').Integration} Integration
*/
/**
* Document type integration for ordinary web pages.
*
* This integration is used for web pages and applications that are not handled
* by a more specific integration (eg. for PDFs).
*
* @implements {Integration}
*/
export class HTMLIntegration { export class HTMLIntegration {
constructor(container = document.body) { constructor(container = document.body) {
this.container = container; this.container = container;
......
...@@ -13,8 +13,9 @@ import { PDFMetadata } from './pdf-metadata'; ...@@ -13,8 +13,9 @@ import { PDFMetadata } from './pdf-metadata';
* @typedef {import('../../types/annotator').Anchor} Anchor * @typedef {import('../../types/annotator').Anchor} Anchor
* @typedef {import('../../types/annotator').Annotator} Annotator * @typedef {import('../../types/annotator').Annotator} Annotator
* @typedef {import('../../types/annotator').HypothesisWindow} HypothesisWindow * @typedef {import('../../types/annotator').HypothesisWindow} HypothesisWindow
* @typedef {import('../../types/annotator').Integration} Integration
* @typedef {import('../../types/annotator').SidebarLayout} SidebarLayout
* @typedef {import('../../types/api').Selector} Selector * @typedef {import('../../types/api').Selector} Selector
* @typedef {import('../sidebar').LayoutState} LayoutState
*/ */
// The viewport and controls for PDF.js start breaking down below about 670px // The viewport and controls for PDF.js start breaking down below about 670px
...@@ -22,6 +23,11 @@ import { PDFMetadata } from './pdf-metadata'; ...@@ -22,6 +23,11 @@ import { PDFMetadata } from './pdf-metadata';
// is enough room. Otherwise, allow sidebar to overlap PDF // is enough room. Otherwise, allow sidebar to overlap PDF
const MIN_PDF_WIDTH = 680; const MIN_PDF_WIDTH = 680;
/**
* Integration that works with PDF.js
*
* @implements {Integration}
*/
export class PDFIntegration { export class PDFIntegration {
/** /**
* @param {Annotator} annotator * @param {Annotator} annotator
...@@ -274,7 +280,7 @@ export class PDFIntegration { ...@@ -274,7 +280,7 @@ export class PDFIntegration {
* for the sidebar, and prompt PDF.js to re-render the PDF pages to scale * for the sidebar, and prompt PDF.js to re-render the PDF pages to scale
* within that resized container. * within that resized container.
* *
* @param {LayoutState} sidebarLayout * @param {SidebarLayout} sidebarLayout
* @return {boolean} - True if side-by-side mode was activated * @return {boolean} - True if side-by-side mode was activated
*/ */
fitSideBySide(sidebarLayout) { fitSideBySide(sidebarLayout) {
......
...@@ -72,6 +72,36 @@ ...@@ -72,6 +72,36 @@
* @prop {(ann: AnnotationData) => Promise<Anchor[]>} anchor * @prop {(ann: AnnotationData) => Promise<Anchor[]>} anchor
*/ */
/**
* @typedef SidebarLayout
* @prop {boolean} expanded
* @prop {number} width
*/
/**
* Interface for document type/viewer integrations that handle all the details
* of supporting a specific document type (web page, PDF, ebook etc.).
*
* @typedef Integration
* @prop {(root: HTMLElement, selectors: Selector[]) => Promise<Range>} anchor -
* Attempt to resolve a set of serialized selectors to the corresponding content in the
* current document.
* @prop {(root: HTMLElement, range: Range) => Selector[]|Promise<Selector[]>} describe -
* Generate a list of serializable selectors which represent the content in
* `range`.
* @prop {() => HTMLElement} contentContainer -
* Return the main element that contains the document content. This is used
* by controls such as the bucket bar to know when the content might have scrolled.
* @prop {() => void} destroy -
* Clean up the integration and remove any event listeners, caches etc.
* @prop {(layout: SidebarLayout) => boolean} fitSideBySide -
* Attempt to resize the content so that it is visible alongside the sidebar.
* Returns `true` if the sidebar and content are displayed side-by-side or
* false otherwise.
* @prop {() => Promise<object>} getMetadata
* @prop {() => Promise<string>} uri - Return the URL of the currently loaded document
*/
/** /**
* @typedef DocumentMetadata * @typedef DocumentMetadata
* @prop {string} title * @prop {string} title
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment