all.js 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611
  1. // created 2023-09-25T01:01:55.148Z
  2. // compressed base64-encoded blob for include-ens data
  3. // source: https://github.com/adraffy/ens-normalize.js/blob/main/src/make.js
  4. // see: https://github.com/adraffy/ens-normalize.js#security
  5. // SHA-256: 0565ed049b9cf1614bb9e11ba7d8ac6a6fb96c893253d890f7e2b2884b9ded32
  6. var COMPRESSED$1 = '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';
  7. const FENCED = new Map([[8217,"apostrophe"],[8260,"fraction slash"],[12539,"middle dot"]]);
  8. const NSM_MAX = 4;
  9. function decode_arithmetic(bytes) {
  10. let pos = 0;
  11. function u16() { return (bytes[pos++] << 8) | bytes[pos++]; }
  12. // decode the frequency table
  13. let symbol_count = u16();
  14. let total = 1;
  15. let acc = [0, 1]; // first symbol has frequency 1
  16. for (let i = 1; i < symbol_count; i++) {
  17. acc.push(total += u16());
  18. }
  19. // skip the sized-payload that the last 3 symbols index into
  20. let skip = u16();
  21. let pos_payload = pos;
  22. pos += skip;
  23. let read_width = 0;
  24. let read_buffer = 0;
  25. function read_bit() {
  26. if (read_width == 0) {
  27. // this will read beyond end of buffer
  28. // but (undefined|0) => zero pad
  29. read_buffer = (read_buffer << 8) | bytes[pos++];
  30. read_width = 8;
  31. }
  32. return (read_buffer >> --read_width) & 1;
  33. }
  34. const N = 31;
  35. const FULL = 2**N;
  36. const HALF = FULL >>> 1;
  37. const QRTR = HALF >> 1;
  38. const MASK = FULL - 1;
  39. // fill register
  40. let register = 0;
  41. for (let i = 0; i < N; i++) register = (register << 1) | read_bit();
  42. let symbols = [];
  43. let low = 0;
  44. let range = FULL; // treat like a float
  45. while (true) {
  46. let value = Math.floor((((register - low + 1) * total) - 1) / range);
  47. let start = 0;
  48. let end = symbol_count;
  49. while (end - start > 1) { // binary search
  50. let mid = (start + end) >>> 1;
  51. if (value < acc[mid]) {
  52. end = mid;
  53. } else {
  54. start = mid;
  55. }
  56. }
  57. if (start == 0) break; // first symbol is end mark
  58. symbols.push(start);
  59. let a = low + Math.floor(range * acc[start] / total);
  60. let b = low + Math.floor(range * acc[start+1] / total) - 1;
  61. while (((a ^ b) & HALF) == 0) {
  62. register = (register << 1) & MASK | read_bit();
  63. a = (a << 1) & MASK;
  64. b = (b << 1) & MASK | 1;
  65. }
  66. while (a & ~b & QRTR) {
  67. register = (register & HALF) | ((register << 1) & (MASK >>> 1)) | read_bit();
  68. a = (a << 1) ^ HALF;
  69. b = ((b ^ HALF) << 1) | HALF | 1;
  70. }
  71. low = a;
  72. range = 1 + b - a;
  73. }
  74. let offset = symbol_count - 4;
  75. return symbols.map(x => { // index into payload
  76. switch (x - offset) {
  77. case 3: return offset + 0x10100 + ((bytes[pos_payload++] << 16) | (bytes[pos_payload++] << 8) | bytes[pos_payload++]);
  78. case 2: return offset + 0x100 + ((bytes[pos_payload++] << 8) | bytes[pos_payload++]);
  79. case 1: return offset + bytes[pos_payload++];
  80. default: return x - 1;
  81. }
  82. });
  83. }
  84. // returns an iterator which returns the next symbol
  85. function read_payload(v) {
  86. let pos = 0;
  87. return () => v[pos++];
  88. }
  89. function read_compressed_payload(s) {
  90. return read_payload(decode_arithmetic(unsafe_atob(s)));
  91. }
  92. // unsafe in the sense:
  93. // expected well-formed Base64 w/o padding
  94. // 20220922: added for https://github.com/adraffy/ens-normalize.js/issues/4
  95. function unsafe_atob(s) {
  96. let lookup = [];
  97. [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'].forEach((c, i) => lookup[c.charCodeAt(0)] = i);
  98. let n = s.length;
  99. let ret = new Uint8Array((6 * n) >> 3);
  100. for (let i = 0, pos = 0, width = 0, carry = 0; i < n; i++) {
  101. carry = (carry << 6) | lookup[s.charCodeAt(i)];
  102. width += 6;
  103. if (width >= 8) {
  104. ret[pos++] = (carry >> (width -= 8));
  105. }
  106. }
  107. return ret;
  108. }
  109. // eg. [0,1,2,3...] => [0,-1,1,-2,...]
  110. function signed(i) {
  111. return (i & 1) ? (~i >> 1) : (i >> 1);
  112. }
  113. function read_deltas(n, next) {
  114. let v = Array(n);
  115. for (let i = 0, x = 0; i < n; i++) v[i] = x += signed(next());
  116. return v;
  117. }
  118. // [123][5] => [0 3] [1 1] [0 0]
  119. function read_sorted(next, prev = 0) {
  120. let ret = [];
  121. while (true) {
  122. let x = next();
  123. let n = next();
  124. if (!n) break;
  125. prev += x;
  126. for (let i = 0; i < n; i++) {
  127. ret.push(prev + i);
  128. }
  129. prev += n + 1;
  130. }
  131. return ret;
  132. }
  133. function read_sorted_arrays(next) {
  134. return read_array_while(() => {
  135. let v = read_sorted(next);
  136. if (v.length) return v;
  137. });
  138. }
  139. // returns map of x => ys
  140. function read_mapped(next) {
  141. let ret = [];
  142. while (true) {
  143. let w = next();
  144. if (w == 0) break;
  145. ret.push(read_linear_table(w, next));
  146. }
  147. while (true) {
  148. let w = next() - 1;
  149. if (w < 0) break;
  150. ret.push(read_replacement_table(w, next));
  151. }
  152. return ret.flat();
  153. }
  154. // read until next is falsy
  155. // return array of read values
  156. function read_array_while(next) {
  157. let v = [];
  158. while (true) {
  159. let x = next(v.length);
  160. if (!x) break;
  161. v.push(x);
  162. }
  163. return v;
  164. }
  165. // read w columns of length n
  166. // return as n rows of length w
  167. function read_transposed(n, w, next) {
  168. let m = Array(n).fill().map(() => []);
  169. for (let i = 0; i < w; i++) {
  170. read_deltas(n, next).forEach((x, j) => m[j].push(x));
  171. }
  172. return m;
  173. }
  174. // returns [[x, ys], [x+dx, ys+dy], [x+2*dx, ys+2*dy], ...]
  175. // where dx/dy = steps, n = run size, w = length of y
  176. function read_linear_table(w, next) {
  177. let dx = 1 + next();
  178. let dy = next();
  179. let vN = read_array_while(next);
  180. let m = read_transposed(vN.length, 1+w, next);
  181. return m.flatMap((v, i) => {
  182. let [x, ...ys] = v;
  183. return Array(vN[i]).fill().map((_, j) => {
  184. let j_dy = j * dy;
  185. return [x + j * dx, ys.map(y => y + j_dy)];
  186. });
  187. });
  188. }
  189. // return [[x, ys...], ...]
  190. // where w = length of y
  191. function read_replacement_table(w, next) {
  192. let n = 1 + next();
  193. let m = read_transposed(n, 1+w, next);
  194. return m.map(v => [v[0], v.slice(1)]);
  195. }
  196. function read_trie(next) {
  197. let ret = [];
  198. let sorted = read_sorted(next);
  199. expand(decode([]), []);
  200. return ret; // not sorted
  201. function decode(Q) { // characters that lead into this node
  202. let S = next(); // state: valid, save, check
  203. let B = read_array_while(() => { // buckets leading to new nodes
  204. let cps = read_sorted(next).map(i => sorted[i]);
  205. if (cps.length) return decode(cps);
  206. });
  207. return {S, B, Q};
  208. }
  209. function expand({S, B}, cps, saved) {
  210. if (S & 4 && saved === cps[cps.length-1]) return;
  211. if (S & 2) saved = cps[cps.length-1];
  212. if (S & 1) ret.push(cps);
  213. for (let br of B) {
  214. for (let cp of br.Q) {
  215. expand(br, [...cps, cp], saved);
  216. }
  217. }
  218. }
  219. }
  220. function hex_cp(cp) {
  221. return cp.toString(16).toUpperCase().padStart(2, '0');
  222. }
  223. function quote_cp(cp) {
  224. return `{${hex_cp(cp)}}`; // raffy convention: like "\u{X}" w/o the "\u"
  225. }
  226. /*
  227. export function explode_cp(s) {
  228. return [...s].map(c => c.codePointAt(0));
  229. }
  230. */
  231. function explode_cp(s) { // this is about 2x faster
  232. let cps = [];
  233. for (let pos = 0, len = s.length; pos < len; ) {
  234. let cp = s.codePointAt(pos);
  235. pos += cp < 0x10000 ? 1 : 2;
  236. cps.push(cp);
  237. }
  238. return cps;
  239. }
  240. function str_from_cps(cps) {
  241. const chunk = 4096;
  242. let len = cps.length;
  243. if (len < chunk) return String.fromCodePoint(...cps);
  244. let buf = [];
  245. for (let i = 0; i < len; ) {
  246. buf.push(String.fromCodePoint(...cps.slice(i, i += chunk)));
  247. }
  248. return buf.join('');
  249. }
  250. function compare_arrays(a, b) {
  251. let n = a.length;
  252. let c = n - b.length;
  253. for (let i = 0; c == 0 && i < n; i++) c = a[i] - b[i];
  254. return c;
  255. }
  256. function random_choice(v, rng = Math.random) {
  257. return v[rng() * v.length|0];
  258. }
  259. function random_sample(v, n, rng = Math.random) {
  260. v = v.slice(); // make copy
  261. if (v.length > n) {
  262. for (let i = 0; i < n; i++) { // shuffle prefix n
  263. let temp = v[i];
  264. let j = Math.floor(i + rng() * (v.length - i));
  265. v[i] = v[j];
  266. v[j] = temp;
  267. }
  268. v = v.slice(0, n); // truncate
  269. }
  270. return v;
  271. }
  272. function run_tests(fn, tests) {
  273. let errors = [];
  274. for (let test of tests) {
  275. let {name, norm, error} = test;
  276. if (typeof norm !== 'string') norm = name;
  277. try {
  278. let result = fn(name);
  279. if (error) {
  280. errors.push({type: 'expected error', result, ...test});
  281. } else if (result != norm) {
  282. errors.push({type: 'wrong norm', result, ...test});
  283. }
  284. } catch (err) {
  285. if (!error) {
  286. errors.push({type: 'unexpected error', result: err.message, ...test});
  287. }
  288. }
  289. }
  290. return errors;
  291. }
  292. // created 2023-09-25T01:01:55.148Z
  293. // compressed base64-encoded blob for include-nf data
  294. // source: https://github.com/adraffy/ens-normalize.js/blob/main/src/make.js
  295. // see: https://github.com/adraffy/ens-normalize.js#security
  296. // SHA-256: a974b6f8541fc29d919bc85118af0a44015851fab5343f8679cb31be2bdb209e
  297. var COMPRESSED = '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';
  298. // https://unicode.org/reports/tr15/
  299. // for reference implementation
  300. // see: /derive/nf.js
  301. // algorithmic hangul
  302. // https://www.unicode.org/versions/Unicode15.0.0/ch03.pdf (page 144)
  303. const S0 = 0xAC00;
  304. const L0 = 0x1100;
  305. const V0 = 0x1161;
  306. const T0 = 0x11A7;
  307. const L_COUNT = 19;
  308. const V_COUNT = 21;
  309. const T_COUNT = 28;
  310. const N_COUNT = V_COUNT * T_COUNT;
  311. const S_COUNT = L_COUNT * N_COUNT;
  312. const S1 = S0 + S_COUNT;
  313. const L1 = L0 + L_COUNT;
  314. const V1 = V0 + V_COUNT;
  315. const T1 = T0 + T_COUNT;
  316. function unpack_cc(packed) {
  317. return (packed >> 24) & 0xFF;
  318. }
  319. function unpack_cp(packed) {
  320. return packed & 0xFFFFFF;
  321. }
  322. let SHIFTED_RANK, EXCLUSIONS, DECOMP, RECOMP;
  323. function init$1() {
  324. //console.time('nf');
  325. let r = read_compressed_payload(COMPRESSED);
  326. SHIFTED_RANK = new Map(read_sorted_arrays(r).flatMap((v, i) => v.map(x => [x, (i+1) << 24]))); // pre-shifted
  327. EXCLUSIONS = new Set(read_sorted(r));
  328. DECOMP = new Map();
  329. RECOMP = new Map();
  330. for (let [cp, cps] of read_mapped(r)) {
  331. if (!EXCLUSIONS.has(cp) && cps.length == 2) {
  332. let [a, b] = cps;
  333. let bucket = RECOMP.get(a);
  334. if (!bucket) {
  335. bucket = new Map();
  336. RECOMP.set(a, bucket);
  337. }
  338. bucket.set(b, cp);
  339. }
  340. DECOMP.set(cp, cps.reverse()); // stored reversed
  341. }
  342. //console.timeEnd('nf');
  343. // 20230905: 11ms
  344. }
  345. function is_hangul(cp) {
  346. return cp >= S0 && cp < S1;
  347. }
  348. function compose_pair(a, b) {
  349. if (a >= L0 && a < L1 && b >= V0 && b < V1) {
  350. return S0 + (a - L0) * N_COUNT + (b - V0) * T_COUNT;
  351. } else if (is_hangul(a) && b > T0 && b < T1 && (a - S0) % T_COUNT == 0) {
  352. return a + (b - T0);
  353. } else {
  354. let recomp = RECOMP.get(a);
  355. if (recomp) {
  356. recomp = recomp.get(b);
  357. if (recomp) {
  358. return recomp;
  359. }
  360. }
  361. return -1;
  362. }
  363. }
  364. function decomposed(cps) {
  365. if (!SHIFTED_RANK) init$1();
  366. let ret = [];
  367. let buf = [];
  368. let check_order = false;
  369. function add(cp) {
  370. let cc = SHIFTED_RANK.get(cp);
  371. if (cc) {
  372. check_order = true;
  373. cp |= cc;
  374. }
  375. ret.push(cp);
  376. }
  377. for (let cp of cps) {
  378. while (true) {
  379. if (cp < 0x80) {
  380. ret.push(cp);
  381. } else if (is_hangul(cp)) {
  382. let s_index = cp - S0;
  383. let l_index = s_index / N_COUNT | 0;
  384. let v_index = (s_index % N_COUNT) / T_COUNT | 0;
  385. let t_index = s_index % T_COUNT;
  386. add(L0 + l_index);
  387. add(V0 + v_index);
  388. if (t_index > 0) add(T0 + t_index);
  389. } else {
  390. let mapped = DECOMP.get(cp);
  391. if (mapped) {
  392. buf.push(...mapped);
  393. } else {
  394. add(cp);
  395. }
  396. }
  397. if (!buf.length) break;
  398. cp = buf.pop();
  399. }
  400. }
  401. if (check_order && ret.length > 1) {
  402. let prev_cc = unpack_cc(ret[0]);
  403. for (let i = 1; i < ret.length; i++) {
  404. let cc = unpack_cc(ret[i]);
  405. if (cc == 0 || prev_cc <= cc) {
  406. prev_cc = cc;
  407. continue;
  408. }
  409. let j = i-1;
  410. while (true) {
  411. let tmp = ret[j+1];
  412. ret[j+1] = ret[j];
  413. ret[j] = tmp;
  414. if (!j) break;
  415. prev_cc = unpack_cc(ret[--j]);
  416. if (prev_cc <= cc) break;
  417. }
  418. prev_cc = unpack_cc(ret[i]);
  419. }
  420. }
  421. return ret;
  422. }
  423. function composed_from_decomposed(v) {
  424. let ret = [];
  425. let stack = [];
  426. let prev_cp = -1;
  427. let prev_cc = 0;
  428. for (let packed of v) {
  429. let cc = unpack_cc(packed);
  430. let cp = unpack_cp(packed);
  431. if (prev_cp == -1) {
  432. if (cc == 0) {
  433. prev_cp = cp;
  434. } else {
  435. ret.push(cp);
  436. }
  437. } else if (prev_cc > 0 && prev_cc >= cc) {
  438. if (cc == 0) {
  439. ret.push(prev_cp, ...stack);
  440. stack.length = 0;
  441. prev_cp = cp;
  442. } else {
  443. stack.push(cp);
  444. }
  445. prev_cc = cc;
  446. } else {
  447. let composed = compose_pair(prev_cp, cp);
  448. if (composed >= 0) {
  449. prev_cp = composed;
  450. } else if (prev_cc == 0 && cc == 0) {
  451. ret.push(prev_cp);
  452. prev_cp = cp;
  453. } else {
  454. stack.push(cp);
  455. prev_cc = cc;
  456. }
  457. }
  458. }
  459. if (prev_cp >= 0) {
  460. ret.push(prev_cp, ...stack);
  461. }
  462. return ret;
  463. }
  464. // note: cps can be iterable
  465. function nfd(cps) {
  466. return decomposed(cps).map(unpack_cp);
  467. }
  468. function nfc(cps) {
  469. return composed_from_decomposed(decomposed(cps));
  470. }
  471. const HYPHEN = 0x2D;
  472. const STOP = 0x2E;
  473. const STOP_CH = '.';
  474. const FE0F = 0xFE0F;
  475. const UNIQUE_PH = 1;
  476. // 20230913: replace [...v] with Array_from(v) to avoid large spreads
  477. const Array_from = x => Array.from(x); // Array.from.bind(Array);
  478. function group_has_cp(g, cp) {
  479. // 20230913: keep primary and secondary distinct instead of creating valid union
  480. return g.P.has(cp) || g.Q.has(cp);
  481. }
  482. class Emoji extends Array {
  483. get is_emoji() { return true; } // free tagging system
  484. }
  485. let MAPPED, IGNORED, CM, NSM, ESCAPE, NFC_CHECK, GROUPS, WHOLE_VALID, WHOLE_MAP, VALID, EMOJI_LIST, EMOJI_ROOT;
  486. function init() {
  487. if (MAPPED) return;
  488. let r = read_compressed_payload(COMPRESSED$1);
  489. const read_sorted_array = () => read_sorted(r);
  490. const read_sorted_set = () => new Set(read_sorted_array());
  491. const set_add_many = (set, v) => v.forEach(x => set.add(x));
  492. MAPPED = new Map(read_mapped(r));
  493. IGNORED = read_sorted_set(); // ignored characters are not valid, so just read raw codepoints
  494. /*
  495. // direct include from payload is smaller than the decompression code
  496. const FENCED = new Map(read_array_while(() => {
  497. let cp = r();
  498. if (cp) return [cp, read_str(r())];
  499. }));
  500. */
  501. // 20230217: we still need all CM for proper error formatting
  502. // but norm only needs NSM subset that are potentially-valid
  503. CM = read_sorted_array();
  504. NSM = new Set(read_sorted_array().map(i => CM[i]));
  505. CM = new Set(CM);
  506. ESCAPE = read_sorted_set(); // characters that should not be printed
  507. NFC_CHECK = read_sorted_set(); // only needed to illustrate ens_tokenize() transformations
  508. let chunks = read_sorted_arrays(r);
  509. let unrestricted = r();
  510. //const read_chunked = () => new Set(read_sorted_array().flatMap(i => chunks[i]).concat(read_sorted_array()));
  511. const read_chunked = () => {
  512. // 20230921: build set in parts, 2x faster
  513. let set = new Set();
  514. read_sorted_array().forEach(i => set_add_many(set, chunks[i]));
  515. set_add_many(set, read_sorted_array());
  516. return set;
  517. };
  518. GROUPS = read_array_while(i => {
  519. // minifier property mangling seems unsafe
  520. // so these are manually renamed to single chars
  521. let N = read_array_while(r).map(x => x+0x60);
  522. if (N.length) {
  523. let R = i >= unrestricted; // unrestricted then restricted
  524. N[0] -= 32; // capitalize
  525. N = str_from_cps(N);
  526. if (R) N=`Restricted[${N}]`;
  527. let P = read_chunked(); // primary
  528. let Q = read_chunked(); // secondary
  529. let M = !r(); // not-whitelisted, check for NSM
  530. // *** this code currently isn't needed ***
  531. /*
  532. let V = [...P, ...Q].sort((a, b) => a-b); // derive: sorted valid
  533. let M = r()-1; // number of combining mark
  534. if (M < 0) { // whitelisted
  535. M = new Map(read_array_while(() => {
  536. let i = r();
  537. if (i) return [V[i-1], read_array_while(() => {
  538. let v = read_array_while(r);
  539. if (v.length) return v.map(x => x-1);
  540. })];
  541. }));
  542. }*/
  543. return {N, P, Q, M, R};
  544. }
  545. });
  546. // decode compressed wholes
  547. WHOLE_VALID = read_sorted_set();
  548. WHOLE_MAP = new Map();
  549. let wholes = read_sorted_array().concat(Array_from(WHOLE_VALID)).sort((a, b) => a-b); // must be sorted
  550. wholes.forEach((cp, i) => {
  551. let d = r();
  552. let w = wholes[i] = d ? wholes[i-d] : {V: [], M: new Map()};
  553. w.V.push(cp); // add to member set
  554. if (!WHOLE_VALID.has(cp)) {
  555. WHOLE_MAP.set(cp, w); // register with whole map
  556. }
  557. });
  558. // compute confusable-extent complements
  559. // usage: WHOLE_MAP.get(cp).M.get(cp) = complement set
  560. for (let {V, M} of new Set(WHOLE_MAP.values())) {
  561. // connect all groups that have each whole character
  562. let recs = [];
  563. for (let cp of V) {
  564. let gs = GROUPS.filter(g => group_has_cp(g, cp));
  565. let rec = recs.find(({G}) => gs.some(g => G.has(g)));
  566. if (!rec) {
  567. rec = {G: new Set(), V: []};
  568. recs.push(rec);
  569. }
  570. rec.V.push(cp);
  571. set_add_many(rec.G, gs);
  572. }
  573. // per character cache groups which are not a member of the extent
  574. let union = recs.flatMap(x => Array_from(x.G)); // all of the groups used by this whole
  575. for (let {G, V} of recs) {
  576. let complement = new Set(union.filter(g => !G.has(g))); // groups not covered by the extent
  577. for (let cp of V) {
  578. M.set(cp, complement); // this is the same reference
  579. }
  580. }
  581. }
  582. // compute valid set
  583. // 20230924: VALID was union but can be re-used
  584. VALID = new Set(); // exists in 1+ groups
  585. let multi = new Set(); // exists in 2+ groups
  586. const add_to_union = cp => VALID.has(cp) ? multi.add(cp) : VALID.add(cp);
  587. for (let g of GROUPS) {
  588. for (let cp of g.P) add_to_union(cp);
  589. for (let cp of g.Q) add_to_union(cp);
  590. }
  591. // dual purpose WHOLE_MAP: return placeholder if unique non-confusable
  592. for (let cp of VALID) {
  593. if (!WHOLE_MAP.has(cp) && !multi.has(cp)) {
  594. WHOLE_MAP.set(cp, UNIQUE_PH);
  595. }
  596. }
  597. // add all decomposed parts
  598. // see derive: "Valid is Closed (via Brute-force)"
  599. set_add_many(VALID, nfd(VALID));
  600. // decode emoji
  601. // 20230719: emoji are now fully-expanded to avoid quirk logic
  602. EMOJI_LIST = read_trie(r).map(v => Emoji.from(v)).sort(compare_arrays);
  603. EMOJI_ROOT = new Map(); // this has approx 7K nodes (2+ per emoji)
  604. for (let cps of EMOJI_LIST) {
  605. // 20230719: change to *slightly* stricter algorithm which disallows
  606. // insertion of misplaced FE0F in emoji sequences (matching ENSIP-15)
  607. // example: beautified [A B] (eg. flag emoji)
  608. // before: allow: [A FE0F B], error: [A FE0F FE0F B]
  609. // after: error: both
  610. // note: this code now matches ENSNormalize.{cs,java} logic
  611. let prev = [EMOJI_ROOT];
  612. for (let cp of cps) {
  613. let next = prev.map(node => {
  614. let child = node.get(cp);
  615. if (!child) {
  616. // should this be object?
  617. // (most have 1-2 items, few have many)
  618. // 20230719: no, v8 default map is 4?
  619. child = new Map();
  620. node.set(cp, child);
  621. }
  622. return child;
  623. });
  624. if (cp === FE0F) {
  625. prev.push(...next); // less than 20 elements
  626. } else {
  627. prev = next;
  628. }
  629. }
  630. for (let x of prev) {
  631. x.V = cps;
  632. }
  633. }
  634. }
  635. // if escaped: {HEX}
  636. // else: "x" {HEX}
  637. function quoted_cp(cp) {
  638. return (should_escape(cp) ? '' : `${bidi_qq(safe_str_from_cps([cp]))} `) + quote_cp(cp);
  639. }
  640. // 20230211: some messages can be mixed-directional and result in spillover
  641. // use 200E after a quoted string to force the remainder of a string from
  642. // acquring the direction of the quote
  643. // https://www.w3.org/International/questions/qa-bidi-unicode-controls#exceptions
  644. function bidi_qq(s) {
  645. return `"${s}"\u200E`; // strong LTR
  646. }
  647. function check_label_extension(cps) {
  648. if (cps.length >= 4 && cps[2] == HYPHEN && cps[3] == HYPHEN) {
  649. throw new Error(`invalid label extension: "${str_from_cps(cps.slice(0, 4))}"`); // this can only be ascii so cant be bidi
  650. }
  651. }
  652. function check_leading_underscore(cps) {
  653. const UNDERSCORE = 0x5F;
  654. for (let i = cps.lastIndexOf(UNDERSCORE); i > 0; ) {
  655. if (cps[--i] !== UNDERSCORE) {
  656. throw new Error('underscore allowed only at start');
  657. }
  658. }
  659. }
  660. // check that a fenced cp is not leading, trailing, or touching another fenced cp
  661. function check_fenced(cps) {
  662. let cp = cps[0];
  663. let prev = FENCED.get(cp);
  664. if (prev) throw error_placement(`leading ${prev}`);
  665. let n = cps.length;
  666. let last = -1; // prevents trailing from throwing
  667. for (let i = 1; i < n; i++) {
  668. cp = cps[i];
  669. let match = FENCED.get(cp);
  670. if (match) {
  671. // since cps[0] isn't fenced, cps[1] cannot throw
  672. if (last == i) throw error_placement(`${prev} + ${match}`);
  673. last = i + 1;
  674. prev = match;
  675. }
  676. }
  677. if (last == n) throw error_placement(`trailing ${prev}`);
  678. }
  679. // create a safe to print string
  680. // invisibles are escaped
  681. // leading cm uses placeholder
  682. // if cps exceed max, middle truncate with ellipsis
  683. // quoter(cp) => string, eg. 3000 => "{3000}"
  684. // note: in html, you'd call this function then replace [<>&] with entities
  685. function safe_str_from_cps(cps, max = Infinity, quoter = quote_cp) {
  686. //if (Number.isInteger(cps)) cps = [cps];
  687. //if (!Array.isArray(cps)) throw new TypeError(`expected codepoints`);
  688. let buf = [];
  689. if (is_combining_mark(cps[0])) buf.push('◌');
  690. if (cps.length > max) {
  691. max >>= 1;
  692. cps = [...cps.slice(0, max), 0x2026, ...cps.slice(-max)];
  693. }
  694. let prev = 0;
  695. let n = cps.length;
  696. for (let i = 0; i < n; i++) {
  697. let cp = cps[i];
  698. if (should_escape(cp)) {
  699. buf.push(str_from_cps(cps.slice(prev, i)));
  700. buf.push(quoter(cp));
  701. prev = i + 1;
  702. }
  703. }
  704. buf.push(str_from_cps(cps.slice(prev, n)));
  705. return buf.join('');
  706. }
  707. // note: set(s) cannot be exposed because they can be modified
  708. // note: Object.freeze() doesn't work
  709. function is_combining_mark(cp) {
  710. init();
  711. return CM.has(cp);
  712. }
  713. function should_escape(cp) {
  714. init();
  715. return ESCAPE.has(cp);
  716. }
  717. // return all supported emoji as fully-qualified emoji
  718. // ordered by length then lexicographic
  719. function ens_emoji() {
  720. init();
  721. return EMOJI_LIST.map(x => x.slice()); // emoji are exposed so copy
  722. }
  723. function ens_normalize_fragment(frag, decompose) {
  724. init();
  725. let nf = decompose ? nfd : nfc;
  726. return frag.split(STOP_CH).map(label => str_from_cps(tokens_from_str(explode_cp(label), nf, filter_fe0f).flat())).join(STOP_CH);
  727. }
  728. function ens_normalize(name) {
  729. return flatten(split(name, nfc, filter_fe0f));
  730. }
  731. function ens_beautify(name) {
  732. let labels = split(name, nfc, x => x); // emoji not exposed
  733. for (let {type, output, error} of labels) {
  734. if (error) break; // flatten will throw
  735. // replace leading/trailing hyphen
  736. // 20230121: consider beautifing all or leading/trailing hyphen to unicode variant
  737. // not exactly the same in every font, but very similar: "-" vs "‐"
  738. /*
  739. const UNICODE_HYPHEN = 0x2010;
  740. // maybe this should replace all for visual consistancy?
  741. // `node tools/reg-count.js regex ^-\{2,\}` => 592
  742. //for (let i = 0; i < output.length; i++) if (output[i] == 0x2D) output[i] = 0x2010;
  743. if (output[0] == HYPHEN) output[0] = UNICODE_HYPHEN;
  744. let end = output.length-1;
  745. if (output[end] == HYPHEN) output[end] = UNICODE_HYPHEN;
  746. */
  747. // 20230123: WHATWG URL uses "CheckHyphens" false
  748. // https://url.spec.whatwg.org/#idna
  749. // update ethereum symbol
  750. // ξ => Ξ if not greek
  751. if (type !== 'Greek') array_replace(output, 0x3BE, 0x39E);
  752. // 20221213: fixes bidi subdomain issue, but breaks invariant (200E is disallowed)
  753. // could be fixed with special case for: 2D (.) + 200E (LTR)
  754. // https://discuss.ens.domains/t/bidi-label-ordering-spoof/15824
  755. //output.splice(0, 0, 0x200E);
  756. }
  757. return flatten(labels);
  758. }
  759. function array_replace(v, a, b) {
  760. let prev = 0;
  761. while (true) {
  762. let next = v.indexOf(a, prev);
  763. if (next < 0) break;
  764. v[next] = b;
  765. prev = next + 1;
  766. }
  767. }
  768. function ens_split(name, preserve_emoji) {
  769. return split(name, nfc, preserve_emoji ? x => x.slice() : filter_fe0f); // emoji are exposed so copy
  770. }
  771. function split(name, nf, ef) {
  772. if (!name) return []; // 20230719: empty name allowance
  773. init();
  774. let offset = 0;
  775. // https://unicode.org/reports/tr46/#Validity_Criteria
  776. // 4.) "The label must not contain a U+002E ( . ) FULL STOP."
  777. return name.split(STOP_CH).map(label => {
  778. let input = explode_cp(label);
  779. let info = {
  780. input,
  781. offset, // codepoint, not substring!
  782. };
  783. offset += input.length + 1; // + stop
  784. try {
  785. // 1.) "The label must be in Unicode Normalization Form NFC"
  786. let tokens = info.tokens = tokens_from_str(input, nf, ef);
  787. let token_count = tokens.length;
  788. let type;
  789. if (!token_count) { // the label was effectively empty (could of had ignored characters)
  790. //norm = [];
  791. //type = 'None'; // use this instead of next match, "ASCII"
  792. // 20230120: change to strict
  793. // https://discuss.ens.domains/t/ens-name-normalization-2nd/14564/59
  794. throw new Error(`empty label`);
  795. }
  796. let norm = info.output = tokens.flat();
  797. check_leading_underscore(norm);
  798. let emoji = info.emoji = token_count > 1 || tokens[0].is_emoji; // same as: tokens.some(x => x.is_emoji);
  799. if (!emoji && norm.every(cp => cp < 0x80)) { // special case for ascii
  800. // 20230123: matches matches WHATWG, see note 3.3
  801. check_label_extension(norm); // only needed for ascii
  802. // cant have fenced
  803. // cant have cm
  804. // cant have wholes
  805. // see derive: "Fastpath ASCII"
  806. type = 'ASCII';
  807. } else {
  808. let chars = tokens.flatMap(x => x.is_emoji ? [] : x); // all of the nfc tokens concat together
  809. if (!chars.length) { // theres no text, just emoji
  810. type = 'Emoji';
  811. } else {
  812. // 5.) "The label must not begin with a combining mark, that is: General_Category=Mark."
  813. if (CM.has(norm[0])) throw error_placement('leading combining mark');
  814. for (let i = 1; i < token_count; i++) { // we've already checked the first token
  815. let cps = tokens[i];
  816. if (!cps.is_emoji && CM.has(cps[0])) { // every text token has emoji neighbors, eg. EtEEEtEt...
  817. // bidi_qq() not needed since emoji is LTR and cps is a CM
  818. throw error_placement(`emoji + combining mark: "${str_from_cps(tokens[i-1])} + ${safe_str_from_cps([cps[0]])}"`);
  819. }
  820. }
  821. check_fenced(norm);
  822. let unique = Array_from(new Set(chars));
  823. let [g] = determine_group(unique); // take the first match
  824. // see derive: "Matching Groups have Same CM Style"
  825. // alternative: could form a hybrid type: Latin/Japanese/...
  826. check_group(g, chars); // need text in order
  827. check_whole(g, unique); // only need unique text (order would be required for multiple-char confusables)
  828. type = g.N;
  829. // 20230121: consider exposing restricted flag
  830. // it's simpler to just check for 'Restricted'
  831. // or even better: type.endsWith(']')
  832. //if (g.R) info.restricted = true;
  833. }
  834. }
  835. info.type = type;
  836. } catch (err) {
  837. info.error = err; // use full error object
  838. }
  839. return info;
  840. });
  841. }
  842. function check_whole(group, unique) {
  843. let maker;
  844. let shared = [];
  845. for (let cp of unique) {
  846. let whole = WHOLE_MAP.get(cp);
  847. if (whole === UNIQUE_PH) return; // unique, non-confusable
  848. if (whole) {
  849. let set = whole.M.get(cp); // groups which have a character that look-like this character
  850. maker = maker ? maker.filter(g => set.has(g)) : Array_from(set);
  851. if (!maker.length) return; // confusable intersection is empty
  852. } else {
  853. shared.push(cp);
  854. }
  855. }
  856. if (maker) {
  857. // we have 1+ confusable
  858. // check if any of the remaining groups
  859. // contain the shared characters too
  860. for (let g of maker) {
  861. if (shared.every(cp => group_has_cp(g, cp))) {
  862. throw new Error(`whole-script confusable: ${group.N}/${g.N}`);
  863. }
  864. }
  865. }
  866. }
  867. // assumption: unique.size > 0
  868. // returns list of matching groups
  869. function determine_group(unique) {
  870. let groups = GROUPS;
  871. for (let cp of unique) {
  872. // note: we need to dodge CM that are whitelisted
  873. // but that code isn't currently necessary
  874. let gs = groups.filter(g => group_has_cp(g, cp));
  875. if (!gs.length) {
  876. if (!GROUPS.some(g => group_has_cp(g, cp))) {
  877. // the character was composed of valid parts
  878. // but it's NFC form is invalid
  879. // 20230716: change to more exact statement, see: ENSNormalize.{cs,java}
  880. // note: this doesn't have to be a composition
  881. // 20230720: change to full check
  882. throw error_disallowed(cp); // this should be rare
  883. } else {
  884. // there is no group that contains all these characters
  885. // throw using the highest priority group that matched
  886. // https://www.unicode.org/reports/tr39/#mixed_script_confusables
  887. throw error_group_member(groups[0], cp);
  888. }
  889. }
  890. groups = gs;
  891. if (gs.length == 1) break; // there is only one group left
  892. }
  893. // there are at least 1 group(s) with all of these characters
  894. return groups;
  895. }
  896. // throw on first error
  897. function flatten(split) {
  898. return split.map(({input, error, output}) => {
  899. if (error) {
  900. // don't print label again if just a single label
  901. let msg = error.message;
  902. // bidi_qq() only necessary if msg is digits
  903. throw new Error(split.length == 1 ? msg : `Invalid label ${bidi_qq(safe_str_from_cps(input, 63))}: ${msg}`);
  904. }
  905. return str_from_cps(output);
  906. }).join(STOP_CH);
  907. }
  908. function error_disallowed(cp) {
  909. // TODO: add cp to error?
  910. return new Error(`disallowed character: ${quoted_cp(cp)}`);
  911. }
  912. function error_group_member(g, cp) {
  913. let quoted = quoted_cp(cp);
  914. let gg = GROUPS.find(g => g.P.has(cp)); // only check primary
  915. if (gg) {
  916. quoted = `${gg.N} ${quoted}`;
  917. }
  918. return new Error(`illegal mixture: ${g.N} + ${quoted}`);
  919. }
  920. function error_placement(where) {
  921. return new Error(`illegal placement: ${where}`);
  922. }
  923. // assumption: cps.length > 0
  924. // assumption: cps[0] isn't a CM
  925. // assumption: the previous character isn't an emoji
  926. function check_group(g, cps) {
  927. for (let cp of cps) {
  928. if (!group_has_cp(g, cp)) {
  929. // for whitelisted scripts, this will throw illegal mixture on invalid cm, eg. "e{300}{300}"
  930. // at the moment, it's unnecessary to introduce an extra error type
  931. // until there exists a whitelisted multi-character
  932. // eg. if (M < 0 && is_combining_mark(cp)) { ... }
  933. // there are 3 cases:
  934. // 1. illegal cm for wrong group => mixture error
  935. // 2. illegal cm for same group => cm error
  936. // requires set of whitelist cm per group:
  937. // eg. new Set([...g.P, ...g.Q].flatMap(nfc).filter(cp => CM.has(cp)))
  938. // 3. wrong group => mixture error
  939. throw error_group_member(g, cp);
  940. }
  941. }
  942. //if (M >= 0) { // we have a known fixed cm count
  943. if (g.M) { // we need to check for NSM
  944. let decomposed = nfd(cps);
  945. for (let i = 1, e = decomposed.length; i < e; i++) { // see: assumption
  946. // 20230210: bugfix: using cps instead of decomposed h/t Carbon225
  947. /*
  948. if (CM.has(decomposed[i])) {
  949. let j = i + 1;
  950. while (j < e && CM.has(decomposed[j])) j++;
  951. if (j - i > M) {
  952. throw new Error(`too many combining marks: ${g.N} ${bidi_qq(str_from_cps(decomposed.slice(i-1, j)))} (${j-i}/${M})`);
  953. }
  954. i = j;
  955. }
  956. */
  957. // 20230217: switch to NSM counting
  958. // https://www.unicode.org/reports/tr39/#Optional_Detection
  959. if (NSM.has(decomposed[i])) {
  960. let j = i + 1;
  961. for (let cp; j < e && NSM.has(cp = decomposed[j]); j++) {
  962. // a. Forbid sequences of the same nonspacing mark.
  963. for (let k = i; k < j; k++) { // O(n^2) but n < 100
  964. if (decomposed[k] == cp) {
  965. throw new Error(`duplicate non-spacing marks: ${quoted_cp(cp)}`);
  966. }
  967. }
  968. }
  969. // parse to end so we have full nsm count
  970. // b. Forbid sequences of more than 4 nonspacing marks (gc=Mn or gc=Me).
  971. if (j - i > NSM_MAX) {
  972. // note: this slice starts with a base char or spacing-mark cm
  973. throw new Error(`excessive non-spacing marks: ${bidi_qq(safe_str_from_cps(decomposed.slice(i-1, j)))} (${j-i}/${NSM_MAX})`);
  974. }
  975. i = j;
  976. }
  977. }
  978. }
  979. // *** this code currently isn't needed ***
  980. /*
  981. let cm_whitelist = M instanceof Map;
  982. for (let i = 0, e = cps.length; i < e; ) {
  983. let cp = cps[i++];
  984. let seqs = cm_whitelist && M.get(cp);
  985. if (seqs) {
  986. // list of codepoints that can follow
  987. // if this exists, this will always be 1+
  988. let j = i;
  989. while (j < e && CM.has(cps[j])) j++;
  990. let cms = cps.slice(i, j);
  991. let match = seqs.find(seq => !compare_arrays(seq, cms));
  992. if (!match) throw new Error(`disallowed combining mark sequence: "${safe_str_from_cps([cp, ...cms])}"`);
  993. i = j;
  994. } else if (!V.has(cp)) {
  995. // https://www.unicode.org/reports/tr39/#mixed_script_confusables
  996. let quoted = quoted_cp(cp);
  997. for (let cp of cps) {
  998. let u = UNIQUE.get(cp);
  999. if (u && u !== g) {
  1000. // if both scripts are restricted this error is confusing
  1001. // because we don't differentiate RestrictedA from RestrictedB
  1002. if (!u.R) quoted = `${quoted} is ${u.N}`;
  1003. break;
  1004. }
  1005. }
  1006. throw new Error(`disallowed ${g.N} character: ${quoted}`);
  1007. //throw new Error(`disallowed character: ${quoted} (expected ${g.N})`);
  1008. //throw new Error(`${g.N} does not allow: ${quoted}`);
  1009. }
  1010. }
  1011. if (!cm_whitelist) {
  1012. let decomposed = nfd(cps);
  1013. for (let i = 1, e = decomposed.length; i < e; i++) { // we know it can't be cm leading
  1014. if (CM.has(decomposed[i])) {
  1015. let j = i + 1;
  1016. while (j < e && CM.has(decomposed[j])) j++;
  1017. if (j - i > M) {
  1018. throw new Error(`too many combining marks: "${str_from_cps(decomposed.slice(i-1, j))}" (${j-i}/${M})`);
  1019. }
  1020. i = j;
  1021. }
  1022. }
  1023. }
  1024. */
  1025. }
  1026. // given a list of codepoints
  1027. // returns a list of lists, where emoji are a fully-qualified (as Array subclass)
  1028. // eg. explode_cp("abc💩d") => [[61, 62, 63], Emoji[1F4A9, FE0F], [64]]
  1029. // 20230818: rename for 'process' name collision h/t Javarome
  1030. // https://github.com/adraffy/ens-normalize.js/issues/23
  1031. function tokens_from_str(input, nf, ef) {
  1032. let ret = [];
  1033. let chars = [];
  1034. input = input.slice().reverse(); // flip so we can pop
  1035. while (input.length) {
  1036. let emoji = consume_emoji_reversed(input);
  1037. if (emoji) {
  1038. if (chars.length) {
  1039. ret.push(nf(chars));
  1040. chars = [];
  1041. }
  1042. ret.push(ef(emoji));
  1043. } else {
  1044. let cp = input.pop();
  1045. if (VALID.has(cp)) {
  1046. chars.push(cp);
  1047. } else {
  1048. let cps = MAPPED.get(cp);
  1049. if (cps) {
  1050. chars.push(...cps); // less than 10 elements
  1051. } else if (!IGNORED.has(cp)) {
  1052. // 20230912: unicode 15.1 changed the order of processing such that
  1053. // disallowed parts are only rejected after NFC
  1054. // https://unicode.org/reports/tr46/#Validity_Criteria
  1055. // this doesn't impact normalization as of today
  1056. // technically, this error can be removed as the group logic will apply similar logic
  1057. // however the error type might be less clear
  1058. throw error_disallowed(cp);
  1059. }
  1060. }
  1061. }
  1062. }
  1063. if (chars.length) {
  1064. ret.push(nf(chars));
  1065. }
  1066. return ret;
  1067. }
  1068. function filter_fe0f(cps) {
  1069. return cps.filter(cp => cp != FE0F);
  1070. }
  1071. // given array of codepoints
  1072. // returns the longest valid emoji sequence (or undefined if no match)
  1073. // *MUTATES* the supplied array
  1074. // disallows interleaved ignored characters
  1075. // fills (optional) eaten array with matched codepoints
  1076. function consume_emoji_reversed(cps, eaten) {
  1077. let node = EMOJI_ROOT;
  1078. let emoji;
  1079. let pos = cps.length;
  1080. while (pos) {
  1081. node = node.get(cps[--pos]);
  1082. if (!node) break;
  1083. let {V} = node;
  1084. if (V) { // this is a valid emoji (so far)
  1085. emoji = V;
  1086. if (eaten) eaten.push(...cps.slice(pos).reverse()); // (optional) copy input, used for ens_tokenize()
  1087. cps.length = pos; // truncate
  1088. }
  1089. }
  1090. return emoji;
  1091. }
  1092. // ************************************************************
  1093. // tokenizer
  1094. const TY_VALID = 'valid';
  1095. const TY_MAPPED = 'mapped';
  1096. const TY_IGNORED = 'ignored';
  1097. const TY_DISALLOWED = 'disallowed';
  1098. const TY_EMOJI = 'emoji';
  1099. const TY_NFC = 'nfc';
  1100. const TY_STOP = 'stop';
  1101. function ens_tokenize(name, {
  1102. nf = true, // collapse unnormalized runs into a single token
  1103. } = {}) {
  1104. init();
  1105. let input = explode_cp(name).reverse();
  1106. let eaten = [];
  1107. let tokens = [];
  1108. while (input.length) {
  1109. let emoji = consume_emoji_reversed(input, eaten);
  1110. if (emoji) {
  1111. tokens.push({
  1112. type: TY_EMOJI,
  1113. emoji: emoji.slice(), // copy emoji
  1114. input: eaten,
  1115. cps: filter_fe0f(emoji)
  1116. });
  1117. eaten = []; // reset buffer
  1118. } else {
  1119. let cp = input.pop();
  1120. if (cp == STOP) {
  1121. tokens.push({type: TY_STOP, cp});
  1122. } else if (VALID.has(cp)) {
  1123. tokens.push({type: TY_VALID, cps: [cp]});
  1124. } else if (IGNORED.has(cp)) {
  1125. tokens.push({type: TY_IGNORED, cp});
  1126. } else {
  1127. let cps = MAPPED.get(cp);
  1128. if (cps) {
  1129. tokens.push({type: TY_MAPPED, cp, cps: cps.slice()});
  1130. } else {
  1131. tokens.push({type: TY_DISALLOWED, cp});
  1132. }
  1133. }
  1134. }
  1135. }
  1136. if (nf) {
  1137. for (let i = 0, start = -1; i < tokens.length; i++) {
  1138. let token = tokens[i];
  1139. if (is_valid_or_mapped(token.type)) {
  1140. if (requires_check(token.cps)) { // normalization might be needed
  1141. let end = i + 1;
  1142. for (let pos = end; pos < tokens.length; pos++) { // find adjacent text
  1143. let {type, cps} = tokens[pos];
  1144. if (is_valid_or_mapped(type)) {
  1145. if (!requires_check(cps)) break;
  1146. end = pos + 1;
  1147. } else if (type !== TY_IGNORED) { // || type !== TY_DISALLOWED) {
  1148. break;
  1149. }
  1150. }
  1151. if (start < 0) start = i;
  1152. let slice = tokens.slice(start, end);
  1153. let cps0 = slice.flatMap(x => is_valid_or_mapped(x.type) ? x.cps : []); // strip junk tokens
  1154. let cps = nfc(cps0);
  1155. if (compare_arrays(cps, cps0)) { // bundle into an nfc token
  1156. tokens.splice(start, end - start, {
  1157. type: TY_NFC,
  1158. input: cps0, // there are 3 states: tokens0 ==(process)=> input ==(nfc)=> tokens/cps
  1159. cps,
  1160. tokens0: collapse_valid_tokens(slice),
  1161. tokens: ens_tokenize(str_from_cps(cps), {nf: false})
  1162. });
  1163. i = start;
  1164. } else {
  1165. i = end - 1; // skip to end of slice
  1166. }
  1167. start = -1; // reset
  1168. } else {
  1169. start = i; // remember last
  1170. }
  1171. } else if (token.type !== TY_IGNORED) { // 20221024: is this correct?
  1172. start = -1; // reset
  1173. }
  1174. }
  1175. }
  1176. return collapse_valid_tokens(tokens);
  1177. }
  1178. function is_valid_or_mapped(type) {
  1179. return type == TY_VALID || type == TY_MAPPED;
  1180. }
  1181. function requires_check(cps) {
  1182. return cps.some(cp => NFC_CHECK.has(cp));
  1183. }
  1184. function collapse_valid_tokens(tokens) {
  1185. for (let i = 0; i < tokens.length; i++) {
  1186. if (tokens[i].type == TY_VALID) {
  1187. let j = i + 1;
  1188. while (j < tokens.length && tokens[j].type == TY_VALID) j++;
  1189. tokens.splice(i, j - i, {type: TY_VALID, cps: tokens.slice(i, j).flatMap(x => x.cps)});
  1190. }
  1191. }
  1192. return tokens;
  1193. }
  1194. function hex_seq(cps) {
  1195. return cps.map(hex_cp).join(' ');
  1196. }
  1197. function create_arrow_span() {
  1198. let span = document.createElement('span');
  1199. span.classList.add('arrow');
  1200. span.innerHTML = '➔'; // '→';
  1201. return span;
  1202. }
  1203. function span_from_cp(cp, in_emoji) {
  1204. let span = document.createElement('span');
  1205. if (cp == 0x200D) {
  1206. span.classList.add('mod', 'zwj');
  1207. span.innerText = 'ZWJ';
  1208. } else if (cp == 0x200C) {
  1209. span.classList.add('mod', 'zwj');
  1210. span.innerText = 'ZWNJ';
  1211. } else if (cp == 0xFE0F) {
  1212. span.classList.add('mod', 'dropped', 'style');
  1213. span.innerText = 'FE0F';
  1214. } else if (cp == 0x20E3) {
  1215. span.classList.add('mod', 'keycap');
  1216. span.innerText = 'Keycap';
  1217. } else if (cp >= 0xE0021 && cp <= 0xE007E) { // printable ascii tag
  1218. span.classList.add('mod', 'tag');
  1219. span.innerText = String.fromCodePoint(cp - 0xE0000);
  1220. } else if (cp == 0xE007F) { // tag end
  1221. span.classList.add('mod', 'tag', 'end');
  1222. span.innerText = '⌫'; // 🏷️
  1223. } else if (!in_emoji && should_escape(cp)) {
  1224. span.classList.add('code');
  1225. span.innerText = hex_cp(cp);
  1226. } else {
  1227. span.innerText = safe_str_from_cps([cp]);
  1228. }
  1229. return span;
  1230. }
  1231. // idea
  1232. //export function dom_from_token(token) {
  1233. function format_tooltip(obj, extra) {
  1234. let lines = Object.entries(obj).map(([k, v]) => `${k}: ${v}`);
  1235. if (Array.isArray(extra)) lines.push(...extra);
  1236. return lines.join('\n');
  1237. }
  1238. function isolated_safe(cps) {
  1239. return cps.map(cp => safe_str_from_cps([cp])).join('\u{200B}')
  1240. }
  1241. // TODO: these options are shit, fix this
  1242. function dom_from_tokens(tokens, {
  1243. before = false,
  1244. tld_class = true,
  1245. components = false,
  1246. emoji_url = 'https://emojipedia.org/%s',
  1247. extra = () => {},
  1248. } = {}) {
  1249. let div = document.createElement('div');
  1250. div.classList.add('tokens');
  1251. /*
  1252. if (before) {
  1253. // dont use normalized form unless its simple
  1254. tokens = tokens.flatMap(token => {
  1255. if (token.type === 'nfc' && !token.tokens.every(t => t.type == 'valid')) {
  1256. return token.tokens;
  1257. } else {
  1258. return token;
  1259. }
  1260. });
  1261. }
  1262. */
  1263. div.append(...tokens.map((token, i) => {
  1264. let el;
  1265. switch (token.type) {
  1266. case 'emoji': {
  1267. el = document.createElement(emoji_url ? 'a' : 'span');
  1268. if (emoji_url) el.href = emoji_url.replace('%s', String.fromCodePoint(...token.emoji));
  1269. let cps = before ? token.input : token.cps;
  1270. if (components) {
  1271. el.append(...cps.map(cp => span_from_cp(cp, true)));
  1272. } else {
  1273. el.innerText = String.fromCodePoint(...token.emoji); // use fully-qualified form
  1274. }
  1275. el.title = format_tooltip({
  1276. Type: 'Emoji',
  1277. Hex: hex_seq(cps),
  1278. Beautified: hex_seq(token.emoji),
  1279. }, extra(token.type, cps));
  1280. break;
  1281. }
  1282. case 'nfc': {
  1283. el = document.createElement('div');
  1284. // get the cps from the original tokens
  1285. let cps0 = token.tokens0.flatMap(t => t.type === 'valid' ? t.cps : t.cp); // this can only be mapped/ignored/valid
  1286. // break every valid token into individual characters
  1287. let lhs = dom_from_tokens(token.tokens0.flatMap(t => t.type === 'valid' ? t.cps.map(cp => ({type: 'valid', cps: [cp]})) : t), {components, before, emoji_url, extra});
  1288. lhs.title = format_tooltip({
  1289. Type: 'NFC (Unnormalized)',
  1290. Hex: hex_seq(cps0),
  1291. }, extra(token.type, cps0));
  1292. el.append(lhs);
  1293. if (!before) {
  1294. let rhs = dom_from_tokens(token.tokens, {components, emoji_url, extra});
  1295. rhs.title = format_tooltip({
  1296. Type: 'NFC (Normalized)',
  1297. Hex: hex_seq(token.cps),
  1298. }, extra(token.type, token.cps));
  1299. el.append(create_arrow_span(), rhs);
  1300. }
  1301. break;
  1302. }
  1303. case 'valid': {
  1304. el = document.createElement('span');
  1305. let form = safe_str_from_cps(token.cps);
  1306. if (tld_class && (tokens.length == 1 || (i === tokens.length-1 && tokens[i-1].type === 'stop')) && /[a-z]/.test(form)) {
  1307. // theres just 1 token/or we're the last token with a stop before us
  1308. //el.classList.add(form);
  1309. // 20230909: this triggered for stupid things
  1310. el.dataset.tld = form;
  1311. }
  1312. el.innerText = form;
  1313. el.title = format_tooltip({
  1314. Type: 'Valid',
  1315. Hex: hex_seq(token.cps),
  1316. }, extra(token.type, token.cps));
  1317. break;
  1318. }
  1319. case 'mapped': {
  1320. el = document.createElement('div');
  1321. let span_src = document.createElement('span');
  1322. span_src.classList.add('before');
  1323. span_src.innerText = safe_str_from_cps([token.cp]); // isolate ? isolated_safe([token.cp]) :
  1324. span_src.title = format_tooltip({
  1325. Type: 'Mapped (Match)',
  1326. Hex: hex_cp(token.cp),
  1327. }, extra(token.type, [token.cp]));
  1328. el.append(span_src);
  1329. if (!before) {
  1330. let span_dst = document.createElement('span');
  1331. span_dst.innerText = isolated_safe(token.cps); // safe_str_from_cps(token.cps);
  1332. span_dst.title = format_tooltip({
  1333. Type: 'Mapped (Replacement)',
  1334. Hex: hex_seq(token.cps),
  1335. }, extra(token.type, token.cps));
  1336. el.append(create_arrow_span(), span_dst);
  1337. }
  1338. break;
  1339. }
  1340. case 'stop':
  1341. case 'ignored':
  1342. case 'disallowed': {
  1343. el = span_from_cp(token.cp);
  1344. el.title = format_tooltip({
  1345. Type: token.type,
  1346. Hex: hex_cp(token.cp),
  1347. }, extra(token.type, [token.cp]));
  1348. break;
  1349. }
  1350. default: throw new TypeError(`unknown token type: ${token.type}`);
  1351. }
  1352. el.classList.add(token.type);
  1353. return el;
  1354. }));
  1355. return div;
  1356. }
  1357. function use_default_style() {
  1358. let style = document.createElement('style');
  1359. style.innerText = `
  1360. .tokens {
  1361. display: flex;
  1362. flex-wrap: wrap;
  1363. gap: 2px;
  1364. }
  1365. .tokens > * {
  1366. padding: 2px 4px;
  1367. display: flex;
  1368. align-items: center;
  1369. gap: 4px;
  1370. border-radius: 5px;
  1371. overflow: hidden;
  1372. }
  1373. .tokens a {
  1374. text-decoration: none;
  1375. }
  1376. .tokens a:hover {
  1377. border-color: #00f;
  1378. }
  1379. .tokens .valid {
  1380. background: #cfc;
  1381. border: 2px solid #0a0;
  1382. line-break: anywhere;
  1383. }
  1384. .tokens [data-tld="eth"].valid {
  1385. color: #fff;
  1386. background: #58f;
  1387. border-color: #58f;
  1388. }
  1389. .tokens [data-tld="art"].valid {
  1390. color: #fff;
  1391. background: #333;
  1392. border-color: #333;
  1393. }
  1394. .tokens [data-tld="box"].valid {
  1395. color: #fff;
  1396. background: #666;
  1397. border-color: #666;
  1398. }
  1399. .tokens [data-tld="com"].valid,
  1400. .tokens [data-tld="net"].valid,
  1401. .tokens [data-tld="org"].valid,
  1402. .tokens [data-tld="io"].valid,
  1403. .tokens [data-tld="cash"].valid,
  1404. .tokens [data-tld="xyz"].valid {
  1405. color: #fff;
  1406. background: #0a0;
  1407. border-color: #0a0;
  1408. }
  1409. .tokens .ignored {
  1410. color: #fff;
  1411. background: #aaa;
  1412. font-size: 75%;
  1413. font-family: monospace;
  1414. }
  1415. .tokens .disallowed {
  1416. background: #c00;
  1417. min-width: 5px;
  1418. min-height: 1em;
  1419. border-radius: 5px;
  1420. color: #fff;
  1421. }
  1422. .tokens .disallowed.code {
  1423. font-size: 75%;
  1424. background: #800;
  1425. }
  1426. .tokens .disallowed.mod {
  1427. border: 2px solid #800;
  1428. font-size: 80%;
  1429. }
  1430. .tokens .disallowed.mod.tag {
  1431. background: #f00;
  1432. color: #000;
  1433. }
  1434. .tokens .mapped {
  1435. display: flex;
  1436. border: 2px solid #66f;
  1437. background: #ccf;
  1438. }
  1439. .tokens .mapped span:first-child {
  1440. margin-bottom: -4px;
  1441. border-bottom: 4px solid #000;
  1442. text-align: center;
  1443. min-width: 0.5rem;
  1444. }
  1445. .tokens .stop {
  1446. font-weight: bold;
  1447. background: linear-gradient(#fff, #ff0);
  1448. padding-bottom: 0;
  1449. border: 1px solid #ccc;
  1450. }
  1451. .tokens .emoji {
  1452. border: 2px solid #0aa;
  1453. background: #cff;
  1454. color: #000;
  1455. }
  1456. .tokens .mod {
  1457. color: #fff;
  1458. }
  1459. .tokens * .mod {
  1460. font-size: 70%;
  1461. padding: 2px;
  1462. border-radius: 3px;
  1463. }
  1464. .tokens .emoji .mod {
  1465. background: #333;
  1466. }
  1467. .tokens .emoji .mod.zwj {
  1468. background: #0aa;
  1469. }
  1470. .tokens .emoji .mod.tag {
  1471. background: #0aa;
  1472. }
  1473. .tokens .emoji .mod.tag.end {
  1474. background: #066;
  1475. }
  1476. .tokens .emoji .mod.dropped {
  1477. background: #aaa;
  1478. }
  1479. .tokens .arrow {
  1480. color: rgba(0, 0, 0, 0.35);
  1481. user-select: none;
  1482. margin: 0 -2px;
  1483. }
  1484. .tokens .code {
  1485. font-family: monospace;
  1486. }
  1487. .tokens .nfc {
  1488. display: flex;
  1489. border: 2px solid #c80;
  1490. background: #fd8;
  1491. border-radius: 5px;
  1492. padding: 2px;
  1493. }`;
  1494. document.body.append(style);
  1495. }
  1496. // see: https://github.com/adraffy/ens-normalize.js#security
  1497. const derived = "2023-09-06T06:00:29.074Z";
  1498. const unicode = "15.1.0 (2023-09-06T02:58:19.261Z)";
  1499. const cldr = "43.1 (2023-09-03T21:58:22.687Z)";
  1500. const base64_ens_hash = "0565ed049b9cf1614bb9e11ba7d8ac6a6fb96c893253d890f7e2b2884b9ded32";
  1501. const base64_nf_hash = "a974b6f8541fc29d919bc85118af0a44015851fab5343f8679cb31be2bdb209e";
  1502. const spec_hash = "1f6d3bdb7a724fe3b91f6d73ab14defcb719e0f4ab79022089c940e7e9c56b9c";
  1503. const built = "2023-09-25T01:01:55.148Z";
  1504. const version = "1.10.1";
  1505. var includeVersions = /*#__PURE__*/Object.freeze({
  1506. __proto__: null,
  1507. base64_ens_hash: base64_ens_hash,
  1508. base64_nf_hash: base64_nf_hash,
  1509. built: built,
  1510. cldr: cldr,
  1511. derived: derived,
  1512. spec_hash: spec_hash,
  1513. unicode: unicode,
  1514. version: version
  1515. });
  1516. export { compare_arrays, dom_from_tokens, ens_beautify, ens_emoji, ens_normalize, ens_normalize_fragment, ens_split, ens_tokenize, explode_cp, hex_cp, is_combining_mark, nfc, nfd, quote_cp, random_choice, random_sample, run_tests, safe_str_from_cps, should_escape, str_from_cps, use_default_style, includeVersions as versions };