bidi.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /**
  2. * @licstart The following is the entire license notice for the
  3. * Javascript code in this page
  4. *
  5. * Copyright 2019 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. Object.defineProperty(exports, "__esModule", {
  24. value: true
  25. });
  26. exports.bidi = bidi;
  27. var _util = require("../shared/util");
  28. var 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'];
  29. var 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'];
  30. function isOdd(i) {
  31. return (i & 1) !== 0;
  32. }
  33. function isEven(i) {
  34. return (i & 1) === 0;
  35. }
  36. function findUnequal(arr, start, value) {
  37. for (var j = start, jj = arr.length; j < jj; ++j) {
  38. if (arr[j] !== value) {
  39. return j;
  40. }
  41. }
  42. return j;
  43. }
  44. function setValues(arr, start, end, value) {
  45. for (var j = start; j < end; ++j) {
  46. arr[j] = value;
  47. }
  48. }
  49. function reverseValues(arr, start, end) {
  50. for (var i = start, j = end - 1; i < j; ++i, --j) {
  51. var temp = arr[i];
  52. arr[i] = arr[j];
  53. arr[j] = temp;
  54. }
  55. }
  56. function createBidiText(str, isLTR, vertical) {
  57. return {
  58. str: str,
  59. dir: vertical ? 'ttb' : isLTR ? 'ltr' : 'rtl'
  60. };
  61. }
  62. var chars = [];
  63. var types = [];
  64. function bidi(str, startLevel, vertical) {
  65. var isLTR = true;
  66. var strLength = str.length;
  67. if (strLength === 0 || vertical) {
  68. return createBidiText(str, isLTR, vertical);
  69. }
  70. chars.length = strLength;
  71. types.length = strLength;
  72. var numBidi = 0;
  73. var i, ii;
  74. for (i = 0; i < strLength; ++i) {
  75. chars[i] = str.charAt(i);
  76. var charCode = str.charCodeAt(i);
  77. var charType = 'L';
  78. if (charCode <= 0x00ff) {
  79. charType = baseTypes[charCode];
  80. } else if (0x0590 <= charCode && charCode <= 0x05f4) {
  81. charType = 'R';
  82. } else if (0x0600 <= charCode && charCode <= 0x06ff) {
  83. charType = arabicTypes[charCode & 0xff];
  84. if (!charType) {
  85. (0, _util.warn)('Bidi: invalid Unicode character ' + charCode.toString(16));
  86. }
  87. } else if (0x0700 <= charCode && charCode <= 0x08AC) {
  88. charType = 'AL';
  89. }
  90. if (charType === 'R' || charType === 'AL' || charType === 'AN') {
  91. numBidi++;
  92. }
  93. types[i] = charType;
  94. }
  95. if (numBidi === 0) {
  96. isLTR = true;
  97. return createBidiText(str, isLTR);
  98. }
  99. if (startLevel === -1) {
  100. if (numBidi / strLength < 0.3) {
  101. isLTR = true;
  102. startLevel = 0;
  103. } else {
  104. isLTR = false;
  105. startLevel = 1;
  106. }
  107. }
  108. var levels = [];
  109. for (i = 0; i < strLength; ++i) {
  110. levels[i] = startLevel;
  111. }
  112. var e = isOdd(startLevel) ? 'R' : 'L';
  113. var sor = e;
  114. var eor = sor;
  115. var lastType = sor;
  116. for (i = 0; i < strLength; ++i) {
  117. if (types[i] === 'NSM') {
  118. types[i] = lastType;
  119. } else {
  120. lastType = types[i];
  121. }
  122. }
  123. lastType = sor;
  124. var t;
  125. for (i = 0; i < strLength; ++i) {
  126. t = types[i];
  127. if (t === 'EN') {
  128. types[i] = lastType === 'AL' ? 'AN' : 'EN';
  129. } else if (t === 'R' || t === 'L' || t === 'AL') {
  130. lastType = t;
  131. }
  132. }
  133. for (i = 0; i < strLength; ++i) {
  134. t = types[i];
  135. if (t === 'AL') {
  136. types[i] = 'R';
  137. }
  138. }
  139. for (i = 1; i < strLength - 1; ++i) {
  140. if (types[i] === 'ES' && types[i - 1] === 'EN' && types[i + 1] === 'EN') {
  141. types[i] = 'EN';
  142. }
  143. if (types[i] === 'CS' && (types[i - 1] === 'EN' || types[i - 1] === 'AN') && types[i + 1] === types[i - 1]) {
  144. types[i] = types[i - 1];
  145. }
  146. }
  147. for (i = 0; i < strLength; ++i) {
  148. if (types[i] === 'EN') {
  149. var j;
  150. for (j = i - 1; j >= 0; --j) {
  151. if (types[j] !== 'ET') {
  152. break;
  153. }
  154. types[j] = 'EN';
  155. }
  156. for (j = i + 1; j < strLength; ++j) {
  157. if (types[j] !== 'ET') {
  158. break;
  159. }
  160. types[j] = 'EN';
  161. }
  162. }
  163. }
  164. for (i = 0; i < strLength; ++i) {
  165. t = types[i];
  166. if (t === 'WS' || t === 'ES' || t === 'ET' || t === 'CS') {
  167. types[i] = 'ON';
  168. }
  169. }
  170. lastType = sor;
  171. for (i = 0; i < strLength; ++i) {
  172. t = types[i];
  173. if (t === 'EN') {
  174. types[i] = lastType === 'L' ? 'L' : 'EN';
  175. } else if (t === 'R' || t === 'L') {
  176. lastType = t;
  177. }
  178. }
  179. for (i = 0; i < strLength; ++i) {
  180. if (types[i] === 'ON') {
  181. var end = findUnequal(types, i + 1, 'ON');
  182. var before = sor;
  183. if (i > 0) {
  184. before = types[i - 1];
  185. }
  186. var after = eor;
  187. if (end + 1 < strLength) {
  188. after = types[end + 1];
  189. }
  190. if (before !== 'L') {
  191. before = 'R';
  192. }
  193. if (after !== 'L') {
  194. after = 'R';
  195. }
  196. if (before === after) {
  197. setValues(types, i, end, before);
  198. }
  199. i = end - 1;
  200. }
  201. }
  202. for (i = 0; i < strLength; ++i) {
  203. if (types[i] === 'ON') {
  204. types[i] = e;
  205. }
  206. }
  207. for (i = 0; i < strLength; ++i) {
  208. t = types[i];
  209. if (isEven(levels[i])) {
  210. if (t === 'R') {
  211. levels[i] += 1;
  212. } else if (t === 'AN' || t === 'EN') {
  213. levels[i] += 2;
  214. }
  215. } else {
  216. if (t === 'L' || t === 'AN' || t === 'EN') {
  217. levels[i] += 1;
  218. }
  219. }
  220. }
  221. var highestLevel = -1;
  222. var lowestOddLevel = 99;
  223. var level;
  224. for (i = 0, ii = levels.length; i < ii; ++i) {
  225. level = levels[i];
  226. if (highestLevel < level) {
  227. highestLevel = level;
  228. }
  229. if (lowestOddLevel > level && isOdd(level)) {
  230. lowestOddLevel = level;
  231. }
  232. }
  233. for (level = highestLevel; level >= lowestOddLevel; --level) {
  234. var start = -1;
  235. for (i = 0, ii = levels.length; i < ii; ++i) {
  236. if (levels[i] < level) {
  237. if (start >= 0) {
  238. reverseValues(chars, start, i);
  239. start = -1;
  240. }
  241. } else if (start < 0) {
  242. start = i;
  243. }
  244. }
  245. if (start >= 0) {
  246. reverseValues(chars, start, levels.length);
  247. }
  248. }
  249. for (i = 0, ii = chars.length; i < ii; ++i) {
  250. var ch = chars[i];
  251. if (ch === '<' || ch === '>') {
  252. chars[i] = '';
  253. }
  254. }
  255. return createBidiText(chars.join(''), isLTR);
  256. }