UNPKG

9.59 kBJavaScriptView Raw
1/**
2 * @licstart The following is the entire license notice for the
3 * JavaScript code in this page
4 *
5 * Copyright 2022 Mozilla Foundation
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * @licend The above is the entire license notice for the
20 * JavaScript code in this page
21 */
22"use strict";
23
24Object.defineProperty(exports, "__esModule", {
25 value: true
26});
27exports.bidi = bidi;
28
29var _util = require("../shared/util.js");
30
31const baseTypes = ["BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "S", "B", "S", "WS", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", "B", "B", "S", "WS", "ON", "ON", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "ON", "ES", "CS", "ES", "CS", "CS", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "CS", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "BN", "BN", "BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "CS", "ON", "ET", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "L", "ON", "ON", "BN", "ON", "ON", "ET", "ET", "EN", "EN", "ON", "L", "ON", "ON", "ON", "EN", "L", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L"];
32const arabicTypes = ["AN", "AN", "AN", "AN", "AN", "AN", "ON", "ON", "AL", "ET", "ET", "AL", "CS", "AL", "ON", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", "AL", "AL", "AL", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "NSM", "NSM", "ON", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "AL", "AL", "AL", "AL", "AL", "AL"];
33
34function isOdd(i) {
35 return (i & 1) !== 0;
36}
37
38function isEven(i) {
39 return (i & 1) === 0;
40}
41
42function findUnequal(arr, start, value) {
43 let j, jj;
44
45 for (j = start, jj = arr.length; j < jj; ++j) {
46 if (arr[j] !== value) {
47 return j;
48 }
49 }
50
51 return j;
52}
53
54function setValues(arr, start, end, value) {
55 for (let j = start; j < end; ++j) {
56 arr[j] = value;
57 }
58}
59
60function reverseValues(arr, start, end) {
61 for (let i = start, j = end - 1; i < j; ++i, --j) {
62 const temp = arr[i];
63 arr[i] = arr[j];
64 arr[j] = temp;
65 }
66}
67
68function createBidiText(str, isLTR, vertical = false) {
69 let dir = "ltr";
70
71 if (vertical) {
72 dir = "ttb";
73 } else if (!isLTR) {
74 dir = "rtl";
75 }
76
77 return {
78 str,
79 dir
80 };
81}
82
83const chars = [];
84const types = [];
85
86function bidi(str, startLevel = -1, vertical = false) {
87 let isLTR = true;
88 const strLength = str.length;
89
90 if (strLength === 0 || vertical) {
91 return createBidiText(str, isLTR, vertical);
92 }
93
94 chars.length = strLength;
95 types.length = strLength;
96 let numBidi = 0;
97 let i, ii;
98
99 for (i = 0; i < strLength; ++i) {
100 chars[i] = str.charAt(i);
101 const charCode = str.charCodeAt(i);
102 let charType = "L";
103
104 if (charCode <= 0x00ff) {
105 charType = baseTypes[charCode];
106 } else if (0x0590 <= charCode && charCode <= 0x05f4) {
107 charType = "R";
108 } else if (0x0600 <= charCode && charCode <= 0x06ff) {
109 charType = arabicTypes[charCode & 0xff];
110
111 if (!charType) {
112 (0, _util.warn)("Bidi: invalid Unicode character " + charCode.toString(16));
113 }
114 } else if (0x0700 <= charCode && charCode <= 0x08ac) {
115 charType = "AL";
116 }
117
118 if (charType === "R" || charType === "AL" || charType === "AN") {
119 numBidi++;
120 }
121
122 types[i] = charType;
123 }
124
125 if (numBidi === 0) {
126 isLTR = true;
127 return createBidiText(str, isLTR);
128 }
129
130 if (startLevel === -1) {
131 if (numBidi / strLength < 0.3 && strLength > 4) {
132 isLTR = true;
133 startLevel = 0;
134 } else {
135 isLTR = false;
136 startLevel = 1;
137 }
138 }
139
140 const levels = [];
141
142 for (i = 0; i < strLength; ++i) {
143 levels[i] = startLevel;
144 }
145
146 const e = isOdd(startLevel) ? "R" : "L";
147 const sor = e;
148 const eor = sor;
149 let lastType = sor;
150
151 for (i = 0; i < strLength; ++i) {
152 if (types[i] === "NSM") {
153 types[i] = lastType;
154 } else {
155 lastType = types[i];
156 }
157 }
158
159 lastType = sor;
160 let t;
161
162 for (i = 0; i < strLength; ++i) {
163 t = types[i];
164
165 if (t === "EN") {
166 types[i] = lastType === "AL" ? "AN" : "EN";
167 } else if (t === "R" || t === "L" || t === "AL") {
168 lastType = t;
169 }
170 }
171
172 for (i = 0; i < strLength; ++i) {
173 t = types[i];
174
175 if (t === "AL") {
176 types[i] = "R";
177 }
178 }
179
180 for (i = 1; i < strLength - 1; ++i) {
181 if (types[i] === "ES" && types[i - 1] === "EN" && types[i + 1] === "EN") {
182 types[i] = "EN";
183 }
184
185 if (types[i] === "CS" && (types[i - 1] === "EN" || types[i - 1] === "AN") && types[i + 1] === types[i - 1]) {
186 types[i] = types[i - 1];
187 }
188 }
189
190 for (i = 0; i < strLength; ++i) {
191 if (types[i] === "EN") {
192 for (let j = i - 1; j >= 0; --j) {
193 if (types[j] !== "ET") {
194 break;
195 }
196
197 types[j] = "EN";
198 }
199
200 for (let j = i + 1; j < strLength; ++j) {
201 if (types[j] !== "ET") {
202 break;
203 }
204
205 types[j] = "EN";
206 }
207 }
208 }
209
210 for (i = 0; i < strLength; ++i) {
211 t = types[i];
212
213 if (t === "WS" || t === "ES" || t === "ET" || t === "CS") {
214 types[i] = "ON";
215 }
216 }
217
218 lastType = sor;
219
220 for (i = 0; i < strLength; ++i) {
221 t = types[i];
222
223 if (t === "EN") {
224 types[i] = lastType === "L" ? "L" : "EN";
225 } else if (t === "R" || t === "L") {
226 lastType = t;
227 }
228 }
229
230 for (i = 0; i < strLength; ++i) {
231 if (types[i] === "ON") {
232 const end = findUnequal(types, i + 1, "ON");
233 let before = sor;
234
235 if (i > 0) {
236 before = types[i - 1];
237 }
238
239 let after = eor;
240
241 if (end + 1 < strLength) {
242 after = types[end + 1];
243 }
244
245 if (before !== "L") {
246 before = "R";
247 }
248
249 if (after !== "L") {
250 after = "R";
251 }
252
253 if (before === after) {
254 setValues(types, i, end, before);
255 }
256
257 i = end - 1;
258 }
259 }
260
261 for (i = 0; i < strLength; ++i) {
262 if (types[i] === "ON") {
263 types[i] = e;
264 }
265 }
266
267 for (i = 0; i < strLength; ++i) {
268 t = types[i];
269
270 if (isEven(levels[i])) {
271 if (t === "R") {
272 levels[i] += 1;
273 } else if (t === "AN" || t === "EN") {
274 levels[i] += 2;
275 }
276 } else {
277 if (t === "L" || t === "AN" || t === "EN") {
278 levels[i] += 1;
279 }
280 }
281 }
282
283 let highestLevel = -1;
284 let lowestOddLevel = 99;
285 let level;
286
287 for (i = 0, ii = levels.length; i < ii; ++i) {
288 level = levels[i];
289
290 if (highestLevel < level) {
291 highestLevel = level;
292 }
293
294 if (lowestOddLevel > level && isOdd(level)) {
295 lowestOddLevel = level;
296 }
297 }
298
299 for (level = highestLevel; level >= lowestOddLevel; --level) {
300 let start = -1;
301
302 for (i = 0, ii = levels.length; i < ii; ++i) {
303 if (levels[i] < level) {
304 if (start >= 0) {
305 reverseValues(chars, start, i);
306 start = -1;
307 }
308 } else if (start < 0) {
309 start = i;
310 }
311 }
312
313 if (start >= 0) {
314 reverseValues(chars, start, levels.length);
315 }
316 }
317
318 for (i = 0, ii = chars.length; i < ii; ++i) {
319 const ch = chars[i];
320
321 if (ch === "<" || ch === ">") {
322 chars[i] = "";
323 }
324 }
325
326 return createBidiText(chars.join(""), isLTR);
327}
\No newline at end of file