Complete Email Sortierer implementation with Appwrite and Stripe integration
This commit is contained in:
22
server/node_modules/bidi-js/LICENSE.txt
generated
vendored
Normal file
22
server/node_modules/bidi-js/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2021 Jason Johnston
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
133
server/node_modules/bidi-js/README.md
generated
vendored
Normal file
133
server/node_modules/bidi-js/README.md
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
# bidi-js
|
||||
|
||||
This is a pure JavaScript implementation of the [Unicode Bidirectional Algorithm](https://www.unicode.org/reports/tr9/) version 13.0.0. Its goals, in no particular order, are to be:
|
||||
|
||||
* Correct
|
||||
* Small
|
||||
* Fast
|
||||
|
||||
|
||||
## Conformance
|
||||
|
||||
This implementation currently conforms to section [UAX-C1](https://unicode.org/reports/tr9/#C1) of the bidi spec, as verified by running all the provided [conformance tests](https://unicode.org/reports/tr9/#Bidi_Conformance_Testing).
|
||||
|
||||
## Compatibility
|
||||
|
||||
It has no external dependencies and therefore should run just fine in any relatively capable web browser, Node.js, etc. The provided distribution `.js` files are valid ES5.
|
||||
|
||||
## Usage
|
||||
|
||||
Install it from npm:
|
||||
|
||||
```shell
|
||||
npm install bidi-js
|
||||
```
|
||||
|
||||
[](https://npmjs.org/package/bidi-js)
|
||||
|
||||
Import and initialize:
|
||||
|
||||
```js
|
||||
import bidiFactory from 'bidi-js'
|
||||
// or: const bidiFactory = require('bidi-js')
|
||||
|
||||
const bidi = bidiFactory()
|
||||
```
|
||||
|
||||
The `bidi-js` package's only export is a factory function which you _must invoke_ to return a `bidi` object; that object exposes the methods for bidi processing.
|
||||
|
||||
(_Why a factory function?_ The main reason is to ensure the entire module's code is wrapped within a single self-contained function with no closure dependencies. This enables that function to be stringified and passed into a web worker, for example.)
|
||||
|
||||
Now that you have the `bidi` object, you can:
|
||||
|
||||
### Calculate bidi embedding levels
|
||||
|
||||
```js
|
||||
const embeddingLevels = bidi.getEmbeddingLevels(
|
||||
text, //the input string containing mixed-direction text
|
||||
explicitDirection //"ltr" or "rtl" if you don't want to auto-detect it
|
||||
)
|
||||
|
||||
const { levels, paragraphs } = embeddingLevels
|
||||
```
|
||||
|
||||
The result object `embeddingLevels` will usually be passed to other functions described below. Its contents, should you need to inspect them individually, are:
|
||||
|
||||
* `levels` is a `Uint8Array` holding the calculated [bidi embedding levels](https://unicode.org/reports/tr9/#BD2) for each character in the string. The most important thing to know about these levels is that any given character is in a right-to-left scope if its embedding level is an odd number, and left-to-right if it's an even number.
|
||||
|
||||
* `paragraphs` is an array of `{start, end, level}` objects, one for each paragraph in the text (paragraphs are separated by explicit breaking characters, not soft line wrapping). The `start` and `end` indices are inclusive, and `level` is the resolved base embedding level of that paragraph.
|
||||
|
||||
### Calculate character reorderings
|
||||
|
||||
```js
|
||||
const flips = bidi.getReorderSegments(
|
||||
text, //the full input string
|
||||
embeddingLevels //the full result object from getEmbeddingLevels
|
||||
)
|
||||
|
||||
// Process all reversal sequences, in order:
|
||||
flips.forEach(range => {
|
||||
const [start, end] = range
|
||||
// Reverse this sequence of characters from start to end, inclusive
|
||||
for (let i = start; i <= end; i++) {
|
||||
//...
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
Each "flip" is a range that should be reversed in place; they must all be applied in order.
|
||||
|
||||
Sometimes you don't want to process the whole string at once, but just a particular substring. A common example would be if you've applied line wrapping, in which case you need to process each line individually (in particular this does some special handling for trailing whitespace for each line). For this you can pass the extra `start` and `end` parameters:
|
||||
|
||||
```js
|
||||
yourWrappedLines.forEach(([lineStart, lineEnd]) => {
|
||||
const flips = bidi.getReorderSegments(
|
||||
text,
|
||||
embeddingLevels,
|
||||
lineStart,
|
||||
lineEnd //inclusive
|
||||
)
|
||||
// ...process flips for this line
|
||||
})
|
||||
```
|
||||
|
||||
### Handle right-to-left mirrored characters
|
||||
|
||||
Some characters that resolve to right-to-left need to be swapped with their "mirrored" characters. Examples of this are opening/closing parentheses. You can determine all the characters that need to be mirrored like so:
|
||||
|
||||
```js
|
||||
const mirrored = bidi.getMirroredCharactersMap(
|
||||
text,
|
||||
embeddingLevels
|
||||
)
|
||||
```
|
||||
|
||||
This returns a `Map` of numeric character indices to replacement characters.
|
||||
|
||||
You can also process just a substring with extra `start` and `end` parameters:
|
||||
|
||||
```js
|
||||
const mirrored = bidi.getMirroredCharactersMap(
|
||||
text,
|
||||
embeddingLevels,
|
||||
start,
|
||||
end //inclusive
|
||||
)
|
||||
```
|
||||
|
||||
If you'd rather process mirrored characters individually, you can use the single `getMirroredCharacter` function, just make sure you only do it for right-to-left characters (those whose embedding level is an odd number.) It will return `null` if the character doesn't support mirroring.
|
||||
|
||||
```js
|
||||
const mirroredChar = (embeddingLevels.levels[charIndex] & 1) //odd number means RTL
|
||||
? bidi.getMirroredCharacter(text[charIndex])
|
||||
: null
|
||||
```
|
||||
|
||||
### Get a character's bidi type
|
||||
|
||||
This is used internally, but you can also ask for the ["bidi character type"](https://unicode.org/reports/tr9/#BD1) of any character, should you need it:
|
||||
|
||||
```js
|
||||
const bidiType = bidi.getBidiCharTypeName(string[charIndex])
|
||||
// e.g. "L", "R", "AL", "NSM", ...
|
||||
```
|
||||
1010
server/node_modules/bidi-js/dist/bidi.js
generated
vendored
Normal file
1010
server/node_modules/bidi-js/dist/bidi.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
server/node_modules/bidi-js/dist/bidi.min.js
generated
vendored
Normal file
1
server/node_modules/bidi-js/dist/bidi.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
server/node_modules/bidi-js/dist/bidi.min.mjs
generated
vendored
Normal file
1
server/node_modules/bidi-js/dist/bidi.min.mjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1002
server/node_modules/bidi-js/dist/bidi.mjs
generated
vendored
Normal file
1002
server/node_modules/bidi-js/dist/bidi.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
39
server/node_modules/bidi-js/package.json
generated
vendored
Normal file
39
server/node_modules/bidi-js/package.json
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "bidi-js",
|
||||
"version": "1.0.3",
|
||||
"description": "A JavaScript implementation of the Unicode Bidirectional Algorithm",
|
||||
"main": "dist/bidi.js",
|
||||
"module": "dist/bidi.mjs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/lojjic/bidi-js.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c rollup.config.js",
|
||||
"test": "npx babel-node --plugins @babel/plugin-transform-modules-commonjs test/runTestsOnSrc.js",
|
||||
"test-build": "node test/runTestsOnBuild.js"
|
||||
},
|
||||
"author": "Jason Johnston",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.13.16",
|
||||
"@babel/core": "^7.14.0",
|
||||
"@babel/node": "^7.13.13",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.13.8",
|
||||
"@babel/preset-env": "^7.14.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-buble": "^0.21.3",
|
||||
"node-fetch": "^2.6.1",
|
||||
"rollup": "^2.45.1",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
},
|
||||
"files": [
|
||||
"/dist",
|
||||
"/src",
|
||||
"/LICENSE.txt",
|
||||
"/README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
}
|
||||
30
server/node_modules/bidi-js/src/brackets.js
generated
vendored
Normal file
30
server/node_modules/bidi-js/src/brackets.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import data from './data/bidiBrackets.data.js'
|
||||
import { parseCharacterMap } from './util/parseCharacterMap.js'
|
||||
|
||||
let openToClose, closeToOpen, canonical
|
||||
|
||||
function parse () {
|
||||
if (!openToClose) {
|
||||
//const start = performance.now()
|
||||
let { map, reverseMap } = parseCharacterMap(data.pairs, true)
|
||||
openToClose = map
|
||||
closeToOpen = reverseMap
|
||||
canonical = parseCharacterMap(data.canonical, false).map
|
||||
//console.log(`brackets parsed in ${performance.now() - start}ms`)
|
||||
}
|
||||
}
|
||||
|
||||
export function openingToClosingBracket (char) {
|
||||
parse()
|
||||
return openToClose.get(char) || null
|
||||
}
|
||||
|
||||
export function closingToOpeningBracket (char) {
|
||||
parse()
|
||||
return closeToOpen.get(char) || null
|
||||
}
|
||||
|
||||
export function getCanonicalBracket (char) {
|
||||
parse()
|
||||
return canonical.get(char) || null
|
||||
}
|
||||
66
server/node_modules/bidi-js/src/charTypes.js
generated
vendored
Normal file
66
server/node_modules/bidi-js/src/charTypes.js
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import DATA from './data/bidiCharTypes.data.js'
|
||||
|
||||
const TYPES = {}
|
||||
const TYPES_TO_NAMES = {}
|
||||
TYPES.L = 1 //L is the default
|
||||
TYPES_TO_NAMES[1] = 'L'
|
||||
Object.keys(DATA).forEach((type, i) => {
|
||||
TYPES[type] = 1 << (i + 1)
|
||||
TYPES_TO_NAMES[TYPES[type]] = type
|
||||
})
|
||||
Object.freeze(TYPES)
|
||||
|
||||
const ISOLATE_INIT_TYPES = TYPES.LRI | TYPES.RLI | TYPES.FSI
|
||||
const STRONG_TYPES = TYPES.L | TYPES.R | TYPES.AL
|
||||
const NEUTRAL_ISOLATE_TYPES = TYPES.B | TYPES.S | TYPES.WS | TYPES.ON | TYPES.FSI | TYPES.LRI | TYPES.RLI | TYPES.PDI
|
||||
const BN_LIKE_TYPES = TYPES.BN | TYPES.RLE | TYPES.LRE | TYPES.RLO | TYPES.LRO | TYPES.PDF
|
||||
const TRAILING_TYPES = TYPES.S | TYPES.WS | TYPES.B | ISOLATE_INIT_TYPES | TYPES.PDI | BN_LIKE_TYPES
|
||||
|
||||
let map = null
|
||||
|
||||
function parseData () {
|
||||
if (!map) {
|
||||
//const start = performance.now()
|
||||
map = new Map()
|
||||
for (let type in DATA) {
|
||||
if (DATA.hasOwnProperty(type)) {
|
||||
let lastCode = 0
|
||||
DATA[type].split(',').forEach(range => {
|
||||
let [skip, step] = range.split('+')
|
||||
skip = parseInt(skip, 36)
|
||||
step = step ? parseInt(step, 36) : 0
|
||||
map.set(lastCode += skip, TYPES[type])
|
||||
for (let i = 0; i < step; i++) {
|
||||
map.set(++lastCode, TYPES[type])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
//console.log(`char types parsed in ${performance.now() - start}ms`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} char
|
||||
* @return {number}
|
||||
*/
|
||||
function getBidiCharType (char) {
|
||||
parseData()
|
||||
return map.get(char.codePointAt(0)) || TYPES.L
|
||||
}
|
||||
|
||||
function getBidiCharTypeName(char) {
|
||||
return TYPES_TO_NAMES[getBidiCharType(char)]
|
||||
}
|
||||
|
||||
export {
|
||||
getBidiCharType,
|
||||
getBidiCharTypeName,
|
||||
TYPES,
|
||||
TYPES_TO_NAMES,
|
||||
ISOLATE_INIT_TYPES,
|
||||
STRONG_TYPES,
|
||||
NEUTRAL_ISOLATE_TYPES,
|
||||
BN_LIKE_TYPES,
|
||||
TRAILING_TYPES
|
||||
}
|
||||
5
server/node_modules/bidi-js/src/data/bidiBrackets.data.js
generated
vendored
Normal file
5
server/node_modules/bidi-js/src/data/bidiBrackets.data.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Bidi bracket pairs data, auto generated
|
||||
export default {
|
||||
"pairs": "14>1,1e>2,u>2,2wt>1,1>1,1ge>1,1wp>1,1j>1,f>1,hm>1,1>1,u>1,u6>1,1>1,+5,28>1,w>1,1>1,+3,b8>1,1>1,+3,1>3,-1>-1,3>1,1>1,+2,1s>1,1>1,x>1,th>1,1>1,+2,db>1,1>1,+3,3>1,1>1,+2,14qm>1,1>1,+1,4q>1,1e>2,u>2,2>1,+1",
|
||||
"canonical": "6f1>-6dx,6dy>-6dx,6ec>-6ed,6ee>-6ed,6ww>2jj,-2ji>2jj,14r4>-1e7l,1e7m>-1e7l,1e7m>-1e5c,1e5d>-1e5b,1e5c>-14qx,14qy>-14qx,14vn>-1ecg,1ech>-1ecg,1edu>-1ecg,1eci>-1ecg,1eda>-1ecg,1eci>-1ecg,1eci>-168q,168r>-168q,168s>-14ye,14yf>-14ye"
|
||||
}
|
||||
25
server/node_modules/bidi-js/src/data/bidiCharTypes.data.js
generated
vendored
Normal file
25
server/node_modules/bidi-js/src/data/bidiCharTypes.data.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Bidi character types data, auto generated
|
||||
export default {
|
||||
"R": "13k,1a,2,3,3,2+1j,ch+16,a+1,5+2,2+n,5,a,4,6+16,4+3,h+1b,4mo,179q,2+9,2+11,2i9+7y,2+68,4,3+4,5+13,4+3,2+4k,3+29,8+cf,1t+7z,w+17,3+3m,1t+3z,16o1+5r,8+30,8+mc,29+1r,29+4v,75+73",
|
||||
"EN": "1c+9,3d+1,6,187+9,513,4+5,7+9,sf+j,175h+9,qw+q,161f+1d,4xt+a,25i+9",
|
||||
"ES": "17,2,6dp+1,f+1,av,16vr,mx+1,4o,2",
|
||||
"ET": "z+2,3h+3,b+1,ym,3e+1,2o,p4+1,8,6u,7c,g6,1wc,1n9+4,30+1b,2n,6d,qhx+1,h0m,a+1,49+2,63+1,4+1,6bb+3,12jj",
|
||||
"AN": "16o+5,2j+9,2+1,35,ed,1ff2+9,87+u",
|
||||
"CS": "18,2+1,b,2u,12k,55v,l,17v0,2,3,53,2+1,b",
|
||||
"B": "a,3,f+2,2v,690",
|
||||
"S": "9,2,k",
|
||||
"WS": "c,k,4f4,1vk+a,u,1j,335",
|
||||
"ON": "x+1,4+4,h+5,r+5,r+3,z,5+3,2+1,2+1,5,2+2,3+4,o,w,ci+1,8+d,3+d,6+8,2+g,39+1,9,6+1,2,33,b8,3+1,3c+1,7+1,5r,b,7h+3,sa+5,2,3i+6,jg+3,ur+9,2v,ij+1,9g+9,7+a,8m,4+1,49+x,14u,2+2,c+2,e+2,e+2,e+1,i+n,e+e,2+p,u+2,e+2,36+1,2+3,2+1,b,2+2,6+5,2,2,2,h+1,5+4,6+3,3+f,16+2,5+3l,3+81,1y+p,2+40,q+a,m+13,2r+ch,2+9e,75+hf,3+v,2+2w,6e+5,f+6,75+2a,1a+p,2+2g,d+5x,r+b,6+3,4+o,g,6+1,6+2,2k+1,4,2j,5h+z,1m+1,1e+f,t+2,1f+e,d+3,4o+3,2s+1,w,535+1r,h3l+1i,93+2,2s,b+1,3l+x,2v,4g+3,21+3,kz+1,g5v+1,5a,j+9,n+v,2,3,2+8,2+1,3+2,2,3,46+1,4+4,h+5,r+5,r+a,3h+2,4+6,b+4,78,1r+24,4+c,4,1hb,ey+6,103+j,16j+c,1ux+7,5+g,fsh,jdq+1t,4,57+2e,p1,1m,1m,1m,1m,4kt+1,7j+17,5+2r,d+e,3+e,2+e,2+10,m+4,w,1n+5,1q,4z+5,4b+rb,9+c,4+c,4+37,d+2g,8+b,l+b,5+1j,9+9,7+13,9+t,3+1,27+3c,2+29,2+3q,d+d,3+4,4+2,6+6,a+o,8+6,a+2,e+6,16+42,2+1i",
|
||||
"BN": "0+8,6+d,2s+5,2+p,e,4m9,1kt+2,2b+5,5+5,17q9+v,7k,6p+8,6+1,119d+3,440+7,96s+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+1,1ekf+75,6p+2rz,1ben+1,1ekf+1,1ekf+1",
|
||||
"NSM": "lc+33,7o+6,7c+18,2,2+1,2+1,2,21+a,1d+k,h,2u+6,3+5,3+1,2+3,10,v+q,2k+a,1n+8,a,p+3,2+8,2+2,2+4,18+2,3c+e,2+v,1k,2,5+7,5,4+6,b+1,u,1n,5+3,9,l+1,r,3+1,1m,5+1,5+1,3+2,4,v+1,4,c+1,1m,5+4,2+1,5,l+1,n+5,2,1n,3,2+3,9,8+1,c+1,v,1q,d,1f,4,1m+2,6+2,2+3,8+1,c+1,u,1n,g+1,l+1,t+1,1m+1,5+3,9,l+1,u,21,8+2,2,2j,3+6,d+7,2r,3+8,c+5,23+1,s,2,2,1k+d,2+4,2+1,6+a,2+z,a,2v+3,2+5,2+1,3+1,q+1,5+2,h+3,e,3+1,7,g,jk+2,qb+2,u+2,u+1,v+1,1t+1,2+6,9,3+a,a,1a+2,3c+1,z,3b+2,5+1,a,7+2,64+1,3,1n,2+6,2,2,3+7,7+9,3,1d+g,1s+3,1d,2+4,2,6,15+8,d+1,x+3,3+1,2+2,1l,2+1,4,2+2,1n+7,3+1,49+2,2+c,2+6,5,7,4+1,5j+1l,2+4,k1+w,2db+2,3y,2p+v,ff+3,30+1,n9x+3,2+9,x+1,29+1,7l,4,5,q+1,6,48+1,r+h,e,13+7,q+a,1b+2,1d,3+3,3+1,14,1w+5,3+1,3+1,d,9,1c,1g,2+2,3+1,6+1,2,17+1,9,6n,3,5,fn5,ki+f,h+f,r2,6b,46+4,1af+2,2+1,6+3,15+2,5,4m+1,fy+3,as+1,4a+a,4x,1j+e,1l+2,1e+3,3+1,1y+2,11+4,2+7,1r,d+1,1h+8,b+3,3,2o+2,3,2+1,7,4h,4+7,m+1,1m+1,4,12+6,4+4,5g+7,3+2,2,o,2d+5,2,5+1,2+1,6n+3,7+1,2+1,s+1,2e+7,3,2+1,2z,2,3+5,2,2u+2,3+3,2+4,78+8,2+1,75+1,2,5,41+3,3+1,5,x+5,3+1,15+5,3+3,9,a+5,3+2,1b+c,2+1,bb+6,2+5,2d+l,3+6,2+1,2+1,3f+5,4,2+1,2+6,2,21+1,4,2,9o+1,f0c+4,1o+6,t5,1s+3,2a,f5l+1,43t+2,i+7,3+6,v+3,45+2,1j0+1i,5+1d,9,f,n+4,2+e,11t+6,2+g,3+6,2+1,2+4,7a+6,c6+3,15t+6,32+6,gzhy+6n",
|
||||
"AL": "16w,3,2,e+1b,z+2,2+2s,g+1,8+1,b+m,2+t,s+2i,c+e,4h+f,1d+1e,1bwe+dp,3+3z,x+c,2+1,35+3y,2rm+z,5+7,b+5,dt+l,c+u,17nl+27,1t+27,4x+6n,3+d",
|
||||
"LRO": "6ct",
|
||||
"RLO": "6cu",
|
||||
"LRE": "6cq",
|
||||
"RLE": "6cr",
|
||||
"PDF": "6cs",
|
||||
"LRI": "6ee",
|
||||
"RLI": "6ef",
|
||||
"FSI": "6eg",
|
||||
"PDI": "6eh"
|
||||
}
|
||||
2
server/node_modules/bidi-js/src/data/bidiMirroring.data.js
generated
vendored
Normal file
2
server/node_modules/bidi-js/src/data/bidiMirroring.data.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Bidi mirrored chars data, auto generated
|
||||
export default "14>1,j>2,t>2,u>2,1a>g,2v3>1,1>1,1ge>1,1wd>1,b>1,1j>1,f>1,ai>3,-2>3,+1,8>1k0,-1jq>1y7,-1y6>1hf,-1he>1h6,-1h5>1ha,-1h8>1qi,-1pu>1,6>3u,-3s>7,6>1,1>1,f>1,1>1,+2,3>1,1>1,+13,4>1,1>1,6>1eo,-1ee>1,3>1mg,-1me>1mk,-1mj>1mi,-1mg>1mi,-1md>1,1>1,+2,1>10k,-103>1,1>1,4>1,5>1,1>1,+10,3>1,1>8,-7>8,+1,-6>7,+1,a>1,1>1,u>1,u6>1,1>1,+5,26>1,1>1,2>1,2>2,8>1,7>1,4>1,1>1,+5,b8>1,1>1,+3,1>3,-2>1,2>1,1>1,+2,c>1,3>1,1>1,+2,h>1,3>1,a>1,1>1,2>1,3>1,1>1,d>1,f>1,3>1,1a>1,1>1,6>1,7>1,13>1,k>1,1>1,+19,4>1,1>1,+2,2>1,1>1,+18,m>1,a>1,1>1,lk>1,1>1,4>1,2>1,f>1,3>1,1>1,+3,db>1,1>1,+3,3>1,1>1,+2,14qm>1,1>1,+1,6>1,4j>1,j>2,t>2,u>2,2>1,+1"
|
||||
690
server/node_modules/bidi-js/src/embeddingLevels.js
generated
vendored
Normal file
690
server/node_modules/bidi-js/src/embeddingLevels.js
generated
vendored
Normal file
@@ -0,0 +1,690 @@
|
||||
import {
|
||||
BN_LIKE_TYPES,
|
||||
getBidiCharType,
|
||||
ISOLATE_INIT_TYPES,
|
||||
NEUTRAL_ISOLATE_TYPES,
|
||||
STRONG_TYPES,
|
||||
TRAILING_TYPES,
|
||||
TYPES
|
||||
} from './charTypes.js'
|
||||
import { closingToOpeningBracket, getCanonicalBracket, openingToClosingBracket } from './brackets.js'
|
||||
|
||||
// Local type aliases
|
||||
const {
|
||||
L: TYPE_L,
|
||||
R: TYPE_R,
|
||||
EN: TYPE_EN,
|
||||
ES: TYPE_ES,
|
||||
ET: TYPE_ET,
|
||||
AN: TYPE_AN,
|
||||
CS: TYPE_CS,
|
||||
B: TYPE_B,
|
||||
S: TYPE_S,
|
||||
ON: TYPE_ON,
|
||||
BN: TYPE_BN,
|
||||
NSM: TYPE_NSM,
|
||||
AL: TYPE_AL,
|
||||
LRO: TYPE_LRO,
|
||||
RLO: TYPE_RLO,
|
||||
LRE: TYPE_LRE,
|
||||
RLE: TYPE_RLE,
|
||||
PDF: TYPE_PDF,
|
||||
LRI: TYPE_LRI,
|
||||
RLI: TYPE_RLI,
|
||||
FSI: TYPE_FSI,
|
||||
PDI: TYPE_PDI
|
||||
} = TYPES
|
||||
|
||||
/**
|
||||
* @typedef {object} GetEmbeddingLevelsResult
|
||||
* @property {{start, end, level}[]} paragraphs
|
||||
* @property {Uint8Array} levels
|
||||
*/
|
||||
|
||||
/**
|
||||
* This function applies the Bidirectional Algorithm to a string, returning the resolved embedding levels
|
||||
* in a single Uint8Array plus a list of objects holding each paragraph's start and end indices and resolved
|
||||
* base embedding level.
|
||||
*
|
||||
* @param {string} string - The input string
|
||||
* @param {"ltr"|"rtl"|"auto"} [baseDirection] - Use "ltr" or "rtl" to force a base paragraph direction,
|
||||
* otherwise a direction will be chosen automatically from each paragraph's contents.
|
||||
* @return {GetEmbeddingLevelsResult}
|
||||
*/
|
||||
export function getEmbeddingLevels (string, baseDirection) {
|
||||
const MAX_DEPTH = 125
|
||||
|
||||
// Start by mapping all characters to their unicode type, as a bitmask integer
|
||||
const charTypes = new Uint32Array(string.length)
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
charTypes[i] = getBidiCharType(string[i])
|
||||
}
|
||||
|
||||
const charTypeCounts = new Map() //will be cleared at start of each paragraph
|
||||
function changeCharType(i, type) {
|
||||
const oldType = charTypes[i]
|
||||
charTypes[i] = type
|
||||
charTypeCounts.set(oldType, charTypeCounts.get(oldType) - 1)
|
||||
if (oldType & NEUTRAL_ISOLATE_TYPES) {
|
||||
charTypeCounts.set(NEUTRAL_ISOLATE_TYPES, charTypeCounts.get(NEUTRAL_ISOLATE_TYPES) - 1)
|
||||
}
|
||||
charTypeCounts.set(type, (charTypeCounts.get(type) || 0) + 1)
|
||||
if (type & NEUTRAL_ISOLATE_TYPES) {
|
||||
charTypeCounts.set(NEUTRAL_ISOLATE_TYPES, (charTypeCounts.get(NEUTRAL_ISOLATE_TYPES) || 0) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const embedLevels = new Uint8Array(string.length)
|
||||
const isolationPairs = new Map() //init->pdi and pdi->init
|
||||
|
||||
// === 3.3.1 The Paragraph Level ===
|
||||
// 3.3.1 P1: Split the text into paragraphs
|
||||
const paragraphs = [] // [{start, end, level}, ...]
|
||||
let paragraph = null
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
if (!paragraph) {
|
||||
paragraphs.push(paragraph = {
|
||||
start: i,
|
||||
end: string.length - 1,
|
||||
// 3.3.1 P2-P3: Determine the paragraph level
|
||||
level: baseDirection === 'rtl' ? 1 : baseDirection === 'ltr' ? 0 : determineAutoEmbedLevel(i, false)
|
||||
})
|
||||
}
|
||||
if (charTypes[i] & TYPE_B) {
|
||||
paragraph.end = i
|
||||
paragraph = null
|
||||
}
|
||||
}
|
||||
|
||||
const FORMATTING_TYPES = TYPE_RLE | TYPE_LRE | TYPE_RLO | TYPE_LRO | ISOLATE_INIT_TYPES | TYPE_PDI | TYPE_PDF | TYPE_B
|
||||
const nextEven = n => n + ((n & 1) ? 1 : 2)
|
||||
const nextOdd = n => n + ((n & 1) ? 2 : 1)
|
||||
|
||||
// Everything from here on will operate per paragraph.
|
||||
for (let paraIdx = 0; paraIdx < paragraphs.length; paraIdx++) {
|
||||
paragraph = paragraphs[paraIdx]
|
||||
const statusStack = [{
|
||||
_level: paragraph.level,
|
||||
_override: 0, //0=neutral, 1=L, 2=R
|
||||
_isolate: 0 //bool
|
||||
}]
|
||||
let stackTop
|
||||
let overflowIsolateCount = 0
|
||||
let overflowEmbeddingCount = 0
|
||||
let validIsolateCount = 0
|
||||
charTypeCounts.clear()
|
||||
|
||||
// === 3.3.2 Explicit Levels and Directions ===
|
||||
for (let i = paragraph.start; i <= paragraph.end; i++) {
|
||||
let charType = charTypes[i]
|
||||
stackTop = statusStack[statusStack.length - 1]
|
||||
|
||||
// Set initial counts
|
||||
charTypeCounts.set(charType, (charTypeCounts.get(charType) || 0) + 1)
|
||||
if (charType & NEUTRAL_ISOLATE_TYPES) {
|
||||
charTypeCounts.set(NEUTRAL_ISOLATE_TYPES, (charTypeCounts.get(NEUTRAL_ISOLATE_TYPES) || 0) + 1)
|
||||
}
|
||||
|
||||
// Explicit Embeddings: 3.3.2 X2 - X3
|
||||
if (charType & FORMATTING_TYPES) { //prefilter all formatters
|
||||
if (charType & (TYPE_RLE | TYPE_LRE)) {
|
||||
embedLevels[i] = stackTop._level // 5.2
|
||||
const level = (charType === TYPE_RLE ? nextOdd : nextEven)(stackTop._level)
|
||||
if (level <= MAX_DEPTH && !overflowIsolateCount && !overflowEmbeddingCount) {
|
||||
statusStack.push({
|
||||
_level: level,
|
||||
_override: 0,
|
||||
_isolate: 0
|
||||
})
|
||||
} else if (!overflowIsolateCount) {
|
||||
overflowEmbeddingCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit Overrides: 3.3.2 X4 - X5
|
||||
else if (charType & (TYPE_RLO | TYPE_LRO)) {
|
||||
embedLevels[i] = stackTop._level // 5.2
|
||||
const level = (charType === TYPE_RLO ? nextOdd : nextEven)(stackTop._level)
|
||||
if (level <= MAX_DEPTH && !overflowIsolateCount && !overflowEmbeddingCount) {
|
||||
statusStack.push({
|
||||
_level: level,
|
||||
_override: (charType & TYPE_RLO) ? TYPE_R : TYPE_L,
|
||||
_isolate: 0
|
||||
})
|
||||
} else if (!overflowIsolateCount) {
|
||||
overflowEmbeddingCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Isolates: 3.3.2 X5a - X5c
|
||||
else if (charType & ISOLATE_INIT_TYPES) {
|
||||
// X5c - FSI becomes either RLI or LRI
|
||||
if (charType & TYPE_FSI) {
|
||||
charType = determineAutoEmbedLevel(i + 1, true) === 1 ? TYPE_RLI : TYPE_LRI
|
||||
}
|
||||
|
||||
embedLevels[i] = stackTop._level
|
||||
if (stackTop._override) {
|
||||
changeCharType(i, stackTop._override)
|
||||
}
|
||||
const level = (charType === TYPE_RLI ? nextOdd : nextEven)(stackTop._level)
|
||||
if (level <= MAX_DEPTH && overflowIsolateCount === 0 && overflowEmbeddingCount === 0) {
|
||||
validIsolateCount++
|
||||
statusStack.push({
|
||||
_level: level,
|
||||
_override: 0,
|
||||
_isolate: 1,
|
||||
_isolInitIndex: i
|
||||
})
|
||||
} else {
|
||||
overflowIsolateCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Terminating Isolates: 3.3.2 X6a
|
||||
else if (charType & TYPE_PDI) {
|
||||
if (overflowIsolateCount > 0) {
|
||||
overflowIsolateCount--
|
||||
} else if (validIsolateCount > 0) {
|
||||
overflowEmbeddingCount = 0
|
||||
while (!statusStack[statusStack.length - 1]._isolate) {
|
||||
statusStack.pop()
|
||||
}
|
||||
// Add to isolation pairs bidirectional mapping:
|
||||
const isolInitIndex = statusStack[statusStack.length - 1]._isolInitIndex
|
||||
if (isolInitIndex != null) {
|
||||
isolationPairs.set(isolInitIndex, i)
|
||||
isolationPairs.set(i, isolInitIndex)
|
||||
}
|
||||
statusStack.pop()
|
||||
validIsolateCount--
|
||||
}
|
||||
stackTop = statusStack[statusStack.length - 1]
|
||||
embedLevels[i] = stackTop._level
|
||||
if (stackTop._override) {
|
||||
changeCharType(i, stackTop._override)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Terminating Embeddings and Overrides: 3.3.2 X7
|
||||
else if (charType & TYPE_PDF) {
|
||||
if (overflowIsolateCount === 0) {
|
||||
if (overflowEmbeddingCount > 0) {
|
||||
overflowEmbeddingCount--
|
||||
} else if (!stackTop._isolate && statusStack.length > 1) {
|
||||
statusStack.pop()
|
||||
stackTop = statusStack[statusStack.length - 1]
|
||||
}
|
||||
}
|
||||
embedLevels[i] = stackTop._level // 5.2
|
||||
}
|
||||
|
||||
// End of Paragraph: 3.3.2 X8
|
||||
else if (charType & TYPE_B) {
|
||||
embedLevels[i] = paragraph.level
|
||||
}
|
||||
}
|
||||
|
||||
// Non-formatting characters: 3.3.2 X6
|
||||
else {
|
||||
embedLevels[i] = stackTop._level
|
||||
// NOTE: This exclusion of BN seems to go against what section 5.2 says, but is required for test passage
|
||||
if (stackTop._override && charType !== TYPE_BN) {
|
||||
changeCharType(i, stackTop._override)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 3.3.3 Preparations for Implicit Processing ===
|
||||
|
||||
// Remove all RLE, LRE, RLO, LRO, PDF, and BN characters: 3.3.3 X9
|
||||
// Note: Due to section 5.2, we won't remove them, but we'll use the BN_LIKE_TYPES bitset to
|
||||
// easily ignore them all from here on out.
|
||||
|
||||
// 3.3.3 X10
|
||||
// Compute the set of isolating run sequences as specified by BD13
|
||||
const levelRuns = []
|
||||
let currentRun = null
|
||||
let isolationLevel = 0
|
||||
for (let i = paragraph.start; i <= paragraph.end; i++) {
|
||||
const charType = charTypes[i]
|
||||
if (!(charType & BN_LIKE_TYPES)) {
|
||||
const lvl = embedLevels[i]
|
||||
const isIsolInit = charType & ISOLATE_INIT_TYPES
|
||||
const isPDI = charType === TYPE_PDI
|
||||
if (isIsolInit) {
|
||||
isolationLevel++
|
||||
}
|
||||
if (currentRun && lvl === currentRun._level) {
|
||||
currentRun._end = i
|
||||
currentRun._endsWithIsolInit = isIsolInit
|
||||
} else {
|
||||
levelRuns.push(currentRun = {
|
||||
_start: i,
|
||||
_end: i,
|
||||
_level: lvl,
|
||||
_startsWithPDI: isPDI,
|
||||
_endsWithIsolInit: isIsolInit
|
||||
})
|
||||
}
|
||||
if (isPDI) {
|
||||
isolationLevel--
|
||||
}
|
||||
}
|
||||
}
|
||||
const isolatingRunSeqs = [] // [{seqIndices: [], sosType: L|R, eosType: L|R}]
|
||||
for (let runIdx = 0; runIdx < levelRuns.length; runIdx++) {
|
||||
const run = levelRuns[runIdx]
|
||||
if (!run._startsWithPDI || (run._startsWithPDI && !isolationPairs.has(run._start))) {
|
||||
const seqRuns = [currentRun = run]
|
||||
for (let pdiIndex; currentRun && currentRun._endsWithIsolInit && (pdiIndex = isolationPairs.get(currentRun._end)) != null;) {
|
||||
for (let i = runIdx + 1; i < levelRuns.length; i++) {
|
||||
if (levelRuns[i]._start === pdiIndex) {
|
||||
seqRuns.push(currentRun = levelRuns[i])
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// build flat list of indices across all runs:
|
||||
const seqIndices = []
|
||||
for (let i = 0; i < seqRuns.length; i++) {
|
||||
const run = seqRuns[i]
|
||||
for (let j = run._start; j <= run._end; j++) {
|
||||
seqIndices.push(j)
|
||||
}
|
||||
}
|
||||
// determine the sos/eos types:
|
||||
let firstLevel = embedLevels[seqIndices[0]]
|
||||
let prevLevel = paragraph.level
|
||||
for (let i = seqIndices[0] - 1; i >= 0; i--) {
|
||||
if (!(charTypes[i] & BN_LIKE_TYPES)) { //5.2
|
||||
prevLevel = embedLevels[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
const lastIndex = seqIndices[seqIndices.length - 1]
|
||||
let lastLevel = embedLevels[lastIndex]
|
||||
let nextLevel = paragraph.level
|
||||
if (!(charTypes[lastIndex] & ISOLATE_INIT_TYPES)) {
|
||||
for (let i = lastIndex + 1; i <= paragraph.end; i++) {
|
||||
if (!(charTypes[i] & BN_LIKE_TYPES)) { //5.2
|
||||
nextLevel = embedLevels[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
isolatingRunSeqs.push({
|
||||
_seqIndices: seqIndices,
|
||||
_sosType: Math.max(prevLevel, firstLevel) % 2 ? TYPE_R : TYPE_L,
|
||||
_eosType: Math.max(nextLevel, lastLevel) % 2 ? TYPE_R : TYPE_L
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// The next steps are done per isolating run sequence
|
||||
for (let seqIdx = 0; seqIdx < isolatingRunSeqs.length; seqIdx++) {
|
||||
const { _seqIndices: seqIndices, _sosType: sosType, _eosType: eosType } = isolatingRunSeqs[seqIdx]
|
||||
/**
|
||||
* All the level runs in an isolating run sequence have the same embedding level.
|
||||
*
|
||||
* DO NOT change any `embedLevels[i]` within the current scope.
|
||||
*/
|
||||
const embedDirection = ((embedLevels[seqIndices[0]]) & 1) ? TYPE_R : TYPE_L;
|
||||
|
||||
// === 3.3.4 Resolving Weak Types ===
|
||||
|
||||
// W1 + 5.2. Search backward from each NSM to the first character in the isolating run sequence whose
|
||||
// bidirectional type is not BN, and set the NSM to ON if it is an isolate initiator or PDI, and to its
|
||||
// type otherwise. If the NSM is the first non-BN character, change the NSM to the type of sos.
|
||||
if (charTypeCounts.get(TYPE_NSM)) {
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & TYPE_NSM) {
|
||||
let prevType = sosType
|
||||
for (let sj = si - 1; sj >= 0; sj--) {
|
||||
if (!(charTypes[seqIndices[sj]] & BN_LIKE_TYPES)) { //5.2 scan back to first non-BN
|
||||
prevType = charTypes[seqIndices[sj]]
|
||||
break
|
||||
}
|
||||
}
|
||||
changeCharType(i, (prevType & (ISOLATE_INIT_TYPES | TYPE_PDI)) ? TYPE_ON : prevType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sos)
|
||||
// is found. If an AL is found, change the type of the European number to Arabic number.
|
||||
if (charTypeCounts.get(TYPE_EN)) {
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & TYPE_EN) {
|
||||
for (let sj = si - 1; sj >= -1; sj--) {
|
||||
const prevCharType = sj === -1 ? sosType : charTypes[seqIndices[sj]]
|
||||
if (prevCharType & STRONG_TYPES) {
|
||||
if (prevCharType === TYPE_AL) {
|
||||
changeCharType(i, TYPE_AN)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W3. Change all ALs to R
|
||||
if (charTypeCounts.get(TYPE_AL)) {
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & TYPE_AL) {
|
||||
changeCharType(i, TYPE_R)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W4. A single European separator between two European numbers changes to a European number. A single common
|
||||
// separator between two numbers of the same type changes to that type.
|
||||
if (charTypeCounts.get(TYPE_ES) || charTypeCounts.get(TYPE_CS)) {
|
||||
for (let si = 1; si < seqIndices.length - 1; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & (TYPE_ES | TYPE_CS)) {
|
||||
let prevType = 0, nextType = 0
|
||||
for (let sj = si - 1; sj >= 0; sj--) {
|
||||
prevType = charTypes[seqIndices[sj]]
|
||||
if (!(prevType & BN_LIKE_TYPES)) { //5.2
|
||||
break
|
||||
}
|
||||
}
|
||||
for (let sj = si + 1; sj < seqIndices.length; sj++) {
|
||||
nextType = charTypes[seqIndices[sj]]
|
||||
if (!(nextType & BN_LIKE_TYPES)) { //5.2
|
||||
break
|
||||
}
|
||||
}
|
||||
if (prevType === nextType && (charTypes[i] === TYPE_ES ? prevType === TYPE_EN : (prevType & (TYPE_EN | TYPE_AN)))) {
|
||||
changeCharType(i, prevType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
|
||||
if (charTypeCounts.get(TYPE_EN)) {
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & TYPE_EN) {
|
||||
for (let sj = si - 1; sj >= 0 && (charTypes[seqIndices[sj]] & (TYPE_ET | BN_LIKE_TYPES)); sj--) {
|
||||
changeCharType(seqIndices[sj], TYPE_EN)
|
||||
}
|
||||
for (si++; si < seqIndices.length && (charTypes[seqIndices[si]] & (TYPE_ET | BN_LIKE_TYPES | TYPE_EN)); si++) {
|
||||
if (charTypes[seqIndices[si]] !== TYPE_EN) {
|
||||
changeCharType(seqIndices[si], TYPE_EN)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W6. Otherwise, separators and terminators change to Other Neutral.
|
||||
if (charTypeCounts.get(TYPE_ET) || charTypeCounts.get(TYPE_ES) || charTypeCounts.get(TYPE_CS)) {
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & (TYPE_ET | TYPE_ES | TYPE_CS)) {
|
||||
changeCharType(i, TYPE_ON)
|
||||
// 5.2 transform adjacent BNs too:
|
||||
for (let sj = si - 1; sj >= 0 && (charTypes[seqIndices[sj]] & BN_LIKE_TYPES); sj--) {
|
||||
changeCharType(seqIndices[sj], TYPE_ON)
|
||||
}
|
||||
for (let sj = si + 1; sj < seqIndices.length && (charTypes[seqIndices[sj]] & BN_LIKE_TYPES); sj++) {
|
||||
changeCharType(seqIndices[sj], TYPE_ON)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// W7. Search backward from each instance of a European number until the first strong type (R, L, or sos)
|
||||
// is found. If an L is found, then change the type of the European number to L.
|
||||
// NOTE: implemented in single forward pass for efficiency
|
||||
if (charTypeCounts.get(TYPE_EN)) {
|
||||
for (let si = 0, prevStrongType = sosType; si < seqIndices.length; si++) {
|
||||
const i = seqIndices[si]
|
||||
const type = charTypes[i]
|
||||
if (type & TYPE_EN) {
|
||||
if (prevStrongType === TYPE_L) {
|
||||
changeCharType(i, TYPE_L)
|
||||
}
|
||||
} else if (type & STRONG_TYPES) {
|
||||
prevStrongType = type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 3.3.5 Resolving Neutral and Isolate Formatting Types ===
|
||||
|
||||
if (charTypeCounts.get(NEUTRAL_ISOLATE_TYPES)) {
|
||||
// N0. Process bracket pairs in an isolating run sequence sequentially in the logical order of the text
|
||||
// positions of the opening paired brackets using the logic given below. Within this scope, bidirectional
|
||||
// types EN and AN are treated as R.
|
||||
const R_TYPES_FOR_N_STEPS = (TYPE_R | TYPE_EN | TYPE_AN)
|
||||
const STRONG_TYPES_FOR_N_STEPS = R_TYPES_FOR_N_STEPS | TYPE_L
|
||||
|
||||
// * Identify the bracket pairs in the current isolating run sequence according to BD16.
|
||||
const bracketPairs = []
|
||||
{
|
||||
const openerStack = []
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
// NOTE: for any potential bracket character we also test that it still carries a NI
|
||||
// type, as that may have been changed earlier. This doesn't seem to be explicitly
|
||||
// called out in the spec, but is required for passage of certain tests.
|
||||
if (charTypes[seqIndices[si]] & NEUTRAL_ISOLATE_TYPES) {
|
||||
const char = string[seqIndices[si]]
|
||||
let oppositeBracket
|
||||
// Opening bracket
|
||||
if (openingToClosingBracket(char) !== null) {
|
||||
if (openerStack.length < 63) {
|
||||
openerStack.push({ char, seqIndex: si })
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Closing bracket
|
||||
else if ((oppositeBracket = closingToOpeningBracket(char)) !== null) {
|
||||
for (let stackIdx = openerStack.length - 1; stackIdx >= 0; stackIdx--) {
|
||||
const stackChar = openerStack[stackIdx].char
|
||||
if (stackChar === oppositeBracket ||
|
||||
stackChar === closingToOpeningBracket(getCanonicalBracket(char)) ||
|
||||
openingToClosingBracket(getCanonicalBracket(stackChar)) === char
|
||||
) {
|
||||
bracketPairs.push([openerStack[stackIdx].seqIndex, si])
|
||||
openerStack.length = stackIdx //pop the matching bracket and all following
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bracketPairs.sort((a, b) => a[0] - b[0])
|
||||
}
|
||||
// * For each bracket-pair element in the list of pairs of text positions
|
||||
for (let pairIdx = 0; pairIdx < bracketPairs.length; pairIdx++) {
|
||||
const [openSeqIdx, closeSeqIdx] = bracketPairs[pairIdx]
|
||||
// a. Inspect the bidirectional types of the characters enclosed within the bracket pair.
|
||||
// b. If any strong type (either L or R) matching the embedding direction is found, set the type for both
|
||||
// brackets in the pair to match the embedding direction.
|
||||
let foundStrongType = false
|
||||
let useStrongType = 0
|
||||
for (let si = openSeqIdx + 1; si < closeSeqIdx; si++) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & STRONG_TYPES_FOR_N_STEPS) {
|
||||
foundStrongType = true
|
||||
const lr = (charTypes[i] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L
|
||||
if (lr === embedDirection) {
|
||||
useStrongType = lr
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// c. Otherwise, if there is a strong type it must be opposite the embedding direction. Therefore, test
|
||||
// for an established context with a preceding strong type by checking backwards before the opening paired
|
||||
// bracket until the first strong type (L, R, or sos) is found.
|
||||
// 1. If the preceding strong type is also opposite the embedding direction, context is established, so
|
||||
// set the type for both brackets in the pair to that direction.
|
||||
// 2. Otherwise set the type for both brackets in the pair to the embedding direction.
|
||||
if (foundStrongType && !useStrongType) {
|
||||
useStrongType = sosType
|
||||
for (let si = openSeqIdx - 1; si >= 0; si--) {
|
||||
const i = seqIndices[si]
|
||||
if (charTypes[i] & STRONG_TYPES_FOR_N_STEPS) {
|
||||
const lr = (charTypes[i] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L
|
||||
if (lr !== embedDirection) {
|
||||
useStrongType = lr
|
||||
} else {
|
||||
useStrongType = embedDirection
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useStrongType) {
|
||||
charTypes[seqIndices[openSeqIdx]] = charTypes[seqIndices[closeSeqIdx]] = useStrongType
|
||||
// * Any number of characters that had original bidirectional character type NSM prior to the application
|
||||
// of W1 that immediately follow a paired bracket which changed to L or R under N0 should change to match
|
||||
// the type of their preceding bracket.
|
||||
if (useStrongType !== embedDirection) {
|
||||
for (let si = openSeqIdx + 1; si < seqIndices.length; si++) {
|
||||
if (!(charTypes[seqIndices[si]] & BN_LIKE_TYPES)) {
|
||||
if (getBidiCharType(string[seqIndices[si]]) & TYPE_NSM) {
|
||||
charTypes[seqIndices[si]] = useStrongType
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useStrongType !== embedDirection) {
|
||||
for (let si = closeSeqIdx + 1; si < seqIndices.length; si++) {
|
||||
if (!(charTypes[seqIndices[si]] & BN_LIKE_TYPES)) {
|
||||
if (getBidiCharType(string[seqIndices[si]]) & TYPE_NSM) {
|
||||
charTypes[seqIndices[si]] = useStrongType
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// N1. A sequence of NIs takes the direction of the surrounding strong text if the text on both sides has the
|
||||
// same direction.
|
||||
// N2. Any remaining NIs take the embedding direction.
|
||||
for (let si = 0; si < seqIndices.length; si++) {
|
||||
if (charTypes[seqIndices[si]] & NEUTRAL_ISOLATE_TYPES) {
|
||||
let niRunStart = si, niRunEnd = si
|
||||
let prevType = sosType //si === 0 ? sosType : (charTypes[seqIndices[si - 1]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L
|
||||
for (let si2 = si - 1; si2 >= 0; si2--) {
|
||||
if (charTypes[seqIndices[si2]] & BN_LIKE_TYPES) {
|
||||
niRunStart = si2 //5.2 treat BNs adjacent to NIs as NIs
|
||||
} else {
|
||||
prevType = (charTypes[seqIndices[si2]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L
|
||||
break
|
||||
}
|
||||
}
|
||||
let nextType = eosType
|
||||
for (let si2 = si + 1; si2 < seqIndices.length; si2++) {
|
||||
if (charTypes[seqIndices[si2]] & (NEUTRAL_ISOLATE_TYPES | BN_LIKE_TYPES)) {
|
||||
niRunEnd = si2
|
||||
} else {
|
||||
nextType = (charTypes[seqIndices[si2]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L
|
||||
break
|
||||
}
|
||||
}
|
||||
for (let sj = niRunStart; sj <= niRunEnd; sj++) {
|
||||
charTypes[seqIndices[sj]] = prevType === nextType ? prevType : embedDirection
|
||||
}
|
||||
si = niRunEnd
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 3.3.6 Resolving Implicit Levels ===
|
||||
|
||||
for (let i = paragraph.start; i <= paragraph.end; i++) {
|
||||
const level = embedLevels[i]
|
||||
const type = charTypes[i]
|
||||
// I2. For all characters with an odd (right-to-left) embedding level, those of type L, EN or AN go up one level.
|
||||
if (level & 1) {
|
||||
if (type & (TYPE_L | TYPE_EN | TYPE_AN)) {
|
||||
embedLevels[i]++
|
||||
}
|
||||
}
|
||||
// I1. For all characters with an even (left-to-right) embedding level, those of type R go up one level
|
||||
// and those of type AN or EN go up two levels.
|
||||
else {
|
||||
if (type & TYPE_R) {
|
||||
embedLevels[i]++
|
||||
} else if (type & (TYPE_AN | TYPE_EN)) {
|
||||
embedLevels[i] += 2
|
||||
}
|
||||
}
|
||||
|
||||
// 5.2: Resolve any LRE, RLE, LRO, RLO, PDF, or BN to the level of the preceding character if there is one,
|
||||
// and otherwise to the base level.
|
||||
if (type & BN_LIKE_TYPES) {
|
||||
embedLevels[i] = i === 0 ? paragraph.level : embedLevels[i - 1]
|
||||
}
|
||||
|
||||
// 3.4 L1.1-4: Reset the embedding level of segment/paragraph separators, and any sequence of whitespace or
|
||||
// isolate formatting characters preceding them or the end of the paragraph, to the paragraph level.
|
||||
// NOTE: this will also need to be applied to each individual line ending after line wrapping occurs.
|
||||
if (i === paragraph.end || getBidiCharType(string[i]) & (TYPE_S | TYPE_B)) {
|
||||
for (let j = i; j >= 0 && (getBidiCharType(string[j]) & TRAILING_TYPES); j--) {
|
||||
embedLevels[j] = paragraph.level
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DONE! The resolved levels can then be used, after line wrapping, to flip runs of characters
|
||||
// according to section 3.4 Reordering Resolved Levels
|
||||
return {
|
||||
levels: embedLevels,
|
||||
paragraphs
|
||||
}
|
||||
|
||||
function determineAutoEmbedLevel (start, isFSI) {
|
||||
// 3.3.1 P2 - P3
|
||||
for (let i = start; i < string.length; i++) {
|
||||
const charType = charTypes[i]
|
||||
if (charType & (TYPE_R | TYPE_AL)) {
|
||||
return 1
|
||||
}
|
||||
if ((charType & (TYPE_B | TYPE_L)) || (isFSI && charType === TYPE_PDI)) {
|
||||
return 0
|
||||
}
|
||||
if (charType & ISOLATE_INIT_TYPES) {
|
||||
const pdi = indexOfMatchingPDI(i)
|
||||
i = pdi === -1 ? string.length : pdi
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function indexOfMatchingPDI (isolateStart) {
|
||||
// 3.1.2 BD9
|
||||
let isolationLevel = 1
|
||||
for (let i = isolateStart + 1; i < string.length; i++) {
|
||||
const charType = charTypes[i]
|
||||
if (charType & TYPE_B) {
|
||||
break
|
||||
}
|
||||
if (charType & TYPE_PDI) {
|
||||
if (--isolationLevel === 0) {
|
||||
return i
|
||||
}
|
||||
} else if (charType & ISOLATE_INIT_TYPES) {
|
||||
isolationLevel++
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
5
server/node_modules/bidi-js/src/index.js
generated
vendored
Normal file
5
server/node_modules/bidi-js/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export { getEmbeddingLevels } from './embeddingLevels.js'
|
||||
export { getReorderSegments, getReorderedIndices, getReorderedString } from './reordering.js'
|
||||
export { getBidiCharType, getBidiCharTypeName } from './charTypes.js'
|
||||
export { getMirroredCharacter, getMirroredCharactersMap } from './mirroring.js'
|
||||
export { closingToOpeningBracket, openingToClosingBracket, getCanonicalBracket } from './brackets.js'
|
||||
48
server/node_modules/bidi-js/src/mirroring.js
generated
vendored
Normal file
48
server/node_modules/bidi-js/src/mirroring.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import data from './data/bidiMirroring.data.js'
|
||||
import { parseCharacterMap } from './util/parseCharacterMap.js'
|
||||
|
||||
let mirrorMap
|
||||
|
||||
function parse () {
|
||||
if (!mirrorMap) {
|
||||
//const start = performance.now()
|
||||
const { map, reverseMap } = parseCharacterMap(data, true)
|
||||
// Combine both maps into one
|
||||
reverseMap.forEach((value, key) => {
|
||||
map.set(key, value)
|
||||
})
|
||||
mirrorMap = map
|
||||
//console.log(`mirrored chars parsed in ${performance.now() - start}ms`)
|
||||
}
|
||||
}
|
||||
|
||||
export function getMirroredCharacter (char) {
|
||||
parse()
|
||||
return mirrorMap.get(char) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string and its resolved embedding levels, build a map of indices to replacement chars
|
||||
* for any characters in right-to-left segments that have defined mirrored characters.
|
||||
* @param string
|
||||
* @param embeddingLevels
|
||||
* @param [start]
|
||||
* @param [end]
|
||||
* @return {Map<number, string>}
|
||||
*/
|
||||
export function getMirroredCharactersMap(string, embeddingLevels, start, end) {
|
||||
let strLen = string.length
|
||||
start = Math.max(0, start == null ? 0 : +start)
|
||||
end = Math.min(strLen - 1, end == null ? strLen - 1 : +end)
|
||||
|
||||
const map = new Map()
|
||||
for (let i = start; i <= end; i++) {
|
||||
if (embeddingLevels[i] & 1) { //only odd (rtl) levels
|
||||
const mirror = getMirroredCharacter(string[i])
|
||||
if (mirror !== null) {
|
||||
map.set(i, mirror)
|
||||
}
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
99
server/node_modules/bidi-js/src/reordering.js
generated
vendored
Normal file
99
server/node_modules/bidi-js/src/reordering.js
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import { getBidiCharType, TRAILING_TYPES } from './charTypes.js'
|
||||
import { getMirroredCharacter } from './mirroring.js'
|
||||
|
||||
/**
|
||||
* Given a start and end denoting a single line within a string, and a set of precalculated
|
||||
* bidi embedding levels, produce a list of segments whose ordering should be flipped, in sequence.
|
||||
* @param {string} string - the full input string
|
||||
* @param {GetEmbeddingLevelsResult} embeddingLevelsResult - the result object from getEmbeddingLevels
|
||||
* @param {number} [start] - first character in a subset of the full string
|
||||
* @param {number} [end] - last character in a subset of the full string
|
||||
* @return {number[][]} - the list of start/end segments that should be flipped, in order.
|
||||
*/
|
||||
export function getReorderSegments(string, embeddingLevelsResult, start, end) {
|
||||
let strLen = string.length
|
||||
start = Math.max(0, start == null ? 0 : +start)
|
||||
end = Math.min(strLen - 1, end == null ? strLen - 1 : +end)
|
||||
|
||||
const segments = []
|
||||
embeddingLevelsResult.paragraphs.forEach(paragraph => {
|
||||
const lineStart = Math.max(start, paragraph.start)
|
||||
const lineEnd = Math.min(end, paragraph.end)
|
||||
if (lineStart < lineEnd) {
|
||||
// Local slice for mutation
|
||||
const lineLevels = embeddingLevelsResult.levels.slice(lineStart, lineEnd + 1)
|
||||
|
||||
// 3.4 L1.4: Reset any sequence of whitespace characters and/or isolate formatting characters at the
|
||||
// end of the line to the paragraph level.
|
||||
for (let i = lineEnd; i >= lineStart && (getBidiCharType(string[i]) & TRAILING_TYPES); i--) {
|
||||
lineLevels[i] = paragraph.level
|
||||
}
|
||||
|
||||
// L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels
|
||||
// not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
|
||||
let maxLevel = paragraph.level
|
||||
let minOddLevel = Infinity
|
||||
for (let i = 0; i < lineLevels.length; i++) {
|
||||
const level = lineLevels[i]
|
||||
if (level > maxLevel) maxLevel = level
|
||||
if (level < minOddLevel) minOddLevel = level | 1
|
||||
}
|
||||
for (let lvl = maxLevel; lvl >= minOddLevel; lvl--) {
|
||||
for (let i = 0; i < lineLevels.length; i++) {
|
||||
if (lineLevels[i] >= lvl) {
|
||||
const segStart = i
|
||||
while (i + 1 < lineLevels.length && lineLevels[i + 1] >= lvl) {
|
||||
i++
|
||||
}
|
||||
if (i > segStart) {
|
||||
segments.push([segStart + lineStart, i + lineStart])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return segments
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {GetEmbeddingLevelsResult} embedLevelsResult
|
||||
* @param {number} [start]
|
||||
* @param {number} [end]
|
||||
* @return {string} the new string with bidi segments reordered
|
||||
*/
|
||||
export function getReorderedString(string, embedLevelsResult, start, end) {
|
||||
const indices = getReorderedIndices(string, embedLevelsResult, start, end)
|
||||
const chars = [...string]
|
||||
indices.forEach((charIndex, i) => {
|
||||
chars[i] = (
|
||||
(embedLevelsResult.levels[charIndex] & 1) ? getMirroredCharacter(string[charIndex]) : null
|
||||
) || string[charIndex]
|
||||
})
|
||||
return chars.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {GetEmbeddingLevelsResult} embedLevelsResult
|
||||
* @param {number} [start]
|
||||
* @param {number} [end]
|
||||
* @return {number[]} an array with character indices in their new bidi order
|
||||
*/
|
||||
export function getReorderedIndices(string, embedLevelsResult, start, end) {
|
||||
const segments = getReorderSegments(string, embedLevelsResult, start, end)
|
||||
// Fill an array with indices
|
||||
const indices = []
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
indices[i] = i
|
||||
}
|
||||
// Reverse each segment in order
|
||||
segments.forEach(([start, end]) => {
|
||||
const slice = indices.slice(start, end + 1)
|
||||
for (let i = slice.length; i--;) {
|
||||
indices[end - i] = slice[i]
|
||||
}
|
||||
})
|
||||
return indices
|
||||
}
|
||||
30
server/node_modules/bidi-js/src/util/parseCharacterMap.js
generated
vendored
Normal file
30
server/node_modules/bidi-js/src/util/parseCharacterMap.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Parses an string that holds encoded codepoint mappings, e.g. for bracket pairs or
|
||||
* mirroring characters, as encoded by scripts/generateBidiData.js. Returns an object
|
||||
* holding the `map`, and optionally a `reverseMap` if `includeReverse:true`.
|
||||
* @param {string} encodedString
|
||||
* @param {boolean} includeReverse - true if you want reverseMap in the output
|
||||
* @return {{map: Map<number, number>, reverseMap?: Map<number, number>}}
|
||||
*/
|
||||
export function parseCharacterMap (encodedString, includeReverse) {
|
||||
const radix = 36
|
||||
let lastCode = 0
|
||||
const map = new Map()
|
||||
const reverseMap = includeReverse && new Map()
|
||||
let prevPair
|
||||
encodedString.split(',').forEach(function visit(entry) {
|
||||
if (entry.indexOf('+') !== -1) {
|
||||
for (let i = +entry; i--;) {
|
||||
visit(prevPair)
|
||||
}
|
||||
} else {
|
||||
prevPair = entry
|
||||
let [a, b] = entry.split('>')
|
||||
a = String.fromCodePoint(lastCode += parseInt(a, radix))
|
||||
b = String.fromCodePoint(lastCode += parseInt(b, radix))
|
||||
map.set(a, b)
|
||||
includeReverse && reverseMap.set(b, a)
|
||||
}
|
||||
})
|
||||
return { map, reverseMap }
|
||||
}
|
||||
Reference in New Issue
Block a user