import * as CFI from '../epubcfi.js' const parser = new DOMParser() const XML = str => parser.parseFromString(str, 'application/xml') const XHTML = str => parser.parseFromString(str, 'application/xhtml+xml') { // example from EPUB CFI spec const opf = XML(` en `) const a = opf.getElementById('chap01ref') const b = CFI.toElement(opf, CFI.parse('/6/4[chap01ref]')[0]) const c = CFI.toElement(opf, CFI.parse('/6/4')[0]) console.assert(a === b) console.assert(a === c) } { // example from EPUB CFI spec const page = XHTML(`

xxxyyy0123456789

…

`) // the exact same page with some text nodes removed, CDATA & comment added, // and characters changed to entities const page2 = XHTML(`

xxxyyy4589

…

`) // the exact same page with nodes are to be ignored const page3 = XHTML(`

This is ignored!

Also ignored

xxxyyyNote: we put ignored text in this span but not the other ones because although the CFI library should ignore them, they won't be ignored by DOM Ranges, which will break the tests.0123456789

…

`) const filter = node => node.nodeType !== 1 ? NodeFilter.FILTER_ACCEPT : node.matches('.reject') ? NodeFilter.FILTER_REJECT : node.matches('.skip') ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT const test = (page, filter) => { for (const cfi of [ '/4[body01]/10[para05]/3:10', '/4[body01]/16[svgimg]', '/4[body01]/10[para05]/1:0', '/4[body01]/10[para05]/2/1:0', '/4[body01]/10[para05]/2/1:3', ]) { const range = CFI.toRange(page, CFI.parse(cfi), filter) const a = CFI.fromRange(range, filter) const b = `epubcfi(${cfi})` console.assert(a === b, `expected ${b}, got ${a}`) } for (let i = 0; i < 10; i++) { const cfi = `/4/10,/3:${i},/3:${i+1}` const range = CFI.toRange(page, CFI.parse(cfi), filter) const n = `${i}` console.assert(range.toString() === n, `expected ${n}, got ${range}`) } } test(page) test(page2) test(page, filter) test(page2, filter) test(page3, filter) } { // regression: selections inside FILTER_SKIP wrappers must preserve offsets // https://github.com/johnfactotum/foliate-js/issues/100 const page = XHTML(`

Hello, World

Hello, World

`) const filter = node => node.nodeType === 1 && node.classList?.contains('SKIP') ? NodeFilter.FILTER_SKIP : NodeFilter.FILTER_ACCEPT // cfi1 const para1 = page.getElementById('test-skip-1') const text1 = para1.firstChild const range1 = page.createRange() range1.setStart(text1, 3) range1.setEnd(text1, 8) const cfi1 = CFI.fromRange(range1, filter) const expected1 = 'epubcfi(/4/2[test-skip-1],/1:3,/1:8)' console.assert(cfi1 === expected1, `expected ${expected1}, got ${cfi1}`) // cfi2 const para2 = page.getElementById('test-skip-2') const skips = para2.querySelectorAll('.SKIP') const tail2 = para2.lastChild const range2 = page.createRange() range2.setStart(skips[1].firstChild, 1) range2.setEnd(tail2, 4) const cfi2 = CFI.fromRange(range2, filter) const expected2 = 'epubcfi(/4/4[test-skip-2],/1:3,/1:8)' console.assert(cfi2 === expected2, `expected ${expected2}, got ${cfi2}`) const rebuilt = CFI.toRange(page, CFI.parse('/4/4[test-skip-2],/1:3,/1:8'), filter) console.assert(rebuilt.toString() === 'lo, W', `expected lo, W, got ${rebuilt}`) const roundtrip = CFI.fromRange(rebuilt, filter) console.assert(roundtrip === expected2, `expected ${expected2}, got ${roundtrip}`) } { // special characters in ID assertions const opf = XML(` `) const a = opf.getElementById('chap0]!/1ref^') const b = CFI.toElement(opf, CFI.parse('/6/4[chap0^]!/1ref^^]')[0]) console.assert(a === b) const page = XHTML(`

xxxyyy0123456789

…

`) for (const cfi of [ '/4[body0^]!/1^^]/10[para^]/0^,/5]/3:10', '/4[body0^]!/1^^]/16[s^]^[vgimg]', '/4[body0^]!/1^^]/10[para^]/0^,/5]/1:0', '/4[body0^]!/1^^]/10[para^]/0^,/5]/2/1:0', '/4[body0^]!/1^^]/10[para^]/0^,/5]/2/1:3', ]) { const range = CFI.toRange(page, CFI.parse(cfi)) const a = CFI.fromRange(range) const b = `epubcfi(${cfi})` console.assert(a === b, `expected ${b}, got ${a}`) } for (let i = 0; i < 10; i++) { const cfi = `/4[body0^]!/1^^]/10[para^]/0^,^/5],/3:${i},/3:${i+1}` const range = CFI.toRange(page, CFI.parse(cfi)) const n = `${i}` console.assert(range.toString() === n, `expected ${n}, got ${range}`) } } { for (const [a, b, c] of [ ['/6/4!/10', '/6/4!/10', 0], ['/6/4!/2/3:0', '/6/4!/2', 1], ['/6/4!/2/4/6/8/10/3:0', '/6/4!/4', -1], [ '/6/4[chap0^]!/1ref^^]!/4[body01^^]/10[para^]^,05^^]', '/6/4!/4/10', 0, ], [ '/6/4[chap0^]!/1ref^^]!/4[body01^^],/10[para^]^,05^^],/15:10[foo^]]', '/6/4!/4/12', -1, ], ['/6/4', '/6/4!/2', -1], ['/6/4!/2', '/6/4!/2!/2', -1], ]) { const x = CFI.compare(a, b) console.assert(x === c, `compare ${a} and ${b}, expected ${c}, got ${x}`) } }