bidi.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /* Copyright 2017 Mozilla Foundation
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. 'use strict';
  16. var sharedUtil = require('../shared/util.js');
  17. var warn = sharedUtil.warn;
  18. var baseTypes = [
  19. 'BN',
  20. 'BN',
  21. 'BN',
  22. 'BN',
  23. 'BN',
  24. 'BN',
  25. 'BN',
  26. 'BN',
  27. 'BN',
  28. 'S',
  29. 'B',
  30. 'S',
  31. 'WS',
  32. 'B',
  33. 'BN',
  34. 'BN',
  35. 'BN',
  36. 'BN',
  37. 'BN',
  38. 'BN',
  39. 'BN',
  40. 'BN',
  41. 'BN',
  42. 'BN',
  43. 'BN',
  44. 'BN',
  45. 'BN',
  46. 'BN',
  47. 'B',
  48. 'B',
  49. 'B',
  50. 'S',
  51. 'WS',
  52. 'ON',
  53. 'ON',
  54. 'ET',
  55. 'ET',
  56. 'ET',
  57. 'ON',
  58. 'ON',
  59. 'ON',
  60. 'ON',
  61. 'ON',
  62. 'ES',
  63. 'CS',
  64. 'ES',
  65. 'CS',
  66. 'CS',
  67. 'EN',
  68. 'EN',
  69. 'EN',
  70. 'EN',
  71. 'EN',
  72. 'EN',
  73. 'EN',
  74. 'EN',
  75. 'EN',
  76. 'EN',
  77. 'CS',
  78. 'ON',
  79. 'ON',
  80. 'ON',
  81. 'ON',
  82. 'ON',
  83. 'ON',
  84. 'L',
  85. 'L',
  86. 'L',
  87. 'L',
  88. 'L',
  89. 'L',
  90. 'L',
  91. 'L',
  92. 'L',
  93. 'L',
  94. 'L',
  95. 'L',
  96. 'L',
  97. 'L',
  98. 'L',
  99. 'L',
  100. 'L',
  101. 'L',
  102. 'L',
  103. 'L',
  104. 'L',
  105. 'L',
  106. 'L',
  107. 'L',
  108. 'L',
  109. 'L',
  110. 'ON',
  111. 'ON',
  112. 'ON',
  113. 'ON',
  114. 'ON',
  115. 'ON',
  116. 'L',
  117. 'L',
  118. 'L',
  119. 'L',
  120. 'L',
  121. 'L',
  122. 'L',
  123. 'L',
  124. 'L',
  125. 'L',
  126. 'L',
  127. 'L',
  128. 'L',
  129. 'L',
  130. 'L',
  131. 'L',
  132. 'L',
  133. 'L',
  134. 'L',
  135. 'L',
  136. 'L',
  137. 'L',
  138. 'L',
  139. 'L',
  140. 'L',
  141. 'L',
  142. 'ON',
  143. 'ON',
  144. 'ON',
  145. 'ON',
  146. 'BN',
  147. 'BN',
  148. 'BN',
  149. 'BN',
  150. 'BN',
  151. 'BN',
  152. 'B',
  153. 'BN',
  154. 'BN',
  155. 'BN',
  156. 'BN',
  157. 'BN',
  158. 'BN',
  159. 'BN',
  160. 'BN',
  161. 'BN',
  162. 'BN',
  163. 'BN',
  164. 'BN',
  165. 'BN',
  166. 'BN',
  167. 'BN',
  168. 'BN',
  169. 'BN',
  170. 'BN',
  171. 'BN',
  172. 'BN',
  173. 'BN',
  174. 'BN',
  175. 'BN',
  176. 'BN',
  177. 'BN',
  178. 'BN',
  179. 'CS',
  180. 'ON',
  181. 'ET',
  182. 'ET',
  183. 'ET',
  184. 'ET',
  185. 'ON',
  186. 'ON',
  187. 'ON',
  188. 'ON',
  189. 'L',
  190. 'ON',
  191. 'ON',
  192. 'BN',
  193. 'ON',
  194. 'ON',
  195. 'ET',
  196. 'ET',
  197. 'EN',
  198. 'EN',
  199. 'ON',
  200. 'L',
  201. 'ON',
  202. 'ON',
  203. 'ON',
  204. 'EN',
  205. 'L',
  206. 'ON',
  207. 'ON',
  208. 'ON',
  209. 'ON',
  210. 'ON',
  211. 'L',
  212. 'L',
  213. 'L',
  214. 'L',
  215. 'L',
  216. 'L',
  217. 'L',
  218. 'L',
  219. 'L',
  220. 'L',
  221. 'L',
  222. 'L',
  223. 'L',
  224. 'L',
  225. 'L',
  226. 'L',
  227. 'L',
  228. 'L',
  229. 'L',
  230. 'L',
  231. 'L',
  232. 'L',
  233. 'L',
  234. 'ON',
  235. 'L',
  236. 'L',
  237. 'L',
  238. 'L',
  239. 'L',
  240. 'L',
  241. 'L',
  242. 'L',
  243. 'L',
  244. 'L',
  245. 'L',
  246. 'L',
  247. 'L',
  248. 'L',
  249. 'L',
  250. 'L',
  251. 'L',
  252. 'L',
  253. 'L',
  254. 'L',
  255. 'L',
  256. 'L',
  257. 'L',
  258. 'L',
  259. 'L',
  260. 'L',
  261. 'L',
  262. 'L',
  263. 'L',
  264. 'L',
  265. 'L',
  266. 'ON',
  267. 'L',
  268. 'L',
  269. 'L',
  270. 'L',
  271. 'L',
  272. 'L',
  273. 'L',
  274. 'L'
  275. ];
  276. var arabicTypes = [
  277. 'AN',
  278. 'AN',
  279. 'AN',
  280. 'AN',
  281. 'AN',
  282. 'AN',
  283. 'ON',
  284. 'ON',
  285. 'AL',
  286. 'ET',
  287. 'ET',
  288. 'AL',
  289. 'CS',
  290. 'AL',
  291. 'ON',
  292. 'ON',
  293. 'NSM',
  294. 'NSM',
  295. 'NSM',
  296. 'NSM',
  297. 'NSM',
  298. 'NSM',
  299. 'NSM',
  300. 'NSM',
  301. 'NSM',
  302. 'NSM',
  303. 'NSM',
  304. 'AL',
  305. 'AL',
  306. '',
  307. 'AL',
  308. 'AL',
  309. 'AL',
  310. 'AL',
  311. 'AL',
  312. 'AL',
  313. 'AL',
  314. 'AL',
  315. 'AL',
  316. 'AL',
  317. 'AL',
  318. 'AL',
  319. 'AL',
  320. 'AL',
  321. 'AL',
  322. 'AL',
  323. 'AL',
  324. 'AL',
  325. 'AL',
  326. 'AL',
  327. 'AL',
  328. 'AL',
  329. 'AL',
  330. 'AL',
  331. 'AL',
  332. 'AL',
  333. 'AL',
  334. 'AL',
  335. 'AL',
  336. 'AL',
  337. 'AL',
  338. 'AL',
  339. 'AL',
  340. 'AL',
  341. 'AL',
  342. 'AL',
  343. 'AL',
  344. 'AL',
  345. 'AL',
  346. 'AL',
  347. 'AL',
  348. 'AL',
  349. 'AL',
  350. 'AL',
  351. 'AL',
  352. 'NSM',
  353. 'NSM',
  354. 'NSM',
  355. 'NSM',
  356. 'NSM',
  357. 'NSM',
  358. 'NSM',
  359. 'NSM',
  360. 'NSM',
  361. 'NSM',
  362. 'NSM',
  363. 'NSM',
  364. 'NSM',
  365. 'NSM',
  366. 'NSM',
  367. 'NSM',
  368. 'NSM',
  369. 'NSM',
  370. 'NSM',
  371. 'NSM',
  372. 'NSM',
  373. 'AN',
  374. 'AN',
  375. 'AN',
  376. 'AN',
  377. 'AN',
  378. 'AN',
  379. 'AN',
  380. 'AN',
  381. 'AN',
  382. 'AN',
  383. 'ET',
  384. 'AN',
  385. 'AN',
  386. 'AL',
  387. 'AL',
  388. 'AL',
  389. 'NSM',
  390. 'AL',
  391. 'AL',
  392. 'AL',
  393. 'AL',
  394. 'AL',
  395. 'AL',
  396. 'AL',
  397. 'AL',
  398. 'AL',
  399. 'AL',
  400. 'AL',
  401. 'AL',
  402. 'AL',
  403. 'AL',
  404. 'AL',
  405. 'AL',
  406. 'AL',
  407. 'AL',
  408. 'AL',
  409. 'AL',
  410. 'AL',
  411. 'AL',
  412. 'AL',
  413. 'AL',
  414. 'AL',
  415. 'AL',
  416. 'AL',
  417. 'AL',
  418. 'AL',
  419. 'AL',
  420. 'AL',
  421. 'AL',
  422. 'AL',
  423. 'AL',
  424. 'AL',
  425. 'AL',
  426. 'AL',
  427. 'AL',
  428. 'AL',
  429. 'AL',
  430. 'AL',
  431. 'AL',
  432. 'AL',
  433. 'AL',
  434. 'AL',
  435. 'AL',
  436. 'AL',
  437. 'AL',
  438. 'AL',
  439. 'AL',
  440. 'AL',
  441. 'AL',
  442. 'AL',
  443. 'AL',
  444. 'AL',
  445. 'AL',
  446. 'AL',
  447. 'AL',
  448. 'AL',
  449. 'AL',
  450. 'AL',
  451. 'AL',
  452. 'AL',
  453. 'AL',
  454. 'AL',
  455. 'AL',
  456. 'AL',
  457. 'AL',
  458. 'AL',
  459. 'AL',
  460. 'AL',
  461. 'AL',
  462. 'AL',
  463. 'AL',
  464. 'AL',
  465. 'AL',
  466. 'AL',
  467. 'AL',
  468. 'AL',
  469. 'AL',
  470. 'AL',
  471. 'AL',
  472. 'AL',
  473. 'AL',
  474. 'AL',
  475. 'AL',
  476. 'AL',
  477. 'AL',
  478. 'AL',
  479. 'AL',
  480. 'AL',
  481. 'AL',
  482. 'AL',
  483. 'AL',
  484. 'AL',
  485. 'AL',
  486. 'AL',
  487. 'AL',
  488. 'AL',
  489. 'AL',
  490. 'AL',
  491. 'NSM',
  492. 'NSM',
  493. 'NSM',
  494. 'NSM',
  495. 'NSM',
  496. 'NSM',
  497. 'NSM',
  498. 'AN',
  499. 'ON',
  500. 'NSM',
  501. 'NSM',
  502. 'NSM',
  503. 'NSM',
  504. 'NSM',
  505. 'NSM',
  506. 'AL',
  507. 'AL',
  508. 'NSM',
  509. 'NSM',
  510. 'ON',
  511. 'NSM',
  512. 'NSM',
  513. 'NSM',
  514. 'NSM',
  515. 'AL',
  516. 'AL',
  517. 'EN',
  518. 'EN',
  519. 'EN',
  520. 'EN',
  521. 'EN',
  522. 'EN',
  523. 'EN',
  524. 'EN',
  525. 'EN',
  526. 'EN',
  527. 'AL',
  528. 'AL',
  529. 'AL',
  530. 'AL',
  531. 'AL',
  532. 'AL'
  533. ];
  534. function isOdd(i) {
  535. return (i & 1) !== 0;
  536. }
  537. function isEven(i) {
  538. return (i & 1) === 0;
  539. }
  540. function findUnequal(arr, start, value) {
  541. for (var j = start, jj = arr.length; j < jj; ++j) {
  542. if (arr[j] !== value) {
  543. return j;
  544. }
  545. }
  546. return j;
  547. }
  548. function setValues(arr, start, end, value) {
  549. for (var j = start; j < end; ++j) {
  550. arr[j] = value;
  551. }
  552. }
  553. function reverseValues(arr, start, end) {
  554. for (var i = start, j = end - 1; i < j; ++i, --j) {
  555. var temp = arr[i];
  556. arr[i] = arr[j];
  557. arr[j] = temp;
  558. }
  559. }
  560. function createBidiText(str, isLTR, vertical) {
  561. return {
  562. str: str,
  563. dir: vertical ? 'ttb' : isLTR ? 'ltr' : 'rtl'
  564. };
  565. }
  566. var chars = [];
  567. var types = [];
  568. function bidi(str, startLevel, vertical) {
  569. var isLTR = true;
  570. var strLength = str.length;
  571. if (strLength === 0 || vertical) {
  572. return createBidiText(str, isLTR, vertical);
  573. }
  574. chars.length = strLength;
  575. types.length = strLength;
  576. var numBidi = 0;
  577. var i, ii;
  578. for (i = 0; i < strLength; ++i) {
  579. chars[i] = str.charAt(i);
  580. var charCode = str.charCodeAt(i);
  581. var charType = 'L';
  582. if (charCode <= 0x00ff) {
  583. charType = baseTypes[charCode];
  584. } else if (0x0590 <= charCode && charCode <= 0x05f4) {
  585. charType = 'R';
  586. } else if (0x0600 <= charCode && charCode <= 0x06ff) {
  587. charType = arabicTypes[charCode & 0xff];
  588. if (!charType) {
  589. warn('Bidi: invalid Unicode character ' + charCode.toString(16));
  590. }
  591. } else if (0x0700 <= charCode && charCode <= 0x08AC) {
  592. charType = 'AL';
  593. }
  594. if (charType === 'R' || charType === 'AL' || charType === 'AN') {
  595. numBidi++;
  596. }
  597. types[i] = charType;
  598. }
  599. if (numBidi === 0) {
  600. isLTR = true;
  601. return createBidiText(str, isLTR);
  602. }
  603. if (startLevel === -1) {
  604. if (numBidi / strLength < 0.3) {
  605. isLTR = true;
  606. startLevel = 0;
  607. } else {
  608. isLTR = false;
  609. startLevel = 1;
  610. }
  611. }
  612. var levels = [];
  613. for (i = 0; i < strLength; ++i) {
  614. levels[i] = startLevel;
  615. }
  616. var e = isOdd(startLevel) ? 'R' : 'L';
  617. var sor = e;
  618. var eor = sor;
  619. var lastType = sor;
  620. for (i = 0; i < strLength; ++i) {
  621. if (types[i] === 'NSM') {
  622. types[i] = lastType;
  623. } else {
  624. lastType = types[i];
  625. }
  626. }
  627. lastType = sor;
  628. var t;
  629. for (i = 0; i < strLength; ++i) {
  630. t = types[i];
  631. if (t === 'EN') {
  632. types[i] = lastType === 'AL' ? 'AN' : 'EN';
  633. } else if (t === 'R' || t === 'L' || t === 'AL') {
  634. lastType = t;
  635. }
  636. }
  637. for (i = 0; i < strLength; ++i) {
  638. t = types[i];
  639. if (t === 'AL') {
  640. types[i] = 'R';
  641. }
  642. }
  643. for (i = 1; i < strLength - 1; ++i) {
  644. if (types[i] === 'ES' && types[i - 1] === 'EN' && types[i + 1] === 'EN') {
  645. types[i] = 'EN';
  646. }
  647. if (types[i] === 'CS' && (types[i - 1] === 'EN' || types[i - 1] === 'AN') && types[i + 1] === types[i - 1]) {
  648. types[i] = types[i - 1];
  649. }
  650. }
  651. for (i = 0; i < strLength; ++i) {
  652. if (types[i] === 'EN') {
  653. var j;
  654. for (j = i - 1; j >= 0; --j) {
  655. if (types[j] !== 'ET') {
  656. break;
  657. }
  658. types[j] = 'EN';
  659. }
  660. for (j = i + 1; j < strLength; ++j) {
  661. if (types[j] !== 'ET') {
  662. break;
  663. }
  664. types[j] = 'EN';
  665. }
  666. }
  667. }
  668. for (i = 0; i < strLength; ++i) {
  669. t = types[i];
  670. if (t === 'WS' || t === 'ES' || t === 'ET' || t === 'CS') {
  671. types[i] = 'ON';
  672. }
  673. }
  674. lastType = sor;
  675. for (i = 0; i < strLength; ++i) {
  676. t = types[i];
  677. if (t === 'EN') {
  678. types[i] = lastType === 'L' ? 'L' : 'EN';
  679. } else if (t === 'R' || t === 'L') {
  680. lastType = t;
  681. }
  682. }
  683. for (i = 0; i < strLength; ++i) {
  684. if (types[i] === 'ON') {
  685. var end = findUnequal(types, i + 1, 'ON');
  686. var before = sor;
  687. if (i > 0) {
  688. before = types[i - 1];
  689. }
  690. var after = eor;
  691. if (end + 1 < strLength) {
  692. after = types[end + 1];
  693. }
  694. if (before !== 'L') {
  695. before = 'R';
  696. }
  697. if (after !== 'L') {
  698. after = 'R';
  699. }
  700. if (before === after) {
  701. setValues(types, i, end, before);
  702. }
  703. i = end - 1;
  704. }
  705. }
  706. for (i = 0; i < strLength; ++i) {
  707. if (types[i] === 'ON') {
  708. types[i] = e;
  709. }
  710. }
  711. for (i = 0; i < strLength; ++i) {
  712. t = types[i];
  713. if (isEven(levels[i])) {
  714. if (t === 'R') {
  715. levels[i] += 1;
  716. } else if (t === 'AN' || t === 'EN') {
  717. levels[i] += 2;
  718. }
  719. } else {
  720. if (t === 'L' || t === 'AN' || t === 'EN') {
  721. levels[i] += 1;
  722. }
  723. }
  724. }
  725. var highestLevel = -1;
  726. var lowestOddLevel = 99;
  727. var level;
  728. for (i = 0, ii = levels.length; i < ii; ++i) {
  729. level = levels[i];
  730. if (highestLevel < level) {
  731. highestLevel = level;
  732. }
  733. if (lowestOddLevel > level && isOdd(level)) {
  734. lowestOddLevel = level;
  735. }
  736. }
  737. for (level = highestLevel; level >= lowestOddLevel; --level) {
  738. var start = -1;
  739. for (i = 0, ii = levels.length; i < ii; ++i) {
  740. if (levels[i] < level) {
  741. if (start >= 0) {
  742. reverseValues(chars, start, i);
  743. start = -1;
  744. }
  745. } else if (start < 0) {
  746. start = i;
  747. }
  748. }
  749. if (start >= 0) {
  750. reverseValues(chars, start, levels.length);
  751. }
  752. }
  753. for (i = 0, ii = chars.length; i < ii; ++i) {
  754. var ch = chars[i];
  755. if (ch === '<' || ch === '>') {
  756. chars[i] = '';
  757. }
  758. }
  759. return createBidiText(chars.join(''), isLTR);
  760. }
  761. exports.bidi = bidi;