diff --git a/src/BookReader.js b/src/BookReader.js index 5fabcce6e..e6dc8f5da 100644 --- a/src/BookReader.js +++ b/src/BookReader.js @@ -111,6 +111,8 @@ BookReader.PLUGINS = { chapters: null, /** @type {typeof import('./plugins/plugin.experiments.js').ExperimentsPlugin | null}*/ experiments: null, + /** @type {typeof import('./plugins/plugin.iframe.js').IframePlugin | null}*/ + iframe: null, /** @type {typeof import('./plugins/plugin.resume.js').ResumePlugin | null}*/ resume: null, /** @type {typeof import('./plugins/search/plugin.search.js').SearchPlugin | null}*/ @@ -181,6 +183,7 @@ BookReader.prototype.setup = function(options) { autoplay: BookReader.PLUGINS.autoplay ? new BookReader.PLUGINS.autoplay(this) : null, chapters: BookReader.PLUGINS.chapters ? new BookReader.PLUGINS.chapters(this) : null, experiments: BookReader.PLUGINS.experiments ? new BookReader.PLUGINS.experiments(this) : null, + iframe: BookReader.PLUGINS.iframe ? new BookReader.PLUGINS.iframe(this) : null, search: BookReader.PLUGINS.search ? new BookReader.PLUGINS.search(this) : null, resume: BookReader.PLUGINS.resume ? new BookReader.PLUGINS.resume(this) : null, textSelection: BookReader.PLUGINS.textSelection ? new BookReader.PLUGINS.textSelection(this) : null, diff --git a/src/plugins/plugin.iframe.js b/src/plugins/plugin.iframe.js index 77285acdd..a05567d01 100644 --- a/src/plugins/plugin.iframe.js +++ b/src/plugins/plugin.iframe.js @@ -1,48 +1,47 @@ -/* global BookReader */ -/** - * Plugin for two-way communication between a BookReader in an IFrame and the - * parent web page - */ +// @ts-check +import { EVENTS } from '../BookReader/events.js'; +import { BookReaderPlugin } from '../BookReaderPlugin.js'; const MESSAGE_TYPE_FRAGMENT_CHANGE = 'bookReaderFragmentChange'; -BookReader.prototype.init = (function (super_) { - return function () { - super_.call(this); - _attachEventListeners(this); - }; -})(BookReader.prototype.init); - /** - * @private - * Using window.postMessage() and event listeners, the plugin notifies the - * parent window when pages change, and the parent window can also - * explicitly request a page change by sending its own message. - * - * @param {BookReader} br - * @param {Window?} [parent] + * Plugin for two-way communication between a BookReader in an IFrame and the + * parent web page. Using window.postMessage() and event listeners, the + * plugin notifies the parent window when pages change, and the parent + * window can also explicitly request a page change by sending its own + * message. */ -export function _attachEventListeners(br, parent = window.parent) { - // Not embedded, abort - if (!parent) { - return; +export class IframePlugin extends BookReaderPlugin { + options = { + enabled: true, } - br.bind(BookReader.eventNames.fragmentChange, () => { - const fragment = br.fragmentFromParams(br.paramsFromCurrent()); - - parent.postMessage( - { type: MESSAGE_TYPE_FRAGMENT_CHANGE, fragment }, - '*', - ); - }); - - window.addEventListener('message', event => { - // Not a recognized message type, abort - if (!event.data || event.data.type !== MESSAGE_TYPE_FRAGMENT_CHANGE) { + /** @override */ + init() { + // Not enabled, or not embedded, abort + if (!this.options.enabled || !window.parent) { return; } - br.updateFromParams(br.paramsFromFragment(event.data.fragment)); - }); + this.br.bind(EVENTS.fragmentChange, () => { + const fragment = this.br.fragmentFromParams(this.br.paramsFromCurrent()); + + window.parent.postMessage( + { type: MESSAGE_TYPE_FRAGMENT_CHANGE, fragment }, + '*', + ); + }); + + window.addEventListener('message', event => { + // Not a recognized message type, abort + if (!event.data || event.data.type !== MESSAGE_TYPE_FRAGMENT_CHANGE) { + return; + } + + this.br.updateFromParams(this.br.paramsFromFragment(event.data.fragment)); + }); + } } + +const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader); +BookReader?.registerPlugin('iframe', IframePlugin); diff --git a/tests/jest/plugins/plugin.iframe.test.js b/tests/jest/plugins/plugin.iframe.test.js index 17ec3f118..dadee1120 100644 --- a/tests/jest/plugins/plugin.iframe.test.js +++ b/tests/jest/plugins/plugin.iframe.test.js @@ -1,7 +1,7 @@ import sinon from 'sinon'; import BookReader from '@/src/BookReader.js'; -import { _attachEventListeners } from '@/src/plugins/plugin.iframe.js'; +import { IframePlugin } from '@/src/plugins/plugin.iframe.js'; afterEach(() => { sinon.restore(); @@ -9,10 +9,11 @@ afterEach(() => { test('listens for br events if parent', () => { const br = new BookReader(); + const plugin = new IframePlugin(br); sinon.spy(br, 'bind'); sinon.spy(window, 'addEventListener'); - _attachEventListeners(br, window.parent); + plugin.init(); expect(br.bind.callCount).toBe(1); expect(window.addEventListener.callCount).toBe(1); expect(window.addEventListener.args[0][0]).toBe('message'); @@ -20,18 +21,21 @@ test('listens for br events if parent', () => { test('does not attach if not in iframe', () => { const br = new BookReader(); + const plugin = new IframePlugin(br); + sinon.stub(window, 'parent').value(null); sinon.spy(br, 'bind'); sinon.spy(window, 'addEventListener'); - _attachEventListeners(br, null); + plugin.init(); expect(br.bind.callCount).toBe(0); expect(window.addEventListener.callCount).toBe(0); }); test('updates params when window receives a message', () => { const br = new BookReader(); + const plugin = new IframePlugin(br); sinon.spy(br, 'updateFromParams'); - _attachEventListeners(br); + plugin.init(); window.dispatchEvent(new MessageEvent('message', { data: { type: 'bookReaderFragmentChange',