locale-index-of
Advanced tools
Comparing version 2.0.0 to 3.0.0
110
index.js
@@ -21,19 +21,11 @@ export default function exported(Intl) { | ||
const collator = getCollator(Intl, localesOrCollator, options); | ||
const resolvedOptions = collator.resolvedOptions(); | ||
if (resolvedOptions.ignorePunctuation === false) { | ||
return indexOf(collator, string, substring); | ||
} | ||
const punctuationCollator = new Intl.Collator(resolvedOptions.locale, {ignorePunctuation: true}); | ||
return noPunctuationIndexOf(collator, string, substring, punctuationCollator); | ||
return indexOf(Intl, collator, string, substring); | ||
} | ||
export function indexOf(collator, string, substring) { | ||
const stringLength = string.length; | ||
const substringLength = substring.length; | ||
export function indexOf(Intl, collator, string, substring) { | ||
const slicesGenerator = makeSlicesGenerator(Intl, collator, string, substring); | ||
for (let index = 0; index <= stringLength - substringLength; index += 1) { | ||
const potentialMatch = string.substring(index, index + substringLength); | ||
if (collator.compare(potentialMatch, substring) === 0) { | ||
for (const { slice, index } of slicesGenerator) { | ||
if (collator.compare(slice, substring) === 0) { | ||
indexOf.lastLength = slice.length; | ||
return index; | ||
@@ -45,62 +37,60 @@ } | ||
export function noPunctuationIndexOf(collator, string, substring, punctuationCollator) { | ||
const stringLength = string.length; | ||
const substringLength = substring.length; | ||
export function* makeSlicesGenerator(Intl, collator, string, substring) { | ||
const { ignorePunctuation, locale } = collator.resolvedOptions(); | ||
// a cache for string characters punctuation values | ||
const characterIsConsidered = new Array(stringLength); | ||
const punctuationCollator = ignorePunctuation ? | ||
new Intl.Collator(locale, { ignorePunctuation: true }) : | ||
null; | ||
function isConsidered(character) { | ||
function isConsidered(grapheme) { | ||
// concatenation with 'a' is a workaround for Node issue | ||
return (punctuationCollator.compare('a', 'a' + character) === 0) ? 0 : 1; | ||
return punctuationCollator.compare('a', `a${grapheme}`) !== 0; | ||
} | ||
function updateConsideredCharacterAt(index) { | ||
characterIsConsidered[index] = isConsidered(string[index]); | ||
function countOfConsideredGraphemes(graphemes) { | ||
const count = punctuationCollator ? | ||
graphemes.filter(({ considered }) => considered).length : | ||
graphemes.length; | ||
return count; | ||
} | ||
function consideredStringCharacters(start, count) { | ||
return sum(characterIsConsidered.slice(start, start + count)); | ||
} | ||
let consideredSubstringCharacters = 0; | ||
let index; | ||
const segmenter = Intl.Segmenter ? | ||
new Intl.Segmenter(locale, { granularity: 'grapheme' }) : | ||
{ | ||
*segment(string) { | ||
const { length } = string; | ||
// have to use that instead of `for segment of string` because we need index of chars, not code points | ||
for (let index = 0; index < length; index += 1) { | ||
const segment = string[index]; | ||
yield { segment, index }; | ||
} | ||
} | ||
}; | ||
// count the punctuation characters in the substring, also prefill the initial portion of the cache | ||
for (index = 0; index < substringLength; index += 1) { | ||
consideredSubstringCharacters += isConsidered(substring[index]); | ||
updateConsideredCharacterAt(index); | ||
} | ||
const substringGraphemes = Array.from(segmenter.segment(substring)); | ||
const substringLength = punctuationCollator ? | ||
substringGraphemes.filter(({ segment }) => isConsidered(segment)).length : | ||
substringGraphemes.length; | ||
for (index = 0; index < stringLength; index += 1) { | ||
let potentialMatchLength = consideredSubstringCharacters; | ||
updateConsideredCharacterAt(index + potentialMatchLength); | ||
const sliceArray = []; | ||
for (const grapheme of segmenter.segment(string)) { | ||
const isAlreadyFull = countOfConsideredGraphemes(sliceArray) === substringLength; | ||
if (isAlreadyFull) { | ||
sliceArray.shift(); | ||
} | ||
// increase the length of the potential match until any of these | ||
// a) it contains the same amount of considered characters as the substring | ||
// b) it reaches the end of the string | ||
while ( | ||
consideredStringCharacters(index, potentialMatchLength) < consideredSubstringCharacters && | ||
index + potentialMatchLength <= stringLength | ||
) { | ||
potentialMatchLength += 1; | ||
updateConsideredCharacterAt(index + potentialMatchLength); | ||
const considered = punctuationCollator ? isConsidered(grapheme.segment) : undefined; | ||
sliceArray.push({ ...grapheme, considered }); | ||
const isNotYetFull = countOfConsideredGraphemes(sliceArray) < substringLength; | ||
if (isNotYetFull) { | ||
continue; | ||
} | ||
// now the potential match contains the same amount of considered characters as the substring | ||
// and they can be compared fairly | ||
const potentialMatch = string.substring(index, index + potentialMatchLength); | ||
if (collator.compare(potentialMatch, substring) === 0) { | ||
noPunctuationIndexOf.lastLength = potentialMatchLength; | ||
return index; | ||
} | ||
const slice = sliceArray.map(({ segment }) => segment).join(''); | ||
const index = sliceArray[0].index; | ||
yield { slice, index }; | ||
} | ||
return -1; | ||
} | ||
export function sum(list) { | ||
return list.reduce(function(accumulator, item) { | ||
return accumulator + item; | ||
}, 0); | ||
} | ||
export function getCollator(Intl, localesOrCollator, options) { | ||
@@ -107,0 +97,0 @@ if (localesOrCollator && localesOrCollator instanceof Intl.Collator) { |
{ | ||
"name": "locale-index-of", | ||
"version": "2.0.0", | ||
"version": "3.0.0", | ||
"description": "A prollyfill for String.prototype.localeIndexOf - a locale-aware Intl-powered version of indexOf.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -37,3 +37,5 @@ # localeIndexOf | ||
npm install locale-index-of | ||
```sh | ||
npm install locale-index-of | ||
``` | ||
@@ -43,25 +45,21 @@ | ||
var localeIndexOf = require('locale-index-of')(Intl); | ||
localeIndexOf('a café', 'cafe', 'de', { sensitivity: 'base' }); // = 2 | ||
```js | ||
import localeIndexOfMaker from 'locale-index-of'; | ||
const localeIndexOf = localeIndexOfMaker(Intl); | ||
localeIndexOf('a café', 'cafe', 'de', { sensitivity: 'base' }); // = 2 | ||
``` | ||
or alternatively | ||
require('locale-index-of').prollyfill(); | ||
'a café'.localeIndexOf('cafe', 'de', { sensitivity: 'base' }); // = 2 | ||
```js | ||
import { prollyfill } from 'locale-index-of'; | ||
prollyfill(); | ||
'a café'.localeIndexOf('cafe', 'de', { sensitivity: 'base' }); // = 2 | ||
``` | ||
### Node.js support | ||
Please note that “official” Node.js builds from nodejs.org | ||
only supported English locate in `Intl` for several major versions | ||
before version 13. Everything works fine with v13+. If you need that | ||
in earlier versions of Node you could try the version in your distribution, | ||
build Node yourself or check the Node documentation for `Intl` | ||
for more options. | ||
## API | ||
### default export `require('locale-index-of')` | ||
### default export `localeIndexOfMaker` | ||
@@ -71,5 +69,8 @@ The default export of the module is a function. Give it the `Intl` object | ||
var localeIndexOf = require('locale-index-of')(Intl); | ||
```js | ||
import localeIndexOfMaker from 'locale-index-of'; | ||
const localeIndexOf = localeIndexOfMaker(Intl); | ||
``` | ||
### `localeIndexOf(string, substring, locales, options)` | ||
@@ -98,3 +99,6 @@ | ||
String.prototype.localeIndexOf = require('locale-index-of').prototypeLocaleIndexOf(Intl); | ||
```js | ||
import { prototypeLocaleIndexOf } from 'locale-index-of'; | ||
String.prototype.localeIndexOf = prototypeLocaleIndexOf(Intl); | ||
``` | ||
@@ -131,3 +135,3 @@ | ||
what you have looked for, this length is exposed on | ||
`require('locale-index-of').noPunctuationIndexOf.lastLength`. | ||
`indexOf.lastLength`. | ||
@@ -134,0 +138,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5
138
9253
91