-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
109 lines (90 loc) · 3.23 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
export default function exported(Intl) {
return function localeIndexOf(string, substring, localesOrCollator, options) {
return functional(Intl, string, substring, localesOrCollator, options);
};
}
export function prototypeLocaleIndexOf(Intl) {
return function prototypeLocaleIndexOf(substring, localesOrCollator, options) {
return functional(Intl, this, substring, localesOrCollator, options);
};
}
export function prollyfill() {
String.prototype.localeIndexOf = (typeof Intl !== 'undefined') ?
prototypeLocaleIndexOf(Intl) :
String.prototype.indexOf;
}
export function functional(Intl, string, substring, localesOrCollator, options) {
const collator = getCollator(Intl, localesOrCollator, options);
return indexOf(Intl, collator, string, substring);
}
export function indexOf(Intl, collator, string, substring) {
const slicesGenerator = makeSlicesGenerator(Intl, collator, string, substring);
for (const { slice, index } of slicesGenerator) {
if (collator.compare(slice, substring) === 0) {
indexOf.lastLength = slice.length;
return index;
}
}
return -1;
}
export function* makeSlicesGenerator(Intl, collator, string, substring) {
const { ignorePunctuation, locale } = collator.resolvedOptions();
const punctuationCollator = ignorePunctuation ?
new Intl.Collator(locale, { ignorePunctuation: true }) :
null;
function isConsidered(grapheme) {
// concatenation with 'a' is a workaround for Node issue
return punctuationCollator.compare('a', `a${grapheme}`) !== 0;
}
function countOfConsideredGraphemes(graphemes) {
const count = punctuationCollator ?
graphemes.filter(({ considered }) => considered).length :
graphemes.length;
return count;
}
const segmenter = Intl.Segmenter ?
new Intl.Segmenter(locale, { granularity: 'grapheme' }) :
{
* segment(string) {
let index = 0;
for (const segment of string) {
yield { segment, index };
index += segment.length;
}
}
};
const substringGraphemes = Array.from(segmenter.segment(substring));
const substringLength = punctuationCollator ?
substringGraphemes.filter(({ segment }) => isConsidered(segment)).length :
substringGraphemes.length;
const sliceArray = [];
for (const grapheme of segmenter.segment(string)) {
const isAlreadyFull = countOfConsideredGraphemes(sliceArray) === substringLength;
if (isAlreadyFull) {
sliceArray.shift();
}
const considered = punctuationCollator ? isConsidered(grapheme.segment) : undefined;
sliceArray.push({ ...grapheme, considered });
const isNotYetFull = countOfConsideredGraphemes(sliceArray) < substringLength;
if (isNotYetFull) {
continue;
}
const slice = sliceArray.map(({ segment }) => segment).join('');
const index = sliceArray[0].index;
yield { slice, index };
}
}
export function getCollator(Intl, localesOrCollator, options) {
if (localesOrCollator && localesOrCollator instanceof Intl.Collator) {
return localesOrCollator;
}
options = getOptions(options);
return new Intl.Collator(localesOrCollator, options);
}
export function getOptions(options) {
if (!options) {
options = {};
}
options.usage = 'search';
return options;
}