Commit 6e841c14 authored by Robert Knight's avatar Robert Knight

Add function for testing whether an annotation matches a frame's segment

Add a utility for comparing the document segment information captured in
an annotation against the segment info provided by a frame. This will be
used to send annotations on EPUBs to the guest frame only when the
corresponding chapter is loaded.
parent a1bf8115
import { stripCFIAssertions } from '../util/cfi';
import type { SegmentInfo } from '../../types/annotator';
import type { Annotation, EPUBContentSelector } from '../../types/api';
/**
* Return true if an annotation matches the currently loaded segment of a document.
*
* For web pages and PDFs, this is always true. In EPUBs, this is used to send
* only the annotations for the current chapter to the guest frame.
*/
export function annotationMatchesSegment(
ann: Annotation,
segment: SegmentInfo
) {
const selector = ann.target[0].selector?.find(
s => s.type === 'EPUBContentSelector'
) as EPUBContentSelector;
if (!selector) {
return true;
}
return Boolean(
(segment.url && selector.url === segment.url) ||
(segment.cfi &&
selector.cfi &&
stripCFIAssertions(selector.cfi) === stripCFIAssertions(segment.cfi))
);
}
import { annotationMatchesSegment } from '../annotation-segment';
describe('annotationMatchesSegment', () => {
it('returns true if annotation has no segment selectors', () => {
const ann = {
target: [
{
selectors: [
{
type: 'TextQuoteSelector',
exact: 'test',
},
],
},
],
};
const segment = { cfi: '/2' };
// If an annotation does not have segment information, we can't tell if
// it matches the current segment or not. We optimistically assume it does.
assert.isTrue(annotationMatchesSegment(ann, segment));
});
[
// Both segment and annotation have CFIs.
{
segment: { cfi: '/2' },
selector: { type: 'EPUBContentSelector', cfi: '/2' },
expected: true,
},
{
segment: { cfi: '/2' },
selector: { type: 'EPUBContentSelector', cfi: '/4' },
expected: false,
},
// CFIs may contain assertions in `[...]` brackets. We could use these to
// check whether a CFI made on an older version of a document is still
// valid. These are currently ignored however.
{
segment: { cfi: '/2[idref=4]' },
selector: { type: 'EPUBContentSelector', cfi: '/2[idref=5]' },
expected: true,
},
// Neither segment nor annotation have properties that can be compared.
{
segment: { cfi: '/2' },
selector: { type: 'EPUBContentSelector', url: '/chapters/01.xhtml' },
expected: false,
},
{
segment: { url: '/chapters/01.xhtml' },
selector: { type: 'EPUBContentSelector', cfi: '/2' },
expected: false,
},
// Both segment and annotation have URLs that can be compared.
{
segment: { url: '/chapters/02.xhtml' },
selector: { type: 'EPUBContentSelector', url: '/chapters/02.xhtml' },
expected: true,
},
{
segment: { url: '/chapters/02.xhtml' },
selector: { type: 'EPUBContentSelector', url: '/chapters/04.xhtml' },
expected: false,
},
].forEach(({ segment, selector, expected }, index) => {
it(`returns true if CFI or URL matches current segment (${index})`, () => {
const ann = { target: [{ selector: [selector] }] };
assert.equal(annotationMatchesSegment(ann, segment), expected);
});
});
});
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