diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
| commit | 76cb9c2a39d477a64824a985ade40507e3bbade1 (patch) | |
| tree | 41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/bidi-js | |
| parent | 819a39a21ac992b1393244a4c283bbb125208c69 (diff) | |
| download | neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2 neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip | |
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/bidi-js')
17 files changed, 3208 insertions, 0 deletions
diff --git a/vanilla/node_modules/bidi-js/LICENSE.txt b/vanilla/node_modules/bidi-js/LICENSE.txt new file mode 100644 index 0000000..2dabff0 --- /dev/null +++ b/vanilla/node_modules/bidi-js/LICENSE.txt @@ -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. diff --git a/vanilla/node_modules/bidi-js/README.md b/vanilla/node_modules/bidi-js/README.md new file mode 100644 index 0000000..cf848bb --- /dev/null +++ b/vanilla/node_modules/bidi-js/README.md @@ -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", ... +``` diff --git a/vanilla/node_modules/bidi-js/dist/bidi.js b/vanilla/node_modules/bidi-js/dist/bidi.js new file mode 100644 index 0000000..0292af9 --- /dev/null +++ b/vanilla/node_modules/bidi-js/dist/bidi.js @@ -0,0 +1,1010 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bidi_js = factory()); +}(this, (function () { 'use strict'; + + function bidiFactory() { + var bidi = (function (exports) { + + // Bidi character types data, auto generated + var DATA = { + "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" + }; + + var TYPES = {}; + var TYPES_TO_NAMES = {}; + TYPES.L = 1; //L is the default + TYPES_TO_NAMES[1] = 'L'; + Object.keys(DATA).forEach(function (type, i) { + TYPES[type] = 1 << (i + 1); + TYPES_TO_NAMES[TYPES[type]] = type; + }); + Object.freeze(TYPES); + + var ISOLATE_INIT_TYPES = TYPES.LRI | TYPES.RLI | TYPES.FSI; + var STRONG_TYPES = TYPES.L | TYPES.R | TYPES.AL; + var NEUTRAL_ISOLATE_TYPES = TYPES.B | TYPES.S | TYPES.WS | TYPES.ON | TYPES.FSI | TYPES.LRI | TYPES.RLI | TYPES.PDI; + var BN_LIKE_TYPES = TYPES.BN | TYPES.RLE | TYPES.LRE | TYPES.RLO | TYPES.LRO | TYPES.PDF; + var TRAILING_TYPES = TYPES.S | TYPES.WS | TYPES.B | ISOLATE_INIT_TYPES | TYPES.PDI | BN_LIKE_TYPES; + + var map = null; + + function parseData () { + if (!map) { + //const start = performance.now() + map = new Map(); + var loop = function ( type ) { + if (DATA.hasOwnProperty(type)) { + var lastCode = 0; + DATA[type].split(',').forEach(function (range) { + var ref = range.split('+'); + var skip = ref[0]; + var step = ref[1]; + skip = parseInt(skip, 36); + step = step ? parseInt(step, 36) : 0; + map.set(lastCode += skip, TYPES[type]); + for (var i = 0; i < step; i++) { + map.set(++lastCode, TYPES[type]); + } + }); + } + }; + + for (var type in DATA) loop( 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)] + } + + // Bidi bracket pairs data, auto generated + var data$1 = { + "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" + }; + + /** + * 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>}} + */ + function parseCharacterMap (encodedString, includeReverse) { + var radix = 36; + var lastCode = 0; + var map = new Map(); + var reverseMap = includeReverse && new Map(); + var prevPair; + encodedString.split(',').forEach(function visit(entry) { + if (entry.indexOf('+') !== -1) { + for (var i = +entry; i--;) { + visit(prevPair); + } + } else { + prevPair = entry; + var ref = entry.split('>'); + var a = ref[0]; + var b = ref[1]; + 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: map, reverseMap: reverseMap } + } + + var openToClose, closeToOpen, canonical; + + function parse$1 () { + if (!openToClose) { + //const start = performance.now() + var ref = parseCharacterMap(data$1.pairs, true); + var map = ref.map; + var reverseMap = ref.reverseMap; + openToClose = map; + closeToOpen = reverseMap; + canonical = parseCharacterMap(data$1.canonical, false).map; + //console.log(`brackets parsed in ${performance.now() - start}ms`) + } + } + + function openingToClosingBracket (char) { + parse$1(); + return openToClose.get(char) || null + } + + function closingToOpeningBracket (char) { + parse$1(); + return closeToOpen.get(char) || null + } + + function getCanonicalBracket (char) { + parse$1(); + return canonical.get(char) || null + } + + // Local type aliases + var TYPE_L = TYPES.L; + var TYPE_R = TYPES.R; + var TYPE_EN = TYPES.EN; + var TYPE_ES = TYPES.ES; + var TYPE_ET = TYPES.ET; + var TYPE_AN = TYPES.AN; + var TYPE_CS = TYPES.CS; + var TYPE_B = TYPES.B; + var TYPE_S = TYPES.S; + var TYPE_ON = TYPES.ON; + var TYPE_BN = TYPES.BN; + var TYPE_NSM = TYPES.NSM; + var TYPE_AL = TYPES.AL; + var TYPE_LRO = TYPES.LRO; + var TYPE_RLO = TYPES.RLO; + var TYPE_LRE = TYPES.LRE; + var TYPE_RLE = TYPES.RLE; + var TYPE_PDF = TYPES.PDF; + var TYPE_LRI = TYPES.LRI; + var TYPE_RLI = TYPES.RLI; + var TYPE_FSI = TYPES.FSI; + var TYPE_PDI = TYPES.PDI; + + /** + * @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} + */ + function getEmbeddingLevels (string, baseDirection) { + var MAX_DEPTH = 125; + + // Start by mapping all characters to their unicode type, as a bitmask integer + var charTypes = new Uint32Array(string.length); + for (var i = 0; i < string.length; i++) { + charTypes[i] = getBidiCharType(string[i]); + } + + var charTypeCounts = new Map(); //will be cleared at start of each paragraph + function changeCharType(i, type) { + var 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); + } + } + + var embedLevels = new Uint8Array(string.length); + var isolationPairs = new Map(); //init->pdi and pdi->init + + // === 3.3.1 The Paragraph Level === + // 3.3.1 P1: Split the text into paragraphs + var paragraphs = []; // [{start, end, level}, ...] + var paragraph = null; + for (var i$1 = 0; i$1 < string.length; i$1++) { + if (!paragraph) { + paragraphs.push(paragraph = { + start: i$1, + end: string.length - 1, + // 3.3.1 P2-P3: Determine the paragraph level + level: baseDirection === 'rtl' ? 1 : baseDirection === 'ltr' ? 0 : determineAutoEmbedLevel(i$1, false) + }); + } + if (charTypes[i$1] & TYPE_B) { + paragraph.end = i$1; + paragraph = null; + } + } + + var FORMATTING_TYPES = TYPE_RLE | TYPE_LRE | TYPE_RLO | TYPE_LRO | ISOLATE_INIT_TYPES | TYPE_PDI | TYPE_PDF | TYPE_B; + var nextEven = function (n) { return n + ((n & 1) ? 1 : 2); }; + var nextOdd = function (n) { return n + ((n & 1) ? 2 : 1); }; + + // Everything from here on will operate per paragraph. + for (var paraIdx = 0; paraIdx < paragraphs.length; paraIdx++) { + paragraph = paragraphs[paraIdx]; + var statusStack = [{ + _level: paragraph.level, + _override: 0, //0=neutral, 1=L, 2=R + _isolate: 0 //bool + }]; + var stackTop = (void 0); + var overflowIsolateCount = 0; + var overflowEmbeddingCount = 0; + var validIsolateCount = 0; + charTypeCounts.clear(); + + // === 3.3.2 Explicit Levels and Directions === + for (var i$2 = paragraph.start; i$2 <= paragraph.end; i$2++) { + var charType = charTypes[i$2]; + 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$2] = stackTop._level; // 5.2 + var 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$2] = stackTop._level; // 5.2 + var level$1 = (charType === TYPE_RLO ? nextOdd : nextEven)(stackTop._level); + if (level$1 <= MAX_DEPTH && !overflowIsolateCount && !overflowEmbeddingCount) { + statusStack.push({ + _level: level$1, + _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$2 + 1, true) === 1 ? TYPE_RLI : TYPE_LRI; + } + + embedLevels[i$2] = stackTop._level; + if (stackTop._override) { + changeCharType(i$2, stackTop._override); + } + var level$2 = (charType === TYPE_RLI ? nextOdd : nextEven)(stackTop._level); + if (level$2 <= MAX_DEPTH && overflowIsolateCount === 0 && overflowEmbeddingCount === 0) { + validIsolateCount++; + statusStack.push({ + _level: level$2, + _override: 0, + _isolate: 1, + _isolInitIndex: i$2 + }); + } 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: + var isolInitIndex = statusStack[statusStack.length - 1]._isolInitIndex; + if (isolInitIndex != null) { + isolationPairs.set(isolInitIndex, i$2); + isolationPairs.set(i$2, isolInitIndex); + } + statusStack.pop(); + validIsolateCount--; + } + stackTop = statusStack[statusStack.length - 1]; + embedLevels[i$2] = stackTop._level; + if (stackTop._override) { + changeCharType(i$2, 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$2] = stackTop._level; // 5.2 + } + + // End of Paragraph: 3.3.2 X8 + else if (charType & TYPE_B) { + embedLevels[i$2] = paragraph.level; + } + } + + // Non-formatting characters: 3.3.2 X6 + else { + embedLevels[i$2] = 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$2, 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 + var levelRuns = []; + var currentRun = null; + for (var i$3 = paragraph.start; i$3 <= paragraph.end; i$3++) { + var charType$1 = charTypes[i$3]; + if (!(charType$1 & BN_LIKE_TYPES)) { + var lvl = embedLevels[i$3]; + var isIsolInit = charType$1 & ISOLATE_INIT_TYPES; + var isPDI = charType$1 === TYPE_PDI; + if (currentRun && lvl === currentRun._level) { + currentRun._end = i$3; + currentRun._endsWithIsolInit = isIsolInit; + } else { + levelRuns.push(currentRun = { + _start: i$3, + _end: i$3, + _level: lvl, + _startsWithPDI: isPDI, + _endsWithIsolInit: isIsolInit + }); + } + } + } + var isolatingRunSeqs = []; // [{seqIndices: [], sosType: L|R, eosType: L|R}] + for (var runIdx = 0; runIdx < levelRuns.length; runIdx++) { + var run = levelRuns[runIdx]; + if (!run._startsWithPDI || (run._startsWithPDI && !isolationPairs.has(run._start))) { + var seqRuns = [currentRun = run]; + for (var pdiIndex = (void 0); currentRun && currentRun._endsWithIsolInit && (pdiIndex = isolationPairs.get(currentRun._end)) != null;) { + for (var i$4 = runIdx + 1; i$4 < levelRuns.length; i$4++) { + if (levelRuns[i$4]._start === pdiIndex) { + seqRuns.push(currentRun = levelRuns[i$4]); + break + } + } + } + // build flat list of indices across all runs: + var seqIndices = []; + for (var i$5 = 0; i$5 < seqRuns.length; i$5++) { + var run$1 = seqRuns[i$5]; + for (var j = run$1._start; j <= run$1._end; j++) { + seqIndices.push(j); + } + } + // determine the sos/eos types: + var firstLevel = embedLevels[seqIndices[0]]; + var prevLevel = paragraph.level; + for (var i$6 = seqIndices[0] - 1; i$6 >= 0; i$6--) { + if (!(charTypes[i$6] & BN_LIKE_TYPES)) { //5.2 + prevLevel = embedLevels[i$6]; + break + } + } + var lastIndex = seqIndices[seqIndices.length - 1]; + var lastLevel = embedLevels[lastIndex]; + var nextLevel = paragraph.level; + if (!(charTypes[lastIndex] & ISOLATE_INIT_TYPES)) { + for (var i$7 = lastIndex + 1; i$7 <= paragraph.end; i$7++) { + if (!(charTypes[i$7] & BN_LIKE_TYPES)) { //5.2 + nextLevel = embedLevels[i$7]; + 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 (var seqIdx = 0; seqIdx < isolatingRunSeqs.length; seqIdx++) { + var ref = isolatingRunSeqs[seqIdx]; + var seqIndices$1 = ref._seqIndices; + var sosType = ref._sosType; + var eosType = ref._eosType; + /** + * All the level runs in an isolating run sequence have the same embedding level. + * + * DO NOT change any `embedLevels[i]` within the current scope. + */ + var embedDirection = ((embedLevels[seqIndices$1[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 (var si = 0; si < seqIndices$1.length; si++) { + var i$8 = seqIndices$1[si]; + if (charTypes[i$8] & TYPE_NSM) { + var prevType = sosType; + for (var sj = si - 1; sj >= 0; sj--) { + if (!(charTypes[seqIndices$1[sj]] & BN_LIKE_TYPES)) { //5.2 scan back to first non-BN + prevType = charTypes[seqIndices$1[sj]]; + break + } + } + changeCharType(i$8, (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 (var si$1 = 0; si$1 < seqIndices$1.length; si$1++) { + var i$9 = seqIndices$1[si$1]; + if (charTypes[i$9] & TYPE_EN) { + for (var sj$1 = si$1 - 1; sj$1 >= -1; sj$1--) { + var prevCharType = sj$1 === -1 ? sosType : charTypes[seqIndices$1[sj$1]]; + if (prevCharType & STRONG_TYPES) { + if (prevCharType === TYPE_AL) { + changeCharType(i$9, TYPE_AN); + } + break + } + } + } + } + } + + // W3. Change all ALs to R + if (charTypeCounts.get(TYPE_AL)) { + for (var si$2 = 0; si$2 < seqIndices$1.length; si$2++) { + var i$10 = seqIndices$1[si$2]; + if (charTypes[i$10] & TYPE_AL) { + changeCharType(i$10, 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 (var si$3 = 1; si$3 < seqIndices$1.length - 1; si$3++) { + var i$11 = seqIndices$1[si$3]; + if (charTypes[i$11] & (TYPE_ES | TYPE_CS)) { + var prevType$1 = 0, nextType = 0; + for (var sj$2 = si$3 - 1; sj$2 >= 0; sj$2--) { + prevType$1 = charTypes[seqIndices$1[sj$2]]; + if (!(prevType$1 & BN_LIKE_TYPES)) { //5.2 + break + } + } + for (var sj$3 = si$3 + 1; sj$3 < seqIndices$1.length; sj$3++) { + nextType = charTypes[seqIndices$1[sj$3]]; + if (!(nextType & BN_LIKE_TYPES)) { //5.2 + break + } + } + if (prevType$1 === nextType && (charTypes[i$11] === TYPE_ES ? prevType$1 === TYPE_EN : (prevType$1 & (TYPE_EN | TYPE_AN)))) { + changeCharType(i$11, prevType$1); + } + } + } + } + + // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. + if (charTypeCounts.get(TYPE_EN)) { + for (var si$4 = 0; si$4 < seqIndices$1.length; si$4++) { + var i$12 = seqIndices$1[si$4]; + if (charTypes[i$12] & TYPE_EN) { + for (var sj$4 = si$4 - 1; sj$4 >= 0 && (charTypes[seqIndices$1[sj$4]] & (TYPE_ET | BN_LIKE_TYPES)); sj$4--) { + changeCharType(seqIndices$1[sj$4], TYPE_EN); + } + for (si$4++; si$4 < seqIndices$1.length && (charTypes[seqIndices$1[si$4]] & (TYPE_ET | BN_LIKE_TYPES | TYPE_EN)); si$4++) { + if (charTypes[seqIndices$1[si$4]] !== TYPE_EN) { + changeCharType(seqIndices$1[si$4], 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 (var si$5 = 0; si$5 < seqIndices$1.length; si$5++) { + var i$13 = seqIndices$1[si$5]; + if (charTypes[i$13] & (TYPE_ET | TYPE_ES | TYPE_CS)) { + changeCharType(i$13, TYPE_ON); + // 5.2 transform adjacent BNs too: + for (var sj$5 = si$5 - 1; sj$5 >= 0 && (charTypes[seqIndices$1[sj$5]] & BN_LIKE_TYPES); sj$5--) { + changeCharType(seqIndices$1[sj$5], TYPE_ON); + } + for (var sj$6 = si$5 + 1; sj$6 < seqIndices$1.length && (charTypes[seqIndices$1[sj$6]] & BN_LIKE_TYPES); sj$6++) { + changeCharType(seqIndices$1[sj$6], 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 (var si$6 = 0, prevStrongType = sosType; si$6 < seqIndices$1.length; si$6++) { + var i$14 = seqIndices$1[si$6]; + var type = charTypes[i$14]; + if (type & TYPE_EN) { + if (prevStrongType === TYPE_L) { + changeCharType(i$14, 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. + var R_TYPES_FOR_N_STEPS = (TYPE_R | TYPE_EN | TYPE_AN); + var 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. + var bracketPairs = []; + { + var openerStack = []; + for (var si$7 = 0; si$7 < seqIndices$1.length; si$7++) { + // 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$1[si$7]] & NEUTRAL_ISOLATE_TYPES) { + var char = string[seqIndices$1[si$7]]; + var oppositeBracket = (void 0); + // Opening bracket + if (openingToClosingBracket(char) !== null) { + if (openerStack.length < 63) { + openerStack.push({ char: char, seqIndex: si$7 }); + } else { + break + } + } + // Closing bracket + else if ((oppositeBracket = closingToOpeningBracket(char)) !== null) { + for (var stackIdx = openerStack.length - 1; stackIdx >= 0; stackIdx--) { + var stackChar = openerStack[stackIdx].char; + if (stackChar === oppositeBracket || + stackChar === closingToOpeningBracket(getCanonicalBracket(char)) || + openingToClosingBracket(getCanonicalBracket(stackChar)) === char + ) { + bracketPairs.push([openerStack[stackIdx].seqIndex, si$7]); + openerStack.length = stackIdx; //pop the matching bracket and all following + break + } + } + } + } + } + bracketPairs.sort(function (a, b) { return a[0] - b[0]; }); + } + // * For each bracket-pair element in the list of pairs of text positions + for (var pairIdx = 0; pairIdx < bracketPairs.length; pairIdx++) { + var ref$1 = bracketPairs[pairIdx]; + var openSeqIdx = ref$1[0]; + var closeSeqIdx = ref$1[1]; + // 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. + var foundStrongType = false; + var useStrongType = 0; + for (var si$8 = openSeqIdx + 1; si$8 < closeSeqIdx; si$8++) { + var i$15 = seqIndices$1[si$8]; + if (charTypes[i$15] & STRONG_TYPES_FOR_N_STEPS) { + foundStrongType = true; + var lr = (charTypes[i$15] & 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 (var si$9 = openSeqIdx - 1; si$9 >= 0; si$9--) { + var i$16 = seqIndices$1[si$9]; + if (charTypes[i$16] & STRONG_TYPES_FOR_N_STEPS) { + var lr$1 = (charTypes[i$16] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L; + if (lr$1 !== embedDirection) { + useStrongType = lr$1; + } else { + useStrongType = embedDirection; + } + break + } + } + } + if (useStrongType) { + charTypes[seqIndices$1[openSeqIdx]] = charTypes[seqIndices$1[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 (var si$10 = openSeqIdx + 1; si$10 < seqIndices$1.length; si$10++) { + if (!(charTypes[seqIndices$1[si$10]] & BN_LIKE_TYPES)) { + if (getBidiCharType(string[seqIndices$1[si$10]]) & TYPE_NSM) { + charTypes[seqIndices$1[si$10]] = useStrongType; + } + break + } + } + } + if (useStrongType !== embedDirection) { + for (var si$11 = closeSeqIdx + 1; si$11 < seqIndices$1.length; si$11++) { + if (!(charTypes[seqIndices$1[si$11]] & BN_LIKE_TYPES)) { + if (getBidiCharType(string[seqIndices$1[si$11]]) & TYPE_NSM) { + charTypes[seqIndices$1[si$11]] = 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 (var si$12 = 0; si$12 < seqIndices$1.length; si$12++) { + if (charTypes[seqIndices$1[si$12]] & NEUTRAL_ISOLATE_TYPES) { + var niRunStart = si$12, niRunEnd = si$12; + var prevType$2 = sosType; //si === 0 ? sosType : (charTypes[seqIndices[si - 1]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L + for (var si2 = si$12 - 1; si2 >= 0; si2--) { + if (charTypes[seqIndices$1[si2]] & BN_LIKE_TYPES) { + niRunStart = si2; //5.2 treat BNs adjacent to NIs as NIs + } else { + prevType$2 = (charTypes[seqIndices$1[si2]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L; + break + } + } + var nextType$1 = eosType; + for (var si2$1 = si$12 + 1; si2$1 < seqIndices$1.length; si2$1++) { + if (charTypes[seqIndices$1[si2$1]] & (NEUTRAL_ISOLATE_TYPES | BN_LIKE_TYPES)) { + niRunEnd = si2$1; + } else { + nextType$1 = (charTypes[seqIndices$1[si2$1]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L; + break + } + } + for (var sj$7 = niRunStart; sj$7 <= niRunEnd; sj$7++) { + charTypes[seqIndices$1[sj$7]] = prevType$2 === nextType$1 ? prevType$2 : embedDirection; + } + si$12 = niRunEnd; + } + } + } + } + + // === 3.3.6 Resolving Implicit Levels === + + for (var i$17 = paragraph.start; i$17 <= paragraph.end; i$17++) { + var level$3 = embedLevels[i$17]; + var type$1 = charTypes[i$17]; + // 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$3 & 1) { + if (type$1 & (TYPE_L | TYPE_EN | TYPE_AN)) { + embedLevels[i$17]++; + } + } + // 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$1 & TYPE_R) { + embedLevels[i$17]++; + } else if (type$1 & (TYPE_AN | TYPE_EN)) { + embedLevels[i$17] += 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$1 & BN_LIKE_TYPES) { + embedLevels[i$17] = i$17 === 0 ? paragraph.level : embedLevels[i$17 - 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$17 === paragraph.end || getBidiCharType(string[i$17]) & (TYPE_S | TYPE_B)) { + for (var j$1 = i$17; j$1 >= 0 && (getBidiCharType(string[j$1]) & TRAILING_TYPES); j$1--) { + embedLevels[j$1] = 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: paragraphs + } + + function determineAutoEmbedLevel (start, isFSI) { + // 3.3.1 P2 - P3 + for (var i = start; i < string.length; i++) { + var 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) { + var pdi = indexOfMatchingPDI(i); + i = pdi === -1 ? string.length : pdi; + } + } + return 0 + } + + function indexOfMatchingPDI (isolateStart) { + // 3.1.2 BD9 + var isolationLevel = 1; + for (var i = isolateStart + 1; i < string.length; i++) { + var 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 + } + } + + // Bidi mirrored chars data, auto generated + var data = "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"; + + var mirrorMap; + + function parse () { + if (!mirrorMap) { + //const start = performance.now() + var ref = parseCharacterMap(data, true); + var map = ref.map; + var reverseMap = ref.reverseMap; + // Combine both maps into one + reverseMap.forEach(function (value, key) { + map.set(key, value); + }); + mirrorMap = map; + //console.log(`mirrored chars parsed in ${performance.now() - start}ms`) + } + } + + 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>} + */ + function getMirroredCharactersMap(string, embeddingLevels, start, end) { + var strLen = string.length; + start = Math.max(0, start == null ? 0 : +start); + end = Math.min(strLen - 1, end == null ? strLen - 1 : +end); + + var map = new Map(); + for (var i = start; i <= end; i++) { + if (embeddingLevels[i] & 1) { //only odd (rtl) levels + var mirror = getMirroredCharacter(string[i]); + if (mirror !== null) { + map.set(i, mirror); + } + } + } + return map + } + + /** + * 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. + */ + function getReorderSegments(string, embeddingLevelsResult, start, end) { + var strLen = string.length; + start = Math.max(0, start == null ? 0 : +start); + end = Math.min(strLen - 1, end == null ? strLen - 1 : +end); + + var segments = []; + embeddingLevelsResult.paragraphs.forEach(function (paragraph) { + var lineStart = Math.max(start, paragraph.start); + var lineEnd = Math.min(end, paragraph.end); + if (lineStart < lineEnd) { + // Local slice for mutation + var 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 (var 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. + var maxLevel = paragraph.level; + var minOddLevel = Infinity; + for (var i$1 = 0; i$1 < lineLevels.length; i$1++) { + var level = lineLevels[i$1]; + if (level > maxLevel) { maxLevel = level; } + if (level < minOddLevel) { minOddLevel = level | 1; } + } + for (var lvl = maxLevel; lvl >= minOddLevel; lvl--) { + for (var i$2 = 0; i$2 < lineLevels.length; i$2++) { + if (lineLevels[i$2] >= lvl) { + var segStart = i$2; + while (i$2 + 1 < lineLevels.length && lineLevels[i$2 + 1] >= lvl) { + i$2++; + } + if (i$2 > segStart) { + segments.push([segStart + lineStart, i$2 + lineStart]); + } + } + } + } + } + }); + return segments + } + + /** + * @param {string} string + * @param {GetEmbeddingLevelsResult} embedLevelsResult + * @param {number} [start] + * @param {number} [end] + * @return {string} the new string with bidi segments reordered + */ + function getReorderedString(string, embedLevelsResult, start, end) { + var indices = getReorderedIndices(string, embedLevelsResult, start, end); + var chars = [].concat( string ); + indices.forEach(function (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 + */ + function getReorderedIndices(string, embedLevelsResult, start, end) { + var segments = getReorderSegments(string, embedLevelsResult, start, end); + // Fill an array with indices + var indices = []; + for (var i = 0; i < string.length; i++) { + indices[i] = i; + } + // Reverse each segment in order + segments.forEach(function (ref) { + var start = ref[0]; + var end = ref[1]; + + var slice = indices.slice(start, end + 1); + for (var i = slice.length; i--;) { + indices[end - i] = slice[i]; + } + }); + return indices + } + + exports.closingToOpeningBracket = closingToOpeningBracket; + exports.getBidiCharType = getBidiCharType; + exports.getBidiCharTypeName = getBidiCharTypeName; + exports.getCanonicalBracket = getCanonicalBracket; + exports.getEmbeddingLevels = getEmbeddingLevels; + exports.getMirroredCharacter = getMirroredCharacter; + exports.getMirroredCharactersMap = getMirroredCharactersMap; + exports.getReorderSegments = getReorderSegments; + exports.getReorderedIndices = getReorderedIndices; + exports.getReorderedString = getReorderedString; + exports.openingToClosingBracket = openingToClosingBracket; + + Object.defineProperty(exports, '__esModule', { value: true }); + + return exports; + + }({})); + return bidi} + + return bidiFactory; + +}))); diff --git a/vanilla/node_modules/bidi-js/dist/bidi.min.js b/vanilla/node_modules/bidi-js/dist/bidi.min.js new file mode 100644 index 0000000..bd0ca4d --- /dev/null +++ b/vanilla/node_modules/bidi-js/dist/bidi.min.js @@ -0,0 +1 @@ +!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(r="undefined"!=typeof globalThis?globalThis:r||self).bidi_js=e()}(this,(function(){"use strict";return function(){return function(r){var e={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"},f={},a={};f.L=1,a[1]="L",Object.keys(e).forEach((function(r,e){f[r]=1<<e+1,a[f[r]]=r})),Object.freeze(f);var n=f.LRI|f.RLI|f.FSI,i=f.L|f.R|f.AL,v=f.B|f.S|f.WS|f.ON|f.FSI|f.LRI|f.RLI|f.PDI,o=f.BN|f.RLE|f.LRE|f.RLO|f.LRO|f.PDF,t=f.S|f.WS|f.B|n|f.PDI|o,u=null;function l(r){return function(){if(!u){u=new Map;var r=function(r){if(e.hasOwnProperty(r)){var a=0;e[r].split(",").forEach((function(e){var n=e.split("+"),i=n[0],v=n[1];i=parseInt(i,36),v=v?parseInt(v,36):0,u.set(a+=i,f[r]);for(var o=0;o<v;o++)u.set(++a,f[r])}))}};for(var a in e)r(a)}}(),u.get(r.codePointAt(0))||f.L}var c,d,b,s="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",h="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";function k(r,e){var f,a=0,n=new Map,i=e&&new Map;return r.split(",").forEach((function r(v){if(-1!==v.indexOf("+"))for(var o=+v;o--;)r(f);else{f=v;var t=v.split(">"),u=t[0],l=t[1];u=String.fromCodePoint(a+=parseInt(u,36)),l=String.fromCodePoint(a+=parseInt(l,36)),n.set(u,l),e&&i.set(l,u)}})),{map:n,reverseMap:i}}function m(){if(!c){var r=k(s,!0),e=r.map,f=r.reverseMap;c=e,d=f,b=k(h,!1).map}}function j(r){return m(),c.get(r)||null}function g(r){return m(),d.get(r)||null}function p(r){return m(),b.get(r)||null}var q=f.L,w=f.R,y=f.EN,x=f.ES,_=f.ET,M=f.AN,z=f.CS,I=f.B,L=f.S,S=f.ON,R=f.BN,O=f.NSM,E=f.AL,N=f.LRO,T=f.RLO,A=f.LRE,D=f.RLE,P=f.PDF,W=f.LRI,B=f.RLI,F=f.FSI,U=f.PDI;var C;function G(r){return function(){if(!C){var r=k("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",!0),e=r.map;r.reverseMap.forEach((function(r,f){e.set(f,r)})),C=e}}(),C.get(r)||null}function H(r,e,f,a){var n=r.length;f=Math.max(0,null==f?0:+f),a=Math.min(n-1,null==a?n-1:+a);var i=[];return e.paragraphs.forEach((function(n){var v=Math.max(f,n.start),o=Math.min(a,n.end);if(v<o){for(var u=e.levels.slice(v,o+1),c=o;c>=v&&l(r[c])&t;c--)u[c]=n.level;for(var d=n.level,b=1/0,s=0;s<u.length;s++){var h=u[s];h>d&&(d=h),h<b&&(b=1|h)}for(var k=d;k>=b;k--)for(var m=0;m<u.length;m++)if(u[m]>=k){for(var j=m;m+1<u.length&&u[m+1]>=k;)m++;m>j&&i.push([j+v,m+v])}}})),i}function J(r,e,f,a){for(var n=H(r,e,f,a),i=[],v=0;v<r.length;v++)i[v]=v;return n.forEach((function(r){for(var e=r[0],f=r[1],a=i.slice(e,f+1),n=a.length;n--;)i[f-n]=a[n]})),i}return r.closingToOpeningBracket=g,r.getBidiCharType=l,r.getBidiCharTypeName=function(r){return a[l(r)]},r.getCanonicalBracket=p,r.getEmbeddingLevels=function(r,e){for(var f=new Uint32Array(r.length),a=0;a<r.length;a++)f[a]=l(r[a]);var u=new Map;function c(r,e){var a=f[r];f[r]=e,u.set(a,u.get(a)-1),a&v&&u.set(v,u.get(v)-1),u.set(e,(u.get(e)||0)+1),e&v&&u.set(v,(u.get(v)||0)+1)}for(var d=new Uint8Array(r.length),b=new Map,s=[],h=null,k=0;k<r.length;k++)h||s.push(h={start:k,end:r.length-1,level:"rtl"===e?1:"ltr"===e?0:Fe(k,!1)}),f[k]&I&&(h.end=k,h=null);for(var m=D|A|T|N|n|U|P|I,C=function(r){return r+(1&r?1:2)},G=function(r){return r+(1&r?2:1)},H=0;H<s.length;H++){var J=[{i:(h=s[H]).level,v:0,o:0}],K=void 0,Q=0,V=0,X=0;u.clear();for(var Y=h.start;Y<=h.end;Y++){var Z=f[Y];if(K=J[J.length-1],u.set(Z,(u.get(Z)||0)+1),Z&v&&u.set(v,(u.get(v)||0)+1),Z&m)if(Z&(D|A)){d[Y]=K.i;var $=(Z===D?G:C)(K.i);$<=125&&!Q&&!V?J.push({i:$,v:0,o:0}):Q||V++}else if(Z&(T|N)){d[Y]=K.i;var rr=(Z===T?G:C)(K.i);rr<=125&&!Q&&!V?J.push({i:rr,v:Z&T?w:q,o:0}):Q||V++}else if(Z&n){Z&F&&(Z=1===Fe(Y+1,!0)?B:W),d[Y]=K.i,K.v&&c(Y,K.v);var er=(Z===B?G:C)(K.i);er<=125&&0===Q&&0===V?(X++,J.push({i:er,v:0,o:1,t:Y})):Q++}else if(Z&U){if(Q>0)Q--;else if(X>0){for(V=0;!J[J.length-1].o;)J.pop();var fr=J[J.length-1].t;null!=fr&&(b.set(fr,Y),b.set(Y,fr)),J.pop(),X--}K=J[J.length-1],d[Y]=K.i,K.v&&c(Y,K.v)}else Z&P?(0===Q&&(V>0?V--:!K.o&&J.length>1&&(J.pop(),K=J[J.length-1])),d[Y]=K.i):Z&I&&(d[Y]=h.level);else d[Y]=K.i,K.v&&Z!==R&&c(Y,K.v)}for(var ar=[],nr=null,ir=h.start;ir<=h.end;ir++){var vr=f[ir];if(!(vr&o)){var or=d[ir],tr=vr&n,ur=vr===U;nr&&or===nr.i?(nr.u=ir,nr.l=tr):ar.push(nr={h:ir,u:ir,i:or,k:ur,l:tr})}}for(var lr=[],cr=0;cr<ar.length;cr++){var dr=ar[cr];if(!dr.k||dr.k&&!b.has(dr.h)){for(var br=[nr=dr],sr=void 0;nr&&nr.l&&null!=(sr=b.get(nr.u));)for(var hr=cr+1;hr<ar.length;hr++)if(ar[hr].h===sr){br.push(nr=ar[hr]);break}for(var kr=[],mr=0;mr<br.length;mr++)for(var jr=br[mr],gr=jr.h;gr<=jr.u;gr++)kr.push(gr);for(var pr=d[kr[0]],qr=h.level,wr=kr[0]-1;wr>=0;wr--)if(!(f[wr]&o)){qr=d[wr];break}var yr=kr[kr.length-1],xr=d[yr],_r=h.level;if(!(f[yr]&n))for(var Mr=yr+1;Mr<=h.end;Mr++)if(!(f[Mr]&o)){_r=d[Mr];break}lr.push({m:kr,j:Math.max(qr,pr)%2?w:q,g:Math.max(_r,xr)%2?w:q})}}for(var zr=0;zr<lr.length;zr++){var Ir=lr[zr],Lr=Ir.m,Sr=Ir.j,Rr=Ir.g,Or=1&d[Lr[0]]?w:q;if(u.get(O))for(var Er=0;Er<Lr.length;Er++){var Nr=Lr[Er];if(f[Nr]&O){for(var Tr=Sr,Ar=Er-1;Ar>=0;Ar--)if(!(f[Lr[Ar]]&o)){Tr=f[Lr[Ar]];break}c(Nr,Tr&(n|U)?S:Tr)}}if(u.get(y))for(var Dr=0;Dr<Lr.length;Dr++){var Pr=Lr[Dr];if(f[Pr]&y)for(var Wr=Dr-1;Wr>=-1;Wr--){var Br=-1===Wr?Sr:f[Lr[Wr]];if(Br&i){Br===E&&c(Pr,M);break}}}if(u.get(E))for(var Fr=0;Fr<Lr.length;Fr++){var Ur=Lr[Fr];f[Ur]&E&&c(Ur,w)}if(u.get(x)||u.get(z))for(var Cr=1;Cr<Lr.length-1;Cr++){var Gr=Lr[Cr];if(f[Gr]&(x|z)){for(var Hr=0,Jr=0,Kr=Cr-1;Kr>=0&&(Hr=f[Lr[Kr]])&o;Kr--);for(var Qr=Cr+1;Qr<Lr.length&&(Jr=f[Lr[Qr]])&o;Qr++);Hr===Jr&&(f[Gr]===x?Hr===y:Hr&(y|M))&&c(Gr,Hr)}}if(u.get(y))for(var Vr=0;Vr<Lr.length;Vr++){var Xr=Lr[Vr];if(f[Xr]&y){for(var Yr=Vr-1;Yr>=0&&f[Lr[Yr]]&(_|o);Yr--)c(Lr[Yr],y);for(Vr++;Vr<Lr.length&&f[Lr[Vr]]&(_|o|y);Vr++)f[Lr[Vr]]!==y&&c(Lr[Vr],y)}}if(u.get(_)||u.get(x)||u.get(z))for(var Zr=0;Zr<Lr.length;Zr++){var $r=Lr[Zr];if(f[$r]&(_|x|z)){c($r,S);for(var re=Zr-1;re>=0&&f[Lr[re]]&o;re--)c(Lr[re],S);for(var ee=Zr+1;ee<Lr.length&&f[Lr[ee]]&o;ee++)c(Lr[ee],S)}}if(u.get(y))for(var fe=0,ae=Sr;fe<Lr.length;fe++){var ne=Lr[fe],ie=f[ne];ie&y?ae===q&&c(ne,q):ie&i&&(ae=ie)}if(u.get(v)){for(var ve=w|y|M,oe=ve|q,te=[],ue=[],le=0;le<Lr.length;le++)if(f[Lr[le]]&v){var ce=r[Lr[le]],de=void 0;if(null!==j(ce)){if(!(ue.length<63))break;ue.push({char:ce,seqIndex:le})}else if(null!==(de=g(ce)))for(var be=ue.length-1;be>=0;be--){var se=ue[be].char;if(se===de||se===g(p(ce))||j(p(se))===ce){te.push([ue[be].seqIndex,le]),ue.length=be;break}}}te.sort((function(r,e){return r[0]-e[0]}));for(var he=0;he<te.length;he++){for(var ke=te[he],me=ke[0],je=ke[1],ge=!1,pe=0,qe=me+1;qe<je;qe++){var we=Lr[qe];if(f[we]&oe){ge=!0;var ye=f[we]&ve?w:q;if(ye===Or){pe=ye;break}}}if(ge&&!pe){pe=Sr;for(var xe=me-1;xe>=0;xe--){var _e=Lr[xe];if(f[_e]&oe){var Me=f[_e]&ve?w:q;pe=Me!==Or?Me:Or;break}}}if(pe){if(f[Lr[me]]=f[Lr[je]]=pe,pe!==Or)for(var ze=me+1;ze<Lr.length;ze++)if(!(f[Lr[ze]]&o)){l(r[Lr[ze]])&O&&(f[Lr[ze]]=pe);break}if(pe!==Or)for(var Ie=je+1;Ie<Lr.length;Ie++)if(!(f[Lr[Ie]]&o)){l(r[Lr[Ie]])&O&&(f[Lr[Ie]]=pe);break}}}for(var Le=0;Le<Lr.length;Le++)if(f[Lr[Le]]&v){for(var Se=Le,Re=Le,Oe=Sr,Ee=Le-1;Ee>=0;Ee--){if(!(f[Lr[Ee]]&o)){Oe=f[Lr[Ee]]&ve?w:q;break}Se=Ee}for(var Ne=Rr,Te=Le+1;Te<Lr.length;Te++){if(!(f[Lr[Te]]&(v|o))){Ne=f[Lr[Te]]&ve?w:q;break}Re=Te}for(var Ae=Se;Ae<=Re;Ae++)f[Lr[Ae]]=Oe===Ne?Oe:Or;Le=Re}}}for(var De=h.start;De<=h.end;De++){var Pe=d[De],We=f[De];if(1&Pe?We&(q|y|M)&&d[De]++:We&w?d[De]++:We&(M|y)&&(d[De]+=2),We&o&&(d[De]=0===De?h.level:d[De-1]),De===h.end||l(r[De])&(L|I))for(var Be=De;Be>=0&&l(r[Be])&t;Be--)d[Be]=h.level}}return{levels:d,paragraphs:s};function Fe(e,a){for(var i=e;i<r.length;i++){var v=f[i];if(v&(w|E))return 1;if(v&(I|q)||a&&v===U)return 0;if(v&n){var o=Ue(i);i=-1===o?r.length:o}}return 0}function Ue(e){for(var a=1,i=e+1;i<r.length;i++){var v=f[i];if(v&I)break;if(v&U){if(0==--a)return i}else v&n&&a++}return-1}},r.getMirroredCharacter=G,r.getMirroredCharactersMap=function(r,e,f,a){var n=r.length;f=Math.max(0,null==f?0:+f),a=Math.min(n-1,null==a?n-1:+a);for(var i=new Map,v=f;v<=a;v++)if(1&e[v]){var o=G(r[v]);null!==o&&i.set(v,o)}return i},r.getReorderSegments=H,r.getReorderedIndices=J,r.getReorderedString=function(r,e,f,a){var n=J(r,e,f,a),i=[].concat(r);return n.forEach((function(f,a){i[a]=(1&e.levels[f]?G(r[f]):null)||r[f]})),i.join("")},r.openingToClosingBracket=j,Object.defineProperty(r,"p",{value:!0}),r}({})}})); diff --git a/vanilla/node_modules/bidi-js/dist/bidi.min.mjs b/vanilla/node_modules/bidi-js/dist/bidi.min.mjs new file mode 100644 index 0000000..913340f --- /dev/null +++ b/vanilla/node_modules/bidi-js/dist/bidi.min.mjs @@ -0,0 +1 @@ +function r(){return function(r){var e={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"},f={},a={};f.L=1,a[1]="L",Object.keys(e).forEach((function(r,e){f[r]=1<<e+1,a[f[r]]=r})),Object.freeze(f);var n=f.LRI|f.RLI|f.FSI,v=f.L|f.R|f.AL,i=f.B|f.S|f.WS|f.ON|f.FSI|f.LRI|f.RLI|f.PDI,o=f.BN|f.RLE|f.LRE|f.RLO|f.LRO|f.PDF,t=f.S|f.WS|f.B|n|f.PDI|o,u=null;function l(r){return function(){if(!u){u=new Map;var r=function(r){if(e.hasOwnProperty(r)){var a=0;e[r].split(",").forEach((function(e){var n=e.split("+"),v=n[0],i=n[1];v=parseInt(v,36),i=i?parseInt(i,36):0,u.set(a+=v,f[r]);for(var o=0;o<i;o++)u.set(++a,f[r])}))}};for(var a in e)r(a)}}(),u.get(r.codePointAt(0))||f.L}var c,d,b,s="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",k="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";function h(r,e){var f,a=0,n=new Map,v=e&&new Map;return r.split(",").forEach((function r(i){if(-1!==i.indexOf("+"))for(var o=+i;o--;)r(f);else{f=i;var t=i.split(">"),u=t[0],l=t[1];u=String.fromCodePoint(a+=parseInt(u,36)),l=String.fromCodePoint(a+=parseInt(l,36)),n.set(u,l),e&&v.set(l,u)}})),{map:n,reverseMap:v}}function m(){if(!c){var r=h(s,!0),e=r.map,f=r.reverseMap;c=e,d=f,b=h(k,!1).map}}function j(r){return m(),c.get(r)||null}function g(r){return m(),d.get(r)||null}function p(r){return m(),b.get(r)||null}var q=f.L,w=f.R,x=f.EN,_=f.ES,y=f.ET,M=f.AN,z=f.CS,I=f.B,L=f.S,S=f.ON,R=f.BN,O=f.NSM,E=f.AL,N=f.LRO,A=f.RLO,D=f.LRE,P=f.RLE,T=f.PDF,W=f.LRI,B=f.RLI,F=f.FSI,U=f.PDI;var C;function G(r){return function(){if(!C){var r=h("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",!0),e=r.map;r.reverseMap.forEach((function(r,f){e.set(f,r)})),C=e}}(),C.get(r)||null}function H(r,e,f,a){var n=r.length;f=Math.max(0,null==f?0:+f),a=Math.min(n-1,null==a?n-1:+a);var v=[];return e.paragraphs.forEach((function(n){var i=Math.max(f,n.start),o=Math.min(a,n.end);if(i<o){for(var u=e.levels.slice(i,o+1),c=o;c>=i&&l(r[c])&t;c--)u[c]=n.level;for(var d=n.level,b=1/0,s=0;s<u.length;s++){var k=u[s];k>d&&(d=k),k<b&&(b=1|k)}for(var h=d;h>=b;h--)for(var m=0;m<u.length;m++)if(u[m]>=h){for(var j=m;m+1<u.length&&u[m+1]>=h;)m++;m>j&&v.push([j+i,m+i])}}})),v}function J(r,e,f,a){for(var n=H(r,e,f,a),v=[],i=0;i<r.length;i++)v[i]=i;return n.forEach((function(r){for(var e=r[0],f=r[1],a=v.slice(e,f+1),n=a.length;n--;)v[f-n]=a[n]})),v}return r.closingToOpeningBracket=g,r.getBidiCharType=l,r.getBidiCharTypeName=function(r){return a[l(r)]},r.getCanonicalBracket=p,r.getEmbeddingLevels=function(r,e){for(var f=new Uint32Array(r.length),a=0;a<r.length;a++)f[a]=l(r[a]);var u=new Map;function c(r,e){var a=f[r];f[r]=e,u.set(a,u.get(a)-1),a&i&&u.set(i,u.get(i)-1),u.set(e,(u.get(e)||0)+1),e&i&&u.set(i,(u.get(i)||0)+1)}for(var d=new Uint8Array(r.length),b=new Map,s=[],k=null,h=0;h<r.length;h++)k||s.push(k={start:h,end:r.length-1,level:"rtl"===e?1:"ltr"===e?0:Fe(h,!1)}),f[h]&I&&(k.end=h,k=null);for(var m=P|D|A|N|n|U|T|I,C=function(r){return r+(1&r?1:2)},G=function(r){return r+(1&r?2:1)},H=0;H<s.length;H++){var J=[{v:(k=s[H]).level,i:0,o:0}],K=void 0,Q=0,V=0,X=0;u.clear();for(var Y=k.start;Y<=k.end;Y++){var Z=f[Y];if(K=J[J.length-1],u.set(Z,(u.get(Z)||0)+1),Z&i&&u.set(i,(u.get(i)||0)+1),Z&m)if(Z&(P|D)){d[Y]=K.v;var $=(Z===P?G:C)(K.v);$<=125&&!Q&&!V?J.push({v:$,i:0,o:0}):Q||V++}else if(Z&(A|N)){d[Y]=K.v;var rr=(Z===A?G:C)(K.v);rr<=125&&!Q&&!V?J.push({v:rr,i:Z&A?w:q,o:0}):Q||V++}else if(Z&n){Z&F&&(Z=1===Fe(Y+1,!0)?B:W),d[Y]=K.v,K.i&&c(Y,K.i);var er=(Z===B?G:C)(K.v);er<=125&&0===Q&&0===V?(X++,J.push({v:er,i:0,o:1,t:Y})):Q++}else if(Z&U){if(Q>0)Q--;else if(X>0){for(V=0;!J[J.length-1].o;)J.pop();var fr=J[J.length-1].t;null!=fr&&(b.set(fr,Y),b.set(Y,fr)),J.pop(),X--}K=J[J.length-1],d[Y]=K.v,K.i&&c(Y,K.i)}else Z&T?(0===Q&&(V>0?V--:!K.o&&J.length>1&&(J.pop(),K=J[J.length-1])),d[Y]=K.v):Z&I&&(d[Y]=k.level);else d[Y]=K.v,K.i&&Z!==R&&c(Y,K.i)}for(var ar=[],nr=null,vr=k.start;vr<=k.end;vr++){var ir=f[vr];if(!(ir&o)){var or=d[vr],tr=ir&n,ur=ir===U;nr&&or===nr.v?(nr.u=vr,nr.l=tr):ar.push(nr={k:vr,u:vr,v:or,h:ur,l:tr})}}for(var lr=[],cr=0;cr<ar.length;cr++){var dr=ar[cr];if(!dr.h||dr.h&&!b.has(dr.k)){for(var br=[nr=dr],sr=void 0;nr&&nr.l&&null!=(sr=b.get(nr.u));)for(var kr=cr+1;kr<ar.length;kr++)if(ar[kr].k===sr){br.push(nr=ar[kr]);break}for(var hr=[],mr=0;mr<br.length;mr++)for(var jr=br[mr],gr=jr.k;gr<=jr.u;gr++)hr.push(gr);for(var pr=d[hr[0]],qr=k.level,wr=hr[0]-1;wr>=0;wr--)if(!(f[wr]&o)){qr=d[wr];break}var xr=hr[hr.length-1],_r=d[xr],yr=k.level;if(!(f[xr]&n))for(var Mr=xr+1;Mr<=k.end;Mr++)if(!(f[Mr]&o)){yr=d[Mr];break}lr.push({m:hr,j:Math.max(qr,pr)%2?w:q,g:Math.max(yr,_r)%2?w:q})}}for(var zr=0;zr<lr.length;zr++){var Ir=lr[zr],Lr=Ir.m,Sr=Ir.j,Rr=Ir.g,Or=1&d[Lr[0]]?w:q;if(u.get(O))for(var Er=0;Er<Lr.length;Er++){var Nr=Lr[Er];if(f[Nr]&O){for(var Ar=Sr,Dr=Er-1;Dr>=0;Dr--)if(!(f[Lr[Dr]]&o)){Ar=f[Lr[Dr]];break}c(Nr,Ar&(n|U)?S:Ar)}}if(u.get(x))for(var Pr=0;Pr<Lr.length;Pr++){var Tr=Lr[Pr];if(f[Tr]&x)for(var Wr=Pr-1;Wr>=-1;Wr--){var Br=-1===Wr?Sr:f[Lr[Wr]];if(Br&v){Br===E&&c(Tr,M);break}}}if(u.get(E))for(var Fr=0;Fr<Lr.length;Fr++){var Ur=Lr[Fr];f[Ur]&E&&c(Ur,w)}if(u.get(_)||u.get(z))for(var Cr=1;Cr<Lr.length-1;Cr++){var Gr=Lr[Cr];if(f[Gr]&(_|z)){for(var Hr=0,Jr=0,Kr=Cr-1;Kr>=0&&(Hr=f[Lr[Kr]])&o;Kr--);for(var Qr=Cr+1;Qr<Lr.length&&(Jr=f[Lr[Qr]])&o;Qr++);Hr===Jr&&(f[Gr]===_?Hr===x:Hr&(x|M))&&c(Gr,Hr)}}if(u.get(x))for(var Vr=0;Vr<Lr.length;Vr++){var Xr=Lr[Vr];if(f[Xr]&x){for(var Yr=Vr-1;Yr>=0&&f[Lr[Yr]]&(y|o);Yr--)c(Lr[Yr],x);for(Vr++;Vr<Lr.length&&f[Lr[Vr]]&(y|o|x);Vr++)f[Lr[Vr]]!==x&&c(Lr[Vr],x)}}if(u.get(y)||u.get(_)||u.get(z))for(var Zr=0;Zr<Lr.length;Zr++){var $r=Lr[Zr];if(f[$r]&(y|_|z)){c($r,S);for(var re=Zr-1;re>=0&&f[Lr[re]]&o;re--)c(Lr[re],S);for(var ee=Zr+1;ee<Lr.length&&f[Lr[ee]]&o;ee++)c(Lr[ee],S)}}if(u.get(x))for(var fe=0,ae=Sr;fe<Lr.length;fe++){var ne=Lr[fe],ve=f[ne];ve&x?ae===q&&c(ne,q):ve&v&&(ae=ve)}if(u.get(i)){for(var ie=w|x|M,oe=ie|q,te=[],ue=[],le=0;le<Lr.length;le++)if(f[Lr[le]]&i){var ce=r[Lr[le]],de=void 0;if(null!==j(ce)){if(!(ue.length<63))break;ue.push({char:ce,seqIndex:le})}else if(null!==(de=g(ce)))for(var be=ue.length-1;be>=0;be--){var se=ue[be].char;if(se===de||se===g(p(ce))||j(p(se))===ce){te.push([ue[be].seqIndex,le]),ue.length=be;break}}}te.sort((function(r,e){return r[0]-e[0]}));for(var ke=0;ke<te.length;ke++){for(var he=te[ke],me=he[0],je=he[1],ge=!1,pe=0,qe=me+1;qe<je;qe++){var we=Lr[qe];if(f[we]&oe){ge=!0;var xe=f[we]&ie?w:q;if(xe===Or){pe=xe;break}}}if(ge&&!pe){pe=Sr;for(var _e=me-1;_e>=0;_e--){var ye=Lr[_e];if(f[ye]&oe){var Me=f[ye]&ie?w:q;pe=Me!==Or?Me:Or;break}}}if(pe){if(f[Lr[me]]=f[Lr[je]]=pe,pe!==Or)for(var ze=me+1;ze<Lr.length;ze++)if(!(f[Lr[ze]]&o)){l(r[Lr[ze]])&O&&(f[Lr[ze]]=pe);break}if(pe!==Or)for(var Ie=je+1;Ie<Lr.length;Ie++)if(!(f[Lr[Ie]]&o)){l(r[Lr[Ie]])&O&&(f[Lr[Ie]]=pe);break}}}for(var Le=0;Le<Lr.length;Le++)if(f[Lr[Le]]&i){for(var Se=Le,Re=Le,Oe=Sr,Ee=Le-1;Ee>=0;Ee--){if(!(f[Lr[Ee]]&o)){Oe=f[Lr[Ee]]&ie?w:q;break}Se=Ee}for(var Ne=Rr,Ae=Le+1;Ae<Lr.length;Ae++){if(!(f[Lr[Ae]]&(i|o))){Ne=f[Lr[Ae]]&ie?w:q;break}Re=Ae}for(var De=Se;De<=Re;De++)f[Lr[De]]=Oe===Ne?Oe:Or;Le=Re}}}for(var Pe=k.start;Pe<=k.end;Pe++){var Te=d[Pe],We=f[Pe];if(1&Te?We&(q|x|M)&&d[Pe]++:We&w?d[Pe]++:We&(M|x)&&(d[Pe]+=2),We&o&&(d[Pe]=0===Pe?k.level:d[Pe-1]),Pe===k.end||l(r[Pe])&(L|I))for(var Be=Pe;Be>=0&&l(r[Be])&t;Be--)d[Be]=k.level}}return{levels:d,paragraphs:s};function Fe(e,a){for(var v=e;v<r.length;v++){var i=f[v];if(i&(w|E))return 1;if(i&(I|q)||a&&i===U)return 0;if(i&n){var o=Ue(v);v=-1===o?r.length:o}}return 0}function Ue(e){for(var a=1,v=e+1;v<r.length;v++){var i=f[v];if(i&I)break;if(i&U){if(0==--a)return v}else i&n&&a++}return-1}},r.getMirroredCharacter=G,r.getMirroredCharactersMap=function(r,e,f,a){var n=r.length;f=Math.max(0,null==f?0:+f),a=Math.min(n-1,null==a?n-1:+a);for(var v=new Map,i=f;i<=a;i++)if(1&e[i]){var o=G(r[i]);null!==o&&v.set(i,o)}return v},r.getReorderSegments=H,r.getReorderedIndices=J,r.getReorderedString=function(r,e,f,a){var n=J(r,e,f,a),v=[].concat(r);return n.forEach((function(f,a){v[a]=(1&e.levels[f]?G(r[f]):null)||r[f]})),v.join("")},r.openingToClosingBracket=j,Object.defineProperty(r,"p",{value:!0}),r}({})}export default r; diff --git a/vanilla/node_modules/bidi-js/dist/bidi.mjs b/vanilla/node_modules/bidi-js/dist/bidi.mjs new file mode 100644 index 0000000..848c03d --- /dev/null +++ b/vanilla/node_modules/bidi-js/dist/bidi.mjs @@ -0,0 +1,1002 @@ +function bidiFactory() { +var bidi = (function (exports) { + + // Bidi character types data, auto generated + var DATA = { + "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" + }; + + var TYPES = {}; + var TYPES_TO_NAMES = {}; + TYPES.L = 1; //L is the default + TYPES_TO_NAMES[1] = 'L'; + Object.keys(DATA).forEach(function (type, i) { + TYPES[type] = 1 << (i + 1); + TYPES_TO_NAMES[TYPES[type]] = type; + }); + Object.freeze(TYPES); + + var ISOLATE_INIT_TYPES = TYPES.LRI | TYPES.RLI | TYPES.FSI; + var STRONG_TYPES = TYPES.L | TYPES.R | TYPES.AL; + var NEUTRAL_ISOLATE_TYPES = TYPES.B | TYPES.S | TYPES.WS | TYPES.ON | TYPES.FSI | TYPES.LRI | TYPES.RLI | TYPES.PDI; + var BN_LIKE_TYPES = TYPES.BN | TYPES.RLE | TYPES.LRE | TYPES.RLO | TYPES.LRO | TYPES.PDF; + var TRAILING_TYPES = TYPES.S | TYPES.WS | TYPES.B | ISOLATE_INIT_TYPES | TYPES.PDI | BN_LIKE_TYPES; + + var map = null; + + function parseData () { + if (!map) { + //const start = performance.now() + map = new Map(); + var loop = function ( type ) { + if (DATA.hasOwnProperty(type)) { + var lastCode = 0; + DATA[type].split(',').forEach(function (range) { + var ref = range.split('+'); + var skip = ref[0]; + var step = ref[1]; + skip = parseInt(skip, 36); + step = step ? parseInt(step, 36) : 0; + map.set(lastCode += skip, TYPES[type]); + for (var i = 0; i < step; i++) { + map.set(++lastCode, TYPES[type]); + } + }); + } + }; + + for (var type in DATA) loop( 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)] + } + + // Bidi bracket pairs data, auto generated + var data$1 = { + "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" + }; + + /** + * 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>}} + */ + function parseCharacterMap (encodedString, includeReverse) { + var radix = 36; + var lastCode = 0; + var map = new Map(); + var reverseMap = includeReverse && new Map(); + var prevPair; + encodedString.split(',').forEach(function visit(entry) { + if (entry.indexOf('+') !== -1) { + for (var i = +entry; i--;) { + visit(prevPair); + } + } else { + prevPair = entry; + var ref = entry.split('>'); + var a = ref[0]; + var b = ref[1]; + 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: map, reverseMap: reverseMap } + } + + var openToClose, closeToOpen, canonical; + + function parse$1 () { + if (!openToClose) { + //const start = performance.now() + var ref = parseCharacterMap(data$1.pairs, true); + var map = ref.map; + var reverseMap = ref.reverseMap; + openToClose = map; + closeToOpen = reverseMap; + canonical = parseCharacterMap(data$1.canonical, false).map; + //console.log(`brackets parsed in ${performance.now() - start}ms`) + } + } + + function openingToClosingBracket (char) { + parse$1(); + return openToClose.get(char) || null + } + + function closingToOpeningBracket (char) { + parse$1(); + return closeToOpen.get(char) || null + } + + function getCanonicalBracket (char) { + parse$1(); + return canonical.get(char) || null + } + + // Local type aliases + var TYPE_L = TYPES.L; + var TYPE_R = TYPES.R; + var TYPE_EN = TYPES.EN; + var TYPE_ES = TYPES.ES; + var TYPE_ET = TYPES.ET; + var TYPE_AN = TYPES.AN; + var TYPE_CS = TYPES.CS; + var TYPE_B = TYPES.B; + var TYPE_S = TYPES.S; + var TYPE_ON = TYPES.ON; + var TYPE_BN = TYPES.BN; + var TYPE_NSM = TYPES.NSM; + var TYPE_AL = TYPES.AL; + var TYPE_LRO = TYPES.LRO; + var TYPE_RLO = TYPES.RLO; + var TYPE_LRE = TYPES.LRE; + var TYPE_RLE = TYPES.RLE; + var TYPE_PDF = TYPES.PDF; + var TYPE_LRI = TYPES.LRI; + var TYPE_RLI = TYPES.RLI; + var TYPE_FSI = TYPES.FSI; + var TYPE_PDI = TYPES.PDI; + + /** + * @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} + */ + function getEmbeddingLevels (string, baseDirection) { + var MAX_DEPTH = 125; + + // Start by mapping all characters to their unicode type, as a bitmask integer + var charTypes = new Uint32Array(string.length); + for (var i = 0; i < string.length; i++) { + charTypes[i] = getBidiCharType(string[i]); + } + + var charTypeCounts = new Map(); //will be cleared at start of each paragraph + function changeCharType(i, type) { + var 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); + } + } + + var embedLevels = new Uint8Array(string.length); + var isolationPairs = new Map(); //init->pdi and pdi->init + + // === 3.3.1 The Paragraph Level === + // 3.3.1 P1: Split the text into paragraphs + var paragraphs = []; // [{start, end, level}, ...] + var paragraph = null; + for (var i$1 = 0; i$1 < string.length; i$1++) { + if (!paragraph) { + paragraphs.push(paragraph = { + start: i$1, + end: string.length - 1, + // 3.3.1 P2-P3: Determine the paragraph level + level: baseDirection === 'rtl' ? 1 : baseDirection === 'ltr' ? 0 : determineAutoEmbedLevel(i$1, false) + }); + } + if (charTypes[i$1] & TYPE_B) { + paragraph.end = i$1; + paragraph = null; + } + } + + var FORMATTING_TYPES = TYPE_RLE | TYPE_LRE | TYPE_RLO | TYPE_LRO | ISOLATE_INIT_TYPES | TYPE_PDI | TYPE_PDF | TYPE_B; + var nextEven = function (n) { return n + ((n & 1) ? 1 : 2); }; + var nextOdd = function (n) { return n + ((n & 1) ? 2 : 1); }; + + // Everything from here on will operate per paragraph. + for (var paraIdx = 0; paraIdx < paragraphs.length; paraIdx++) { + paragraph = paragraphs[paraIdx]; + var statusStack = [{ + _level: paragraph.level, + _override: 0, //0=neutral, 1=L, 2=R + _isolate: 0 //bool + }]; + var stackTop = (void 0); + var overflowIsolateCount = 0; + var overflowEmbeddingCount = 0; + var validIsolateCount = 0; + charTypeCounts.clear(); + + // === 3.3.2 Explicit Levels and Directions === + for (var i$2 = paragraph.start; i$2 <= paragraph.end; i$2++) { + var charType = charTypes[i$2]; + 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$2] = stackTop._level; // 5.2 + var 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$2] = stackTop._level; // 5.2 + var level$1 = (charType === TYPE_RLO ? nextOdd : nextEven)(stackTop._level); + if (level$1 <= MAX_DEPTH && !overflowIsolateCount && !overflowEmbeddingCount) { + statusStack.push({ + _level: level$1, + _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$2 + 1, true) === 1 ? TYPE_RLI : TYPE_LRI; + } + + embedLevels[i$2] = stackTop._level; + if (stackTop._override) { + changeCharType(i$2, stackTop._override); + } + var level$2 = (charType === TYPE_RLI ? nextOdd : nextEven)(stackTop._level); + if (level$2 <= MAX_DEPTH && overflowIsolateCount === 0 && overflowEmbeddingCount === 0) { + validIsolateCount++; + statusStack.push({ + _level: level$2, + _override: 0, + _isolate: 1, + _isolInitIndex: i$2 + }); + } 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: + var isolInitIndex = statusStack[statusStack.length - 1]._isolInitIndex; + if (isolInitIndex != null) { + isolationPairs.set(isolInitIndex, i$2); + isolationPairs.set(i$2, isolInitIndex); + } + statusStack.pop(); + validIsolateCount--; + } + stackTop = statusStack[statusStack.length - 1]; + embedLevels[i$2] = stackTop._level; + if (stackTop._override) { + changeCharType(i$2, 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$2] = stackTop._level; // 5.2 + } + + // End of Paragraph: 3.3.2 X8 + else if (charType & TYPE_B) { + embedLevels[i$2] = paragraph.level; + } + } + + // Non-formatting characters: 3.3.2 X6 + else { + embedLevels[i$2] = 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$2, 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 + var levelRuns = []; + var currentRun = null; + for (var i$3 = paragraph.start; i$3 <= paragraph.end; i$3++) { + var charType$1 = charTypes[i$3]; + if (!(charType$1 & BN_LIKE_TYPES)) { + var lvl = embedLevels[i$3]; + var isIsolInit = charType$1 & ISOLATE_INIT_TYPES; + var isPDI = charType$1 === TYPE_PDI; + if (currentRun && lvl === currentRun._level) { + currentRun._end = i$3; + currentRun._endsWithIsolInit = isIsolInit; + } else { + levelRuns.push(currentRun = { + _start: i$3, + _end: i$3, + _level: lvl, + _startsWithPDI: isPDI, + _endsWithIsolInit: isIsolInit + }); + } + } + } + var isolatingRunSeqs = []; // [{seqIndices: [], sosType: L|R, eosType: L|R}] + for (var runIdx = 0; runIdx < levelRuns.length; runIdx++) { + var run = levelRuns[runIdx]; + if (!run._startsWithPDI || (run._startsWithPDI && !isolationPairs.has(run._start))) { + var seqRuns = [currentRun = run]; + for (var pdiIndex = (void 0); currentRun && currentRun._endsWithIsolInit && (pdiIndex = isolationPairs.get(currentRun._end)) != null;) { + for (var i$4 = runIdx + 1; i$4 < levelRuns.length; i$4++) { + if (levelRuns[i$4]._start === pdiIndex) { + seqRuns.push(currentRun = levelRuns[i$4]); + break + } + } + } + // build flat list of indices across all runs: + var seqIndices = []; + for (var i$5 = 0; i$5 < seqRuns.length; i$5++) { + var run$1 = seqRuns[i$5]; + for (var j = run$1._start; j <= run$1._end; j++) { + seqIndices.push(j); + } + } + // determine the sos/eos types: + var firstLevel = embedLevels[seqIndices[0]]; + var prevLevel = paragraph.level; + for (var i$6 = seqIndices[0] - 1; i$6 >= 0; i$6--) { + if (!(charTypes[i$6] & BN_LIKE_TYPES)) { //5.2 + prevLevel = embedLevels[i$6]; + break + } + } + var lastIndex = seqIndices[seqIndices.length - 1]; + var lastLevel = embedLevels[lastIndex]; + var nextLevel = paragraph.level; + if (!(charTypes[lastIndex] & ISOLATE_INIT_TYPES)) { + for (var i$7 = lastIndex + 1; i$7 <= paragraph.end; i$7++) { + if (!(charTypes[i$7] & BN_LIKE_TYPES)) { //5.2 + nextLevel = embedLevels[i$7]; + 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 (var seqIdx = 0; seqIdx < isolatingRunSeqs.length; seqIdx++) { + var ref = isolatingRunSeqs[seqIdx]; + var seqIndices$1 = ref._seqIndices; + var sosType = ref._sosType; + var eosType = ref._eosType; + /** + * All the level runs in an isolating run sequence have the same embedding level. + * + * DO NOT change any `embedLevels[i]` within the current scope. + */ + var embedDirection = ((embedLevels[seqIndices$1[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 (var si = 0; si < seqIndices$1.length; si++) { + var i$8 = seqIndices$1[si]; + if (charTypes[i$8] & TYPE_NSM) { + var prevType = sosType; + for (var sj = si - 1; sj >= 0; sj--) { + if (!(charTypes[seqIndices$1[sj]] & BN_LIKE_TYPES)) { //5.2 scan back to first non-BN + prevType = charTypes[seqIndices$1[sj]]; + break + } + } + changeCharType(i$8, (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 (var si$1 = 0; si$1 < seqIndices$1.length; si$1++) { + var i$9 = seqIndices$1[si$1]; + if (charTypes[i$9] & TYPE_EN) { + for (var sj$1 = si$1 - 1; sj$1 >= -1; sj$1--) { + var prevCharType = sj$1 === -1 ? sosType : charTypes[seqIndices$1[sj$1]]; + if (prevCharType & STRONG_TYPES) { + if (prevCharType === TYPE_AL) { + changeCharType(i$9, TYPE_AN); + } + break + } + } + } + } + } + + // W3. Change all ALs to R + if (charTypeCounts.get(TYPE_AL)) { + for (var si$2 = 0; si$2 < seqIndices$1.length; si$2++) { + var i$10 = seqIndices$1[si$2]; + if (charTypes[i$10] & TYPE_AL) { + changeCharType(i$10, 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 (var si$3 = 1; si$3 < seqIndices$1.length - 1; si$3++) { + var i$11 = seqIndices$1[si$3]; + if (charTypes[i$11] & (TYPE_ES | TYPE_CS)) { + var prevType$1 = 0, nextType = 0; + for (var sj$2 = si$3 - 1; sj$2 >= 0; sj$2--) { + prevType$1 = charTypes[seqIndices$1[sj$2]]; + if (!(prevType$1 & BN_LIKE_TYPES)) { //5.2 + break + } + } + for (var sj$3 = si$3 + 1; sj$3 < seqIndices$1.length; sj$3++) { + nextType = charTypes[seqIndices$1[sj$3]]; + if (!(nextType & BN_LIKE_TYPES)) { //5.2 + break + } + } + if (prevType$1 === nextType && (charTypes[i$11] === TYPE_ES ? prevType$1 === TYPE_EN : (prevType$1 & (TYPE_EN | TYPE_AN)))) { + changeCharType(i$11, prevType$1); + } + } + } + } + + // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. + if (charTypeCounts.get(TYPE_EN)) { + for (var si$4 = 0; si$4 < seqIndices$1.length; si$4++) { + var i$12 = seqIndices$1[si$4]; + if (charTypes[i$12] & TYPE_EN) { + for (var sj$4 = si$4 - 1; sj$4 >= 0 && (charTypes[seqIndices$1[sj$4]] & (TYPE_ET | BN_LIKE_TYPES)); sj$4--) { + changeCharType(seqIndices$1[sj$4], TYPE_EN); + } + for (si$4++; si$4 < seqIndices$1.length && (charTypes[seqIndices$1[si$4]] & (TYPE_ET | BN_LIKE_TYPES | TYPE_EN)); si$4++) { + if (charTypes[seqIndices$1[si$4]] !== TYPE_EN) { + changeCharType(seqIndices$1[si$4], 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 (var si$5 = 0; si$5 < seqIndices$1.length; si$5++) { + var i$13 = seqIndices$1[si$5]; + if (charTypes[i$13] & (TYPE_ET | TYPE_ES | TYPE_CS)) { + changeCharType(i$13, TYPE_ON); + // 5.2 transform adjacent BNs too: + for (var sj$5 = si$5 - 1; sj$5 >= 0 && (charTypes[seqIndices$1[sj$5]] & BN_LIKE_TYPES); sj$5--) { + changeCharType(seqIndices$1[sj$5], TYPE_ON); + } + for (var sj$6 = si$5 + 1; sj$6 < seqIndices$1.length && (charTypes[seqIndices$1[sj$6]] & BN_LIKE_TYPES); sj$6++) { + changeCharType(seqIndices$1[sj$6], 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 (var si$6 = 0, prevStrongType = sosType; si$6 < seqIndices$1.length; si$6++) { + var i$14 = seqIndices$1[si$6]; + var type = charTypes[i$14]; + if (type & TYPE_EN) { + if (prevStrongType === TYPE_L) { + changeCharType(i$14, 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. + var R_TYPES_FOR_N_STEPS = (TYPE_R | TYPE_EN | TYPE_AN); + var 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. + var bracketPairs = []; + { + var openerStack = []; + for (var si$7 = 0; si$7 < seqIndices$1.length; si$7++) { + // 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$1[si$7]] & NEUTRAL_ISOLATE_TYPES) { + var char = string[seqIndices$1[si$7]]; + var oppositeBracket = (void 0); + // Opening bracket + if (openingToClosingBracket(char) !== null) { + if (openerStack.length < 63) { + openerStack.push({ char: char, seqIndex: si$7 }); + } else { + break + } + } + // Closing bracket + else if ((oppositeBracket = closingToOpeningBracket(char)) !== null) { + for (var stackIdx = openerStack.length - 1; stackIdx >= 0; stackIdx--) { + var stackChar = openerStack[stackIdx].char; + if (stackChar === oppositeBracket || + stackChar === closingToOpeningBracket(getCanonicalBracket(char)) || + openingToClosingBracket(getCanonicalBracket(stackChar)) === char + ) { + bracketPairs.push([openerStack[stackIdx].seqIndex, si$7]); + openerStack.length = stackIdx; //pop the matching bracket and all following + break + } + } + } + } + } + bracketPairs.sort(function (a, b) { return a[0] - b[0]; }); + } + // * For each bracket-pair element in the list of pairs of text positions + for (var pairIdx = 0; pairIdx < bracketPairs.length; pairIdx++) { + var ref$1 = bracketPairs[pairIdx]; + var openSeqIdx = ref$1[0]; + var closeSeqIdx = ref$1[1]; + // 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. + var foundStrongType = false; + var useStrongType = 0; + for (var si$8 = openSeqIdx + 1; si$8 < closeSeqIdx; si$8++) { + var i$15 = seqIndices$1[si$8]; + if (charTypes[i$15] & STRONG_TYPES_FOR_N_STEPS) { + foundStrongType = true; + var lr = (charTypes[i$15] & 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 (var si$9 = openSeqIdx - 1; si$9 >= 0; si$9--) { + var i$16 = seqIndices$1[si$9]; + if (charTypes[i$16] & STRONG_TYPES_FOR_N_STEPS) { + var lr$1 = (charTypes[i$16] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L; + if (lr$1 !== embedDirection) { + useStrongType = lr$1; + } else { + useStrongType = embedDirection; + } + break + } + } + } + if (useStrongType) { + charTypes[seqIndices$1[openSeqIdx]] = charTypes[seqIndices$1[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 (var si$10 = openSeqIdx + 1; si$10 < seqIndices$1.length; si$10++) { + if (!(charTypes[seqIndices$1[si$10]] & BN_LIKE_TYPES)) { + if (getBidiCharType(string[seqIndices$1[si$10]]) & TYPE_NSM) { + charTypes[seqIndices$1[si$10]] = useStrongType; + } + break + } + } + } + if (useStrongType !== embedDirection) { + for (var si$11 = closeSeqIdx + 1; si$11 < seqIndices$1.length; si$11++) { + if (!(charTypes[seqIndices$1[si$11]] & BN_LIKE_TYPES)) { + if (getBidiCharType(string[seqIndices$1[si$11]]) & TYPE_NSM) { + charTypes[seqIndices$1[si$11]] = 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 (var si$12 = 0; si$12 < seqIndices$1.length; si$12++) { + if (charTypes[seqIndices$1[si$12]] & NEUTRAL_ISOLATE_TYPES) { + var niRunStart = si$12, niRunEnd = si$12; + var prevType$2 = sosType; //si === 0 ? sosType : (charTypes[seqIndices[si - 1]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L + for (var si2 = si$12 - 1; si2 >= 0; si2--) { + if (charTypes[seqIndices$1[si2]] & BN_LIKE_TYPES) { + niRunStart = si2; //5.2 treat BNs adjacent to NIs as NIs + } else { + prevType$2 = (charTypes[seqIndices$1[si2]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L; + break + } + } + var nextType$1 = eosType; + for (var si2$1 = si$12 + 1; si2$1 < seqIndices$1.length; si2$1++) { + if (charTypes[seqIndices$1[si2$1]] & (NEUTRAL_ISOLATE_TYPES | BN_LIKE_TYPES)) { + niRunEnd = si2$1; + } else { + nextType$1 = (charTypes[seqIndices$1[si2$1]] & R_TYPES_FOR_N_STEPS) ? TYPE_R : TYPE_L; + break + } + } + for (var sj$7 = niRunStart; sj$7 <= niRunEnd; sj$7++) { + charTypes[seqIndices$1[sj$7]] = prevType$2 === nextType$1 ? prevType$2 : embedDirection; + } + si$12 = niRunEnd; + } + } + } + } + + // === 3.3.6 Resolving Implicit Levels === + + for (var i$17 = paragraph.start; i$17 <= paragraph.end; i$17++) { + var level$3 = embedLevels[i$17]; + var type$1 = charTypes[i$17]; + // 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$3 & 1) { + if (type$1 & (TYPE_L | TYPE_EN | TYPE_AN)) { + embedLevels[i$17]++; + } + } + // 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$1 & TYPE_R) { + embedLevels[i$17]++; + } else if (type$1 & (TYPE_AN | TYPE_EN)) { + embedLevels[i$17] += 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$1 & BN_LIKE_TYPES) { + embedLevels[i$17] = i$17 === 0 ? paragraph.level : embedLevels[i$17 - 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$17 === paragraph.end || getBidiCharType(string[i$17]) & (TYPE_S | TYPE_B)) { + for (var j$1 = i$17; j$1 >= 0 && (getBidiCharType(string[j$1]) & TRAILING_TYPES); j$1--) { + embedLevels[j$1] = 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: paragraphs + } + + function determineAutoEmbedLevel (start, isFSI) { + // 3.3.1 P2 - P3 + for (var i = start; i < string.length; i++) { + var 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) { + var pdi = indexOfMatchingPDI(i); + i = pdi === -1 ? string.length : pdi; + } + } + return 0 + } + + function indexOfMatchingPDI (isolateStart) { + // 3.1.2 BD9 + var isolationLevel = 1; + for (var i = isolateStart + 1; i < string.length; i++) { + var 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 + } + } + + // Bidi mirrored chars data, auto generated + var data = "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"; + + var mirrorMap; + + function parse () { + if (!mirrorMap) { + //const start = performance.now() + var ref = parseCharacterMap(data, true); + var map = ref.map; + var reverseMap = ref.reverseMap; + // Combine both maps into one + reverseMap.forEach(function (value, key) { + map.set(key, value); + }); + mirrorMap = map; + //console.log(`mirrored chars parsed in ${performance.now() - start}ms`) + } + } + + 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>} + */ + function getMirroredCharactersMap(string, embeddingLevels, start, end) { + var strLen = string.length; + start = Math.max(0, start == null ? 0 : +start); + end = Math.min(strLen - 1, end == null ? strLen - 1 : +end); + + var map = new Map(); + for (var i = start; i <= end; i++) { + if (embeddingLevels[i] & 1) { //only odd (rtl) levels + var mirror = getMirroredCharacter(string[i]); + if (mirror !== null) { + map.set(i, mirror); + } + } + } + return map + } + + /** + * 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. + */ + function getReorderSegments(string, embeddingLevelsResult, start, end) { + var strLen = string.length; + start = Math.max(0, start == null ? 0 : +start); + end = Math.min(strLen - 1, end == null ? strLen - 1 : +end); + + var segments = []; + embeddingLevelsResult.paragraphs.forEach(function (paragraph) { + var lineStart = Math.max(start, paragraph.start); + var lineEnd = Math.min(end, paragraph.end); + if (lineStart < lineEnd) { + // Local slice for mutation + var 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 (var 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. + var maxLevel = paragraph.level; + var minOddLevel = Infinity; + for (var i$1 = 0; i$1 < lineLevels.length; i$1++) { + var level = lineLevels[i$1]; + if (level > maxLevel) { maxLevel = level; } + if (level < minOddLevel) { minOddLevel = level | 1; } + } + for (var lvl = maxLevel; lvl >= minOddLevel; lvl--) { + for (var i$2 = 0; i$2 < lineLevels.length; i$2++) { + if (lineLevels[i$2] >= lvl) { + var segStart = i$2; + while (i$2 + 1 < lineLevels.length && lineLevels[i$2 + 1] >= lvl) { + i$2++; + } + if (i$2 > segStart) { + segments.push([segStart + lineStart, i$2 + lineStart]); + } + } + } + } + } + }); + return segments + } + + /** + * @param {string} string + * @param {GetEmbeddingLevelsResult} embedLevelsResult + * @param {number} [start] + * @param {number} [end] + * @return {string} the new string with bidi segments reordered + */ + function getReorderedString(string, embedLevelsResult, start, end) { + var indices = getReorderedIndices(string, embedLevelsResult, start, end); + var chars = [].concat( string ); + indices.forEach(function (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 + */ + function getReorderedIndices(string, embedLevelsResult, start, end) { + var segments = getReorderSegments(string, embedLevelsResult, start, end); + // Fill an array with indices + var indices = []; + for (var i = 0; i < string.length; i++) { + indices[i] = i; + } + // Reverse each segment in order + segments.forEach(function (ref) { + var start = ref[0]; + var end = ref[1]; + + var slice = indices.slice(start, end + 1); + for (var i = slice.length; i--;) { + indices[end - i] = slice[i]; + } + }); + return indices + } + + exports.closingToOpeningBracket = closingToOpeningBracket; + exports.getBidiCharType = getBidiCharType; + exports.getBidiCharTypeName = getBidiCharTypeName; + exports.getCanonicalBracket = getCanonicalBracket; + exports.getEmbeddingLevels = getEmbeddingLevels; + exports.getMirroredCharacter = getMirroredCharacter; + exports.getMirroredCharactersMap = getMirroredCharactersMap; + exports.getReorderSegments = getReorderSegments; + exports.getReorderedIndices = getReorderedIndices; + exports.getReorderedString = getReorderedString; + exports.openingToClosingBracket = openingToClosingBracket; + + Object.defineProperty(exports, '__esModule', { value: true }); + + return exports; + +}({})); +return bidi} + +export default bidiFactory; diff --git a/vanilla/node_modules/bidi-js/package.json b/vanilla/node_modules/bidi-js/package.json new file mode 100644 index 0000000..d984d32 --- /dev/null +++ b/vanilla/node_modules/bidi-js/package.json @@ -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" + } +} diff --git a/vanilla/node_modules/bidi-js/src/brackets.js b/vanilla/node_modules/bidi-js/src/brackets.js new file mode 100644 index 0000000..c598bc1 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/brackets.js @@ -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 +} diff --git a/vanilla/node_modules/bidi-js/src/charTypes.js b/vanilla/node_modules/bidi-js/src/charTypes.js new file mode 100644 index 0000000..057e871 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/charTypes.js @@ -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 +} diff --git a/vanilla/node_modules/bidi-js/src/data/bidiBrackets.data.js b/vanilla/node_modules/bidi-js/src/data/bidiBrackets.data.js new file mode 100644 index 0000000..885ae97 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/data/bidiBrackets.data.js @@ -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" +} diff --git a/vanilla/node_modules/bidi-js/src/data/bidiCharTypes.data.js b/vanilla/node_modules/bidi-js/src/data/bidiCharTypes.data.js new file mode 100644 index 0000000..b263c80 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/data/bidiCharTypes.data.js @@ -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" +} diff --git a/vanilla/node_modules/bidi-js/src/data/bidiMirroring.data.js b/vanilla/node_modules/bidi-js/src/data/bidiMirroring.data.js new file mode 100644 index 0000000..b9cf987 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/data/bidiMirroring.data.js @@ -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" diff --git a/vanilla/node_modules/bidi-js/src/embeddingLevels.js b/vanilla/node_modules/bidi-js/src/embeddingLevels.js new file mode 100644 index 0000000..3815393 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/embeddingLevels.js @@ -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 + } +} diff --git a/vanilla/node_modules/bidi-js/src/index.js b/vanilla/node_modules/bidi-js/src/index.js new file mode 100644 index 0000000..d146bb0 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/index.js @@ -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' diff --git a/vanilla/node_modules/bidi-js/src/mirroring.js b/vanilla/node_modules/bidi-js/src/mirroring.js new file mode 100644 index 0000000..c214b04 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/mirroring.js @@ -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 +} diff --git a/vanilla/node_modules/bidi-js/src/reordering.js b/vanilla/node_modules/bidi-js/src/reordering.js new file mode 100644 index 0000000..94a42ed --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/reordering.js @@ -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 +} diff --git a/vanilla/node_modules/bidi-js/src/util/parseCharacterMap.js b/vanilla/node_modules/bidi-js/src/util/parseCharacterMap.js new file mode 100644 index 0000000..86a96b8 --- /dev/null +++ b/vanilla/node_modules/bidi-js/src/util/parseCharacterMap.js @@ -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 } +} |
