vue-book-reader
Version:
<div align="center"> <img width=250 src="https://raw.githubusercontent.com/jinhuan138/vue--book-reader/master/public/logo.png" /> <h1>VueReader</h1> </div>
122 lines (121 loc) • 5.3 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const CONTEXT_LENGTH = 50;
const normalizeWhitespace = (str) => str.replace(/\s+/g, " ");
const makeExcerpt = (strs, { startIndex, startOffset, endIndex, endOffset }) => {
const start = strs[startIndex];
const end = strs[endIndex];
const match = start === end ? start.slice(startOffset, endOffset) : start.slice(startOffset) + strs.slice(start + 1, end).join("") + end.slice(0, endOffset);
const trimmedStart = normalizeWhitespace(start.slice(0, startOffset)).trimStart();
const trimmedEnd = normalizeWhitespace(end.slice(endOffset)).trimEnd();
const ellipsisPre = trimmedStart.length < CONTEXT_LENGTH ? "" : "…";
const ellipsisPost = trimmedEnd.length < CONTEXT_LENGTH ? "" : "…";
const pre = `${ellipsisPre}${trimmedStart.slice(-CONTEXT_LENGTH)}`;
const post = `${trimmedEnd.slice(0, CONTEXT_LENGTH)}${ellipsisPost}`;
return { pre, match, post };
};
const simpleSearch = function* (strs, query, options = {}) {
const { locales = "en", sensitivity } = options;
const matchCase = sensitivity === "variant";
const haystack = strs.join("");
const lowerHaystack = matchCase ? haystack : haystack.toLocaleLowerCase(locales);
const needle = matchCase ? query : query.toLocaleLowerCase(locales);
const needleLength = needle.length;
let index = -1;
let strIndex = -1;
let sum = 0;
do {
index = lowerHaystack.indexOf(needle, index + 1);
if (index > -1) {
while (sum <= index)
sum += strs[++strIndex].length;
const startIndex = strIndex;
const startOffset = index - (sum - strs[strIndex].length);
const end = index + needleLength;
while (sum <= end)
sum += strs[++strIndex].length;
const endIndex = strIndex;
const endOffset = end - (sum - strs[strIndex].length);
const range = { startIndex, startOffset, endIndex, endOffset };
yield { range, excerpt: makeExcerpt(strs, range) };
}
} while (index > -1);
};
const segmenterSearch = function* (strs, query, options = {}) {
var _a;
const { locales = "en", granularity = "word", sensitivity = "base" } = options;
let segmenter, collator;
try {
segmenter = new Intl.Segmenter(locales, { usage: "search", granularity });
collator = new Intl.Collator(locales, { sensitivity });
} catch (e) {
console.warn(e);
segmenter = new Intl.Segmenter("en", { usage: "search", granularity });
collator = new Intl.Collator("en", { sensitivity });
}
const queryLength = Array.from(segmenter.segment(query)).length;
const substrArr = [];
let strIndex = 0;
let segments = segmenter.segment(strs[strIndex])[Symbol.iterator]();
main:
while (strIndex < strs.length) {
while (substrArr.length < queryLength) {
const { done, value } = segments.next();
if (done) {
strIndex++;
if (strIndex < strs.length) {
segments = segmenter.segment(strs[strIndex])[Symbol.iterator]();
continue;
} else
break main;
}
const { index, segment } = value;
if (!/[^\p{Format}]/u.test(segment))
continue;
if (/\s/u.test(segment)) {
if (!/\s/u.test((_a = substrArr[substrArr.length - 1]) == null ? void 0 : _a.segment))
substrArr.push({ strIndex, index, segment: " " });
continue;
}
value.strIndex = strIndex;
substrArr.push(value);
}
const substr = substrArr.map((x) => x.segment).join("");
if (collator.compare(query, substr) === 0) {
const endIndex = strIndex;
const lastSeg = substrArr[substrArr.length - 1];
const endOffset = lastSeg.index + lastSeg.segment.length;
const startIndex = substrArr[0].strIndex;
const startOffset = substrArr[0].index;
const range = { startIndex, startOffset, endIndex, endOffset };
yield { range, excerpt: makeExcerpt(strs, range) };
}
substrArr.shift();
}
};
const search = (strs, query, options) => {
const { granularity = "grapheme", sensitivity = "base" } = options;
if (!(Intl == null ? void 0 : Intl.Segmenter) || granularity === "grapheme" && (sensitivity === "variant" || sensitivity === "accent"))
return simpleSearch(strs, query, options);
return segmenterSearch(strs, query, options);
};
const searchMatcher = (textWalker, opts) => {
const { defaultLocale, matchCase, matchDiacritics, matchWholeWords, acceptNode } = opts;
return function* (doc, query) {
const iter = textWalker(doc, function* (strs, makeRange) {
for (const result of search(strs, query, {
locales: doc.body.lang || doc.documentElement.lang || defaultLocale || "en",
granularity: matchWholeWords ? "word" : "grapheme",
sensitivity: matchDiacritics && matchCase ? "variant" : matchDiacritics && !matchCase ? "accent" : !matchDiacritics && matchCase ? "case" : "base"
})) {
const { startIndex, startOffset, endIndex, endOffset } = result.range;
result.range = makeRange(startIndex, startOffset, endIndex, endOffset);
yield result;
}
}, acceptNode);
for (const result of iter)
yield result;
};
};
exports.search = search;
exports.searchMatcher = searchMatcher;