Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/BookReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}*/
Expand Down Expand Up @@ -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,
Expand Down
73 changes: 36 additions & 37 deletions src/plugins/plugin.iframe.js
Original file line number Diff line number Diff line change
@@ -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);
12 changes: 8 additions & 4 deletions tests/jest/plugins/plugin.iframe.test.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
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();
});

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');
});

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',
Expand Down
Loading