Commit 7ba89df5 authored by Robert Knight's avatar Robert Knight

Share `<mosaic-book>` custom element between EPUB and PDF books

Refactor MosaicBookElement so that it can be used for both the EPUB and
PDF book test cases.
parent a02defc8
......@@ -27,7 +27,7 @@
Gutenberg EPUB</a>.
</p>
<mosaic-book></mosaic-book>
<mosaic-book book="little-women"></mosaic-book>
{{{hypothesisScript}}}
</body>
......
......@@ -10,55 +10,8 @@
}
</style>
<script type="module">
/**
* Custom element that holds the book content frame. The class name is not
* important but the `<mosaic-book>` tag name triggers the VitalSource integration.
*/
class MosaicElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
// For now there is only one page.
const pageURLs = [
'/document/vitalsource-pdf-page'
];
let pageIndex = 0;
const setPage = index => {
if (index < 0 || index >= pageURLs.length) {
return;
}
pageIndex = index;
// We remove the current frame and create a new one, rather than just
// change the `src` of the existing iframe, to mimic what Bookshelf
// does. The client should be robust to either approach.
this.contentFrame?.remove();
this.contentFrame = document.createElement('iframe');
this.contentFrame.src = pageURLs[pageIndex];
this.shadowRoot.append(this.contentFrame);
};
const styles = document.createElement('style');
styles.innerHTML = `
iframe {
width: 100%;
height: 800px;
resize: both;
overflow: auto;
}
`;
this.shadowRoot.append(styles);
const controlBar = document.createElement('div');
this.shadowRoot.append(controlBar);
setPage(0);
}
}
customElements.define('mosaic-book', MosaicElement);
import { MosaicBookElement } from '/scripts/vitalsource-mosaic-book-element.js';
customElements.define('mosaic-book', MosaicBookElement);
// The `window.VST` object must exist in the parent frame, otherwise the
// `window.innerPageData` variable is not set in the content frame.
......@@ -84,7 +37,7 @@
by searching for the book ID (9781938168239) in the Bookshelf store.
</p>
<mosaic-book></mosaic-book>
<mosaic-book book="test-pdf"></mosaic-book>
{{{hypothesisScript}}}
</body>
......
......@@ -10,59 +10,28 @@ export class MosaicBookElement extends HTMLElement {
super();
this.attachShadow({ mode: 'open' });
const chapterURLs = [
'/document/little-women-1',
'/document/little-women-2',
'/document/little-women-3',
];
const book = this.getAttribute('book');
let chapterIndex = 0;
if (book === 'little-women') {
this.chapterURLs = [
'/document/little-women-1',
'/document/little-women-2',
'/document/little-women-3',
];
} else if (book === 'test-pdf') {
this.chapterURLs = ['/document/vitalsource-pdf-page'];
} else {
console.warn(`Unknown VitalSource book "${book}"`);
this.chapterURLs = [];
}
const setChapter = (index, { initialLoad = false } = {}) => {
if (index < 0 || index >= chapterURLs.length) {
return;
}
chapterIndex = index;
// We remove the current frame and create a new one, rather than just
// change the `src` of the existing iframe, to mimic what Bookshelf
// does. The client should be robust to either approach.
this.contentFrame?.remove();
this.contentFrame = document.createElement('iframe');
this.shadowRoot.append(this.contentFrame);
const chapterURL = chapterURLs[chapterIndex];
if (initialLoad) {
// Simulate client loading after VS chapter content has already
// loaded.
this.contentFrame.src = chapterURL;
} else {
// Simulate chapter navigation after client is injected. These
// navigations happen in several stages:
//
// 1. The previous chapter's iframe is removed
// 2. A new iframe is created. The initial HTML is a "blank" page
// containing (invisible) content data for the new chapter as
// text in the page.
// 3. The content data is posted to the server via a form
// submission, which returns the decoded HTML.
//
// The client should only inject into the new frame after step 3.
this.contentFrame.src = 'about:blank';
setTimeout(() => {
// Set the final URL in a way that doesn't update the `src` attribute
// of the iframe, to make sure the client isn't relying on that.
this.contentFrame.contentWindow.location.href = chapterURL;
}, 50);
}
};
this.chapterIndex = 0;
const styles = document.createElement('style');
styles.innerHTML = `
iframe {
width: 100%;
height: 400px;
height: 500px;
resize: both;
overflow: auto;
}
......@@ -74,14 +43,54 @@ export class MosaicBookElement extends HTMLElement {
this.prevButton = document.createElement('button');
this.prevButton.textContent = 'Prev chapter';
this.prevButton.onclick = () => setChapter(chapterIndex - 1);
this.prevButton.onclick = () => this.setChapter(this.chapterIndex - 1);
controlBar.append(this.prevButton);
this.nextButton = document.createElement('button');
this.nextButton.textContent = 'Next chapter';
this.nextButton.onclick = () => setChapter(chapterIndex + 1);
this.nextButton.onclick = () => this.setChapter(this.chapterIndex + 1);
controlBar.append(this.nextButton);
setChapter(0, { initialLoad: true });
this.setChapter(0, { initialLoad: true });
}
setChapter(index, { initialLoad = false } = {}) {
if (index < 0 || index >= this.chapterURLs.length) {
return;
}
this.chapterIndex = index;
// We remove the current frame and create a new one, rather than just
// change the `src` of the existing iframe, to mimic what Bookshelf
// does. The client should be robust to either approach.
this.contentFrame?.remove();
this.contentFrame = document.createElement('iframe');
this.shadowRoot.append(this.contentFrame);
const chapterURL = this.chapterURLs[this.chapterIndex];
if (initialLoad) {
// Simulate client loading after VS chapter content has already
// loaded.
this.contentFrame.src = chapterURL;
} else {
// Simulate chapter navigation after client is injected. These
// navigations happen in several stages:
//
// 1. The previous chapter's iframe is removed
// 2. A new iframe is created. The initial HTML is a "blank" page
// containing (invisible) content data for the new chapter as
// text in the page.
// 3. The content data is posted to the server via a form
// submission, which returns the decoded HTML.
//
// The client should only inject into the new frame after step 3.
this.contentFrame.src = 'about:blank';
setTimeout(() => {
// Set the final URL in a way that doesn't update the `src` attribute
// of the iframe, to make sure the client isn't relying on that.
this.contentFrame.contentWindow.location.href = chapterURL;
}, 50);
}
}
}
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