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

Improve splitting of hyphen-separated CFI ranges

Create a `stripCFIRange` utility which strips assertions before separating the
input. This means that hyphens inside assertions will be ignored.
parent 0c16351c
......@@ -34,6 +34,22 @@ function compareArrays(
return a.length - b.length;
}
/**
* Split a hyphen-separated CFI range.
*
* CFI assertions are stripped in the process. If `range` does not contain a
* hyphen, the result will be an empty range with the end point being the same
* as the start point.
*
* @example
* splitCFIRange("/2/4[chap-02]-/2/6[chap-03]") // returns `["/2/4", "/2/6"]`.
*/
export function splitCFIRange(range: string): [string, string] {
const rangeWithoutAssertions = stripCFIAssertions(range);
const [start, end] = rangeWithoutAssertions.split('-', 2);
return [start, end ?? start];
}
/**
* Strip assertions from a Canonical Fragment Identifier.
*
......
......@@ -2,6 +2,7 @@ import {
cfiInRange,
compareCFIs,
documentCFI,
splitCFIRange,
stripCFIAssertions,
} from '../cfi';
......@@ -25,6 +26,25 @@ describe('sidebar/util/cfi', () => {
});
});
describe('splitCFIRange', () => {
[
{
range: '/2/4-/4/6',
expected: ['/2/4', '/4/6'],
},
{
range: '/2/4[chap-02]-/2/6[chap-03]',
expected: ['/2/4', '/2/6'],
},
{
range: '/2/4',
expected: ['/2/4', '/2/4'],
},
].forEach(({ range, expected }) => {
assert.deepEqual(splitCFIRange(range), expected);
});
});
describe('compareCFIs', () => {
[
// Trivial cases
......
import { cfiInRange, stripCFIAssertions } from '../../shared/cfi';
import {
cfiInRange,
splitCFIRange,
stripCFIAssertions,
} from '../../shared/cfi';
import type { SegmentInfo } from '../../types/annotator';
import type { Annotation, EPUBContentSelector } from '../../types/api';
import type { Filters } from '../store/modules/filters';
......@@ -41,7 +45,7 @@ export function segmentMatchesFocusFilters(
filters: Filters,
): boolean {
if (segment.cfi && filters.cfi) {
const [cfiStart, cfiEnd] = filters.cfi.value.split('-');
const [cfiStart, cfiEnd] = splitCFIRange(filters.cfi.value);
return cfiInRange(segment.cfi, cfiStart, cfiEnd);
}
if (segment.pages && filters.page) {
......
import { cfiInRange, stripCFIAssertions } from '../../shared/cfi';
import {
cfiInRange,
splitCFIRange,
stripCFIAssertions,
} from '../../shared/cfi';
import type { Annotation } from '../../types/api';
import { pageLabelInRange } from '../util/page-range';
import * as unicodeUtils from '../util/unicode';
......@@ -113,12 +117,11 @@ const fieldMatchers: Record<string, Matcher | Matcher<number>> = {
//
// If we wanted to use a more standard CFI range representation,
// we could follow https://idpf.org/epub/linking/cfi/#sec-ranges.
if (cfiTerm.includes('-')) {
const [start, end] = cfiTerm.split('-');
return cfiInRange(cfi, start, end);
} else {
return false;
}
//
// If `cfiTerm` does not contain a hyphen, the result will be an empty
// range that does not match any CFIs.
const [start, end] = splitCFIRange(cfiTerm);
return cfiInRange(cfi, start, end);
},
normalize: (val: string) => stripCFIAssertions(val.trim()),
},
......
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