Commit c304f1f4 authored by Robert Knight's avatar Robert Knight

Add EPUBContentSelector and PageSelector selectors to VS annotations

These will be used for various purposes described in the previous commit.
EPUBContentSelector selectors are captured for all VS books.  PageSelector
selectors are currently only captured for PDF-based books where we know the page
numbers are "fixed" and should not change depending on viewer settings. We
_might_ be able to capture them for EPUBs as well in future, though we need to
understand more about how reliable/future-proof they are in that context.
parent b27e7140
...@@ -68,7 +68,12 @@ describe('annotator/integrations/vitalsource', () => { ...@@ -68,7 +68,12 @@ describe('annotator/integrations/vitalsource', () => {
fakeHTMLIntegration = { fakeHTMLIntegration = {
anchor: sinon.stub(), anchor: sinon.stub(),
contentContainer: sinon.stub(), contentContainer: sinon.stub(),
describe: sinon.stub(), describe: sinon.stub().returns([
{
type: 'TextQuoteSelector',
exact: 'dummy selection',
},
]),
destroy: sinon.stub(), destroy: sinon.stub(),
fitSideBySide: sinon.stub().returns(false), fitSideBySide: sinon.stub().returns(false),
scrollToAnchor: sinon.stub(), scrollToAnchor: sinon.stub(),
...@@ -192,13 +197,47 @@ describe('annotator/integrations/vitalsource', () => { ...@@ -192,13 +197,47 @@ describe('annotator/integrations/vitalsource', () => {
}); });
class FakeMosaicBookElement { class FakeMosaicBookElement {
constructor() {
this._format = 'epub';
}
selectEPUBBook() {
this._format = 'epub';
}
selectPDFBook() {
this._format = 'pbk';
}
getBookInfo() { getBookInfo() {
return { return {
format: 'epub', format: this._format,
title: 'Test book title', title: 'Test book title',
isbn: 'TEST-BOOK-ID', isbn: 'TEST-BOOK-ID',
}; };
} }
async getCurrentPage() {
if (this._format === 'epub') {
return {
absoluteURL: '/pages/chapter_02.xhtml',
cfi: '/2',
chapterTitle: 'Chapter two',
index: 1,
page: '20',
};
} else if (this._format === 'pbk') {
return {
absoluteURL: '/pages/2',
cfi: '/1',
index: 1,
page: '2',
chapterTitle: 'First chapter',
};
} else {
throw new Error('Unknown book');
}
}
} }
describe('VitalSourceContentIntegration', () => { describe('VitalSourceContentIntegration', () => {
...@@ -277,6 +316,66 @@ describe('annotator/integrations/vitalsource', () => { ...@@ -277,6 +316,66 @@ describe('annotator/integrations/vitalsource', () => {
assert.calledWith(fakeHTMLIntegration.scrollToAnchor, anchor); assert.calledWith(fakeHTMLIntegration.scrollToAnchor, anchor);
}); });
context('when "book_as_single_document" flag is on', () => {
beforeEach(() => {
featureFlags.update({ book_as_single_document: true });
});
it('adds selector for current EPUB book Content Document', async () => {
const integration = createIntegration();
integration.contentContainer();
assert.calledWith(fakeHTMLIntegration.contentContainer);
const range = new Range();
const selectors = await integration.describe(range);
const cfiSelector = selectors.find(
s => s.type === 'EPUBContentSelector'
);
assert.ok(cfiSelector);
assert.deepEqual(cfiSelector, {
type: 'EPUBContentSelector',
url: '/pages/chapter_02.xhtml',
cfi: '/2',
title: 'Chapter two',
});
const pageSelector = selectors.find(s => s.type === 'PageSelector');
assert.notOk(pageSelector);
});
it('adds selector for current PDF book page', async () => {
fakeBookElement.selectPDFBook();
const integration = createIntegration();
integration.contentContainer();
assert.calledWith(fakeHTMLIntegration.contentContainer);
const range = new Range();
const selectors = await integration.describe(range);
const cfiSelector = selectors.find(
s => s.type === 'EPUBContentSelector'
);
assert.ok(cfiSelector);
assert.deepEqual(cfiSelector, {
type: 'EPUBContentSelector',
url: '/pages/2',
cfi: '/1',
title: 'First chapter',
});
const pageSelector = selectors.find(s => s.type === 'PageSelector');
assert.ok(pageSelector);
assert.deepEqual(pageSelector, {
type: 'PageSelector',
index: 1,
label: '2',
});
});
});
describe('#getMetadata', () => { describe('#getMetadata', () => {
context('when "book_as_single_document" flag is off', () => { context('when "book_as_single_document" flag is off', () => {
it('returns metadata for current page/chapter', async () => { it('returns metadata for current page/chapter', async () => {
......
...@@ -419,6 +419,10 @@ export class VitalSourceContentIntegration ...@@ -419,6 +419,10 @@ export class VitalSourceContentIntegration
} }
const pageInfo = await this._bookElement.getCurrentPage(); const pageInfo = await this._bookElement.getCurrentPage();
// We generate an "EPUBContentSelector" with a CFI for all VS books,
// although for PDF-based books the CFI is a string generated from the
// page number.
const extraSelectors: Selector[] = [ const extraSelectors: Selector[] = [
{ {
type: 'EPUBContentSelector', type: 'EPUBContentSelector',
...@@ -428,6 +432,11 @@ export class VitalSourceContentIntegration ...@@ -428,6 +432,11 @@ export class VitalSourceContentIntegration
}, },
]; ];
// If this is a PDF-based book, add a page selector. PDFs always have page
// numbers available. EPUB-based books _may_ have information about how
// content maps to page numbers in a printed edition of the book. We
// currently limit page number selectors to PDFs until more is understood
// about when EPUB page numbers are reliable/likely to remain stable.
const bookInfo = this._bookElement.getBookInfo(); const bookInfo = this._bookElement.getBookInfo();
if (bookInfo.format === 'pbk') { if (bookInfo.format === 'pbk') {
extraSelectors.push({ extraSelectors.push({
......
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