diff --git a/src/1-one/output/api/html.js b/src/1-one/output/api/html.js new file mode 100644 index 0000000..70a32cf --- /dev/null +++ b/src/1-one/output/api/html.js @@ -0,0 +1,77 @@ +const isClass = /^\../ +const isId = /^#./ + +const escapeXml = str => { + str = str.replaceAll('&', '&') + str = str.replaceAll('<', '<') + str = str.replaceAll('>', '>') + str = str.replaceAll('"', '"') + str = str.replaceAll("'", ''') + return str +} + +// interpret .class, #id, tagName +const toTag = function (k) { + let start = '' + let end = '' + k = escapeXml(k) + if (isClass.test(k)) { + start = `` + } + start += '>' + return { start, end } +} + +const getIndex = function (doc, obj) { + const starts = {} + const ends = {} + Object.keys(obj).forEach(k => { + let res = obj[k] + const tag = toTag(k) + if (typeof res === 'string') { + res = doc.match(res) + } + res.docs.forEach(terms => { + // don't highlight implicit terms + if (terms.every(t => t.implicit)) { + return + } + const a = terms[0].id + starts[a] ||= [] + starts[a].push(tag.start) + const b = terms.at(-1).id + ends[b] ||= [] + ends[b].push(tag.end) + }) + }) + return { starts, ends } +} + +const html = function (obj) { + // index ids to highlight + const { starts, ends } = getIndex(this, obj) + // create the text output + let out = '' + this.docs.forEach(terms => { + for (let i = 0; i < terms.length; i += 1) { + const t = terms[i] + // do a span tag + if (Object.hasOwn(starts, t.id)) { + out += starts[t.id].join('') + } + out += t.pre || '' + out += t.text || '' + if (Object.hasOwn(ends, t.id)) { + out += ends[t.id].join('') + } + out += t.post || '' + } + }) + return out +} +export default { html } diff --git a/tests/01-one/output/html.test.js b/tests/01-one/output/html.test.js new file mode 100644 index 0000000..7553ab3 --- /dev/null +++ b/tests/01-one/output/html.test.js @@ -0,0 +1,61 @@ +import test from 'tape' +import nlp from '../../lib/one.js' +const here = '[one/output/html] ' + +test('html-match', function (t) { + let doc = nlp(`match one two. one match two. one two match.`) + let html = doc.html({ i: 'match' }) + t.equal(html, `match one two. one match two. one two match.`, here + 'html tag') + + doc = nlp(`match one two. one match two.`) + html = doc.html({ '.foo': 'match' }) + t.equal(html, `match one two. one match two.`, here + 'html class') + + doc = nlp(`one match match two.`) + html = doc.html({ '#foo': 'match+' }) + t.equal(html, `one match match two.`, here + 'html id') + + doc = nlp(`one match two.`) + html = doc.html({ '.red': 'match+', '.blue': doc.match('two') }) + t.equal(html, `one match two.`, here + 'html two classes') + + doc = nlp(`if i can recall, my grey dog loves pizza crusts (they are really good).`) + html = doc.html({ '.red': 'my grey dog', '.blue': doc.match('loves') }) + t.equal( + html, + `if i can recall, my grey dog loves pizza crusts (they are really good).`, + here + 'html pre test' + ) + + t.end() +}) + +test('html-nest', function (t) { + const doc = nlp(`one match two.`) + let html = doc.html({ + i: 'match', + b: 'one match two', + }) + t.equal(html, `one match two.`, here + 'easy nest') + + html = doc.html({ + b: 'match two', + i: 'match', + }) + t.equal(html, `one match two.`, here + 'hard nest') + + // html = doc.html({ + // i: 'match', + // b: 'match two', + // }) + // t.equal(html, `one match two.`, here + 'harder nest') + + t.end() +}) + +test('html-implicit', function (t) { + const doc = nlp(`he's cool`) + const out = doc.html({ '.foo': '#Verb' }) + t.equal(out, `he's cool`, here + 'implict') + t.end() +})